1 /*****************************************************************************
2 * auhal.c: AUHAL and Coreaudio output plugin
3 *****************************************************************************
4 * Copyright (C) 2005 the VideoLAN team
7 * Authors: Derk-Jan Hartman <hartman at videolan dot org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
34 #include "aout_internal.h"
36 #include <CoreAudio/CoreAudio.h>
37 #include <AudioUnit/AudioUnitProperties.h>
38 #include <AudioUnit/AudioUnitParameters.h>
39 #include <AudioUnit/AudioOutputUnit.h>
40 #include <AudioToolbox/AudioFormat.h>
42 #define STREAM_FORMAT_MSG( pre, sfm ) \
43 pre "[%ld][%4.4s][%ld][%ld][%ld][%ld][%ld][%ld]", \
44 (UInt32)sfm.mSampleRate, (char *)&sfm.mFormatID, \
45 sfm.mFormatFlags, sfm.mBytesPerPacket, \
46 sfm.mFramesPerPacket, sfm.mBytesPerFrame, \
47 sfm.mChannelsPerFrame, sfm.mBitsPerChannel
49 #define STREAM_FORMAT_MSG_FULL( pre, sfm ) \
50 pre ":\nsamplerate: [%ld]\nFormatID: [%4.4s]\nFormatFlags: [%ld]\nBypesPerPacket: [%ld]\nFramesPerPacket: [%ld]\nBytesPerFrame: [%ld]\nChannelsPerFrame: [%ld]\nBitsPerChannel[%ld]", \
51 (UInt32)sfm.mSampleRate, (char *)&sfm.mFormatID, \
52 sfm.mFormatFlags, sfm.mBytesPerPacket, \
53 sfm.mFramesPerPacket, sfm.mBytesPerFrame, \
54 sfm.mChannelsPerFrame, sfm.mBitsPerChannel
56 #define BUFSIZE 0xffffff
57 #define AOUT_VAR_SPDIF_FLAG 0xf00000
61 * - clean up the debug info
63 * - be better at changing stream setup or devices setup changes while playing.
67 /*****************************************************************************
68 * aout_sys_t: private audio output method descriptor
69 *****************************************************************************
70 * This structure is part of the audio output thread descriptor.
71 * It describes the CoreAudio specific properties of an output thread.
72 *****************************************************************************/
75 AudioDeviceID i_default_dev; /* Keeps DeviceID of defaultOutputDevice */
76 AudioDeviceID i_selected_dev; /* Keeps DeviceID of the selected device */
77 UInt32 i_devices; /* Number of CoreAudio Devices */
78 vlc_bool_t b_supports_digital;/* Does the currently selected device support digital mode? */
79 vlc_bool_t b_digital; /* Are we running in digital mode? */
80 mtime_t clock_diff; /* Difference between VLC clock and Device clock */
83 Component au_component; /* The Audiocomponent we use */
84 AudioUnit au_unit; /* The AudioUnit we use */
85 uint8_t p_remainder_buffer[BUFSIZE];
86 uint32_t i_read_bytes;
87 uint32_t i_total_bytes;
89 /* CoreAudio SPDIF mode specific */
90 pid_t i_hog_pid; /* The keep the pid of our hog status */
91 AudioStreamID i_stream_id; /* The StreamID that has a cac3 streamformat */
92 int i_stream_index; /* The index of i_stream_id in an AudioBufferList */
93 AudioStreamBasicDescription stream_format; /* The format we changed the stream to */
94 AudioStreamBasicDescription sfmt_revert; /* The original format of the stream */
95 vlc_bool_t b_revert; /* Wether we need to revert the stream format */
96 vlc_bool_t b_changed_mixing;/* Wether we need to set the mixing mode back */
99 /*****************************************************************************
101 *****************************************************************************/
102 static int Open ( vlc_object_t * );
103 static int OpenAnalog ( aout_instance_t * );
104 static int OpenSPDIF ( aout_instance_t * );
105 static void Close ( vlc_object_t * );
107 static void Play ( aout_instance_t * );
108 static void Probe ( aout_instance_t * );
110 static int AudioDeviceHasOutput ( AudioDeviceID );
111 static int AudioDeviceSupportsDigital( aout_instance_t *, AudioDeviceID );
112 static int AudioStreamSupportsDigital( aout_instance_t *, AudioStreamID );
113 static int AudioStreamChangeFormat ( aout_instance_t *, AudioStreamID, AudioStreamBasicDescription );
115 static OSStatus RenderCallbackAnalog ( vlc_object_t *, AudioUnitRenderActionFlags *, const AudioTimeStamp *,
116 unsigned int, unsigned int, AudioBufferList *);
117 static OSStatus RenderCallbackSPDIF ( AudioDeviceID, const AudioTimeStamp *, const void *, const AudioTimeStamp *,
118 AudioBufferList *, const AudioTimeStamp *, void * );
119 static OSStatus HardwareListener ( AudioHardwarePropertyID, void *);
120 static OSStatus StreamListener ( AudioStreamID, UInt32,
121 AudioDevicePropertyID, void * );
122 static int AudioDeviceCallback ( vlc_object_t *, const char *,
123 vlc_value_t, vlc_value_t, void * );
126 /*****************************************************************************
128 *****************************************************************************/
129 #define ADEV_TEXT N_("Audio Device")
130 #define ADEV_LONGTEXT N_("Choose a number corresponding to the number of an " \
131 "audio device, as listed in your 'Audio Device' menu. This device will " \
132 "then be used by default for audio playback.")
135 set_shortname( "auhal" );
136 set_description( _("HAL AudioUnit output") );
137 set_capability( "audio output", 101 );
138 set_category( CAT_AUDIO );
139 set_subcategory( SUBCAT_AUDIO_AOUT );
140 set_callbacks( Open, Close );
141 add_integer( "macosx-audio-device", 0, NULL, ADEV_TEXT, ADEV_LONGTEXT, VLC_FALSE );
144 /*****************************************************************************
145 * Open: open macosx audio output
146 *****************************************************************************/
147 static int Open( vlc_object_t * p_this )
149 OSStatus err = noErr;
150 UInt32 i_param_size = 0;
151 struct aout_sys_t *p_sys = NULL;
152 vlc_bool_t b_alive = VLC_FALSE;
154 aout_instance_t *p_aout = (aout_instance_t *)p_this;
156 /* Allocate structure */
157 p_aout->output.p_sys = malloc( sizeof( aout_sys_t ) );
158 if( p_aout->output.p_sys == NULL )
160 msg_Err( p_aout, "out of memory" );
161 return( VLC_ENOMEM );
164 p_sys = p_aout->output.p_sys;
165 p_sys->i_default_dev = 0;
166 p_sys->i_selected_dev = 0;
167 p_sys->i_devices = 0;
168 p_sys->b_supports_digital = VLC_FALSE;
169 p_sys->b_digital = VLC_FALSE;
170 p_sys->au_component = NULL;
171 p_sys->au_unit = NULL;
172 p_sys->clock_diff = (mtime_t) 0;
173 p_sys->i_read_bytes = 0;
174 p_sys->i_total_bytes = 0;
175 p_sys->i_hog_pid = -1;
176 p_sys->i_stream_id = 0;
177 p_sys->i_stream_index = -1;
178 p_sys->b_revert = VLC_FALSE;
179 p_sys->b_changed_mixing = VLC_FALSE;
180 memset( p_sys->p_remainder_buffer, 0, sizeof(uint8_t) * BUFSIZE );
182 p_aout->output.pf_play = Play;
184 aout_FormatPrint( p_aout, "VLC is looking for:", (audio_sample_format_t *)&p_aout->output.output );
186 /* Persistent device variable */
187 if( var_Type( p_aout->p_vlc, "macosx-audio-device" ) == 0 )
189 var_Create( p_aout->p_vlc, "macosx-audio-device", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
192 /* Build a list of devices */
193 if( var_Type( p_aout, "audio-device" ) == 0 )
198 /* What device do we want? */
199 if( var_Get( p_aout, "audio-device", &val ) < 0 )
201 msg_Err( p_aout, "audio-device var does not exist. device probe failed." );
205 p_sys->i_selected_dev = val.i_int & ~AOUT_VAR_SPDIF_FLAG; /* remove SPDIF flag to get the true DeviceID */
206 p_sys->b_supports_digital = ( val.i_int & AOUT_VAR_SPDIF_FLAG ) ? VLC_TRUE : VLC_FALSE;
208 /* Check if the desired device is alive and usable */
209 /* TODO: add a callback to the device to alert us if the device dies */
210 i_param_size = sizeof( b_alive );
211 err = AudioDeviceGetProperty( p_sys->i_selected_dev, 0, FALSE,
212 kAudioDevicePropertyDeviceIsAlive,
213 &i_param_size, &b_alive );
217 msg_Err( p_aout, "could not check whether device is alive: %4.4s", (char *)&err );
221 if( b_alive == VLC_FALSE )
223 msg_Warn( p_aout, "selected audio device is not alive, switching to default device" );
224 p_sys->i_selected_dev = p_sys->i_default_dev;
227 i_param_size = sizeof( p_sys->i_hog_pid );
228 err = AudioDeviceGetProperty( p_sys->i_selected_dev, 0, FALSE,
229 kAudioDevicePropertyHogMode,
230 &i_param_size, &p_sys->i_hog_pid );
234 /* This is not a fatal error. Some drivers simply don't support this property */
235 msg_Warn( p_aout, "could not check whether device is hogged: %4.4s",
237 p_sys->i_hog_pid = -1;
240 if( p_sys->i_hog_pid != -1 && p_sys->i_hog_pid != getpid() )
242 msg_Err( p_aout, "Selected audio device is exclusively in use by another program." );
246 /* Check for Digital mode or Analog output mode */
247 if( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) && p_sys->b_supports_digital )
249 if( OpenSPDIF( p_aout ) )
254 if( OpenAnalog( p_aout ) )
259 /* If we reach this, this aout has failed */
260 var_Destroy( p_aout, "audio-device" );
261 if( p_sys ) free( p_sys );
265 /*****************************************************************************
266 * Open: open and setup a HAL AudioUnit to do analog (multichannel) audio output
267 *****************************************************************************/
268 static int OpenAnalog( aout_instance_t *p_aout )
270 struct aout_sys_t *p_sys = p_aout->output.p_sys;
271 OSStatus err = noErr;
272 UInt32 i_param_size = 0, i = 0;
274 ComponentDescription desc;
275 AudioStreamBasicDescription DeviceFormat;
276 AudioChannelLayout *layout;
277 AudioChannelLayout new_layout;
278 AURenderCallbackStruct input;
280 /* Lets go find our Component */
281 desc.componentType = kAudioUnitType_Output;
282 desc.componentSubType = kAudioUnitSubType_HALOutput;
283 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
284 desc.componentFlags = 0;
285 desc.componentFlagsMask = 0;
287 p_sys->au_component = FindNextComponent( NULL, &desc );
288 if( p_sys->au_component == NULL )
290 msg_Warn( p_aout, "we cannot find our HAL component" );
294 err = OpenAComponent( p_sys->au_component, &p_sys->au_unit );
297 msg_Warn( p_aout, "we cannot open our HAL component" );
301 /* Set the device we will use for this output unit */
302 err = AudioUnitSetProperty( p_sys->au_unit,
303 kAudioOutputUnitProperty_CurrentDevice,
304 kAudioUnitScope_Global,
306 &p_sys->i_selected_dev,
307 sizeof( AudioDeviceID ));
311 msg_Warn( p_aout, "we cannot select the audio device" );
315 /* Get the current format */
316 i_param_size = sizeof(AudioStreamBasicDescription);
318 err = AudioUnitGetProperty( p_sys->au_unit,
319 kAudioUnitProperty_StreamFormat,
320 kAudioUnitScope_Input,
325 if( err != noErr ) return VLC_FALSE;
326 else msg_Dbg( p_aout, STREAM_FORMAT_MSG( "current format is: ", DeviceFormat ) );
328 /* Get the channel layout of the device side of the unit (vlc -> unit -> device) */
329 err = AudioUnitGetPropertyInfo( p_sys->au_unit,
330 kAudioDevicePropertyPreferredChannelLayout,
331 kAudioUnitScope_Output,
338 layout = (AudioChannelLayout *)malloc( i_param_size);
340 verify_noerr( AudioUnitGetProperty( p_sys->au_unit,
341 kAudioDevicePropertyPreferredChannelLayout,
342 kAudioUnitScope_Output,
347 /* We need to "fill out" the ChannelLayout, because there are multiple ways that it can be set */
348 if( layout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap)
350 /* bitmap defined channellayout */
351 verify_noerr( AudioFormatGetProperty( kAudioFormatProperty_ChannelLayoutForBitmap,
352 sizeof( UInt32), &layout->mChannelBitmap,
356 else if( layout->mChannelLayoutTag != kAudioChannelLayoutTag_UseChannelDescriptions )
358 /* layouttags defined channellayout */
359 verify_noerr( AudioFormatGetProperty( kAudioFormatProperty_ChannelLayoutForTag,
360 sizeof( AudioChannelLayoutTag ), &layout->mChannelLayoutTag,
365 msg_Dbg( p_aout, "layout of AUHAL has %d channels" , (int)layout->mNumberChannelDescriptions );
367 /* Initialize the VLC core channel count */
368 p_aout->output.output.i_physical_channels = 0;
369 i_original = p_aout->output.output.i_original_channels & AOUT_CHAN_PHYSMASK;
371 if( i_original == AOUT_CHAN_CENTER || layout->mNumberChannelDescriptions < 2 )
373 /* We only need Mono or cannot output more than 1 channel */
374 p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
376 else if( i_original == (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT) || layout->mNumberChannelDescriptions < 3 )
378 /* We only need Stereo or cannot output more than 2 channels */
379 p_aout->output.output.i_physical_channels = AOUT_CHAN_RIGHT | AOUT_CHAN_LEFT;
383 /* We want more than stereo and we can do that */
384 for( i = 0; i < layout->mNumberChannelDescriptions; i++ )
386 msg_Dbg( p_aout, "this is channel: %d", (int)layout->mChannelDescriptions[i].mChannelLabel );
388 switch( layout->mChannelDescriptions[i].mChannelLabel )
390 case kAudioChannelLabel_Left:
391 p_aout->output.output.i_physical_channels |= AOUT_CHAN_LEFT;
393 case kAudioChannelLabel_Right:
394 p_aout->output.output.i_physical_channels |= AOUT_CHAN_RIGHT;
396 case kAudioChannelLabel_Center:
397 p_aout->output.output.i_physical_channels |= AOUT_CHAN_CENTER;
399 case kAudioChannelLabel_LFEScreen:
400 p_aout->output.output.i_physical_channels |= AOUT_CHAN_LFE;
402 case kAudioChannelLabel_LeftSurround:
403 p_aout->output.output.i_physical_channels |= AOUT_CHAN_REARLEFT;
405 case kAudioChannelLabel_RightSurround:
406 p_aout->output.output.i_physical_channels |= AOUT_CHAN_REARRIGHT;
408 case kAudioChannelLabel_RearSurroundLeft:
409 p_aout->output.output.i_physical_channels |= AOUT_CHAN_MIDDLELEFT;
411 case kAudioChannelLabel_RearSurroundRight:
412 p_aout->output.output.i_physical_channels |= AOUT_CHAN_MIDDLERIGHT;
414 case kAudioChannelLabel_CenterSurround:
415 p_aout->output.output.i_physical_channels |= AOUT_CHAN_REARCENTER;
418 msg_Warn( p_aout, "Unrecognized channel form provided by driver: %d", (int)layout->mChannelDescriptions[i].mChannelLabel );
421 if( p_aout->output.output.i_physical_channels == 0 )
423 p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
424 msg_Err( p_aout, "You should configure your speaker layout with Audio Midi Setup Utility in /Applications/Utilities. Now using Stereo mode." );
427 if( layout ) free( layout );
431 msg_Warn( p_aout, "this driver does not support kAudioDevicePropertyPreferredChannelLayout. BAD DRIVER AUTHOR !!!" );
432 p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
435 msg_Dbg( p_aout, "selected %d physical channels for device output", aout_FormatNbChannels( &p_aout->output.output ) );
436 msg_Dbg( p_aout, "VLC will output: %s", aout_FormatPrintChannels( &p_aout->output.output ));
438 memset (&new_layout, 0, sizeof(new_layout));
439 switch( aout_FormatNbChannels( &p_aout->output.output ) )
442 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_Mono;
445 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;
448 if( p_aout->output.output.i_physical_channels & AOUT_CHAN_CENTER )
450 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_7; // L R C
452 else if( p_aout->output.output.i_physical_channels & AOUT_CHAN_LFE )
454 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_4; // L R LFE
458 if( p_aout->output.output.i_physical_channels & ( AOUT_CHAN_CENTER | AOUT_CHAN_LFE ) )
460 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_10; // L R C LFE
462 else if( p_aout->output.output.i_physical_channels & ( AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT ) )
464 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_3; // L R Ls Rs
466 else if( p_aout->output.output.i_physical_channels & ( AOUT_CHAN_CENTER | AOUT_CHAN_REARCENTER ) )
468 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_3; // L R C Cs
472 if( p_aout->output.output.i_physical_channels & ( AOUT_CHAN_CENTER ) )
474 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_19; // L R Ls Rs C
476 else if( p_aout->output.output.i_physical_channels & ( AOUT_CHAN_LFE ) )
478 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_18; // L R Ls Rs LFE
482 if( p_aout->output.output.i_physical_channels & ( AOUT_CHAN_LFE ) )
484 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_20; // L R Ls Rs C LFE
488 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_AudioUnit_6_0; // L R Ls Rs C Cs
492 /* FIXME: This is incorrect. VLC uses the internal ordering: L R Lm Rm Lr Rr C LFE but this is wrong */
493 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_6_1_A; // L R C LFE Ls Rs Cs
496 /* FIXME: This is incorrect. VLC uses the internal ordering: L R Lm Rm Lr Rr C LFE but this is wrong */
497 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_7_1_A; // L R C LFE Ls Rs Lc Rc
501 /* Set up the format to be used */
502 DeviceFormat.mSampleRate = p_aout->output.output.i_rate;
503 DeviceFormat.mFormatID = kAudioFormatLinearPCM;
505 /* We use float 32. It's the best supported format by both VLC and Coreaudio */
506 p_aout->output.output.i_format = VLC_FOURCC( 'f','l','3','2');
507 DeviceFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
508 DeviceFormat.mBitsPerChannel = 32;
509 DeviceFormat.mChannelsPerFrame = aout_FormatNbChannels( &p_aout->output.output );
511 /* Calculate framesizes and stuff */
512 DeviceFormat.mFramesPerPacket = 1;
513 DeviceFormat.mBytesPerFrame = DeviceFormat.mBitsPerChannel * DeviceFormat.mChannelsPerFrame / 8;
514 DeviceFormat.mBytesPerPacket = DeviceFormat.mBytesPerFrame * DeviceFormat.mFramesPerPacket;
516 /* Set the desired format */
517 i_param_size = sizeof(AudioStreamBasicDescription);
518 verify_noerr( AudioUnitSetProperty( p_sys->au_unit,
519 kAudioUnitProperty_StreamFormat,
520 kAudioUnitScope_Input,
525 msg_Dbg( p_aout, STREAM_FORMAT_MSG( "we set the AU format: " , DeviceFormat ) );
527 /* Retrieve actual format */
528 verify_noerr( AudioUnitGetProperty( p_sys->au_unit,
529 kAudioUnitProperty_StreamFormat,
530 kAudioUnitScope_Input,
535 msg_Dbg( p_aout, STREAM_FORMAT_MSG( "the actual set AU format is " , DeviceFormat ) );
537 /* Do the last VLC aout setups */
538 aout_FormatPrepare( &p_aout->output.output );
539 p_aout->output.i_nb_samples = 2048;
540 aout_VolumeSoftInit( p_aout );
542 /* set the IOproc callback */
543 input.inputProc = (AURenderCallback) RenderCallbackAnalog;
544 input.inputProcRefCon = p_aout;
546 verify_noerr( AudioUnitSetProperty( p_sys->au_unit,
547 kAudioUnitProperty_SetRenderCallback,
548 kAudioUnitScope_Input,
549 0, &input, sizeof( input ) ) );
551 input.inputProc = (AURenderCallback) RenderCallbackAnalog;
552 input.inputProcRefCon = p_aout;
554 /* Set the new_layout as the layout VLC will use to feed the AU unit */
555 verify_noerr( AudioUnitSetProperty( p_sys->au_unit,
556 kAudioUnitProperty_AudioChannelLayout,
557 kAudioUnitScope_Input,
558 0, &new_layout, sizeof(new_layout) ) );
560 if( new_layout.mNumberChannelDescriptions > 0 )
561 free( new_layout.mChannelDescriptions );
564 verify_noerr( AudioUnitInitialize(p_sys->au_unit) );
566 /* Find the difference between device clock and mdate clock */
567 p_sys->clock_diff = - (mtime_t)
568 AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000;
569 p_sys->clock_diff += mdate();
572 verify_noerr( AudioOutputUnitStart(p_sys->au_unit) );
577 /*****************************************************************************
578 * Setup a encoded digital stream (SPDIF)
579 *****************************************************************************/
580 static int OpenSPDIF( aout_instance_t * p_aout )
582 struct aout_sys_t *p_sys = p_aout->output.p_sys;
583 OSStatus err = noErr;
584 UInt32 i_param_size = 0, b_mix = 0;
585 Boolean b_writeable = VLC_FALSE;
586 AudioStreamID *p_streams = NULL;
587 int i = 0, i_streams = 0;
589 /* Start doing the SPDIF setup proces */
590 p_sys->b_digital = VLC_TRUE;
593 i_param_size = sizeof( p_sys->i_hog_pid );
594 p_sys->i_hog_pid = getpid() ;
596 err = AudioDeviceSetProperty( p_sys->i_selected_dev, 0, 0, FALSE,
597 kAudioDevicePropertyHogMode, i_param_size, &p_sys->i_hog_pid );
601 msg_Err( p_aout, "failed to set hogmode: [%4.4s]", (char *)&err );
605 /* Set mixable to false if we are allowed to */
606 err = AudioDeviceGetPropertyInfo( p_sys->i_selected_dev, 0, FALSE, kAudioDevicePropertySupportsMixing,
607 &i_param_size, &b_writeable );
609 err = AudioDeviceGetProperty( p_sys->i_selected_dev, 0, FALSE, kAudioDevicePropertySupportsMixing,
610 &i_param_size, &b_mix );
612 if( !err && b_writeable )
615 err = AudioDeviceSetProperty( p_sys->i_selected_dev, 0, 0, FALSE,
616 kAudioDevicePropertySupportsMixing, i_param_size, &b_mix );
617 p_sys->b_changed_mixing = VLC_TRUE;
622 msg_Err( p_aout, "failed to set mixmode: [%4.4s]", (char *)&err );
626 /* Get a list of all the streams on this device */
627 err = AudioDeviceGetPropertyInfo( p_sys->i_selected_dev, 0, FALSE,
628 kAudioDevicePropertyStreams,
629 &i_param_size, NULL );
632 msg_Err( p_aout, "could not get number of streams: [%4.4s]", (char *)&err );
636 i_streams = i_param_size / sizeof( AudioStreamID );
637 p_streams = (AudioStreamID *)malloc( i_param_size );
638 if( p_streams == NULL )
640 msg_Err( p_aout, "Out of memory" );
644 err = AudioDeviceGetProperty( p_sys->i_selected_dev, 0, FALSE,
645 kAudioDevicePropertyStreams,
646 &i_param_size, p_streams );
650 msg_Err( p_aout, "could not get number of streams: [%4.4s]", (char *)&err );
651 if( p_streams ) free( p_streams );
655 for( i = 0; i < i_streams && p_sys->i_stream_index < 0 ; i++ )
657 /* Find a stream with a cac3 stream */
658 AudioStreamBasicDescription *p_format_list = NULL;
659 int i_formats = 0, j = 0;
660 vlc_bool_t b_digital = VLC_FALSE;
662 /* Retrieve all the stream formats supported by each output stream */
663 err = AudioStreamGetPropertyInfo( p_streams[i], 0,
664 kAudioStreamPropertyPhysicalFormats,
665 &i_param_size, NULL );
668 msg_Err( p_aout, "could not get number of streamformats: [%4.4s]", (char *)&err );
672 i_formats = i_param_size / sizeof( AudioStreamBasicDescription );
673 p_format_list = (AudioStreamBasicDescription *)malloc( i_param_size );
674 if( p_format_list == NULL )
676 msg_Err( p_aout, "could not malloc the memory" );
680 err = AudioStreamGetProperty( p_streams[i], 0,
681 kAudioStreamPropertyPhysicalFormats,
682 &i_param_size, p_format_list );
685 msg_Err( p_aout, "could not get the list of streamformats: [%4.4s]", (char *)&err );
686 if( p_format_list) free( p_format_list);
690 /* Check if one of the supported formats is a digital format */
691 for( j = 0; j < i_formats; j++ )
693 if( p_format_list[j].mFormatID == 'IAC3' ||
694 p_format_list[j].mFormatID == kAudioFormat60958AC3 )
696 b_digital = VLC_TRUE;
703 /* if this stream supports a digital (cac3) format, then go set it. */
704 int i_requested_rate_format = -1;
705 int i_current_rate_format = -1;
706 int i_backup_rate_format = -1;
708 p_sys->i_stream_id = p_streams[i];
709 p_sys->i_stream_index = i;
711 if( p_sys->b_revert == VLC_FALSE )
713 /* Retrieve the original format of this stream first if not done so already */
714 i_param_size = sizeof( p_sys->sfmt_revert );
715 err = AudioStreamGetProperty( p_sys->i_stream_id, 0,
716 kAudioStreamPropertyPhysicalFormat,
718 &p_sys->sfmt_revert );
721 msg_Err( p_aout, "could not retrieve the original streamformat: [%4.4s]", (char *)&err );
724 p_sys->b_revert = VLC_TRUE;
727 for( j = 0; j < i_formats; j++ )
729 if( p_format_list[j].mFormatID == 'IAC3' ||
730 p_format_list[j].mFormatID == kAudioFormat60958AC3 )
732 if( p_format_list[j].mSampleRate == p_aout->output.output.i_rate )
734 i_requested_rate_format = j;
737 else if( p_format_list[j].mSampleRate == p_sys->sfmt_revert.mSampleRate )
739 i_current_rate_format = j;
743 if( i_backup_rate_format < 0 || p_format_list[j].mSampleRate > p_format_list[i_backup_rate_format].mSampleRate )
744 i_backup_rate_format = j;
750 if( i_requested_rate_format >= 0 ) /* We prefer to output at the samplerate of the original audio */
751 p_sys->stream_format = p_format_list[i_requested_rate_format];
752 else if( i_current_rate_format >= 0 ) /* If not possible, we will try to use the current samplerate of the device */
753 p_sys->stream_format = p_format_list[i_current_rate_format];
754 else p_sys->stream_format = p_format_list[i_backup_rate_format]; /* And if we have to, any digital format will be just fine (highest rate possible) */
756 if( p_format_list ) free( p_format_list );
758 if( p_streams ) free( p_streams );
760 msg_Dbg( p_aout, STREAM_FORMAT_MSG( "original stream format: ", p_sys->sfmt_revert ) );
762 if( !AudioStreamChangeFormat( p_aout, p_sys->i_stream_id, p_sys->stream_format ) )
765 /* Set the format flags */
766 if( p_sys->stream_format.mFormatFlags & kAudioFormatFlagIsBigEndian )
767 p_aout->output.output.i_format = VLC_FOURCC('s','p','d','b');
769 p_aout->output.output.i_format = VLC_FOURCC('s','p','d','i');
770 p_aout->output.output.i_bytes_per_frame = AOUT_SPDIF_SIZE;
771 p_aout->output.output.i_frame_length = A52_FRAME_NB;
772 p_aout->output.i_nb_samples = p_aout->output.output.i_frame_length;
773 p_aout->output.output.i_rate = (unsigned int)p_sys->stream_format.mSampleRate;
774 aout_FormatPrepare( &p_aout->output.output );
775 aout_VolumeNoneInit( p_aout );
777 /* Add IOProc callback */
778 err = AudioDeviceAddIOProc( p_sys->i_selected_dev,
779 (AudioDeviceIOProc)RenderCallbackSPDIF,
783 msg_Err( p_aout, "AudioDeviceAddIOProc failed: [%4.4s]", (char *)&err );
787 /* Check for the difference between the Device clock and mdate */
788 p_sys->clock_diff = - (mtime_t)
789 AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000;
790 p_sys->clock_diff += mdate();
793 err = AudioDeviceStart( p_sys->i_selected_dev, (AudioDeviceIOProc)RenderCallbackSPDIF );
796 msg_Err( p_aout, "AudioDeviceStart failed: [%4.4s]", (char *)&err );
798 err = AudioDeviceRemoveIOProc( p_sys->i_selected_dev,
799 (AudioDeviceIOProc)RenderCallbackSPDIF );
802 msg_Err( p_aout, "AudioDeviceRemoveIOProc failed: [%4.4s]", (char *)&err );
811 /*****************************************************************************
812 * Close: Close HAL AudioUnit
813 *****************************************************************************/
814 static void Close( vlc_object_t * p_this )
816 aout_instance_t *p_aout = (aout_instance_t *)p_this;
817 struct aout_sys_t *p_sys = p_aout->output.p_sys;
818 OSStatus err = noErr;
819 UInt32 i_param_size = 0;
823 verify_noerr( AudioOutputUnitStop( p_sys->au_unit ) );
824 verify_noerr( AudioUnitUninitialize( p_sys->au_unit ) );
825 verify_noerr( CloseComponent( p_sys->au_unit ) );
828 if( p_sys->b_digital )
831 err = AudioDeviceStop( p_sys->i_selected_dev,
832 (AudioDeviceIOProc)RenderCallbackSPDIF );
835 msg_Err( p_aout, "AudioDeviceStop failed: [%4.4s]", (char *)&err );
838 /* Remove IOProc callback */
839 err = AudioDeviceRemoveIOProc( p_sys->i_selected_dev,
840 (AudioDeviceIOProc)RenderCallbackSPDIF );
843 msg_Err( p_aout, "AudioDeviceRemoveIOProc failed: [%4.4s]", (char *)&err );
846 if( p_sys->b_revert )
848 AudioStreamChangeFormat( p_aout, p_sys->i_stream_id, p_sys->sfmt_revert );
851 if( p_sys->b_changed_mixing && p_sys->sfmt_revert.mFormatID != kAudioFormat60958AC3 )
855 /* Revert mixable to true if we are allowed to */
856 err = AudioDeviceGetPropertyInfo( p_sys->i_selected_dev, 0, FALSE, kAudioDevicePropertySupportsMixing,
857 &i_param_size, &b_writeable );
859 err = AudioDeviceGetProperty( p_sys->i_selected_dev, 0, FALSE, kAudioDevicePropertySupportsMixing,
860 &i_param_size, &b_mix );
862 if( !err && b_writeable )
864 msg_Dbg( p_aout, "mixable is: %d", b_mix );
866 err = AudioDeviceSetProperty( p_sys->i_selected_dev, 0, 0, FALSE,
867 kAudioDevicePropertySupportsMixing, i_param_size, &b_mix );
872 msg_Err( p_aout, "failed to set mixmode: [%4.4s]", (char *)&err );
877 err = AudioHardwareRemovePropertyListener( kAudioHardwarePropertyDevices,
882 msg_Err( p_aout, "AudioHardwareRemovePropertyListener failed: [%4.4s]", (char *)&err );
885 if( p_sys->i_hog_pid == getpid() )
887 p_sys->i_hog_pid = -1;
888 i_param_size = sizeof( p_sys->i_hog_pid );
889 err = AudioDeviceSetProperty( p_sys->i_selected_dev, 0, 0, FALSE,
890 kAudioDevicePropertyHogMode, i_param_size, &p_sys->i_hog_pid );
891 if( err != noErr ) msg_Err( p_aout, "Could not release hogmode: [%4.4s]", (char *)&err );
894 if( p_sys ) free( p_sys );
897 /*****************************************************************************
898 * Play: nothing to do
899 *****************************************************************************/
900 static void Play( aout_instance_t * p_aout )
905 /*****************************************************************************
906 * Probe: Check which devices the OS has, and add them to our audio-device menu
907 *****************************************************************************/
908 static void Probe( aout_instance_t * p_aout )
910 OSStatus err = noErr;
911 UInt32 i = 0, i_param_size = 0;
912 AudioDeviceID devid_def = 0;
913 AudioDeviceID *p_devices = NULL;
914 vlc_value_t val, text;
916 struct aout_sys_t *p_sys = p_aout->output.p_sys;
918 /* Get number of devices */
919 err = AudioHardwareGetPropertyInfo( kAudioHardwarePropertyDevices,
920 &i_param_size, NULL );
923 msg_Err( p_aout, "Could not get number of devices: [%4.4s]", (char *)&err );
927 p_sys->i_devices = i_param_size / sizeof( AudioDeviceID );
929 if( p_sys->i_devices < 1 )
931 msg_Err( p_aout, "No audio output devices were found." );
935 msg_Dbg( p_aout, "system has [%ld] device(s)", p_sys->i_devices );
937 /* Allocate DeviceID array */
938 p_devices = (AudioDeviceID*)malloc( sizeof(AudioDeviceID) * p_sys->i_devices );
939 if( p_devices == NULL )
941 msg_Err( p_aout, "out of memory" );
945 /* Populate DeviceID array */
946 err = AudioHardwareGetProperty( kAudioHardwarePropertyDevices,
947 &i_param_size, p_devices );
950 msg_Err( p_aout, "could not get the device IDs: [%4.4s]", (char *)&err );
954 /* Find the ID of the default Device */
955 i_param_size = sizeof( AudioDeviceID );
956 err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice,
957 &i_param_size, &devid_def );
960 msg_Err( p_aout, "could not get default audio device: [%4.4s]", (char *)&err );
963 p_sys->i_default_dev = devid_def;
965 var_Create( p_aout, "audio-device", VLC_VAR_INTEGER|VLC_VAR_HASCHOICE );
966 text.psz_string = _("Audio Device");
967 var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );
969 for( i = 0; i < p_sys->i_devices; i++ )
974 /* Retrieve the length of the device name */
975 err = AudioDeviceGetPropertyInfo(
976 p_devices[i], 0, VLC_FALSE,
977 kAudioDevicePropertyDeviceName,
978 &i_param_size, NULL);
979 if( err ) goto error;
981 /* Retrieve the name of the device */
982 psz_name = (char *)malloc( i_param_size );
983 err = AudioDeviceGetProperty(
984 p_devices[i], 0, VLC_FALSE,
985 kAudioDevicePropertyDeviceName,
986 &i_param_size, psz_name);
987 if( err ) goto error;
989 msg_Dbg( p_aout, "DevID: %#lx DevName: %s", p_devices[i], psz_name );
991 if( !AudioDeviceHasOutput( p_devices[i]) )
993 msg_Dbg( p_aout, "this device is INPUT only. skipping..." );
997 /* Add the menu entries */
998 val.i_int = (int)p_devices[i];
999 text.psz_string = strdup( psz_name );
1000 var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
1001 if( p_sys->i_default_dev == p_devices[i] )
1003 /* The default device is the selected device normally */
1004 var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
1005 var_Set( p_aout, "audio-device", val );
1008 if( AudioDeviceSupportsDigital( p_aout, p_devices[i] ) )
1010 val.i_int = (int)p_devices[i] | AOUT_VAR_SPDIF_FLAG;
1011 asprintf( &text.psz_string, _("%s (Encoded Output)"), psz_name );
1012 var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
1013 if( p_sys->i_default_dev == p_devices[i] && config_GetInt( p_aout, "spdif" ) )
1015 /* I we selected to prefer SPDIF output if available
1016 * then this "dummy" entry should be selected */
1017 var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
1018 var_Set( p_aout, "audio-device", val );
1025 /* If a device is already "preselected", then use this device */
1026 var_Get( p_aout->p_vlc, "macosx-audio-device", &val );
1029 var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
1030 var_Set( p_aout, "audio-device", val );
1033 /* If we change the device we want to use, we should renegotiate the audio chain */
1034 var_AddCallback( p_aout, "audio-device", AudioDeviceCallback, NULL );
1036 /* Attach a Listener so that we are notified of a change in the Device setup */
1037 err = AudioHardwareAddPropertyListener( kAudioHardwarePropertyDevices,
1043 if( p_devices ) free( p_devices );
1047 var_Destroy( p_aout, "audio-device" );
1048 if( p_devices ) free( p_devices );
1052 /*****************************************************************************
1053 * AudioDeviceHasOutput: Checks if the Device actually provides any outputs at all
1054 *****************************************************************************/
1055 static int AudioDeviceHasOutput( AudioDeviceID i_dev_id )
1060 verify_noerr( AudioDeviceGetPropertyInfo( i_dev_id, 0, FALSE, kAudioDevicePropertyStreams, &dataSize, &isWritable) );
1061 if (dataSize == 0) return FALSE;
1066 /*****************************************************************************
1067 * AudioDeviceSupportsDigital: Check i_dev_id for digital stream support.
1068 *****************************************************************************/
1069 static int AudioDeviceSupportsDigital( aout_instance_t *p_aout, AudioDeviceID i_dev_id )
1071 OSStatus err = noErr;
1072 UInt32 i_param_size = 0;
1073 AudioStreamID *p_streams = NULL;
1074 int i = 0, i_streams = 0;
1075 vlc_bool_t b_return = VLC_FALSE;
1077 /* Retrieve all the output streams */
1078 err = AudioDeviceGetPropertyInfo( i_dev_id, 0, FALSE,
1079 kAudioDevicePropertyStreams,
1080 &i_param_size, NULL );
1083 msg_Err( p_aout, "could not get number of streams: [%4.4s]", (char *)&err );
1087 i_streams = i_param_size / sizeof( AudioStreamID );
1088 p_streams = (AudioStreamID *)malloc( i_param_size );
1089 if( p_streams == NULL )
1091 msg_Err( p_aout, "Out of memory" );
1095 err = AudioDeviceGetProperty( i_dev_id, 0, FALSE,
1096 kAudioDevicePropertyStreams,
1097 &i_param_size, p_streams );
1101 msg_Err( p_aout, "could not get number of streams: [%4.4s]", (char *)&err );
1105 for( i = 0; i < i_streams; i++ )
1107 if( AudioStreamSupportsDigital( p_aout, p_streams[i] ) )
1108 b_return = VLC_TRUE;
1111 if( p_streams ) free( p_streams );
1115 /*****************************************************************************
1116 * AudioStreamSupportsDigital: Check i_stream_id for digital stream support.
1117 *****************************************************************************/
1118 static int AudioStreamSupportsDigital( aout_instance_t *p_aout, AudioStreamID i_stream_id )
1120 OSStatus err = noErr;
1121 UInt32 i_param_size = 0;
1122 AudioStreamBasicDescription *p_format_list = NULL;
1123 int i = 0, i_formats = 0;
1124 vlc_bool_t b_return = VLC_FALSE;
1126 /* Retrieve all the stream formats supported by each output stream */
1127 err = AudioStreamGetPropertyInfo( i_stream_id, 0,
1128 kAudioStreamPropertyPhysicalFormats,
1129 &i_param_size, NULL );
1132 msg_Err( p_aout, "could not get number of streamformats: [%4.4s]", (char *)&err );
1136 i_formats = i_param_size / sizeof( AudioStreamBasicDescription );
1137 p_format_list = (AudioStreamBasicDescription *)malloc( i_param_size );
1138 if( p_format_list == NULL )
1140 msg_Err( p_aout, "could not malloc the memory" );
1144 err = AudioStreamGetProperty( i_stream_id, 0,
1145 kAudioStreamPropertyPhysicalFormats,
1146 &i_param_size, p_format_list );
1149 msg_Err( p_aout, "could not get the list of streamformats: [%4.4s]", (char *)&err );
1150 free( p_format_list);
1151 p_format_list = NULL;
1155 for( i = 0; i < i_formats; i++ )
1157 msg_Dbg( p_aout, STREAM_FORMAT_MSG( "supported format: ", p_format_list[i] ) );
1159 if( p_format_list[i].mFormatID == 'IAC3' ||
1160 p_format_list[i].mFormatID == kAudioFormat60958AC3 )
1162 b_return = VLC_TRUE;
1166 if( p_format_list ) free( p_format_list );
1170 /*****************************************************************************
1171 * AudioStreamChangeFormat: Change i_stream_id to change_format
1172 *****************************************************************************/
1173 static int AudioStreamChangeFormat( aout_instance_t *p_aout, AudioStreamID i_stream_id, AudioStreamBasicDescription change_format )
1175 OSStatus err = noErr;
1176 UInt32 i_param_size = 0;
1180 struct timespec timeout;
1181 struct { vlc_mutex_t lock; vlc_cond_t cond; } w;
1183 msg_Dbg( p_aout, STREAM_FORMAT_MSG( "setting stream format: ", change_format ) );
1185 /* Condition because SetProperty is asynchronious */
1186 vlc_cond_init( p_aout, &w.cond );
1187 vlc_mutex_init( p_aout, &w.lock );
1188 vlc_mutex_lock( &w.lock );
1190 /* Install the callback */
1191 err = AudioStreamAddPropertyListener( i_stream_id, 0,
1192 kAudioStreamPropertyPhysicalFormat,
1193 StreamListener, (void *)&w );
1196 msg_Err( p_aout, "AudioStreamAddPropertyListener failed: [%4.4s]", (char *)&err );
1200 /* change the format */
1201 err = AudioStreamSetProperty( i_stream_id, 0, 0,
1202 kAudioStreamPropertyPhysicalFormat,
1203 sizeof( AudioStreamBasicDescription ),
1207 msg_Err( p_aout, "could not set the stream format: [%4.4s]", (char *)&err );
1211 /* The AudioStreamSetProperty is not only asynchronious (requiring the locks)
1212 * it is also not Atomic, in it's behaviour.
1213 * Therefore we check 5 times before we really give up.
1214 * FIXME: failing isn't actually implemented yet. */
1215 for( i = 0; i < 5; i++ )
1217 AudioStreamBasicDescription actual_format;
1219 gettimeofday( &now, NULL );
1220 timeout.tv_sec = now.tv_sec;
1221 timeout.tv_nsec = (now.tv_usec + 500000) * 1000;
1223 if( pthread_cond_timedwait( &w.cond.cond, &w.lock.mutex, &timeout ) )
1225 msg_Dbg( p_aout, "reached timeout" );
1228 i_param_size = sizeof( AudioStreamBasicDescription );
1229 err = AudioStreamGetProperty( i_stream_id, 0,
1230 kAudioStreamPropertyPhysicalFormat,
1234 msg_Dbg( p_aout, STREAM_FORMAT_MSG( "actual format in use: ", actual_format ) );
1235 if( actual_format.mSampleRate == change_format.mSampleRate &&
1236 actual_format.mFormatID == change_format.mFormatID &&
1237 actual_format.mFramesPerPacket == change_format.mFramesPerPacket )
1239 /* The right format is now active */
1242 /* We need to check again */
1245 /* Removing the property listener */
1246 err = AudioStreamRemovePropertyListener( i_stream_id, 0,
1247 kAudioStreamPropertyPhysicalFormat,
1251 msg_Err( p_aout, "AudioStreamRemovePropertyListener failed: [%4.4s]", (char *)&err );
1255 /* Destroy the lock and condition */
1256 vlc_mutex_unlock( &w.lock );
1257 vlc_mutex_destroy( &w.lock );
1258 vlc_cond_destroy( &w.cond );
1263 /*****************************************************************************
1264 * RenderCallbackAnalog: This function is called everytime the AudioUnit wants
1265 * us to provide some more audio data.
1266 * Don't print anything during normal playback, calling blocking function from
1267 * this callback is not allowed.
1268 *****************************************************************************/
1269 static OSStatus RenderCallbackAnalog( vlc_object_t *_p_aout,
1270 AudioUnitRenderActionFlags *ioActionFlags,
1271 const AudioTimeStamp *inTimeStamp,
1272 unsigned int inBusNummer,
1273 unsigned int inNumberFrames,
1274 AudioBufferList *ioData )
1276 AudioTimeStamp host_time;
1277 mtime_t current_date = 0;
1278 uint32_t i_mData_bytes = 0;
1280 aout_instance_t * p_aout = (aout_instance_t *)_p_aout;
1281 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1283 host_time.mFlags = kAudioTimeStampHostTimeValid;
1284 AudioDeviceTranslateTime( p_sys->i_selected_dev, inTimeStamp, &host_time );
1286 /* Check for the difference between the Device clock and mdate */
1287 p_sys->clock_diff = - (mtime_t)
1288 AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000;
1289 p_sys->clock_diff += mdate();
1291 current_date = p_sys->clock_diff +
1292 AudioConvertHostTimeToNanos( host_time.mHostTime ) / 1000;
1293 //- ((mtime_t) 1000000 / p_aout->output.output.i_rate * 31 ); // 31 = Latency in Frames. retrieve somewhere
1295 if( ioData == NULL && ioData->mNumberBuffers < 1 )
1297 msg_Err( p_aout, "no iodata or buffers");
1300 if( ioData->mNumberBuffers > 1 )
1301 msg_Err( p_aout, "well this is weird. seems like there is more than one buffer..." );
1304 if( p_sys->i_total_bytes > 0 )
1306 i_mData_bytes = __MIN( p_sys->i_total_bytes - p_sys->i_read_bytes, ioData->mBuffers[0].mDataByteSize );
1307 p_aout->p_vlc->pf_memcpy( ioData->mBuffers[0].mData, &p_sys->p_remainder_buffer[p_sys->i_read_bytes], i_mData_bytes );
1308 p_sys->i_read_bytes += i_mData_bytes;
1309 current_date += (mtime_t) ( (mtime_t) 1000000 / p_aout->output.output.i_rate ) *
1310 ( i_mData_bytes / 4 / aout_FormatNbChannels( &p_aout->output.output ) ); // 4 is fl32 specific
1312 if( p_sys->i_read_bytes >= p_sys->i_total_bytes )
1313 p_sys->i_read_bytes = p_sys->i_total_bytes = 0;
1316 while( i_mData_bytes < ioData->mBuffers[0].mDataByteSize )
1318 /* We don't have enough data yet */
1319 aout_buffer_t * p_buffer;
1320 p_buffer = aout_OutputNextBuffer( p_aout, current_date , VLC_FALSE );
1322 if( p_buffer != NULL )
1324 uint32_t i_second_mData_bytes = __MIN( p_buffer->i_nb_bytes, ioData->mBuffers[0].mDataByteSize - i_mData_bytes );
1326 p_aout->p_vlc->pf_memcpy( (uint8_t *)ioData->mBuffers[0].mData + i_mData_bytes, p_buffer->p_buffer, i_second_mData_bytes );
1327 i_mData_bytes += i_second_mData_bytes;
1329 if( i_mData_bytes >= ioData->mBuffers[0].mDataByteSize )
1331 p_sys->i_total_bytes = p_buffer->i_nb_bytes - i_second_mData_bytes;
1332 p_aout->p_vlc->pf_memcpy( p_sys->p_remainder_buffer, &p_buffer->p_buffer[i_second_mData_bytes], p_sys->i_total_bytes );
1336 /* update current_date */
1337 current_date += (mtime_t) ( (mtime_t) 1000000 / p_aout->output.output.i_rate ) *
1338 ( i_second_mData_bytes / 4 / aout_FormatNbChannels( &p_aout->output.output ) ); // 4 is fl32 specific
1340 aout_BufferFree( p_buffer );
1344 p_aout->p_vlc->pf_memset( (uint8_t *)ioData->mBuffers[0].mData +i_mData_bytes, 0, ioData->mBuffers[0].mDataByteSize - i_mData_bytes );
1345 i_mData_bytes += ioData->mBuffers[0].mDataByteSize - i_mData_bytes;
1351 /*****************************************************************************
1352 * RenderCallbackSPDIF: callback for SPDIF audio output
1353 *****************************************************************************/
1354 static OSStatus RenderCallbackSPDIF( AudioDeviceID inDevice,
1355 const AudioTimeStamp * inNow,
1356 const void * inInputData,
1357 const AudioTimeStamp * inInputTime,
1358 AudioBufferList * outOutputData,
1359 const AudioTimeStamp * inOutputTime,
1360 void * threadGlobals )
1362 aout_buffer_t * p_buffer;
1363 mtime_t current_date;
1365 aout_instance_t * p_aout = (aout_instance_t *)threadGlobals;
1366 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1368 /* Check for the difference between the Device clock and mdate */
1369 p_sys->clock_diff = - (mtime_t)
1370 AudioConvertHostTimeToNanos( inNow->mHostTime ) / 1000;
1371 p_sys->clock_diff += mdate();
1373 current_date = p_sys->clock_diff +
1374 AudioConvertHostTimeToNanos( inOutputTime->mHostTime ) / 1000;
1375 //- ((mtime_t) 1000000 / p_aout->output.output.i_rate * 31 ); // 31 = Latency in Frames. retrieve somewhere
1377 p_buffer = aout_OutputNextBuffer( p_aout, current_date, VLC_TRUE );
1379 #define BUFFER outOutputData->mBuffers[p_sys->i_stream_index]
1380 if( p_buffer != NULL )
1382 if( (int)BUFFER.mDataByteSize != (int)p_buffer->i_nb_bytes)
1383 msg_Warn( p_aout, "bytesize: %d nb_bytes: %d", (int)BUFFER.mDataByteSize, (int)p_buffer->i_nb_bytes );
1385 /* move data into output data buffer */
1386 p_aout->p_vlc->pf_memcpy( BUFFER.mData,
1387 p_buffer->p_buffer, p_buffer->i_nb_bytes );
1388 aout_BufferFree( p_buffer );
1392 p_aout->p_vlc->pf_memset( BUFFER.mData, 0, BUFFER.mDataByteSize );
1399 /*****************************************************************************
1400 * HardwareListener: Warns us of changes in the list of registered devices
1401 *****************************************************************************/
1402 static OSStatus HardwareListener( AudioHardwarePropertyID inPropertyID,
1403 void * inClientData )
1405 OSStatus err = noErr;
1406 aout_instance_t *p_aout = (aout_instance_t *)inClientData;
1408 switch( inPropertyID )
1410 case kAudioHardwarePropertyDevices:
1412 /* something changed in the list of devices */
1413 /* We trigger the audio-device's aout_ChannelsRestart callback */
1414 var_Change( p_aout, "audio-device", VLC_VAR_TRIGGER_CALLBACKS, NULL, NULL );
1415 var_Destroy( p_aout, "audio-device" );
1423 /*****************************************************************************
1425 *****************************************************************************/
1426 static OSStatus StreamListener( AudioStreamID inStream,
1428 AudioDevicePropertyID inPropertyID,
1429 void * inClientData )
1431 OSStatus err = noErr;
1432 struct { vlc_mutex_t lock; vlc_cond_t cond; } * w = inClientData;
1434 switch( inPropertyID )
1436 case kAudioStreamPropertyPhysicalFormat:
1437 vlc_mutex_lock( &w->lock );
1438 vlc_cond_signal( &w->cond );
1439 vlc_mutex_unlock( &w->lock );
1448 /*****************************************************************************
1449 * AudioDeviceCallback: Callback triggered when the audio-device variable is changed
1450 *****************************************************************************/
1451 static int AudioDeviceCallback( vlc_object_t *p_this, const char *psz_variable,
1452 vlc_value_t old_val, vlc_value_t new_val, void *param )
1454 aout_instance_t *p_aout = (aout_instance_t *)p_this;
1455 var_Set( p_aout->p_vlc, "macosx-audio-device", new_val );
1456 msg_Dbg( p_aout, "Set Device: %#x", new_val.i_int );
1457 return aout_ChannelsRestart( p_this, psz_variable, old_val, new_val, param );