]> git.sesse.net Git - vlc/blob - modules/audio_output/auhal.c
Mac OS X audio module is now unified for analog and digital output. todo: test on...
[vlc] / modules / audio_output / auhal.c
1 /*****************************************************************************
2  * auhal.c: AUHAL output plugin
3  *****************************************************************************
4  * Copyright (C) 2005 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Derk-Jan Hartman <hartman at videolan dot org>
8  *
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.
13  * 
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.
18  *
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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <string.h>
28 #include <stdlib.h>
29
30 #include <vlc/vlc.h>
31 #include <vlc/aout.h>
32
33 #include "aout_internal.h"
34
35 #include <CoreAudio/CoreAudio.h>
36 #include <CoreAudio/CoreAudioTypes.h>
37 #include <AudioUnit/AudioUnitProperties.h>
38 #include <AudioUnit/AudioUnitParameters.h>
39 #include <AudioUnit/AudioOutputUnit.h>
40 #include <AudioToolbox/AudioFormat.h>
41
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
48
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
55
56 #define BUFSIZE 0xffffff
57 #define AOUT_VAR_SPDIF_FLAG 0xf00000
58
59 /*****************************************************************************
60  * aout_sys_t: private audio output method descriptor
61  *****************************************************************************
62  * This structure is part of the audio output thread descriptor.
63  * It describes the CoreAudio specific properties of an output thread.
64  *****************************************************************************/
65 struct aout_sys_t
66 {
67     AudioDeviceID               i_default_dev;  /* Keeps DeviceID of defaultOutputDevice */
68     AudioDeviceID               i_selected_dev; /* Keeps DeviceID of the selected device */
69     UInt32                      i_devices;      /* Number of CoreAudio Devices */
70     vlc_bool_t                  b_supports_digital;/* Does the currently selected device support digital mode? */
71     vlc_bool_t                  b_digital;      /* Are we running in digital mode? */
72     mtime_t                     clock_diff;     /* Difference between VLC clock and Device clock */
73     /* AUHAL specific */
74     Component                   au_component;   /* The Audiocomponent we use */
75     AudioUnit                   au_unit;        /* The AudioUnit we use */
76     uint8_t                     p_remainder_buffer[BUFSIZE];
77     uint32_t                    i_read_bytes;
78     uint32_t                    i_total_bytes;
79     /* CoreAudio SPDIF mode specific */
80     pid_t                       i_hog_pid;      /* The keep the pid of our hog status */
81     AudioStreamID               i_stream_id;    /* The StreamID that has a cac3 streamformat */
82     int                         i_stream_index; /* The index of i_stream_id in an AudioBufferList */
83     AudioStreamBasicDescription stream_format;  /* The format we changed the to */
84     AudioStreamBasicDescription sfmt_revert;    /* The original format of the stream */
85     vlc_bool_t                  b_revert;       /* Wether we need to revert the stream format */
86     vlc_bool_t                  b_changed_mixing;/* Wether we need to set the mixing mode back */
87 };
88
89 /*****************************************************************************
90  * Local prototypes.
91  *****************************************************************************/
92 static int      Open                    ( vlc_object_t * );
93 static int      OpenAnalog              ( aout_instance_t * );
94 static int      OpenSPDIF               ( aout_instance_t * );
95 static void     Close                   ( vlc_object_t * );
96
97 static void     Play                    ( aout_instance_t * );
98 static void     Probe                   ( aout_instance_t * );
99
100 static int      AudioDeviceHasOutput    ( AudioDeviceID );
101 static int      AudioDeviceSupportsDigital( aout_instance_t *, AudioDeviceID );
102 static int      AudioStreamSupportsDigital( aout_instance_t *, AudioStreamID );
103
104 static OSStatus RenderCallbackAnalog    ( vlc_object_t *, AudioUnitRenderActionFlags *, const AudioTimeStamp *,
105                                           unsigned int, unsigned int, AudioBufferList *);
106 static OSStatus RenderCallbackSPDIF     ( AudioDeviceID, const AudioTimeStamp *, const void *, const AudioTimeStamp *,
107                                           AudioBufferList *, const AudioTimeStamp *, void * );
108 static OSStatus HardwareListener        ( AudioHardwarePropertyID, void *);
109 static OSStatus StreamListener          ( AudioStreamID, UInt32,
110                                           AudioDevicePropertyID, void * );
111 static int      AudioDeviceCallback     ( vlc_object_t *, const char *,
112                                           vlc_value_t, vlc_value_t, void * );
113
114
115 /*****************************************************************************
116  * Module descriptor
117  *****************************************************************************/
118 #define ADEV_TEXT N_("Audio Device")
119 #define ADEV_LONGTEXT N_("Choose a number corresponding to the number of an " \
120     "audio device, as listed in your 'Audio Device' menu. This device will " \
121     "then be used by default for audio playback.")
122
123 vlc_module_begin();
124     set_shortname( "auhal" );
125     set_description( _("HAL AudioUnit output") );
126     set_capability( "audio output", 101 );
127     set_category( CAT_AUDIO );
128     set_subcategory( SUBCAT_AUDIO_AOUT );
129     set_callbacks( Open, Close );
130     add_integer( "macosx-audio-device", 0, NULL, ADEV_TEXT, ADEV_LONGTEXT, VLC_FALSE ); 
131 vlc_module_end();
132
133 /*****************************************************************************
134  * Open: open macosx audio output
135  *****************************************************************************/
136 static int Open( vlc_object_t * p_this )
137 {
138     OSStatus                err = noErr;
139     UInt32                  i_param_size = 0;
140     struct aout_sys_t       *p_sys = NULL;
141     vlc_bool_t              b_alive = VLC_FALSE;
142     vlc_value_t             val;
143     aout_instance_t         *p_aout = (aout_instance_t *)p_this;
144
145     /* Allocate structure */
146     p_aout->output.p_sys = malloc( sizeof( aout_sys_t ) );
147     if( p_aout->output.p_sys == NULL )
148     {
149         msg_Err( p_aout, "out of memory" );
150         return( VLC_ENOMEM );
151     }
152
153     p_sys = p_aout->output.p_sys;
154     p_sys->i_default_dev = 0;
155     p_sys->i_selected_dev = 0;
156     p_sys->i_devices = 0;
157     p_sys->b_supports_digital = VLC_FALSE;
158     p_sys->b_digital = VLC_FALSE;
159     p_sys->au_component = NULL;
160     p_sys->au_unit = NULL;
161     p_sys->clock_diff = (mtime_t) 0;
162     p_sys->i_read_bytes = 0;
163     p_sys->i_total_bytes = 0;
164     p_sys->i_hog_pid = -1;
165     p_sys->i_stream_id = 0;
166     p_sys->i_stream_index = 0;
167     p_sys->b_revert = VLC_FALSE;
168     p_sys->b_changed_mixing = VLC_FALSE;
169     memset( p_sys->p_remainder_buffer, 0, sizeof(uint8_t) * BUFSIZE );
170
171     p_aout->output.pf_play = Play;
172     
173     aout_FormatPrint( p_aout, "VLC is looking for:", (audio_sample_format_t *)&p_aout->output.output );
174     
175     /* Persistent device variable */
176     if( var_Type( p_aout->p_vlc, "macosx-audio-device" ) == 0 )
177     {
178         msg_Dbg( p_aout, "create macosx-audio-device" );
179         var_Create( p_aout->p_vlc, "macosx-audio-device", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
180     }
181
182     /* Build a list of devices */
183     if( var_Type( p_aout, "audio-device" ) == 0 )
184     {
185         Probe( p_aout );
186     }
187
188     /* What device do we want? */
189     if( var_Get( p_aout, "audio-device", &val ) < 0 )
190     {
191         msg_Err( p_aout, "audio-device var does not exist. device probe failed." );
192         free( p_sys );
193         return( VLC_ENOVAR );
194     }
195
196     p_sys->i_selected_dev = val.i_int & ~AOUT_VAR_SPDIF_FLAG;
197     p_sys->b_supports_digital = ( val.i_int & AOUT_VAR_SPDIF_FLAG ) ? VLC_TRUE : VLC_FALSE;
198
199     /* Check if the desired device is alive and usable */
200     i_param_size = sizeof( b_alive );
201     err = AudioDeviceGetProperty( p_sys->i_selected_dev, 0, FALSE,
202                                   kAudioDevicePropertyDeviceIsAlive,
203                                   &i_param_size, &b_alive );
204
205     if( err != noErr )
206     {
207         msg_Err( p_aout, "could not check whether device is alive: %4.4s",
208                  (char *)&err );
209         return VLC_EGENERIC;
210     }
211
212     if( b_alive == VLC_FALSE )
213     {
214         msg_Err( p_aout, "Selected audio device is not alive switching to default device" ); 
215         p_sys->i_selected_dev = p_sys->i_default_dev;
216     }
217
218     i_param_size = sizeof( p_sys->i_hog_pid );
219     err = AudioDeviceGetProperty( p_sys->i_selected_dev, 0, FALSE,
220                                   kAudioDevicePropertyHogMode,
221                                   &i_param_size, &p_sys->i_hog_pid );
222
223     if( err != noErr )
224     {
225         msg_Err( p_aout, "could not check whether device is hogged: %4.4s",
226                  (char *)&err );
227         return VLC_EGENERIC;
228     }
229
230     if( p_sys->i_hog_pid != -1 && p_sys->i_hog_pid != getpid() )
231     {
232         msg_Err( p_aout, "Selected audio device is exclusively in use by another program" );
233         var_Destroy( p_aout, "audio-device" );
234         free( p_sys );
235         return VLC_EGENERIC;
236     }
237
238     /* Check for Digital mode or Analog output mode */
239     if( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) && p_sys->b_supports_digital )
240     {
241         if( OpenSPDIF( p_aout ) )
242             return VLC_SUCCESS;
243     }
244     else
245     {
246         if( OpenAnalog( p_aout ) )
247             return VLC_SUCCESS;
248     }
249     
250     /* If we reach this, the Open* failed */
251     var_Destroy( p_aout, "audio-device" );
252     free( p_sys );
253     return VLC_EGENERIC;
254 }
255
256 /*****************************************************************************
257  * Open: open and setup a HAL AudioUnit
258  *****************************************************************************/
259 static int OpenAnalog( aout_instance_t *p_aout )
260 {
261     struct aout_sys_t       *p_sys = p_aout->output.p_sys;
262     OSStatus                err = noErr;
263     UInt32                  i_param_size = 0, i = 0;
264     ComponentDescription    desc;
265         
266     if( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) && !p_sys->b_supports_digital )
267     {
268         msg_Dbg( p_aout, "we had requested a digital stream, but it's not possible for this device" );
269     }
270
271     /* If analog only start setting up AUHAL */
272     /* Lets go find our Component */
273     desc.componentType = kAudioUnitType_Output;
274     desc.componentSubType = kAudioUnitSubType_HALOutput;
275     desc.componentManufacturer = kAudioUnitManufacturer_Apple;
276     desc.componentFlags = 0;
277     desc.componentFlagsMask = 0;
278
279     p_sys->au_component = FindNextComponent( NULL, &desc );
280     if( p_sys->au_component == NULL )
281     {
282         msg_Err( p_aout, "we cannot find our HAL component" );
283         return VLC_FALSE;
284     }
285
286     err = OpenAComponent( p_sys->au_component, &p_sys->au_unit );
287     if( err )
288     {
289         msg_Err( p_aout, "we cannot find our HAL component" );
290         return VLC_FALSE;
291     }
292     
293     /* Enable IO for the component */
294     msg_Dbg( p_aout, "Device: %#x", (int)p_sys->i_selected_dev );
295     
296     /* Set the device */
297     verify_noerr( AudioUnitSetProperty( p_sys->au_unit,
298                          kAudioOutputUnitProperty_CurrentDevice,
299                          kAudioUnitScope_Global,
300                          0,
301                          &p_sys->i_selected_dev,
302                          sizeof( AudioDeviceID )));
303                          
304     /* Get the current format */
305     AudioStreamBasicDescription DeviceFormat;
306     
307     i_param_size = sizeof(AudioStreamBasicDescription);
308
309     verify_noerr( AudioUnitGetProperty( p_sys->au_unit,
310                                    kAudioUnitProperty_StreamFormat,
311                                    kAudioUnitScope_Input,
312                                    0,
313                                    &DeviceFormat,
314                                    &i_param_size ));
315                                    
316     msg_Dbg( p_aout, STREAM_FORMAT_MSG( "current format is: " , DeviceFormat ) );
317
318     /* Get the channel layout */
319     AudioChannelLayout *layout;
320     verify_noerr( AudioUnitGetPropertyInfo( p_sys->au_unit,
321                                    kAudioDevicePropertyPreferredChannelLayout,
322                                    kAudioUnitScope_Output,
323                                    0,
324                                    &i_param_size,
325                                    NULL ));
326
327     layout = (AudioChannelLayout *)malloc( i_param_size);
328
329     verify_noerr( AudioUnitGetProperty( p_sys->au_unit,
330                                    kAudioDevicePropertyPreferredChannelLayout,
331                                    kAudioUnitScope_Output,
332                                    0,
333                                    layout,
334                                    &i_param_size ));
335                                    
336     /* Lets fill out the ChannelLayout */
337     if( layout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap)
338     {
339         msg_Dbg( p_aout, "bitmap defined channellayout" );
340         verify_noerr( AudioFormatGetProperty( kAudioFormatProperty_ChannelLayoutForBitmap,
341                                 sizeof( UInt32), &layout->mChannelBitmap,
342                                 &i_param_size,
343                                 layout ));
344     }
345     else if( layout->mChannelLayoutTag != kAudioChannelLayoutTag_UseChannelDescriptions )
346     {
347         msg_Dbg( p_aout, "layouttags defined channellayout" );
348         verify_noerr( AudioFormatGetProperty( kAudioFormatProperty_ChannelLayoutForTag,
349                                 sizeof( AudioChannelLayoutTag ), &layout->mChannelLayoutTag,
350                                 &i_param_size,
351                                 layout ));
352     }
353
354     msg_Dbg( p_aout, "Layout of AUHAL has %d channels" , (int)layout->mNumberChannelDescriptions );
355     
356     p_aout->output.output.i_physical_channels = 0;
357     for( i = 0; i < layout->mNumberChannelDescriptions; i++ )
358     {
359         msg_Dbg( p_aout, "This is channel: %d", (int)layout->mChannelDescriptions[i].mChannelLabel );
360
361         switch( layout->mChannelDescriptions[i].mChannelLabel )
362         {
363             case kAudioChannelLabel_Left:
364                 p_aout->output.output.i_physical_channels |= AOUT_CHAN_LEFT;
365                 continue;
366             case kAudioChannelLabel_Right:
367                 p_aout->output.output.i_physical_channels |= AOUT_CHAN_RIGHT;
368                 continue;
369             case kAudioChannelLabel_Center:
370                 p_aout->output.output.i_physical_channels |= AOUT_CHAN_CENTER;
371                 continue;
372             case kAudioChannelLabel_LFEScreen:
373                 p_aout->output.output.i_physical_channels |= AOUT_CHAN_LFE;
374                 continue;
375             case kAudioChannelLabel_LeftSurround:
376                 p_aout->output.output.i_physical_channels |= AOUT_CHAN_REARLEFT;
377                 continue;
378             case kAudioChannelLabel_RightSurround:
379                 p_aout->output.output.i_physical_channels |= AOUT_CHAN_REARRIGHT;
380                 continue;
381             case kAudioChannelLabel_RearSurroundLeft:
382                 p_aout->output.output.i_physical_channels |= AOUT_CHAN_MIDDLELEFT;
383                 continue;
384             case kAudioChannelLabel_RearSurroundRight:
385                 p_aout->output.output.i_physical_channels |= AOUT_CHAN_MIDDLERIGHT;
386                 continue;
387             case kAudioChannelLabel_CenterSurround:
388                 p_aout->output.output.i_physical_channels |= AOUT_CHAN_REARCENTER;
389                 continue;
390             default:
391                 msg_Warn( p_aout, "Unrecognized channel form provided by driver: %d", (int)layout->mChannelDescriptions[i].mChannelLabel );
392                 if( i == 0 )
393                 {
394                     msg_Warn( p_aout, "Probably no channellayout is set. force based on channelcount" );
395                     switch( layout->mNumberChannelDescriptions )
396                     {
397                         /* We make assumptions based on number of channels here.
398                          * Unfortunatly Apple has provided no 100% method to retrieve the speaker configuration */
399                         case 1:
400                             p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
401                             break;
402                         case 4:
403                             p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
404                                                                         AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
405                             break;
406                         case 6:
407                             p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
408                                                                         AOUT_CHAN_CENTER | AOUT_CHAN_LFE |
409                                                                         AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
410                             break;
411                         case 7:
412                             p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
413                                                                         AOUT_CHAN_CENTER | AOUT_CHAN_LFE |
414                                                                         AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_REARCENTER;
415                             break;
416                         case 8:
417                             p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
418                                                                         AOUT_CHAN_CENTER | AOUT_CHAN_LFE |
419                                                                         AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT |
420                                                                         AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
421                             break;
422                         case 2:
423                         default:
424                             p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
425                     }
426                 }
427                 break;
428         }
429     }
430     free( layout );
431
432     msg_Dbg( p_aout, "defined %d physical channels for vlc core", aout_FormatNbChannels( &p_aout->output.output ) );
433     msg_Dbg( p_aout, "%s", aout_FormatPrintChannels( &p_aout->output.output ));
434     
435     AudioChannelLayout new_layout;
436     memset (&new_layout, 0, sizeof(new_layout));
437     switch( aout_FormatNbChannels( &p_aout->output.output ) )
438     {
439         case 1:
440             new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_Mono;
441             break;
442         case 2:
443             new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;
444             break;
445         case 3:
446             if( p_aout->output.output.i_physical_channels & AOUT_CHAN_CENTER )
447             {
448                 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_7; // L R C
449             }
450             else if( p_aout->output.output.i_physical_channels & AOUT_CHAN_LFE )
451             {
452                 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_4; // L R LFE
453             }
454             break;
455         case 4:
456             if( p_aout->output.output.i_physical_channels & ( AOUT_CHAN_CENTER | AOUT_CHAN_LFE ) )
457             {
458                 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_10; // L R C LFE
459             }
460             else if( p_aout->output.output.i_physical_channels & ( AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT ) )
461             {
462                 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_3; // L R Ls Rs
463             }
464             else if( p_aout->output.output.i_physical_channels & ( AOUT_CHAN_CENTER | AOUT_CHAN_REARCENTER ) )
465             {
466                 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_3; // L R C Cs
467             }
468             break;
469         case 5:
470             if( p_aout->output.output.i_physical_channels & ( AOUT_CHAN_CENTER ) )
471             {
472                 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_19; // L R Ls Rs C
473             }
474             else if( p_aout->output.output.i_physical_channels & ( AOUT_CHAN_LFE ) )
475             {
476                 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_18; // L R Ls Rs LFE
477             }
478             break;
479         case 6:
480             new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_20; // L R Ls Rs C LFE
481             break;
482         case 7:
483             /* FIXME: This is incorrect. VLC uses the internal ordering: L R Lm Rm Lr Rr C LFE but this is wrong */
484             new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_6_1_A; // L R C LFE Ls Rs Cs
485             break;
486         case 8:
487             /* FIXME: This is incorrect. VLC uses the internal ordering: L R Lm Rm Lr Rr C LFE but this is wrong */
488             new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_7_1_A; // L R C LFE Ls Rs Lc Rc
489             break;
490     }
491
492     /* Set up the format to be used */
493     DeviceFormat.mSampleRate = p_aout->output.output.i_rate;
494     DeviceFormat.mFormatID = kAudioFormatLinearPCM;
495
496     /* We use float 32. It's the best supported format by both VLC and Coreaudio */
497     p_aout->output.output.i_format = VLC_FOURCC( 'f','l','3','2');
498     DeviceFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
499     DeviceFormat.mBitsPerChannel = 32;
500     DeviceFormat.mChannelsPerFrame = aout_FormatNbChannels( &p_aout->output.output );
501     
502     /* Calculate framesizes and stuff */
503     aout_FormatPrepare( &p_aout->output.output );
504     DeviceFormat.mFramesPerPacket = 1;
505     DeviceFormat.mBytesPerFrame = DeviceFormat.mBitsPerChannel * DeviceFormat.mChannelsPerFrame / 8;
506     DeviceFormat.mBytesPerPacket = DeviceFormat.mBytesPerFrame * DeviceFormat.mFramesPerPacket;
507  
508     i_param_size = sizeof(AudioStreamBasicDescription);
509     /* Set desired format (Use CAStreamBasicDescription )*/
510     verify_noerr( AudioUnitSetProperty( p_sys->au_unit,
511                                    kAudioUnitProperty_StreamFormat,
512                                    kAudioUnitScope_Input,
513                                    0,
514                                    &DeviceFormat,
515                                    i_param_size ));
516                                    
517     msg_Dbg( p_aout, STREAM_FORMAT_MSG( "we set the AU format: " , DeviceFormat ) );
518     
519     /* Retrieve actual format??? */
520     verify_noerr( AudioUnitGetProperty( p_sys->au_unit,
521                                    kAudioUnitProperty_StreamFormat,
522                                    kAudioUnitScope_Input,
523                                    0,
524                                    &DeviceFormat,
525                                    &i_param_size ));
526                                    
527     msg_Dbg( p_aout, STREAM_FORMAT_MSG( "the actual set AU format is " , DeviceFormat ) );
528
529     p_aout->output.i_nb_samples = 2048;
530     aout_VolumeSoftInit( p_aout );
531
532     /* Find the difference between device clock and mdate clock */
533     p_sys->clock_diff = - (mtime_t)
534         AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000; 
535     p_sys->clock_diff += mdate();
536
537     /* set the IOproc callback */
538     AURenderCallbackStruct input;
539     input.inputProc = (AURenderCallback) RenderCallbackAnalog;
540     input.inputProcRefCon = p_aout;
541     
542     verify_noerr( AudioUnitSetProperty( p_sys->au_unit,
543                             kAudioUnitProperty_SetRenderCallback,
544                             kAudioUnitScope_Input,
545                             0, &input, sizeof( input ) ) );
546
547     input.inputProc = (AURenderCallback) RenderCallbackAnalog;
548     input.inputProcRefCon = p_aout;
549     
550     /* Set the new_layout as the layout VLC feeds to the AU unit */
551     verify_noerr( AudioUnitSetProperty( p_sys->au_unit,
552                             kAudioUnitProperty_AudioChannelLayout,
553                             kAudioUnitScope_Input,
554                             0, &new_layout, sizeof(new_layout) ) );
555     
556     /* AU initiliaze */
557     verify_noerr( AudioUnitInitialize(p_sys->au_unit) );
558
559     verify_noerr( AudioOutputUnitStart(p_sys->au_unit) );
560     
561     return VLC_TRUE;
562 }
563
564 /*****************************************************************************
565  * Setup a encoded digital stream (SPDIF)
566  *****************************************************************************/
567 static int OpenSPDIF( aout_instance_t * p_aout )
568 {
569     struct aout_sys_t       *p_sys = p_aout->output.p_sys;
570     OSStatus                err = noErr;
571     UInt32                  i_param_size = 0, b_mix = 0;
572     Boolean                 b_writeable = VLC_FALSE;
573     AudioStreamID           *p_streams = NULL;
574     int                     i = 0, i_streams = 0;
575
576     struct timeval now;
577     struct timespec timeout;
578     struct { vlc_mutex_t lock; vlc_cond_t cond; } w;
579
580     /* Start doing the SPDIF setup proces */
581     p_sys->b_digital = VLC_TRUE;
582     msg_Dbg( p_aout, "opening in SPDIF mode" );
583
584     /* Hog the device */
585     i_param_size = sizeof( p_sys->i_hog_pid );
586     p_sys->i_hog_pid = getpid() ;
587     
588     err = AudioDeviceSetProperty( p_sys->i_selected_dev, 0, 0, FALSE,
589                                   kAudioDevicePropertyHogMode, i_param_size, &p_sys->i_hog_pid );
590     
591     if( err != noErr )
592     {
593         msg_Err( p_aout, "failed to set hogmode: : [%4.4s]", (char *)&err );
594         return VLC_FALSE;
595     }
596
597     /* Set mixable to false if we are allowed to */
598     err = AudioDeviceGetPropertyInfo( p_sys->i_selected_dev, 0, FALSE, kAudioDevicePropertySupportsMixing,
599                                     &i_param_size, &b_writeable );
600
601     err = AudioDeviceGetProperty( p_sys->i_selected_dev, 0, FALSE, kAudioDevicePropertySupportsMixing,
602                                     &i_param_size, &b_mix );
603                                     
604     if( !err && b_writeable )
605     {
606         b_mix = 0;
607         err = AudioDeviceSetProperty( p_sys->i_selected_dev, 0, 0, FALSE,
608                             kAudioDevicePropertySupportsMixing, i_param_size, &b_mix );
609         p_sys->b_changed_mixing = VLC_TRUE;
610     }
611     
612     if( err != noErr )
613     {
614         msg_Err( p_aout, "failed to set mixmode: [%4.4s]", (char *)&err );
615         return VLC_FALSE;
616     }
617
618     // Find stream_id of selected device with a cac3 stream
619     err = AudioDeviceGetPropertyInfo( p_sys->i_selected_dev, 0, FALSE,
620                                       kAudioDevicePropertyStreams,
621                                       &i_param_size, NULL );
622     if( err != noErr )
623     {
624         msg_Err( p_aout, "could not get number of streams: [%4.4s]", (char *)&err );
625         return VLC_FALSE;
626     }
627     
628     i_streams = i_param_size / sizeof( AudioStreamID );
629     p_streams = (AudioStreamID *)malloc( i_param_size );
630     if( p_streams == NULL )
631     {
632         msg_Err( p_aout, "Out of memory" );
633         return VLC_FALSE;
634     }
635     
636     err = AudioDeviceGetProperty( p_sys->i_selected_dev, 0, FALSE,
637                                     kAudioDevicePropertyStreams,
638                                     &i_param_size, p_streams );
639     
640     if( err != noErr )
641     {
642         msg_Err( p_aout, "could not get number of streams: [%4.4s]", (char *)&err );
643         if( p_streams ) free( p_streams );
644         return VLC_FALSE;
645     }
646
647     for( i = 0; i < i_streams; i++ )
648     {
649         // Find a stream with a cac3 stream
650         AudioStreamBasicDescription *p_format_list = NULL;
651         int                         i_formats = 0, j = 0;
652         
653         /* Retrieve all the stream formats supported by each output stream */
654         err = AudioStreamGetPropertyInfo( p_streams[i], 0,
655                                           kAudioStreamPropertyPhysicalFormats,
656                                           &i_param_size, NULL );
657         if( err != noErr )
658         {
659             msg_Err( p_aout, "could not get number of streamformats: [%4.4s]", (char *)&err );
660             continue;
661         }
662         
663         i_formats = i_param_size / sizeof( AudioStreamBasicDescription );
664         p_format_list = (AudioStreamBasicDescription *)malloc( i_param_size );
665         if( p_format_list == NULL )
666         {
667             msg_Err( p_aout, "Could not malloc the memory" );
668             continue;
669         }
670         
671         err = AudioStreamGetProperty( p_streams[i], 0,
672                                           kAudioStreamPropertyPhysicalFormats,
673                                           &i_param_size, p_format_list );
674         if( err != noErr )
675         {
676             msg_Err( p_aout, "could not get the list of streamformats: [%4.4s]", (char *)&err );
677             if( p_format_list) free( p_format_list);
678             continue;
679         }
680
681         for( j = 0; j < i_formats; j++ )
682         {
683             if( p_format_list[j].mFormatID == 'IAC3' ||
684                   p_format_list[j].mFormatID == kAudioFormat60958AC3 )
685             {
686                 // found a cac3 format
687                 p_sys->i_stream_id = p_streams[i];
688                 p_sys->i_stream_index = i;
689
690                 if( p_sys->b_revert == VLC_FALSE )
691                 {
692                     i_param_size = sizeof( p_sys->sfmt_revert );
693                     err = AudioStreamGetProperty( p_sys->i_stream_id, 0,
694                                                   kAudioStreamPropertyPhysicalFormat,
695                                                   &i_param_size, 
696                                                   &p_sys->sfmt_revert );
697                     if( err != noErr )
698                     {
699                         msg_Err( p_aout, "could not retrieve the original streamformat: [%4.4s]", (char *)&err );
700                         continue; 
701                     }
702                     p_sys->b_revert = VLC_TRUE;
703                 }
704                 if( p_format_list[j].mSampleRate == p_sys->sfmt_revert.mSampleRate )
705                 {
706                     p_sys->stream_format = p_format_list[j];
707                 }
708             }
709         }
710         if( p_format_list ) free( p_format_list );
711     }
712     
713     if( p_streams ) free( p_streams );
714
715     msg_Dbg( p_aout, STREAM_FORMAT_MSG( "original stream format: ", p_sys->sfmt_revert ) );
716     msg_Dbg( p_aout, STREAM_FORMAT_MSG( "setting stream format: ", p_sys->stream_format ) );
717
718     /* Install the callback */
719     err = AudioStreamAddPropertyListener( p_sys->i_stream_id, 0,
720                                       kAudioStreamPropertyPhysicalFormat,
721                                       StreamListener, (void *)&w );
722     if( err != noErr )
723     {
724         msg_Err( p_aout, "AudioStreamAddPropertyListener failed: [%4.4s]", (char *)&err );
725         return VLC_FALSE;
726     }
727
728     /* Condition because SetProperty is asynchronious */ 
729     vlc_cond_init( p_aout, &w.cond );
730     vlc_mutex_init( p_aout, &w.lock );
731     vlc_mutex_lock( &w.lock );
732     
733     /* change the format */
734     err = AudioStreamSetProperty( p_sys->i_stream_id, 0, 0,
735                                   kAudioStreamPropertyPhysicalFormat,
736                                   sizeof( AudioStreamBasicDescription ),
737                                   &p_sys->stream_format ); 
738     if( err != noErr )
739     {
740         msg_Err( p_aout, "could not set the stream format: [%4.4s]", (char *)&err );
741         vlc_mutex_unlock( &w.lock );
742         vlc_mutex_destroy( &w.lock );
743         vlc_cond_destroy( &w.cond );
744         return VLC_FALSE;
745     }
746
747     gettimeofday( &now, NULL );
748     timeout.tv_sec = now.tv_sec;
749     timeout.tv_nsec = (now.tv_usec + 900000) * 1000;
750
751     pthread_cond_timedwait( &w.cond.cond, &w.lock.mutex, &timeout );
752     vlc_mutex_unlock( &w.lock );
753
754     err = AudioStreamRemovePropertyListener( p_sys->i_stream_id, 0,
755                                         kAudioStreamPropertyPhysicalFormat,
756                                         StreamListener );
757     if( err != noErr )
758     {
759         msg_Err( p_aout, "AudioStreamRemovePropertyListener failed: [%4.4s]", (char *)&err );
760         vlc_mutex_destroy( &w.lock );
761         vlc_cond_destroy( &w.cond );
762         return VLC_FALSE;
763     }
764
765     vlc_mutex_destroy( &w.lock );
766     vlc_cond_destroy( &w.cond );
767     
768     i_param_size = sizeof( AudioStreamBasicDescription );
769     err = AudioStreamGetProperty( p_sys->i_stream_id, 0,
770                                   kAudioStreamPropertyPhysicalFormat,
771                                   &i_param_size, 
772                                   &p_sys->stream_format );
773
774     msg_Dbg( p_aout, STREAM_FORMAT_MSG( "actual format in use: ", p_sys->stream_format ) );
775
776     /* set the format flags */
777     if( p_sys->stream_format.mFormatFlags & kAudioFormatFlagIsBigEndian )
778         p_aout->output.output.i_format = VLC_FOURCC('s','p','d','b');
779     else
780         p_aout->output.output.i_format = VLC_FOURCC('s','p','d','i');
781     p_aout->output.output.i_bytes_per_frame = AOUT_SPDIF_SIZE;
782     p_aout->output.output.i_frame_length = A52_FRAME_NB;
783     p_aout->output.i_nb_samples = p_aout->output.output.i_frame_length;
784     p_aout->output.output.i_rate = (unsigned int)p_sys->stream_format.mSampleRate;
785
786     aout_VolumeNoneInit( p_aout );
787
788     /* Add IOProc callback */
789     err = AudioDeviceAddIOProc( p_sys->i_selected_dev,
790                                 (AudioDeviceIOProc)RenderCallbackSPDIF,
791                                 (void *)p_aout );
792     if( err != noErr )
793     {
794         msg_Err( p_aout, "AudioDeviceAddIOProc failed: [%4.4s]", (char *)&err );
795         return VLC_FALSE;
796     }
797
798     /* Check for the difference between the Device clock and mdate */
799     p_sys->clock_diff = - (mtime_t)
800         AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000; 
801     p_sys->clock_diff += mdate();
802  
803     /* Start device */
804     err = AudioDeviceStart( p_sys->i_selected_dev, (AudioDeviceIOProc)RenderCallbackSPDIF ); 
805     if( err != noErr )
806     {
807         msg_Err( p_aout, "AudioDeviceStart failed: [%4.4s]", (char *)&err );
808
809         err = AudioDeviceRemoveIOProc( p_sys->i_selected_dev, 
810                                        (AudioDeviceIOProc)RenderCallbackSPDIF );
811         if( err != noErr )
812         {
813             msg_Err( p_aout, "AudioDeviceRemoveIOProc failed: [%4.4s]", (char *)&err );
814         }
815         return VLC_FALSE;
816     }
817
818     return VLC_TRUE;
819 }
820
821
822 /*****************************************************************************
823  * Close: Close HAL AudioUnit
824  *****************************************************************************/
825 static void Close( vlc_object_t * p_this )
826 {
827     aout_instance_t     *p_aout = (aout_instance_t *)p_this;
828     struct aout_sys_t   *p_sys = p_aout->output.p_sys;
829     OSStatus            err = noErr;
830     UInt32              i_param_size = 0;
831     
832     if( p_sys->au_unit )
833     {
834         verify_noerr( AudioOutputUnitStop( p_sys->au_unit ) );
835         verify_noerr( AudioUnitUninitialize( p_sys->au_unit ) );
836         verify_noerr( CloseComponent( p_sys->au_unit ) );
837     }
838     
839     if( p_sys->b_digital )
840     {
841         /* Stop device */
842         err = AudioDeviceStop( p_sys->i_selected_dev, 
843                                (AudioDeviceIOProc)RenderCallbackSPDIF ); 
844         if( err != noErr )
845         {
846             msg_Err( p_aout, "AudioDeviceStop failed: [%4.4s]", (char *)&err );
847         }
848
849         /* Remove callback */
850         err = AudioDeviceRemoveIOProc( p_sys->i_selected_dev,
851                                        (AudioDeviceIOProc)RenderCallbackSPDIF );
852         if( err != noErr )
853         {
854             msg_Err( p_aout, "AudioDeviceRemoveIOProc failed: [%4.4s]", (char *)&err );
855         }
856         
857         if( p_sys->b_revert )
858         {
859             struct timeval now;
860             struct timespec timeout;
861             struct { vlc_mutex_t lock; vlc_cond_t cond; } w;
862
863             /* Install the callback */
864             err = AudioStreamAddPropertyListener( p_sys->i_stream_id, 0,
865                                               kAudioStreamPropertyPhysicalFormat,
866                                               StreamListener, (void *)&w );
867             if( err != noErr )
868             {
869                 msg_Err( p_aout, "AudioStreamAddPropertyListener failed: [%4.4s]", (char *)&err );
870             }
871
872             /* Condition because SetProperty is asynchronious */ 
873             vlc_cond_init( p_aout, &w.cond );
874             vlc_mutex_init( p_aout, &w.lock );
875             vlc_mutex_lock( &w.lock );
876
877             msg_Dbg( p_aout, STREAM_FORMAT_MSG( "setting stream format: ", p_sys->sfmt_revert ) );
878
879             err = AudioStreamSetProperty( p_sys->i_stream_id, NULL, 0,
880                                             kAudioStreamPropertyPhysicalFormat,
881                                             sizeof( AudioStreamBasicDescription ),
882                                             &p_sys->sfmt_revert );
883                                             
884             if( err != noErr )
885             {
886                 msg_Err( p_aout, "Streamformat reverse failed: [%4.4s]", (char *)&err );
887             }
888             
889             gettimeofday( &now, NULL );
890             timeout.tv_sec = now.tv_sec;
891             timeout.tv_nsec = (now.tv_usec + 900000) * 1000;
892
893             pthread_cond_timedwait( &w.cond.cond, &w.lock.mutex, &timeout );
894             vlc_mutex_unlock( &w.lock );
895
896             err = AudioStreamRemovePropertyListener( p_sys->i_stream_id, 0,
897                                                 kAudioStreamPropertyPhysicalFormat,
898                                                 StreamListener );
899             if( err != noErr )
900             {
901                 msg_Err( p_aout, "AudioStreamRemovePropertyListener failed: [%4.4s]", (char *)&err );
902             }
903
904             vlc_mutex_destroy( &w.lock );
905             vlc_cond_destroy( &w.cond );
906             
907             i_param_size = sizeof( AudioStreamBasicDescription );
908             err = AudioStreamGetProperty( p_sys->i_stream_id, 0,
909                                           kAudioStreamPropertyPhysicalFormat,
910                                           &i_param_size, 
911                                           &p_sys->stream_format );
912
913             msg_Dbg( p_aout, STREAM_FORMAT_MSG( "actual format in use: ", p_sys->stream_format ) );
914         }
915         if( p_sys->b_changed_mixing && p_sys->sfmt_revert.mFormatID != kAudioFormat60958AC3 )
916         {
917             int b_mix;
918             Boolean b_writeable;
919             /* Revert mixable to true if we are allowed to */
920             err = AudioDeviceGetPropertyInfo( p_sys->i_selected_dev, 0, FALSE, kAudioDevicePropertySupportsMixing,
921                                         &i_param_size, &b_writeable );
922
923             err = AudioDeviceGetProperty( p_sys->i_selected_dev, 0, FALSE, kAudioDevicePropertySupportsMixing,
924                                         &i_param_size, &b_mix );
925                                         
926             if( !err && b_writeable )
927             {
928                 msg_Dbg( p_aout, "mixable is: %d", b_mix );
929                 b_mix = 1;
930                 err = AudioDeviceSetProperty( p_sys->i_selected_dev, 0, 0, FALSE,
931                                     kAudioDevicePropertySupportsMixing, i_param_size, &b_mix );
932             }
933
934             if( err != noErr )
935             {
936                 msg_Err( p_aout, "failed to set mixmode: [%4.4s]", (char *)&err );
937             }
938         }
939     }
940
941     err = AudioHardwareRemovePropertyListener( kAudioHardwarePropertyDevices,
942                                                HardwareListener );
943                                                
944     if( err != noErr )
945     {
946         msg_Err( p_aout, "AudioHardwareRemovePropertyListener failed: [%4.4s]", (char *)&err );
947     }
948     
949     if( p_sys->i_hog_pid == getpid() )
950     {
951         p_sys->i_hog_pid = -1;
952         i_param_size = sizeof( p_sys->i_hog_pid );
953         err = AudioDeviceSetProperty( p_sys->i_selected_dev, 0, 0, FALSE,
954                                          kAudioDevicePropertyHogMode, i_param_size, &p_sys->i_hog_pid );
955         if( err != noErr ) msg_Err( p_aout, "Could not release hogmode: [%4.4s]", (char *)&err );
956     }
957     
958     if( p_sys ) free( p_sys );
959 }
960
961 /*****************************************************************************
962  * Play: nothing to do
963  *****************************************************************************/
964 static void Play( aout_instance_t * p_aout )
965 {
966 }
967
968
969 /*****************************************************************************
970  * Probe
971  *****************************************************************************/
972 static void Probe( aout_instance_t * p_aout )
973 {
974     OSStatus            err = noErr;
975     UInt32              i = 0, i_param_size = 0;
976     AudioDeviceID       devid_def = 0;
977     AudioDeviceID       *p_devices = NULL;
978     vlc_value_t         val, text;
979
980     struct aout_sys_t   *p_sys = p_aout->output.p_sys;
981
982     /* Get number of devices */
983     err = AudioHardwareGetPropertyInfo( kAudioHardwarePropertyDevices,
984                                         &i_param_size, NULL );
985     if( err != noErr )
986     {
987         msg_Err( p_aout, "could not get number of devices: [%4.4s]", (char *)&err );
988         goto error;
989     }
990
991     p_sys->i_devices = i_param_size / sizeof( AudioDeviceID );
992
993     if( p_sys->i_devices < 1 )
994     {
995         msg_Err( p_aout, "no devices found" );
996         goto error;
997     }
998
999     msg_Dbg( p_aout, "system has [%ld] device(s)", p_sys->i_devices );
1000
1001     /* Allocate DeviceID array */
1002     p_devices = (AudioDeviceID*)malloc( sizeof(AudioDeviceID) * p_sys->i_devices );
1003     if( p_devices == NULL )
1004     {
1005         msg_Err( p_aout, "out of memory" );
1006         goto error;
1007     }
1008
1009     /* Populate DeviceID array */
1010     err = AudioHardwareGetProperty( kAudioHardwarePropertyDevices,
1011                                     &i_param_size, p_devices );
1012     if( err != noErr )
1013     {
1014         msg_Err( p_aout, "could not get the device ID's: [%4.4s]", (char *)&err );
1015         goto error;
1016     }
1017
1018     /* Find the ID of the default Device */
1019     i_param_size = sizeof( AudioDeviceID );
1020     err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice,
1021                                     &i_param_size, &devid_def );
1022     if( err != noErr )
1023     {
1024         msg_Err( p_aout, "could not get default audio device: [%4.4s]", (char *)&err );
1025         goto error;
1026     }
1027     p_sys->i_default_dev = devid_def;
1028     
1029     var_Create( p_aout, "audio-device", VLC_VAR_INTEGER|VLC_VAR_HASCHOICE );
1030     text.psz_string = _("Audio Device");
1031     var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );
1032     
1033     for( i = 0; i < p_sys->i_devices; i++ )
1034     {
1035         char *psz_name;
1036         i_param_size = 0;
1037
1038         /* Retrieve the length of the device name */
1039         err = AudioDeviceGetPropertyInfo(
1040                     p_devices[i], 0, VLC_FALSE,
1041                     kAudioDevicePropertyDeviceName,
1042                     &i_param_size, NULL);
1043         if( err ) goto error;
1044
1045         /* Retrieve the name of the device */
1046         psz_name = (char *)malloc( i_param_size );
1047         err = AudioDeviceGetProperty(
1048                     p_devices[i], 0, VLC_FALSE,
1049                     kAudioDevicePropertyDeviceName,
1050                     &i_param_size, psz_name);
1051         if( err ) goto error;
1052
1053         msg_Dbg( p_aout, "DevID: %lu  DevName: %s", p_devices[i], psz_name );
1054
1055         if( !AudioDeviceHasOutput( p_devices[i]) )
1056         {
1057             msg_Dbg( p_aout, "this device is INPUT only. skipping..." );
1058             continue;
1059         }
1060
1061         val.i_int = (int)p_devices[i];
1062         text.psz_string = strdup( psz_name );
1063         var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
1064         if( p_sys->i_default_dev == p_devices[i] )
1065         {
1066             var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
1067             var_Set( p_aout, "audio-device", val );
1068         }
1069
1070         if( AudioDeviceSupportsDigital( p_aout, p_devices[i] ) )
1071         {
1072             val.i_int = (int)p_devices[i] | AOUT_VAR_SPDIF_FLAG;
1073             asprintf( &text.psz_string, "%s (Encoded Output)", psz_name );
1074             var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
1075             if( p_sys->i_default_dev == p_devices[i] && config_GetInt( p_aout, "spdif" ) )
1076             {
1077                 var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
1078                 var_Set( p_aout, "audio-device", val );
1079             }
1080         }
1081         
1082         free( psz_name);
1083     }
1084     
1085     var_Get( p_aout->p_vlc, "macosx-audio-device", &val );
1086     msg_Dbg( p_aout, "device value override1: %#x", val.i_int );
1087     if( val.i_int > 0 )
1088     {
1089         msg_Dbg( p_aout, "device value override2: %#x", val.i_int );
1090         var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
1091         var_Set( p_aout, "audio-device", val );
1092     }
1093     
1094     var_AddCallback( p_aout, "audio-device", AudioDeviceCallback, NULL );
1095
1096     /* attach a Listener so that we are notified of a change in the Device setup */
1097     err = AudioHardwareAddPropertyListener( kAudioHardwarePropertyDevices,
1098                                             HardwareListener, 
1099                                             (void *)p_aout );
1100     if( err )
1101         goto error;
1102
1103     msg_Dbg( p_aout, "succesful finish of deviceslist" );
1104     if( p_devices ) free( p_devices );
1105     return;
1106
1107 error:
1108     var_Destroy( p_aout, "audio-device" );
1109     if( p_devices ) free( p_devices );
1110     return;
1111 }
1112
1113 /*****************************************************************************
1114  * AudioDeviceHasOutput: Checks if the Device actually provides any outputs at all
1115  *****************************************************************************/
1116 static int AudioDeviceHasOutput( AudioDeviceID i_dev_id )
1117 {
1118     UInt32                      dataSize;
1119     Boolean                     isWritable;
1120         
1121     verify_noerr( AudioDeviceGetPropertyInfo( i_dev_id, 0, FALSE, kAudioDevicePropertyStreams, &dataSize, &isWritable) );
1122     if (dataSize == 0) return FALSE;
1123     
1124     return TRUE;
1125 }
1126
1127 /*****************************************************************************
1128  * AudioDeviceSupportsDigital: Check i_dev_id for digital stream support.
1129  *****************************************************************************/
1130 static int AudioDeviceSupportsDigital( aout_instance_t *p_aout, AudioDeviceID i_dev_id )
1131 {
1132     OSStatus                    err = noErr;
1133     UInt32                      i_param_size = 0;
1134     AudioStreamID               *p_streams = NULL;
1135     int                         i = 0, i_streams = 0;
1136     vlc_bool_t                  b_return = VLC_FALSE;
1137     
1138     /* Retrieve all the output streams */
1139     err = AudioDeviceGetPropertyInfo( i_dev_id, 0, FALSE,
1140                                       kAudioDevicePropertyStreams,
1141                                       &i_param_size, NULL );
1142     if( err != noErr )
1143     {
1144         msg_Err( p_aout, "could not get number of streams: [%4.4s]", (char *)&err );
1145         return VLC_FALSE;
1146     }
1147     
1148     i_streams = i_param_size / sizeof( AudioStreamID );
1149     p_streams = (AudioStreamID *)malloc( i_param_size );
1150     if( p_streams == NULL )
1151     {
1152         msg_Err( p_aout, "Out of memory" );
1153         return VLC_ENOMEM;
1154     }
1155     
1156     err = AudioDeviceGetProperty( i_dev_id, 0, FALSE,
1157                                     kAudioDevicePropertyStreams,
1158                                     &i_param_size, p_streams );
1159     
1160     if( err != noErr )
1161     {
1162         msg_Err( p_aout, "could not get number of streams: [%4.4s]", (char *)&err );
1163         return VLC_FALSE;
1164     }
1165
1166     for( i = 0; i < i_streams; i++ )
1167     {
1168         if( AudioStreamSupportsDigital( p_aout, p_streams[i] ) )
1169             b_return = VLC_TRUE;
1170     }
1171     
1172     if( p_streams ) free( p_streams );
1173     return b_return;
1174 }
1175
1176 /*****************************************************************************
1177  * AudioStreamSupportsDigital: Check i_stream_id for digital stream support.
1178  *****************************************************************************/
1179 static int AudioStreamSupportsDigital( aout_instance_t *p_aout, AudioStreamID i_stream_id )
1180 {
1181     OSStatus                    err = noErr;
1182     UInt32                      i_param_size = 0;
1183     AudioStreamBasicDescription *p_format_list = NULL;
1184     int                         i = 0, i_formats = 0;
1185     vlc_bool_t                  b_return = VLC_FALSE;
1186     
1187     /* Retrieve all the stream formats supported by each output stream */
1188     err = AudioStreamGetPropertyInfo( i_stream_id, 0,
1189                                       kAudioStreamPropertyPhysicalFormats,
1190                                       &i_param_size, NULL );
1191     if( err != noErr )
1192     {
1193         msg_Err( p_aout, "could not get number of streamformats: [%4.4s]", (char *)&err );
1194         return VLC_FALSE;
1195     }
1196     
1197     i_formats = i_param_size / sizeof( AudioStreamBasicDescription );
1198     msg_Dbg( p_aout, "number of formats: %d", i_formats );
1199     p_format_list = (AudioStreamBasicDescription *)malloc( i_param_size );
1200     if( p_format_list == NULL )
1201     {
1202         msg_Err( p_aout, "Could not malloc the memory" );
1203         return VLC_FALSE;
1204     }
1205     
1206     err = AudioStreamGetProperty( i_stream_id, 0,
1207                                       kAudioStreamPropertyPhysicalFormats,
1208                                       &i_param_size, p_format_list );
1209     if( err != noErr )
1210     {
1211         msg_Err( p_aout, "could not get the list of streamformats: [%4.4s]", (char *)&err );
1212         free( p_format_list);
1213         p_format_list = NULL;
1214         return VLC_FALSE;
1215     }
1216
1217     for( i = 0; i < i_formats; i++ )
1218     {
1219         msg_Dbg( p_aout, STREAM_FORMAT_MSG( "supported format", p_format_list[i] ) );
1220         
1221         if( p_format_list[i].mFormatID == 'IAC3' ||
1222                   p_format_list[i].mFormatID == kAudioFormat60958AC3 )
1223         {
1224             b_return = VLC_TRUE;
1225         }
1226     }
1227     
1228     if( p_format_list ) free( p_format_list );
1229     return b_return;
1230 }
1231
1232 /*****************************************************************************
1233  * RenderCallbackAnalog: This function is called everytime the AudioUnit wants
1234  * us to provide some more audio data.
1235  * Don't print anything during normal playback, calling blocking function from
1236  * this callback is not allowed.
1237  *****************************************************************************/
1238 static OSStatus RenderCallbackAnalog( vlc_object_t *_p_aout,
1239                                       AudioUnitRenderActionFlags *ioActionFlags,
1240                                       const AudioTimeStamp *inTimeStamp,
1241                                       unsigned int inBusNummer,
1242                                       unsigned int inNumberFrames,
1243                                       AudioBufferList *ioData )
1244 {
1245     AudioTimeStamp  host_time;
1246     mtime_t         current_date = 0;
1247     uint32_t        i_mData_bytes = 0;    
1248
1249     aout_instance_t * p_aout = (aout_instance_t *)_p_aout;
1250     struct aout_sys_t * p_sys = p_aout->output.p_sys;
1251
1252     host_time.mFlags = kAudioTimeStampHostTimeValid;
1253     AudioDeviceTranslateTime( p_sys->i_selected_dev, inTimeStamp, &host_time );
1254
1255     /* Check for the difference between the Device clock and mdate */
1256     p_sys->clock_diff = - (mtime_t)
1257         AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000; 
1258     p_sys->clock_diff += mdate();
1259
1260     current_date = p_sys->clock_diff +
1261                    AudioConvertHostTimeToNanos( host_time.mHostTime ) / 1000;
1262                    //- ((mtime_t) 1000000 / p_aout->output.output.i_rate * 31 ); // 31 = Latency in Frames. retrieve somewhere
1263
1264     if( ioData == NULL && ioData->mNumberBuffers < 1 )
1265     {
1266         msg_Err( p_aout, "no iodata or buffers");
1267         return 0;
1268     }
1269     if( ioData->mNumberBuffers > 1 )
1270         msg_Err( p_aout, "well this is weird. seems like there is more than one buffer..." );
1271
1272
1273     if( p_sys->i_total_bytes > 0 )
1274     {
1275         i_mData_bytes = __MIN( p_sys->i_total_bytes - p_sys->i_read_bytes, ioData->mBuffers[0].mDataByteSize );
1276         p_aout->p_vlc->pf_memcpy( ioData->mBuffers[0].mData, &p_sys->p_remainder_buffer[p_sys->i_read_bytes], i_mData_bytes );
1277         p_sys->i_read_bytes += i_mData_bytes;
1278         current_date += (mtime_t) ( (mtime_t) 1000000 / p_aout->output.output.i_rate ) *
1279                         ( i_mData_bytes / 4 / aout_FormatNbChannels( &p_aout->output.output )  ); // 4 is fl32 specific
1280         
1281         if( p_sys->i_read_bytes >= p_sys->i_total_bytes )
1282             p_sys->i_read_bytes = p_sys->i_total_bytes = 0;
1283     }
1284     
1285     while( i_mData_bytes < ioData->mBuffers[0].mDataByteSize )
1286     {
1287         /* We don't have enough data yet */
1288         aout_buffer_t * p_buffer;
1289         p_buffer = aout_OutputNextBuffer( p_aout, current_date , VLC_FALSE );
1290         
1291         if( p_buffer != NULL )
1292         {
1293             uint32_t i_second_mData_bytes = __MIN( p_buffer->i_nb_bytes, ioData->mBuffers[0].mDataByteSize - i_mData_bytes );
1294             
1295             p_aout->p_vlc->pf_memcpy( (uint8_t *)ioData->mBuffers[0].mData + i_mData_bytes, p_buffer->p_buffer, i_second_mData_bytes );
1296             i_mData_bytes += i_second_mData_bytes;
1297
1298             if( i_mData_bytes >= ioData->mBuffers[0].mDataByteSize )
1299             {
1300                 p_sys->i_total_bytes = p_buffer->i_nb_bytes - i_second_mData_bytes;
1301                 p_aout->p_vlc->pf_memcpy( p_sys->p_remainder_buffer, &p_buffer->p_buffer[i_second_mData_bytes], p_sys->i_total_bytes );
1302             }
1303             else
1304             {
1305                 // update current_date
1306                 current_date += (mtime_t) ( (mtime_t) 1000000 / p_aout->output.output.i_rate ) *
1307                                 ( i_second_mData_bytes / 4 / aout_FormatNbChannels( &p_aout->output.output )  ); // 4 is fl32 specific
1308             }
1309             aout_BufferFree( p_buffer );
1310         }
1311         else
1312         {
1313              p_aout->p_vlc->pf_memset( (uint8_t *)ioData->mBuffers[0].mData +i_mData_bytes, 0, ioData->mBuffers[0].mDataByteSize - i_mData_bytes );
1314              i_mData_bytes += ioData->mBuffers[0].mDataByteSize - i_mData_bytes;
1315         }
1316     }
1317     return( noErr );     
1318 }
1319
1320 /*****************************************************************************
1321  * RenderCallbackSPDIF: callback for SPDIF audio output
1322  *****************************************************************************/
1323 static OSStatus RenderCallbackSPDIF( AudioDeviceID inDevice,
1324                                     const AudioTimeStamp * inNow, 
1325                                     const void * inInputData,
1326                                     const AudioTimeStamp * inInputTime, 
1327                                     AudioBufferList * outOutputData,
1328                                     const AudioTimeStamp * inOutputTime, 
1329                                     void * threadGlobals )
1330 {
1331     aout_buffer_t * p_buffer;
1332     mtime_t         current_date;
1333
1334     aout_instance_t * p_aout = (aout_instance_t *)threadGlobals;
1335     struct aout_sys_t * p_sys = p_aout->output.p_sys;
1336
1337     /* Check for the difference between the Device clock and mdate */
1338     p_sys->clock_diff = - (mtime_t)
1339         AudioConvertHostTimeToNanos( inNow->mHostTime ) / 1000; 
1340     p_sys->clock_diff += mdate();
1341
1342     current_date = p_sys->clock_diff +
1343                    AudioConvertHostTimeToNanos( inOutputTime->mHostTime ) / 1000;
1344                    //- ((mtime_t) 1000000 / p_aout->output.output.i_rate * 31 ); // 31 = Latency in Frames. retrieve somewhere
1345
1346     p_buffer = aout_OutputNextBuffer( p_aout, current_date, VLC_TRUE );
1347
1348 #define BUFFER outOutputData->mBuffers[p_sys->i_stream_index]
1349     if( p_buffer != NULL )
1350     {
1351         if( (int)BUFFER.mDataByteSize != (int)p_buffer->i_nb_bytes)
1352             msg_Warn( p_aout, "bytesize: %d nb_bytes: %d", (int)BUFFER.mDataByteSize, (int)p_buffer->i_nb_bytes );
1353         
1354         /* move data into output data buffer */
1355         p_aout->p_vlc->pf_memcpy( BUFFER.mData,
1356                                   p_buffer->p_buffer, p_buffer->i_nb_bytes );
1357         aout_BufferFree( p_buffer );
1358     }
1359     else
1360     {
1361         p_aout->p_vlc->pf_memset( BUFFER.mData, 0, BUFFER.mDataByteSize );
1362     }
1363 #undef BUFFER
1364
1365     return( noErr );     
1366 }
1367
1368 /*****************************************************************************
1369  * HardwareListener: Warns us of changes in the list of registered devices
1370  *****************************************************************************/
1371 static OSStatus HardwareListener( AudioHardwarePropertyID inPropertyID,
1372                                   void * inClientData )
1373 {
1374     OSStatus err = noErr;
1375     aout_instance_t     *p_aout = (aout_instance_t *)inClientData;
1376
1377     switch( inPropertyID )
1378     {
1379         case kAudioHardwarePropertyDevices:
1380         {
1381             /* something changed in the list of devices */
1382             /* We trigger the audio-device's aout_ChannelsRestart callback */
1383             var_Change( p_aout, "audio-device", VLC_VAR_TRIGGER_CALLBACKS, NULL, NULL );
1384             var_Destroy( p_aout, "audio-device" );
1385         }
1386         break;
1387     }
1388
1389     return( err );
1390 }
1391
1392 /*****************************************************************************
1393  * StreamListener 
1394  *****************************************************************************/
1395 static OSStatus StreamListener( AudioStreamID inStream,
1396                                 UInt32 inChannel,
1397                                 AudioDevicePropertyID inPropertyID,
1398                                 void * inClientData )
1399 {
1400     OSStatus err = noErr;
1401     struct { vlc_mutex_t lock; vlc_cond_t cond; } * w = inClientData;
1402
1403     switch( inPropertyID )
1404     {
1405         case kAudioStreamPropertyPhysicalFormat:
1406             vlc_mutex_lock( &w->lock );
1407             vlc_cond_signal( &w->cond );
1408             vlc_mutex_unlock( &w->lock ); 
1409             break;
1410
1411         default:
1412             break;
1413     }
1414     return( err );
1415 }
1416
1417 /*****************************************************************************
1418  * AudioDeviceCallback: Callback triggered when the audio-device variable is changed
1419  *****************************************************************************/
1420 static int AudioDeviceCallback( vlc_object_t *p_this, const char *psz_variable,
1421                      vlc_value_t old_val, vlc_value_t new_val, void *param )
1422 {
1423     aout_instance_t *p_aout = (aout_instance_t *)p_this;
1424     var_Set( p_aout->p_vlc, "macosx-audio-device", new_val );
1425     msg_Dbg( p_aout, "Set Device: %#x", new_val.i_int );
1426     return aout_ChannelsRestart( p_this, psz_variable, old_val, new_val, param );
1427 }
1428