]> git.sesse.net Git - vlc/blob - modules/audio_output/auhal.c
First steps towards integrating coreaudio and auhal module into one. It does not...
[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 0xf0000000
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     Component                   au_component;   /* The Audiocomponent we use */
73     AudioUnit                   au_unit;        /* The AudioUnit we use */
74     mtime_t                     clock_diff;
75     uint8_t                     p_remainder_buffer[BUFSIZE];
76     uint32_t                    i_read_bytes;
77     uint32_t                    i_total_bytes;
78 };
79
80 /*****************************************************************************
81  * Local prototypes.
82  *****************************************************************************/
83 static int      Open                    ( vlc_object_t * );
84 int             OpenAnalog              ( aout_instance_t * );
85 int             OpenSPDIF               ( aout_instance_t * );
86 static void     Close                   ( vlc_object_t * );
87
88 static void     Play                    ( aout_instance_t * );
89 static void     Probe                   ( aout_instance_t * );
90
91 int             AudioDeviceSupportsDigital( aout_instance_t *, AudioDeviceID );
92 int             AudioDeviceHasOutput    ( AudioDeviceID );
93
94 static OSStatus RenderCallbackAnalog    ( vlc_object_t *, AudioUnitRenderActionFlags *, const AudioTimeStamp *,
95                                           unsigned int, unsigned int, AudioBufferList *);
96 static OSStatus HardwareListener        ( AudioHardwarePropertyID, void *);
97
98 /*****************************************************************************
99  * Module descriptor
100  *****************************************************************************/
101 #define ADEV_TEXT N_("Audio Device")
102 #define ADEV_LONGTEXT N_("Choose a number corresponding to the number of an " \
103     "audio device, as listed in your 'Audio Device' menu. This device will " \
104     "then be used by default for audio playback.")
105
106 vlc_module_begin();
107     set_shortname( "auhal" );
108     set_description( _("HAL AudioUnit output") );
109     set_capability( "audio output", 101 );
110     set_category( CAT_AUDIO );
111     set_subcategory( SUBCAT_AUDIO_AOUT );
112     set_callbacks( Open, Close );
113     //add_integer( "macosx-audio-device", -1, NULL, ADEV_TEXT, ADEV_LONGTEXT, VLC_FALSE ); 
114 vlc_module_end();
115
116 /*****************************************************************************
117  * Open: open a HAL AudioUnit
118  *****************************************************************************/
119 static int Open( vlc_object_t * p_this )
120 {
121     OSStatus                err = noErr;
122     UInt32                  i_param_size = 0, i_hog = 0;
123     struct aout_sys_t       *p_sys = NULL;
124     vlc_bool_t              b_alive = VLC_FALSE;
125     vlc_value_t             val;
126     aout_instance_t         *p_aout = (aout_instance_t *)p_this;
127
128     /* Allocate structure */
129     p_aout->output.p_sys = malloc( sizeof( aout_sys_t ) );
130     if( p_aout->output.p_sys == NULL )
131     {
132         msg_Err( p_aout, "out of memory" );
133         return( VLC_ENOMEM );
134     }
135
136     p_sys = p_aout->output.p_sys;
137     p_sys->i_default_dev = 0;
138     p_sys->i_selected_dev = 0;
139     p_sys->i_devices = 0;
140     p_sys->b_supports_digital = VLC_FALSE;
141     p_sys->b_digital = VLC_FALSE;
142     p_sys->au_component = NULL;
143     p_sys->au_unit = NULL;
144     p_sys->clock_diff = (mtime_t) 0;
145     p_sys->i_read_bytes = 0;
146     p_sys->i_total_bytes = 0;
147     memset( p_sys->p_remainder_buffer, 0, sizeof(uint8_t) * BUFSIZE );
148
149     p_aout->output.pf_play = Play;
150     
151     aout_FormatPrint( p_aout, "VLC is looking for:\n", (audio_sample_format_t *)&p_aout->output.output );
152     
153     /* Build a list of devices */
154     if( var_Type( p_aout, "audio-device" ) == 0 )
155     {
156         Probe( p_aout );
157     }
158
159     /* What device do we want? */
160     if( var_Get( p_aout, "audio-device", &val ) < 0 )
161     {
162         msg_Err( p_aout, "audio-device var does not exist. device probe failed." );
163         free( p_sys );
164         return( VLC_ENOVAR );
165     }
166
167     p_sys->i_selected_dev = val.i_int & ~AOUT_VAR_SPDIF_FLAG;
168     p_sys->b_supports_digital = ( val.i_int & AOUT_VAR_SPDIF_FLAG ) ? VLC_TRUE : VLC_FALSE;
169
170     /* Check if the desired device is alive and usable */
171     i_param_size = sizeof( b_alive );
172     err = AudioDeviceGetProperty( p_sys->i_selected_dev, 0, FALSE,
173                                   kAudioDevicePropertyDeviceIsAlive,
174                                   &i_param_size, &b_alive );
175
176     if( err != noErr )
177     {
178         msg_Err( p_aout, "could not check whether device is alive: %4.4s",
179                  (char *)&err );
180         return VLC_EGENERIC;
181     }
182
183     if( b_alive == VLC_FALSE )
184     {
185         msg_Err( p_aout, "Selected audio device is not alive" ); 
186         var_Destroy( p_aout, "audio-device" );
187         free( p_sys );
188         return VLC_EGENERIC;
189     }
190
191     i_param_size = sizeof( i_hog );
192     err = AudioDeviceGetProperty( p_sys->i_selected_dev, 0, FALSE,
193                                   kAudioDevicePropertyHogMode,
194                                   &i_param_size, &i_hog );
195
196     if( err != noErr )
197     {
198         msg_Err( p_aout, "could not check whether device is hogged: %4.4s",
199                  (char *)&err );
200         return VLC_EGENERIC;
201     }
202
203     if( i_hog != -1 )
204     {
205         msg_Err( p_aout, "Selected audio device is exclusively in use by another program" );
206         var_Destroy( p_aout, "audio-device" );
207         free( p_sys );
208         return VLC_EGENERIC;
209     }
210
211     /* Check for Digital mode or Analog output mode */
212     if( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) && p_sys->b_supports_digital )
213     {
214         if( OpenSPDIF( p_aout ) )
215             return VLC_SUCCESS;
216     }
217     else
218     {
219         if( OpenAnalog( p_aout ) )
220             return VLC_SUCCESS;
221     }
222     
223     /* If we reach this, the Open* failed */
224     var_Destroy( p_aout, "audio-device" );
225     free( p_sys );
226     return VLC_EGENERIC;
227 }
228
229 int OpenAnalog( aout_instance_t *p_aout )
230 {
231     struct aout_sys_t       *p_sys = p_aout->output.p_sys;
232     OSStatus                err = noErr;
233     UInt32                  i_param_size = 0, i = 0;
234     ComponentDescription    desc;
235         
236     if( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) && !p_sys->b_supports_digital )
237     {
238         msg_Dbg( p_aout, "we had requested a digital stream, but it's not possible for this device" );
239     }
240
241     /* If analog only start setting up AUHAL */
242     /* Lets go find our Component */
243     desc.componentType = kAudioUnitType_Output;
244     desc.componentSubType = kAudioUnitSubType_HALOutput;
245     desc.componentManufacturer = kAudioUnitManufacturer_Apple;
246     desc.componentFlags = 0;
247     desc.componentFlagsMask = 0;
248
249     p_sys->au_component = FindNextComponent( NULL, &desc );
250     if( p_sys->au_component == NULL )
251     {
252         msg_Err( p_aout, "we cannot find our HAL component" );
253         return VLC_FALSE;
254     }
255
256     err = OpenAComponent( p_sys->au_component, &p_sys->au_unit );
257     if( err )
258     {
259         msg_Err( p_aout, "we cannot find our HAL component" );
260         return VLC_FALSE;
261     }
262     
263     /* Enable IO for the component */
264     
265     /* Set the device */
266     verify_noerr( AudioUnitSetProperty( p_sys->au_unit,
267                          kAudioOutputUnitProperty_CurrentDevice,
268                          kAudioUnitScope_Global,
269                          0,
270                          &p_sys->i_selected_dev,
271                          sizeof(p_sys->i_selected_dev)));
272                          
273     /* Get the current format */
274     AudioStreamBasicDescription DeviceFormat;
275     
276     i_param_size = sizeof(AudioStreamBasicDescription);
277
278     verify_noerr( AudioUnitGetProperty( p_sys->au_unit,
279                                    kAudioUnitProperty_StreamFormat,
280                                    kAudioUnitScope_Input,
281                                    0,
282                                    &DeviceFormat,
283                                    &i_param_size ));
284                                    
285     msg_Dbg( p_aout, STREAM_FORMAT_MSG( "current format is: " , DeviceFormat ) );
286
287     /* Get the channel layout */
288     AudioChannelLayout *layout;
289     verify_noerr( AudioUnitGetPropertyInfo( p_sys->au_unit,
290                                    kAudioDevicePropertyPreferredChannelLayout,
291                                    kAudioUnitScope_Output,
292                                    0,
293                                    &i_param_size,
294                                    NULL ));
295
296     layout = (AudioChannelLayout *)malloc( i_param_size);
297
298     verify_noerr( AudioUnitGetProperty( p_sys->au_unit,
299                                    kAudioDevicePropertyPreferredChannelLayout,
300                                    kAudioUnitScope_Output,
301                                    0,
302                                    layout,
303                                    &i_param_size ));
304                                    
305     /* Lets fill out the ChannelLayout */
306     if( layout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap)
307     {
308         msg_Dbg( p_aout, "bitmap defined channellayout" );
309         verify_noerr( AudioFormatGetProperty( kAudioFormatProperty_ChannelLayoutForBitmap,
310                                 sizeof( UInt32), &layout->mChannelBitmap,
311                                 &i_param_size,
312                                 layout ));
313     }
314     else if( layout->mChannelLayoutTag != kAudioChannelLayoutTag_UseChannelDescriptions )
315     {
316         msg_Dbg( p_aout, "layouttags defined channellayout" );
317         verify_noerr( AudioFormatGetProperty( kAudioFormatProperty_ChannelLayoutForTag,
318                                 sizeof( AudioChannelLayoutTag ), &layout->mChannelLayoutTag,
319                                 &i_param_size,
320                                 layout ));
321     }
322
323     msg_Dbg( p_aout, "Layout of AUHAL has %d channels" , (int)layout->mNumberChannelDescriptions );
324     
325     p_aout->output.output.i_physical_channels = 0;
326     for( i = 0; i < layout->mNumberChannelDescriptions; i++ )
327     {
328         msg_Dbg( p_aout, "This is channel: %d", (int)layout->mChannelDescriptions[i].mChannelLabel );
329
330         switch( layout->mChannelDescriptions[i].mChannelLabel )
331         {
332             case kAudioChannelLabel_Left:
333                 p_aout->output.output.i_physical_channels |= AOUT_CHAN_LEFT;
334                 continue;
335             case kAudioChannelLabel_Right:
336                 p_aout->output.output.i_physical_channels |= AOUT_CHAN_RIGHT;
337                 continue;
338             case kAudioChannelLabel_Center:
339                 p_aout->output.output.i_physical_channels |= AOUT_CHAN_CENTER;
340                 continue;
341             case kAudioChannelLabel_LFEScreen:
342                 p_aout->output.output.i_physical_channels |= AOUT_CHAN_LFE;
343                 continue;
344             case kAudioChannelLabel_LeftSurround:
345                 p_aout->output.output.i_physical_channels |= AOUT_CHAN_REARLEFT;
346                 continue;
347             case kAudioChannelLabel_RightSurround:
348                 p_aout->output.output.i_physical_channels |= AOUT_CHAN_REARRIGHT;
349                 continue;
350             case kAudioChannelLabel_RearSurroundLeft:
351                 p_aout->output.output.i_physical_channels |= AOUT_CHAN_MIDDLELEFT;
352                 continue;
353             case kAudioChannelLabel_RearSurroundRight:
354                 p_aout->output.output.i_physical_channels |= AOUT_CHAN_MIDDLERIGHT;
355                 continue;
356             case kAudioChannelLabel_CenterSurround:
357                 p_aout->output.output.i_physical_channels |= AOUT_CHAN_REARCENTER;
358                 continue;
359             default:
360                 msg_Warn( p_aout, "Unrecognized channel form provided by driver: %d", (int)layout->mChannelDescriptions[i].mChannelLabel );
361                 if( i == 0 )
362                 {
363                     msg_Warn( p_aout, "Probably no channellayout is set. force based on channelcount" );
364                     switch( layout->mNumberChannelDescriptions )
365                     {
366                         /* We make assumptions based on number of channels here.
367                          * Unfortunatly Apple has provided no 100% method to retrieve the speaker configuration */
368                         case 1:
369                             p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
370                             break;
371                         case 4:
372                             p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
373                                                                         AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
374                             break;
375                         case 6:
376                             p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
377                                                                         AOUT_CHAN_CENTER | AOUT_CHAN_LFE |
378                                                                         AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
379                             break;
380                         case 7:
381                             p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
382                                                                         AOUT_CHAN_CENTER | AOUT_CHAN_LFE |
383                                                                         AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_REARCENTER;
384                             break;
385                         case 8:
386                             p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
387                                                                         AOUT_CHAN_CENTER | AOUT_CHAN_LFE |
388                                                                         AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT |
389                                                                         AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
390                             break;
391                         case 2:
392                         default:
393                             p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
394                     }
395                 }
396                 break;
397         }
398     }
399     free( layout );
400
401     msg_Dbg( p_aout, "defined %d physical channels for vlc core", aout_FormatNbChannels( &p_aout->output.output ) );
402     msg_Dbg( p_aout, "%s", aout_FormatPrintChannels( &p_aout->output.output ));
403     
404     AudioChannelLayout new_layout;
405     memset (&new_layout, 0, sizeof(new_layout));
406     switch( aout_FormatNbChannels( &p_aout->output.output ) )
407     {
408         case 1:
409             new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_Mono;
410             break;
411         case 2:
412             new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;
413             break;
414         case 3:
415             if( p_aout->output.output.i_physical_channels & AOUT_CHAN_CENTER )
416             {
417                 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_7; // L R C
418             }
419             else if( p_aout->output.output.i_physical_channels & AOUT_CHAN_LFE )
420             {
421                 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_4; // L R LFE
422             }
423             break;
424         case 4:
425             if( p_aout->output.output.i_physical_channels & ( AOUT_CHAN_CENTER | AOUT_CHAN_LFE ) )
426             {
427                 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_10; // L R C LFE
428             }
429             else if( p_aout->output.output.i_physical_channels & ( AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT ) )
430             {
431                 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_3; // L R Ls Rs
432             }
433             else if( p_aout->output.output.i_physical_channels & ( AOUT_CHAN_CENTER | AOUT_CHAN_REARCENTER ) )
434             {
435                 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_3; // L R C Cs
436             }
437             break;
438         case 5:
439             if( p_aout->output.output.i_physical_channels & ( AOUT_CHAN_CENTER ) )
440             {
441                 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_19; // L R Ls Rs C
442             }
443             else if( p_aout->output.output.i_physical_channels & ( AOUT_CHAN_LFE ) )
444             {
445                 new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_18; // L R Ls Rs LFE
446             }
447             break;
448         case 6:
449             new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_DVD_20; // L R Ls Rs C LFE
450             break;
451         case 7:
452             /* FIXME: This is incorrect. VLC uses the internal ordering: L R Lm Rm Lr Rr C LFE but this is wrong */
453             new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_6_1_A; // L R C LFE Ls Rs Cs
454             break;
455         case 8:
456             /* FIXME: This is incorrect. VLC uses the internal ordering: L R Lm Rm Lr Rr C LFE but this is wrong */
457             new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_7_1_A; // L R C LFE Ls Rs Lc Rc
458             break;
459     }
460
461     /* Set up the format to be used */
462     DeviceFormat.mSampleRate = p_aout->output.output.i_rate;
463     DeviceFormat.mFormatID = kAudioFormatLinearPCM;
464
465     /* We use float 32. It's the best supported format by both VLC and Coreaudio */
466     p_aout->output.output.i_format = VLC_FOURCC( 'f','l','3','2');
467     DeviceFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
468     DeviceFormat.mBitsPerChannel = 32;
469     DeviceFormat.mChannelsPerFrame = aout_FormatNbChannels( &p_aout->output.output );
470     
471     /* Calculate framesizes and stuff */
472     aout_FormatPrepare( &p_aout->output.output );
473     DeviceFormat.mFramesPerPacket = 1;
474     DeviceFormat.mBytesPerFrame = DeviceFormat.mBitsPerChannel * DeviceFormat.mChannelsPerFrame / 8;
475     DeviceFormat.mBytesPerPacket = DeviceFormat.mBytesPerFrame * DeviceFormat.mFramesPerPacket;
476  
477     i_param_size = sizeof(AudioStreamBasicDescription);
478     /* Set desired format (Use CAStreamBasicDescription )*/
479     verify_noerr( AudioUnitSetProperty( p_sys->au_unit,
480                                    kAudioUnitProperty_StreamFormat,
481                                    kAudioUnitScope_Input,
482                                    0,
483                                    &DeviceFormat,
484                                    i_param_size ));
485                                    
486     msg_Dbg( p_aout, STREAM_FORMAT_MSG( "we set the AU format: " , DeviceFormat ) );
487     
488     /* Retrieve actual format??? */
489     verify_noerr( AudioUnitGetProperty( p_sys->au_unit,
490                                    kAudioUnitProperty_StreamFormat,
491                                    kAudioUnitScope_Input,
492                                    0,
493                                    &DeviceFormat,
494                                    &i_param_size ));
495                                    
496     msg_Dbg( p_aout, STREAM_FORMAT_MSG( "the actual set AU format is " , DeviceFormat ) );
497
498     p_aout->output.i_nb_samples = 2048;
499     aout_VolumeSoftInit( p_aout );
500
501     /* Let's pray for the following operation to be atomic... */
502     p_sys->clock_diff = - (mtime_t)
503         AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000; 
504     p_sys->clock_diff += mdate();
505     
506     p_sys->i_read_bytes = 0;
507     p_sys->i_total_bytes = 0;
508
509     /* set the IOproc callback */
510     AURenderCallbackStruct input;
511     input.inputProc = (AURenderCallback) RenderCallbackAnalog;
512     input.inputProcRefCon = p_aout;
513     
514     verify_noerr( AudioUnitSetProperty( p_sys->au_unit,
515                             kAudioUnitProperty_SetRenderCallback,
516                             kAudioUnitScope_Input,
517                             0, &input, sizeof( input ) ) );
518
519     input.inputProc = (AURenderCallback) RenderCallbackAnalog;
520     input.inputProcRefCon = p_aout;
521     
522     /* Set the new_layout as the layout VLC feeds to the AU unit */
523     verify_noerr( AudioUnitSetProperty( p_sys->au_unit,
524                             kAudioUnitProperty_AudioChannelLayout,
525                             kAudioUnitScope_Input,
526                             0, &new_layout, sizeof(new_layout) ) );
527     
528     /* AU initiliaze */
529     verify_noerr( AudioUnitInitialize(p_sys->au_unit) );
530
531     verify_noerr( AudioOutputUnitStart(p_sys->au_unit) );
532     
533     return VLC_TRUE;
534 }
535
536 /*****************************************************************************
537  * Setup a encoded digital stream (SPDIF)
538  *****************************************************************************/
539 int OpenSPDIF( aout_instance_t * p_aout )
540 {
541     OSStatus            err = noErr;
542     UInt32              i, i_param_size;
543     AudioDeviceID       devid_def;
544     AudioDeviceID       *p_devices = NULL;
545     vlc_value_t         val, text;
546
547     struct aout_sys_t   *p_sys = p_aout->output.p_sys;
548
549     /* Start doing the SPDIF setup proces */
550     p_sys->b_digital = VLC_TRUE;
551     p_aout->output.output.i_format = VLC_FOURCC('s','p','d','i');
552     msg_Dbg( p_aout, "we found a digital stream, and we WANT a digital stream" );
553     
554     return VLC_FALSE;
555
556 error:
557     return VLC_FALSE;
558 }
559
560
561 /*****************************************************************************
562  * Close: Close HAL AudioUnit
563  *****************************************************************************/
564 static void Close( vlc_object_t * p_this )
565 {
566     aout_instance_t     *p_aout = (aout_instance_t *)p_this;
567     struct aout_sys_t   *p_sys = p_aout->output.p_sys;
568     
569     if( p_sys->au_unit )
570     {
571         verify_noerr( AudioOutputUnitStop( p_sys->au_unit ) );
572         verify_noerr( AudioUnitUninitialize( p_sys->au_unit ) );
573         verify_noerr( CloseComponent( p_sys->au_unit ) );
574     }
575     free( p_sys );
576 }
577
578 /*****************************************************************************
579  * Play: nothing to do
580  *****************************************************************************/
581 static void Play( aout_instance_t * p_aout )
582 {
583 }
584
585
586 /*****************************************************************************
587  * Probe
588  *****************************************************************************/
589 static void Probe( aout_instance_t * p_aout )
590 {
591     OSStatus            err = noErr;
592     UInt32              i = 0, i_param_size = 0;
593     AudioDeviceID       devid_def = 0;
594     AudioDeviceID       *p_devices = NULL;
595     vlc_value_t         val, text;
596
597     struct aout_sys_t   *p_sys = p_aout->output.p_sys;
598
599     /* Get number of devices */
600     err = AudioHardwareGetPropertyInfo( kAudioHardwarePropertyDevices,
601                                         &i_param_size, NULL );
602     if( err != noErr )
603     {
604         msg_Err( p_aout, "could not get number of devices: [%4.4s]", (char *)&err );
605         goto error;
606     }
607
608     p_sys->i_devices = i_param_size / sizeof( AudioDeviceID );
609
610     if( p_sys->i_devices < 1 )
611     {
612         msg_Err( p_aout, "no devices found" );
613         goto error;
614     }
615
616     msg_Dbg( p_aout, "system has [%ld] device(s)", p_sys->i_devices );
617
618     /* Allocate DeviceID array */
619     p_devices = (AudioDeviceID*)malloc( sizeof(AudioDeviceID) * p_sys->i_devices );
620     if( p_devices == NULL )
621     {
622         msg_Err( p_aout, "out of memory" );
623         goto error;
624     }
625
626     /* Populate DeviceID array */
627     err = AudioHardwareGetProperty( kAudioHardwarePropertyDevices,
628                                     &i_param_size, p_devices );
629     if( err != noErr )
630     {
631         msg_Err( p_aout, "could not get the device ID's: [%4.4s]", (char *)&err );
632         goto error;
633     }
634
635     /* Find the ID of the default Device */
636     i_param_size = sizeof( AudioDeviceID );
637     err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice,
638                                     &i_param_size, &devid_def );
639     if( err != noErr )
640     {
641         msg_Err( p_aout, "could not get default audio device: [%4.4s]", (char *)&err );
642         goto error;
643     }
644     p_sys->i_default_dev = devid_def;
645     
646     var_Create( p_aout, "audio-device", VLC_VAR_INTEGER|VLC_VAR_HASCHOICE );
647     text.psz_string = _("Audio Device");
648     var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );
649     
650     for( i = 0; i < p_sys->i_devices; i++ )
651     {
652         char *psz_name;
653         i_param_size = 0;
654
655         /* Retrieve the length of the device name */
656         err = AudioDeviceGetPropertyInfo(
657                     p_devices[i], 0, VLC_FALSE,
658                     kAudioDevicePropertyDeviceName,
659                     &i_param_size, NULL);
660         if( err ) goto error;
661
662         /* Retrieve the name of the device */
663         psz_name = (char *)malloc( i_param_size );
664         err = AudioDeviceGetProperty(
665                     p_devices[i], 0, VLC_FALSE,
666                     kAudioDevicePropertyDeviceName,
667                     &i_param_size, psz_name);
668         if( err ) goto error;
669
670         msg_Dbg( p_aout, "DevID: %lu  DevName: %s", p_devices[i], psz_name );
671
672         if( !AudioDeviceHasOutput( p_devices[i]) )
673         {
674             msg_Dbg( p_aout, "this device is INPUT only. skipping..." );
675             continue;
676         }
677
678         val.i_int = (int)p_devices[i];
679         text.psz_string = strdup( psz_name );
680         var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
681         if( p_sys->i_default_dev == p_devices[i] )
682         {
683             var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
684             var_Set( p_aout, "audio-device", val );
685         }
686
687         if( AudioDeviceSupportsDigital( p_aout, p_devices[i]) )
688         {
689             val.i_int = (int)p_devices[i] | AOUT_VAR_SPDIF_FLAG;
690             asprintf( &text.psz_string, "%s (Encoded Output)", psz_name );
691             var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
692             if( p_sys->i_default_dev == p_devices[i] && config_GetInt( p_aout, "spdif" ) )
693             {
694                 var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL );
695                 var_Set( p_aout, "audio-device", val );
696             }
697         }
698         
699         free( psz_name);
700     }
701     var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
702     
703     /* attach a Listener so that we are notified of a change in the Device setup */
704     err = AudioHardwareAddPropertyListener( kAudioHardwarePropertyDevices,
705                                             HardwareListener, 
706                                             (void *)p_aout );
707     if( err )
708         goto error;
709
710     msg_Dbg( p_aout, "succesful finish of deviceslist" );
711     if( p_devices ) free( p_devices );
712     return;
713
714 error:
715     var_Destroy( p_aout, "audio-device" );
716     if( p_devices ) free( p_devices );
717     return;
718 }
719
720 /*****************************************************************************
721  * AudioDeviceSupportsDigital: Check i_dev_id for digital stream support.
722  *****************************************************************************/
723 int AudioDeviceSupportsDigital( aout_instance_t *p_aout, AudioDeviceID i_dev_id )
724 {
725     OSStatus                    err = noErr;
726     UInt32                      i_param_size = 0;
727     AudioStreamBasicDescription *p_format_list = NULL;
728     AudioStreamID               *p_streams = NULL;
729     int                         i = 0, j = 0; 
730     int                         i_formats = 0, i_streams = 0;
731     vlc_bool_t                  b_return = VLC_FALSE;
732     
733     /* Retrieve all the output streams */
734     err = AudioDeviceGetPropertyInfo( i_dev_id, 0, FALSE,
735                                       kAudioDevicePropertyStreams,
736                                       &i_param_size, NULL );
737     if( err != noErr )
738     {
739         msg_Err( p_aout, "could not get number of streams: [%4.4s]", (char *)&err );
740         return VLC_FALSE;
741     }
742     
743     i_streams = i_param_size / sizeof( AudioStreamID );
744     p_streams = (AudioStreamID *)malloc( i_param_size );
745     if( p_streams == NULL )
746     {
747         msg_Err( p_aout, "Out of memory" );
748         return VLC_ENOMEM;
749     }
750     
751     err = AudioDeviceGetProperty( i_dev_id, 0, FALSE,
752                                     kAudioDevicePropertyStreams,
753                                     &i_param_size, p_streams );
754     
755     if( err != noErr )
756     {
757         msg_Err( p_aout, "could not get number of streams: [%4.4s]", (char *)&err );
758         return VLC_FALSE;
759     }
760
761     for( i = 0; i < i_streams; i++ )
762     {
763         p_format_list = NULL;
764         i_formats = 0;
765         msg_Dbg( p_aout, "retrieving formats for stream: %#lx", p_streams[i] );
766
767         /* Retrieve all the stream formats supported by each output stream */
768         err = AudioStreamGetPropertyInfo( p_streams[i], 0,
769                                           kAudioDevicePropertyStreamFormats,
770                                           &i_param_size, NULL );
771         if( err != noErr )
772         {
773             msg_Err( p_aout, "could not get number of streamformats: [%4.4s]", (char *)&err );
774             break;
775         }
776         
777         i_formats = i_param_size / sizeof( AudioStreamBasicDescription );
778         p_format_list = (AudioStreamBasicDescription *)malloc( i_param_size );
779         if( p_format_list == NULL )
780         {
781             break;
782         }
783         
784         err = AudioStreamGetProperty( p_streams[i], 0,
785                                           kAudioDevicePropertyStreamFormats,
786                                           &i_param_size, p_format_list );
787         if( err != noErr )
788         {
789             msg_Err( p_aout, "could not get the list of streamformats: [%4.4s]", (char *)&err );
790             free( p_format_list);
791             break;
792         }
793
794         for( j = 0; j < i_formats; j++ )
795         {
796             msg_Dbg( p_aout, STREAM_FORMAT_MSG( "supported format", p_format_list[j] ) );
797             
798             if( p_format_list[j].mFormatID == 'IAC3' ||
799                       p_format_list[j].mFormatID == kAudioFormat60958AC3 )
800             {
801                 b_return = VLC_TRUE;
802                 msg_Dbg( p_aout, "this device supports a digital stream" );
803                 break;
804             }
805         }
806         
807         if( p_format_list ) free( p_format_list );
808     }
809     
810     if( p_streams ) free( p_streams );
811     return b_return;
812 }
813
814 /*****************************************************************************
815  * RenderCallbackAnalog: This function is called everytime the AudioUnit wants
816  * us to provide some more audio data.
817  * Don't print anything during normal playback, calling blocking function from
818  * this callback is not allowed.
819  *****************************************************************************/
820 static OSStatus RenderCallbackAnalog( vlc_object_t *_p_aout,
821                                       AudioUnitRenderActionFlags *ioActionFlags,
822                                       const AudioTimeStamp *inTimeStamp,
823                                       unsigned int inBusNummer,
824                                       unsigned int inNumberFrames,
825                                       AudioBufferList *ioData )
826 {
827     AudioTimeStamp  host_time;
828     mtime_t         current_date = 0;
829     uint32_t        i_mData_bytes = 0;    
830
831     aout_instance_t * p_aout = (aout_instance_t *)_p_aout;
832     struct aout_sys_t * p_sys = p_aout->output.p_sys;
833
834     host_time.mFlags = kAudioTimeStampHostTimeValid;
835     AudioDeviceTranslateTime( p_sys->i_selected_dev, inTimeStamp, &host_time );
836
837     p_sys->clock_diff = - (mtime_t)
838         AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000; 
839     p_sys->clock_diff += mdate();
840
841     current_date = p_sys->clock_diff +
842                    AudioConvertHostTimeToNanos( host_time.mHostTime ) / 1000;
843                    //- ((mtime_t) 1000000 / p_aout->output.output.i_rate * 31 ); // 31 = Latency in Frames. retrieve somewhere
844
845     if( ioData == NULL && ioData->mNumberBuffers < 1 )
846     {
847         msg_Err( p_aout, "no iodata or buffers");
848         return 0;
849     }
850     if( ioData->mNumberBuffers > 1 )
851         msg_Err( p_aout, "well this is weird. seems like there is more than one buffer..." );
852
853
854     if( p_sys->i_total_bytes > 0 )
855     {
856         i_mData_bytes = __MIN( p_sys->i_total_bytes - p_sys->i_read_bytes, ioData->mBuffers[0].mDataByteSize );
857         p_aout->p_vlc->pf_memcpy( ioData->mBuffers[0].mData, &p_sys->p_remainder_buffer[p_sys->i_read_bytes], i_mData_bytes );
858         p_sys->i_read_bytes += i_mData_bytes;
859         current_date += (mtime_t) ( (mtime_t) 1000000 / p_aout->output.output.i_rate ) *
860                         ( i_mData_bytes / 4 / aout_FormatNbChannels( &p_aout->output.output )  ); // 4 is fl32 specific
861         
862         if( p_sys->i_read_bytes >= p_sys->i_total_bytes )
863             p_sys->i_read_bytes = p_sys->i_total_bytes = 0;
864     }
865     
866     while( i_mData_bytes < ioData->mBuffers[0].mDataByteSize )
867     {
868         /* We don't have enough data yet */
869         aout_buffer_t * p_buffer;
870         p_buffer = aout_OutputNextBuffer( p_aout, current_date , VLC_FALSE );
871         
872         if( p_buffer != NULL )
873         {
874             uint32_t i_second_mData_bytes = __MIN( p_buffer->i_nb_bytes, ioData->mBuffers[0].mDataByteSize - i_mData_bytes );
875             
876             p_aout->p_vlc->pf_memcpy( (uint8_t *)ioData->mBuffers[0].mData + i_mData_bytes, p_buffer->p_buffer, i_second_mData_bytes );
877             i_mData_bytes += i_second_mData_bytes;
878
879             if( i_mData_bytes >= ioData->mBuffers[0].mDataByteSize )
880             {
881                 p_sys->i_total_bytes = p_buffer->i_nb_bytes - i_second_mData_bytes;
882                 p_aout->p_vlc->pf_memcpy( p_sys->p_remainder_buffer, &p_buffer->p_buffer[i_second_mData_bytes], p_sys->i_total_bytes );
883             }
884             else
885             {
886                 // update current_date
887                 current_date += (mtime_t) ( (mtime_t) 1000000 / p_aout->output.output.i_rate ) *
888                                 ( i_second_mData_bytes / 4 / aout_FormatNbChannels( &p_aout->output.output )  ); // 4 is fl32 specific
889             }
890             aout_BufferFree( p_buffer );
891         }
892         else
893         {
894              p_aout->p_vlc->pf_memset( (uint8_t *)ioData->mBuffers[0].mData +i_mData_bytes, 0, ioData->mBuffers[0].mDataByteSize - i_mData_bytes );
895              i_mData_bytes += ioData->mBuffers[0].mDataByteSize - i_mData_bytes;
896         }
897     }
898     return( noErr );     
899 }
900
901 int AudioDeviceHasOutput( AudioDeviceID i_dev_id )
902 {
903     UInt32                      dataSize;
904     Boolean                     isWritable;
905         
906     verify_noerr( AudioDeviceGetPropertyInfo( i_dev_id, 0, FALSE, kAudioDevicePropertyStreams, &dataSize, &isWritable) );
907     if (dataSize == 0) return FALSE;
908     
909     return TRUE;
910 }
911
912 /*****************************************************************************
913  * HardwareListener: Warns us of changes in the list of registered devices
914  *****************************************************************************/
915 static OSStatus HardwareListener( AudioHardwarePropertyID inPropertyID,
916                                   void * inClientData )
917 {
918     OSStatus err = noErr;
919     aout_instance_t     *p_aout = (aout_instance_t *)inClientData;
920
921     switch( inPropertyID )
922     {
923         case kAudioHardwarePropertyDevices:
924         {
925             /* something changed in the list of devices */
926             /* We trigger the audio-device's aout_ChannelsRestart callback */
927             var_Change( p_aout, "audio-device", VLC_VAR_TRIGGER_CALLBACKS, NULL, NULL );
928         }
929         break;
930     }
931
932     return( err );
933 }