]> git.sesse.net Git - vlc/blob - modules/audio_output/coreaudio.c
cdfa8c9f78fcfdd97918d85776c74beee904199c
[vlc] / modules / audio_output / coreaudio.c
1 /*****************************************************************************
2  * coreaudio.c: CoreAudio output plugin
3  *****************************************************************************
4  * Copyright (C) 2002-2003 VideoLAN
5  * $Id: coreaudio.c,v 1.9 2004/01/25 17:58:29 murray Exp $
6  *
7  * Authors: Colin Delacroix <colin@zoy.org>
8  *          Jon Lech Johansen <jon-vl@nanocrew.net>
9  *          Christophe Massiot <massiot@via.ecp.fr>
10  *          Heiko Panther <heiko.panther@web.de>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  * 
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
25  *****************************************************************************/
26
27 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30 #include <string.h>
31 #include <stdlib.h>
32
33 #include <vlc/vlc.h>
34 #include <vlc/aout.h>
35
36 #include "aout_internal.h"
37
38 #include <CoreAudio/CoreAudio.h>
39
40 #define A52_FRAME_NB 1536
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 /*****************************************************************************
50  * aout_class_t 
51  ****************************************************************************/
52 enum AudioDeviceClass
53 {
54     AudioDeviceClassA52     = 1 << 0,
55     AudioDeviceClassPCM     = 1 << 1
56 };
57
58 static struct aout_class_t
59 {
60     UInt32 mFormatID;
61     UInt32 mChannelsPerFrame;
62     enum AudioDeviceClass class;
63     const char * psz_class;
64 }
65 aout_classes[] =
66 {
67     { /* old A/52 format type */
68         'IAC3', 
69         2, 
70         AudioDeviceClassA52, 
71         "Digital A/52" 
72     },
73
74     { /* new A/52 format type */
75         kAudioFormat60958AC3, 
76         2, 
77         AudioDeviceClassA52, 
78         "Digital A/52"
79     },
80
81     {
82         kAudioFormatLinearPCM, 
83         2, 
84         AudioDeviceClassPCM, 
85         "Stereo PCM"
86     },
87
88     {
89         kAudioFormatLinearPCM, 
90         1, 
91         AudioDeviceClassPCM, 
92         "Mono PCM"
93     },
94
95     {
96         kAudioFormatLinearPCM,
97         4,
98         AudioDeviceClassPCM,
99         "4 Channel PCM"
100     },
101
102     {
103         kAudioFormatLinearPCM, 
104         6, 
105         AudioDeviceClassPCM, 
106         "6 Channel PCM"
107     },
108
109     {
110         kAudioFormatLinearPCM,
111         8,
112         AudioDeviceClassPCM,
113         "8 Channel PCM"
114     }
115 }; 
116
117 #define N_AOUT_CLASSES (sizeof(aout_classes)/sizeof(aout_classes[0]))
118
119 /*****************************************************************************
120  * aout_option_t
121  ****************************************************************************/
122 struct aout_option_t
123 {
124     char sz_option[64];
125     UInt32 i_dev, i_idx;
126     UInt32 i_sdx, i_cdx;
127     AudioStreamID i_sid;
128 };
129
130 /*****************************************************************************
131  * aout_dev_t
132  ****************************************************************************/
133 struct aout_dev_t
134 {
135     AudioDeviceID devid;
136     char * psz_device_name;
137     UInt32 i_streams;
138     UInt32 * pi_streams;
139     AudioStreamBasicDescription ** pp_streams;
140 };
141
142 /*****************************************************************************
143  * aout_sys_t: private audio output method descriptor
144  *****************************************************************************
145  * This structure is part of the audio output thread descriptor.
146  * It describes the CoreAudio specific properties of an output thread.
147  *****************************************************************************/
148 struct aout_sys_t
149 {
150     vlc_mutex_t                 lock;
151     vlc_bool_t                  b_hwinfo;
152     UInt32                      i_def_dev;
153     UInt32                      i_devices;
154     struct aout_dev_t *         p_devices;
155     UInt32                      i_sel_opt;
156     UInt32                      i_options;
157     struct aout_option_t *      p_options;
158
159     AudioDeviceID               devid;
160     AudioStreamBasicDescription stream_format;
161     UInt32                      b_dev_alive;
162
163     vlc_bool_t                  b_revert_sfmt;
164     AudioStreamBasicDescription sfmt_revert;
165
166     UInt32                      i_buffer_size;
167     mtime_t                     clock_diff;
168 };
169
170 /*****************************************************************************
171  * Local prototypes.
172  *****************************************************************************/
173 static int      InitHardwareInfo ( aout_instance_t * p_aout );
174 static int      InitDeviceInfo   ( UInt32 i_dev, aout_instance_t * p_aout ); 
175 static void     FreeDeviceInfo   ( UInt32 i_dev, aout_instance_t * p_aout ); 
176 static void     FreeHardwareInfo ( aout_instance_t * p_aout );
177 static int      InitDevice       ( aout_instance_t * p_aout );
178 static void     FreeDevice       ( aout_instance_t * p_aout );
179 static int      GetStreamID      ( AudioDeviceID devid, UInt32 i_idx,
180                                    AudioStreamID * p_sid );
181 static int      InitStreamInfo   ( UInt32 i_dev, aout_instance_t * p_aout,
182                                    UInt32 i_idx );
183 static void     FreeStreamInfo   ( UInt32 i_dev, aout_instance_t * p_aout,
184                                    UInt32 i_idx );
185 static void     InitDeviceVar    ( aout_instance_t * p_aout, int i_option,
186                                    vlc_bool_t b_change );
187
188 static int      Open             ( vlc_object_t * );
189 static void     Close            ( vlc_object_t * );
190
191 static void     Play             ( aout_instance_t * p_aout );
192
193 static OSStatus IOCallback       ( AudioDeviceID inDevice,
194                                    const AudioTimeStamp * inNow, 
195                                    const void * inInputData, 
196                                    const AudioTimeStamp * inInputTime,
197                                    AudioBufferList * outOutputData, 
198                                    const AudioTimeStamp * inOutputTime, 
199                                    void * threadGlobals );
200
201 static OSStatus HardwareListener ( AudioHardwarePropertyID inPropertyID,
202                                    void * inClientData );
203
204 static OSStatus DeviceListener   ( AudioDeviceID inDevice,
205                                    UInt32 inChannel,
206                                    Boolean isInput,
207                                    AudioDevicePropertyID inPropertyID,
208                                    void * inClientData );
209
210 static OSStatus StreamListener   ( AudioStreamID inStream,
211                                    UInt32 inChannel,
212                                    AudioDevicePropertyID inPropertyID,
213                                    void * inClientData );
214
215 /*****************************************************************************
216  * Module descriptor
217  *****************************************************************************/
218 #define ADEV_TEXT N_("Audio Device")
219 #define ADEV_LONGTEXT N_("Choose a number corresponding to the number of an " \
220     "audio device, as listed in your 'Audio Device' menu. This device will " \
221     "then be used by default for audio playback.")
222
223 vlc_module_begin();
224     set_description( _("CoreAudio output") );
225     set_capability( "audio output", 100 );
226     set_callbacks( Open, Close );
227     add_category_hint( N_("Audio"), NULL, VLC_FALSE );
228     add_integer( "coreaudio-dev", -1, NULL, ADEV_TEXT, ADEV_LONGTEXT, VLC_FALSE ); 
229 vlc_module_end();
230
231 /*****************************************************************************
232  * Open: open a CoreAudio HAL device
233  *****************************************************************************/
234 static int Open( vlc_object_t * p_this )
235 {
236     OSStatus err;
237     UInt32 i_param_size;
238     struct aout_sys_t * p_sys;
239     aout_instance_t * p_aout = (aout_instance_t *)p_this;
240
241     /* Allocate structure */
242     p_sys = (struct aout_sys_t *)malloc( sizeof( struct aout_sys_t ) );
243     if( p_sys == NULL )
244     {
245         msg_Err( p_aout, "out of memory" );
246         return( VLC_ENOMEM );
247     }
248
249     memset( p_sys, 0, sizeof( struct aout_sys_t ) );
250
251     p_aout->output.p_sys = p_sys;
252     p_aout->output.pf_play = Play;
253
254     vlc_mutex_init( p_aout, &p_sys->lock );
255
256     if( InitHardwareInfo( p_aout ) )
257     {
258         msg_Err( p_aout, "InitHardwareInfo failed" );
259         vlc_mutex_destroy( &p_sys->lock );
260         free( (void *)p_sys );
261         return( VLC_EGENERIC );
262     } 
263
264     if( var_Type( p_aout, "audio-device" ) == 0 )
265     {
266         InitDeviceVar( p_aout, config_GetInt( p_aout, "coreaudio-dev" ),
267                        VLC_FALSE );
268     }
269
270     if( InitDevice( p_aout ) )
271     {
272         msg_Err( p_aout, "InitDevice failed" );
273         FreeHardwareInfo( p_aout );
274         vlc_mutex_destroy( &p_sys->lock );
275         free( (void *)p_sys );
276         return( VLC_EGENERIC );
277     } 
278
279     /* Get a description of the stream format */
280     i_param_size = sizeof( AudioStreamBasicDescription ); 
281     err = AudioDeviceGetProperty( p_sys->devid, 0, FALSE, 
282                                   kAudioDevicePropertyStreamFormat,
283                                   &i_param_size, &p_sys->stream_format );
284     if( err != noErr )
285     {
286         msg_Err( p_aout, "failed to get stream format: [%4.4s]", 
287                  (char *)&err );
288         FreeDevice( p_aout );
289         FreeHardwareInfo( p_aout );
290         vlc_mutex_destroy( &p_sys->lock );
291         free( (void *)p_sys );
292         return( VLC_EGENERIC );
293     }
294
295     /* Set the output sample rate */
296     p_aout->output.output.i_rate = 
297         (unsigned int)p_sys->stream_format.mSampleRate;
298
299     msg_Dbg( p_aout, STREAM_FORMAT_MSG( "using format",
300                                         p_sys->stream_format ) );
301
302     /* Get the buffer size */
303     i_param_size = sizeof( p_sys->i_buffer_size );
304     err = AudioDeviceGetProperty( p_sys->devid, 0, FALSE, 
305                                   kAudioDevicePropertyBufferSize, 
306                                   &i_param_size, &p_sys->i_buffer_size );
307     if( err != noErr )
308     {
309         msg_Err( p_aout, "failed to get buffer size: [%4.4s]", 
310                  (char *)&err );
311         FreeDevice( p_aout );
312         FreeHardwareInfo( p_aout );
313         vlc_mutex_destroy( &p_sys->lock );
314         free( (void *)p_sys );
315         return( VLC_EGENERIC );
316     }
317
318     msg_Dbg( p_aout, "device buffer size: [%ld]", p_sys->i_buffer_size );
319
320     /* If we do AC3 over SPDIF, set buffer size to one AC3 frame */
321     if( ( p_sys->stream_format.mFormatID == kAudioFormat60958AC3 ||
322           p_sys->stream_format.mFormatID == 'IAC3' ) &&
323         p_sys->i_buffer_size != AOUT_SPDIF_SIZE )
324     {
325         p_sys->i_buffer_size = AOUT_SPDIF_SIZE;
326         i_param_size = sizeof( p_sys->i_buffer_size );
327         err = AudioDeviceSetProperty( p_sys->devid, 0, 0, FALSE,
328                                       kAudioDevicePropertyBufferSize,
329                                       i_param_size, &p_sys->i_buffer_size );
330         if( err != noErr )
331         {
332             msg_Err( p_aout, "failed to set buffer size: [%4.4s]", 
333                      (char *)&err );
334             FreeDevice( p_aout );
335             FreeHardwareInfo( p_aout );
336             vlc_mutex_destroy( &p_sys->lock );
337             free( (void *)p_sys );
338             return( VLC_EGENERIC );
339         }
340
341         msg_Dbg( p_aout, "device buffer size set to: [%ld]", 
342                  p_sys->i_buffer_size );
343
344         /* Set buffer frame size */
345         i_param_size = sizeof( p_aout->output.i_nb_samples );
346         err = AudioDeviceSetProperty( p_sys->devid, 0, 0, FALSE,
347                                       kAudioDevicePropertyBufferFrameSize,
348                                       i_param_size,
349                                       &p_aout->output.i_nb_samples );
350         if( err != noErr )
351         {
352             msg_Err( p_aout, "failed to set buffer frame size: [%4.4s]", 
353                      (char *)&err );
354             FreeDevice( p_aout );
355             FreeHardwareInfo( p_aout );
356             vlc_mutex_destroy( &p_sys->lock );
357             free( (void *)p_sys );
358             return( VLC_EGENERIC );
359         }
360
361         msg_Dbg( p_aout, "device buffer frame size set to: [%d]",
362                  p_aout->output.i_nb_samples );
363     }
364
365     switch( p_sys->stream_format.mFormatID )
366     {
367     case kAudioFormatLinearPCM:
368         p_aout->output.output.i_format = VLC_FOURCC('f','l','3','2');
369
370         switch( p_sys->stream_format.mChannelsPerFrame )
371         {
372         case 1:
373             p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
374             break;
375
376         case 2:
377             p_aout->output.output.i_physical_channels =
378                 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
379             break;
380
381         case 4:
382             p_aout->output.output.i_physical_channels =
383                 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
384                 AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
385             break;
386
387         case 6:
388             p_aout->output.output.i_physical_channels =
389                 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
390                 AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT |
391                 AOUT_CHAN_CENTER | AOUT_CHAN_LFE;
392             break;
393
394         case 8:
395             p_aout->output.output.i_physical_channels =
396                 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
397                 AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT |
398                 AOUT_CHAN_CENTER | AOUT_CHAN_LFE |
399                 AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT;
400             break;
401
402         default:
403             msg_Err( p_aout, "unknown channel count: [%ld]",
404                      p_sys->stream_format.mChannelsPerFrame ); 
405             FreeDevice( p_aout );
406             FreeHardwareInfo( p_aout );
407             vlc_mutex_destroy( &p_sys->lock );
408             free( (void *)p_sys );
409             return( VLC_EGENERIC );
410         }
411
412         p_aout->output.i_nb_samples = (int)( p_sys->i_buffer_size /
413                                       p_sys->stream_format.mBytesPerFrame );
414
415         aout_VolumeSoftInit( p_aout );
416         break;
417
418     case 'IAC3':
419     case kAudioFormat60958AC3:
420         p_aout->output.output.i_format = VLC_FOURCC('s','p','d','i');
421         p_aout->output.output.i_bytes_per_frame = AOUT_SPDIF_SIZE;
422         p_aout->output.output.i_frame_length = A52_FRAME_NB;
423         p_aout->output.i_nb_samples = p_aout->output.output.i_frame_length;
424
425         aout_VolumeNoneInit( p_aout );
426         break;
427
428     default:
429         msg_Err( p_aout, "unknown hardware format: [%4.4s]", 
430                  (char *)&p_sys->stream_format.mFormatID );
431         FreeDevice( p_aout );
432         FreeHardwareInfo( p_aout );
433         vlc_mutex_destroy( &p_sys->lock );
434         free( (void *)p_sys );
435         return( VLC_EGENERIC );
436     }
437
438     /* Add callback */
439     err = AudioDeviceAddIOProc( p_sys->devid,
440                                 (AudioDeviceIOProc)IOCallback,
441                                 (void *)p_aout );
442     if( err != noErr )
443     {
444         msg_Err( p_aout, "AudioDeviceAddIOProc failed: [%4.4s]",
445                  (char *)&err );
446         FreeDevice( p_aout );
447         FreeHardwareInfo( p_aout );
448         vlc_mutex_destroy( &p_sys->lock );
449         free( (void *)p_sys );
450         return( VLC_EGENERIC );
451     }
452  
453     /* Start device */
454     err = AudioDeviceStart( p_sys->devid, (AudioDeviceIOProc)IOCallback ); 
455     if( err != noErr )
456     {
457         msg_Err( p_aout, "AudioDeviceStart failed: [%4.4s]",
458                  (char *)&err );
459
460         err = AudioDeviceRemoveIOProc( p_sys->devid, 
461                                        (AudioDeviceIOProc)IOCallback );
462         if( err != noErr )
463         {
464             msg_Err( p_aout, "AudioDeviceRemoveIOProc failed: [%4.4s]",
465                      (char *)&err );
466         }
467
468         FreeDevice( p_aout );
469         FreeHardwareInfo( p_aout );
470         vlc_mutex_destroy( &p_sys->lock );
471         free( (void *)p_sys );
472
473         return( VLC_EGENERIC );
474     }
475
476     err = AudioHardwareAddPropertyListener( kAudioHardwarePropertyDevices,
477                                             HardwareListener, 
478                                             (void *)p_aout ); 
479     if( err != noErr )
480     {
481         msg_Err( p_aout, "AudioHardwareAddPropertyListener failed: %4.4s",
482                  (char *)&err );
483
484         /* Stop device */
485         err = AudioDeviceStop( p_sys->devid, 
486                                (AudioDeviceIOProc)IOCallback ); 
487         if( err != noErr )
488         {
489             msg_Err( p_aout, "AudioDeviceStop failed: [%4.4s]",
490                      (char *)&err );
491         }
492
493         /* Remove callback */
494         err = AudioDeviceRemoveIOProc( p_sys->devid,
495                                        (AudioDeviceIOProc)IOCallback );
496         if( err != noErr )
497         {
498             msg_Err( p_aout, "AudioDeviceRemoveIOProc failed: [%4.4s]",
499                      (char *)&err );
500         }
501
502         FreeDevice( p_aout );
503         FreeHardwareInfo( p_aout );
504         vlc_mutex_destroy( &p_sys->lock );
505         free( (void *)p_sys );
506
507         return( VLC_EGENERIC );
508     }
509
510     /* Let's pray for the following operation to be atomic... */
511     p_sys->clock_diff = - (mtime_t)
512         AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000; 
513     p_sys->clock_diff += mdate();
514
515     return( VLC_SUCCESS );
516 }
517
518 /*****************************************************************************
519  * Close: close the CoreAudio HAL device
520  *****************************************************************************/
521 static void Close( vlc_object_t * p_this )
522 {
523     OSStatus err; 
524     aout_instance_t * p_aout = (aout_instance_t *)p_this;
525     struct aout_sys_t * p_sys = p_aout->output.p_sys;
526
527     if( p_sys->b_dev_alive )
528     {
529         /* Stop device */
530         err = AudioDeviceStop( p_sys->devid, 
531                                (AudioDeviceIOProc)IOCallback ); 
532         if( err != noErr )
533         {
534             msg_Err( p_aout, "AudioDeviceStop failed: [%4.4s]",
535                      (char *)&err );
536         }
537
538         /* Remove callback */
539         err = AudioDeviceRemoveIOProc( p_sys->devid,
540                                        (AudioDeviceIOProc)IOCallback );
541         if( err != noErr )
542         {
543             msg_Err( p_aout, "AudioDeviceRemoveIOProc failed: [%4.4s]",
544                      (char *)&err );
545         }
546
547         FreeDevice( p_aout );
548     }
549
550     err = AudioHardwareRemovePropertyListener( kAudioHardwarePropertyDevices,
551                                                HardwareListener );
552     if( err != noErr )
553     {
554         msg_Err( p_aout, "AudioHardwareRemovePropertyListener failed: [%4.4s]",
555                  (char *)&err );
556     }
557
558     FreeHardwareInfo( p_aout );
559
560     vlc_mutex_destroy( &p_sys->lock );
561
562     free( p_sys );
563 }
564
565 /*****************************************************************************
566  * Play: nothing to do
567  *****************************************************************************/
568 static void Play( aout_instance_t * p_aout )
569 {
570 }
571
572 /*****************************************************************************
573  * IOCallback: callback for audio output
574  *****************************************************************************/
575 static OSStatus IOCallback( AudioDeviceID inDevice,
576                             const AudioTimeStamp * inNow, 
577                             const void * inInputData,
578                             const AudioTimeStamp * inInputTime, 
579                             AudioBufferList * outOutputData,
580                             const AudioTimeStamp * inOutputTime, 
581                             void * threadGlobals )
582 {
583     aout_buffer_t * p_buffer;
584     AudioTimeStamp  host_time;
585     mtime_t         current_date;
586
587     aout_instance_t * p_aout = (aout_instance_t *)threadGlobals;
588     struct aout_sys_t * p_sys = p_aout->output.p_sys;
589
590     host_time.mFlags = kAudioTimeStampHostTimeValid;
591     AudioDeviceTranslateTime( inDevice, inOutputTime, &host_time );
592
593 #if 1
594     p_sys->clock_diff = - (mtime_t)
595         AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000; 
596     p_sys->clock_diff += mdate();
597 #endif
598
599     current_date = p_sys->clock_diff +
600                    AudioConvertHostTimeToNanos( host_time.mHostTime ) / 1000;
601
602 #define B_SPDI (p_aout->output.output.i_format == VLC_FOURCC('s','p','d','i'))
603     p_buffer = aout_OutputNextBuffer( p_aout, current_date, B_SPDI );
604 #undef B_SPDI
605
606     if( p_buffer != NULL )
607     {
608         /* move data into output data buffer */
609         p_aout->p_vlc->pf_memcpy( outOutputData->mBuffers[ 0 ].mData, 
610                                   p_buffer->p_buffer, 
611                                   p_sys->i_buffer_size );
612
613         aout_BufferFree( p_buffer );
614     }
615     else
616     {
617         if( p_aout->output.output.i_format == VLC_FOURCC('f','l','3','2') )
618         {
619             UInt32 i, i_size = p_sys->i_buffer_size / sizeof(float);
620             float * p = (float *)outOutputData->mBuffers[ 0 ].mData;
621
622             for( i = 0; i < i_size; i++ )
623             {
624                 *p++ = 0.0;
625             }
626         }
627         else
628         {
629             memset( outOutputData->mBuffers[ 0 ].mData, 
630                     0, p_sys->i_buffer_size );
631         }
632     }
633
634     return( noErr );     
635 }
636
637 /*****************************************************************************
638  * InitHardwareInfo
639  *****************************************************************************/
640 static int InitHardwareInfo( aout_instance_t * p_aout )
641 {
642     OSStatus err;
643     UInt32 i, i_param_size;
644     AudioDeviceID devid_def; 
645     AudioDeviceID * p_devices;
646
647     struct aout_sys_t * p_sys = p_aout->output.p_sys;
648
649     vlc_mutex_lock( &p_sys->lock );
650
651     /* Get number of devices */
652     err = AudioHardwareGetPropertyInfo( kAudioHardwarePropertyDevices,
653                                         &i_param_size, NULL );
654     if( err != noErr )
655     {
656         msg_Err( p_aout, "could not get number of devices: [%4.4s]",
657                  (char *)&err );
658         vlc_mutex_unlock( &p_sys->lock );
659         return( VLC_EGENERIC );
660     }
661
662     p_sys->i_devices = i_param_size / sizeof( AudioDeviceID );
663
664     if( p_sys->i_devices < 1 )
665     {
666         msg_Err( p_aout, "no devices found" );
667         vlc_mutex_unlock( &p_sys->lock );
668         return( VLC_EGENERIC );
669     }
670
671     msg_Dbg( p_aout, "system has [%ld] device(s)", p_sys->i_devices );
672
673     /* Allocate DeviceID array */
674     p_devices = (AudioDeviceID *)malloc( i_param_size );
675     if( p_devices == NULL )
676     {
677         msg_Err( p_aout, "out of memory" );
678         vlc_mutex_unlock( &p_sys->lock );
679         return( VLC_ENOMEM );
680     }
681
682     /* Populate DeviceID array */
683     err = AudioHardwareGetProperty( kAudioHardwarePropertyDevices,
684                                     &i_param_size, (void *)p_devices );
685     if( err != noErr )
686     {
687         msg_Err( p_aout, "could not get the device ID's: [%4.4s]",
688                  (char *)&err );
689         free( (void *)p_devices );
690         vlc_mutex_unlock( &p_sys->lock );
691         return( VLC_EGENERIC );
692     }
693
694     i_param_size = sizeof( AudioDeviceID );
695     err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice,
696                                     &i_param_size, (void *)&devid_def );
697     if( err != noErr )
698     {
699         msg_Err( p_aout, "could not get default audio device: [%4.4s]",
700                  (char *)&err );
701         free( (void *)p_devices );
702         vlc_mutex_unlock( &p_sys->lock );
703         return( VLC_EGENERIC );
704     }
705
706     p_sys->p_devices = (struct aout_dev_t *)
707         malloc( sizeof( struct aout_dev_t ) * p_sys->i_devices ); 
708     if( p_sys->p_devices == NULL )
709     {
710         msg_Err( p_aout, "out of memory" );
711         free( (void *)p_devices );
712         vlc_mutex_unlock( &p_sys->lock );
713         return( VLC_ENOMEM );
714     }    
715
716     p_sys->i_options = 0;
717     p_sys->p_options = NULL;
718
719     for( i = 0; i < p_sys->i_devices; i++ )
720     {
721         p_sys->p_devices[i].devid = p_devices[i];
722
723         if( p_devices[i] == devid_def )
724         {
725             p_sys->i_def_dev = i;
726         }
727
728         if( InitDeviceInfo( i, p_aout ) )
729         {
730             UInt32 j;
731
732             msg_Err( p_aout, "InitDeviceInfo(%ld) failed", i );
733
734             for( j = 0; j < i; j++ )
735             {
736                 FreeDeviceInfo( j, p_aout );
737             }
738     
739             free( (void *)p_sys->p_devices );
740             free( (void *)p_devices );
741
742             vlc_mutex_unlock( &p_sys->lock );
743
744             return( VLC_EGENERIC );
745         }
746     }
747
748     free( (void *)p_devices );
749
750     p_sys->b_hwinfo = VLC_TRUE;
751
752     vlc_mutex_unlock( &p_sys->lock );
753
754     return( VLC_SUCCESS );
755 }
756
757 /*****************************************************************************
758  * InitDeviceInfo
759  *****************************************************************************/
760 static int InitDeviceInfo( UInt32 i_dev, aout_instance_t * p_aout ) 
761 {
762     OSStatus err;
763     UInt32 i, i_param_size;
764     AudioBufferList * p_buffer_list;
765
766     struct aout_sys_t * p_sys = p_aout->output.p_sys;
767     struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
768
769     /* Get length of device name */
770     err = AudioDeviceGetPropertyInfo( p_dev->devid, 0, FALSE, 
771                                       kAudioDevicePropertyDeviceName,
772                                       &i_param_size, NULL ); 
773     if( err != noErr )
774     {
775         msg_Err( p_aout, "could not get size of devicename: [%4.4s]",
776                  (char *)&err ); 
777         return( VLC_EGENERIC );
778     }
779
780     /* Allocate memory for device name */
781     p_dev->psz_device_name = (char *)malloc( i_param_size );
782     if( p_dev->psz_device_name == NULL )
783     {
784         msg_Err( p_aout, "out of memory" );
785         return( VLC_ENOMEM );
786     }
787
788     /* Get device name */
789     err = AudioDeviceGetProperty( p_dev->devid, 0, FALSE,
790                                   kAudioDevicePropertyDeviceName,
791                                   &i_param_size, p_dev->psz_device_name ); 
792     if( err != noErr )
793     {
794         msg_Err( p_aout, "could not get devicename: [%4.4s]",
795                  (char *)&err );
796         free( (void *)p_dev->psz_device_name );
797         return( VLC_EGENERIC );
798     }
799
800     msg_Dbg( p_aout, "device [%ld] has name [%s]",
801              i_dev, p_dev->psz_device_name );
802
803     err = AudioDeviceGetPropertyInfo( p_dev->devid, 0, FALSE,
804                                       kAudioDevicePropertyStreamConfiguration,
805                                       &i_param_size, NULL );
806     if( err != noErr )
807     {
808         msg_Err( p_aout, "could not get size of stream configuration: [%4.4s]",
809                  (char *)&err );
810         free( (void *)p_dev->psz_device_name );
811         return( VLC_EGENERIC );
812     }
813
814     p_buffer_list = (AudioBufferList *)malloc( i_param_size );
815     if( p_buffer_list == NULL )
816     {
817         msg_Err( p_aout, "out of memory" );
818         free( (void *)p_dev->psz_device_name );
819         return( VLC_ENOMEM );
820     }
821
822     err = AudioDeviceGetProperty( p_dev->devid, 0, FALSE,
823                                   kAudioDevicePropertyStreamConfiguration,
824                                   &i_param_size, p_buffer_list );
825     if( err != noErr )
826     {
827         msg_Err( p_aout, "could not get stream configuration: [%4.4s]",
828                  (char *)&err );
829         free( (void *)p_dev->psz_device_name );
830         free( (void *)p_buffer_list );
831         return( VLC_EGENERIC );
832     }
833
834     p_dev->i_streams = p_buffer_list->mNumberBuffers;
835     free( (void *)p_buffer_list );
836
837     msg_Dbg( p_aout, "device [%ld] has [%ld] streams", 
838              i_dev, p_dev->i_streams ); 
839
840     p_dev->pi_streams = (UInt32 *)malloc( p_dev->i_streams *
841                                           sizeof( *p_dev->pi_streams ) );
842     if( p_dev->pi_streams == NULL )
843     {
844         msg_Err( p_aout, "out of memory" );
845         free( (void *)p_dev->psz_device_name );
846         return( VLC_ENOMEM );
847     }
848
849     p_dev->pp_streams = (AudioStreamBasicDescription **) 
850                         malloc( p_dev->i_streams * 
851                                 sizeof( *p_dev->pp_streams ) );
852     if( p_dev->pp_streams == NULL )
853     {
854         msg_Err( p_aout, "out of memory" );
855         free( (void *)p_dev->psz_device_name );
856         free( (void *)p_dev->pi_streams );
857         return( VLC_ENOMEM );
858     } 
859
860     for( i = 0; i < p_dev->i_streams; i++ )
861     {
862         if( InitStreamInfo( i_dev, p_aout, i ) )
863         {
864             UInt32 j;
865
866             msg_Err( p_aout, "InitStreamInfo(%ld, %ld) failed", i_dev, i );
867
868             for( j = 0; j < i; j++ )
869             {
870                 FreeStreamInfo( i_dev, p_aout, j );
871             }
872
873             free( (void *)p_dev->psz_device_name );
874             free( (void *)p_dev->pi_streams );
875             free( (void *)p_dev->pp_streams );
876
877             return( VLC_EGENERIC );
878         }
879     }
880
881     return( VLC_SUCCESS );
882 }
883
884 /*****************************************************************************
885  * FreeDeviceInfo
886  *****************************************************************************/
887 static void FreeDeviceInfo( UInt32 i_dev, aout_instance_t * p_aout )
888 {
889     UInt32 i;
890
891     struct aout_sys_t * p_sys = p_aout->output.p_sys;
892     struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
893
894     for( i = 0; i < p_dev->i_streams; i++ )
895     {
896         FreeStreamInfo( i_dev, p_aout, i );
897     }
898
899     free( (void *)p_dev->pp_streams );
900     free( (void *)p_dev->pi_streams );
901     free( (void *)p_dev->psz_device_name );
902 }
903
904 /*****************************************************************************
905  * FreeHardwareInfo
906  *****************************************************************************/
907 static void FreeHardwareInfo( aout_instance_t * p_aout )
908 {
909     UInt32 i;
910
911     struct aout_sys_t * p_sys = p_aout->output.p_sys;
912
913     vlc_mutex_lock( &p_sys->lock );
914
915     if( !p_sys->b_hwinfo )
916     {
917         vlc_mutex_unlock( &p_sys->lock );
918         return;
919     }
920
921     for( i = 0; i < p_sys->i_devices; i++ )
922     {
923         FreeDeviceInfo( i, p_aout );
924     }
925
926     free( (void *)p_sys->p_options );
927     free( (void *)p_sys->p_devices );
928
929     p_sys->b_hwinfo = VLC_FALSE;
930
931     vlc_mutex_unlock( &p_sys->lock );
932 }
933
934 /*****************************************************************************
935  * GetStreamID 
936  *****************************************************************************/
937 static int GetStreamID( AudioDeviceID devid, UInt32 i_idx,
938                         AudioStreamID * p_sid )
939 {
940     OSStatus err;
941     UInt32 i_param_size;
942     AudioStreamID * p_stream_list;
943
944     err = AudioDeviceGetPropertyInfo( devid, 0, FALSE,
945                                       kAudioDevicePropertyStreams,
946                                       &i_param_size, NULL );
947     if( err != noErr )
948     {
949         return( VLC_EGENERIC );
950     }
951
952     p_stream_list = (AudioStreamID *)malloc( i_param_size );
953     if( p_stream_list == NULL )
954     {
955         return( VLC_ENOMEM );
956     }
957
958     err = AudioDeviceGetProperty( devid, 0, FALSE,
959                                   kAudioDevicePropertyStreams,
960                                   &i_param_size, p_stream_list );
961     if( err != noErr )
962     {
963         free( (void *)p_stream_list );
964         return( VLC_EGENERIC );
965     }
966
967     *p_sid = p_stream_list[i_idx - 1];
968
969     free( (void *)p_stream_list );
970
971     return( VLC_SUCCESS );
972 }
973
974 /*****************************************************************************
975  * InitStreamInfo
976  *****************************************************************************/
977 static int InitStreamInfo( UInt32 i_dev, aout_instance_t * p_aout,
978                            UInt32 i_idx )
979 {
980     OSStatus err;
981     AudioStreamID i_sid;
982     UInt32 i, j, i_param_size;
983
984     struct aout_sys_t * p_sys = p_aout->output.p_sys;
985     struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
986
987     if( GetStreamID( p_dev->devid, i_idx + 1, &i_sid ) )
988     {
989         msg_Err( p_aout, "GetStreamID(%ld, %ld) failed", i_dev, i_idx );
990         return( VLC_EGENERIC );
991     }
992
993     err = AudioStreamGetPropertyInfo( i_sid, 0,
994                                       kAudioStreamPropertyPhysicalFormats,
995                                       &i_param_size, NULL );
996     if( err != noErr )
997     {
998         msg_Err( p_aout, "could not retrieve the number of streams: [%4.4s]",
999                  (char *)&err );
1000         return( VLC_EGENERIC );
1001     }
1002
1003 #define P_STREAMS p_dev->pp_streams[i_idx]
1004 #define I_STREAMS p_dev->pi_streams[i_idx]
1005
1006     I_STREAMS = i_param_size / sizeof( AudioStreamBasicDescription );
1007
1008     P_STREAMS = (AudioStreamBasicDescription *)malloc( i_param_size );
1009     if( P_STREAMS == NULL )
1010     {
1011         msg_Err( p_aout, "out of memory" );
1012         return( VLC_ENOMEM );
1013     }
1014
1015     memset( P_STREAMS, 0, i_param_size );
1016
1017     err = AudioStreamGetProperty( i_sid, 0,
1018                                   kAudioStreamPropertyPhysicalFormats,
1019                                   &i_param_size, P_STREAMS );
1020     if( err != noErr )
1021     {
1022         msg_Err( p_aout, "could no get the streams: [%4.4s]",
1023                  (char *)&err );
1024         free( (void *)P_STREAMS );
1025         return( VLC_EGENERIC );
1026     }
1027
1028     for( j = 0; j < N_AOUT_CLASSES; j++ )
1029     {
1030         vlc_bool_t b_found = 0;
1031
1032         for( i = 0; i < I_STREAMS; i++ )
1033         {
1034             if( j == 0 )
1035             {
1036                 msg_Dbg( p_aout, STREAM_FORMAT_MSG( "supported format",
1037                                                     P_STREAMS[i] ) );
1038             }
1039
1040             if( ( P_STREAMS[i].mFormatID == 'IAC3' ||
1041                   P_STREAMS[i].mFormatID == kAudioFormat60958AC3 ) &&
1042                 !AOUT_FMT_NON_LINEAR( &p_aout->output.output ) ) 
1043             {
1044                 continue;
1045             }
1046
1047             if( ( P_STREAMS[i].mFormatID != aout_classes[j].mFormatID ) ||
1048                 ( P_STREAMS[i].mChannelsPerFrame != 
1049                   aout_classes[j].mChannelsPerFrame ) )
1050             {
1051                 continue;
1052             }
1053
1054             b_found = 1;
1055             break;
1056         }
1057
1058         if( b_found )
1059         {
1060             p_sys->p_options = (struct aout_option_t *)
1061                                realloc( p_sys->p_options, 
1062                                         ( p_sys->i_options + 1 ) *
1063                                         sizeof( struct aout_option_t ) ); 
1064             if( p_sys->p_options == NULL )
1065             {
1066                 msg_Err( p_aout, "out of memory" );
1067                 free( (void *)P_STREAMS );
1068                 return( VLC_ENOMEM );
1069             }
1070
1071 #define AOUT_OPTION p_sys->p_options[p_sys->i_options]
1072
1073             snprintf( AOUT_OPTION.sz_option,
1074                       sizeof( AOUT_OPTION.sz_option ) / 
1075                       sizeof( AOUT_OPTION.sz_option[0] ) - 1,
1076                       "%ld: %s (%s)", 
1077                       p_sys->i_options,
1078                       p_dev->psz_device_name, 
1079                       aout_classes[j].psz_class );
1080
1081             AOUT_OPTION.i_sid = i_sid;
1082             AOUT_OPTION.i_dev = i_dev; 
1083             AOUT_OPTION.i_idx = i_idx;
1084             AOUT_OPTION.i_sdx = i;
1085             AOUT_OPTION.i_cdx = j;
1086
1087 #undef AOUT_OPTION
1088
1089             p_sys->i_options++;
1090         } 
1091     }
1092
1093 #undef I_STREAMS
1094 #undef P_STREAMS
1095
1096     return( VLC_SUCCESS );
1097 }
1098
1099 /*****************************************************************************
1100  * FreeStreamInfo
1101  *****************************************************************************/
1102 static void FreeStreamInfo( UInt32 i_dev, aout_instance_t * p_aout,
1103                             UInt32 i_idx )
1104 {
1105     struct aout_sys_t * p_sys = p_aout->output.p_sys;
1106     struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
1107
1108     free( (void *)p_dev->pp_streams[i_idx] );
1109 }
1110
1111 /*****************************************************************************
1112  * InitDevice 
1113  *****************************************************************************/
1114 static int InitDevice( aout_instance_t * p_aout ) 
1115 {
1116     OSStatus err;
1117     vlc_value_t val;
1118     unsigned int i_option;
1119     vlc_bool_t b_found = VLC_FALSE;
1120     UInt32 i, i_stream, i_param_size;
1121
1122     struct aout_dev_t * p_dev;
1123     struct aout_option_t * p_option;
1124     struct aout_sys_t * p_sys = p_aout->output.p_sys;
1125
1126     if( var_Get( p_aout, "audio-device", &val ) < 0 )
1127     {
1128         msg_Err( p_aout, "audio-device var does not exist" );
1129         return( VLC_ENOVAR );
1130     }
1131
1132     i_option = val.i_int;
1133     p_option = &p_sys->p_options[i_option];
1134     p_dev = &p_sys->p_devices[p_option->i_dev];
1135
1136     msg_Dbg( p_aout, "getting device [%ld]", p_option->i_dev );
1137
1138     i_param_size = sizeof( p_sys->b_dev_alive );
1139     err = AudioDeviceGetProperty( p_dev->devid, 0, FALSE,
1140                                   kAudioDevicePropertyDeviceIsAlive,
1141                                   &i_param_size, &p_sys->b_dev_alive );
1142     if( err != noErr )
1143     {
1144         msg_Err( p_aout, "could not check whether device is alive: %4.4s",
1145                  (char *)&err );
1146         return( VLC_EGENERIC );
1147     }
1148
1149 #define P_STREAMS p_dev->pp_streams[p_option->i_idx]
1150 #define I_STREAMS p_dev->pi_streams[p_option->i_idx]
1151
1152     for( i = 0; i < I_STREAMS; i++ )
1153     {
1154         if( P_STREAMS[i].mFormatID ==
1155             aout_classes[p_option->i_cdx].mFormatID &&
1156             P_STREAMS[i].mChannelsPerFrame ==
1157             aout_classes[p_option->i_cdx].mChannelsPerFrame &&
1158             P_STREAMS[i].mSampleRate == p_aout->output.output.i_rate )  
1159         {
1160             b_found = VLC_TRUE;
1161             break;
1162         }
1163     } 
1164
1165     i_stream = b_found ? i : p_option->i_sdx;
1166
1167     i_param_size = sizeof( p_sys->sfmt_revert );
1168     err = AudioStreamGetProperty( p_option->i_sid, 0,
1169                                   kAudioStreamPropertyPhysicalFormat,
1170                                   &i_param_size, 
1171                                   (void *)&p_sys->sfmt_revert );
1172     if( err != noErr )
1173     {
1174         msg_Err( p_aout, "could not retrieve the original streamformat: [%4.4s]",
1175                  (char *)&err );
1176         return( VLC_EGENERIC ); 
1177     }
1178
1179     if( memcmp( &P_STREAMS[i_stream], &p_sys->sfmt_revert, 
1180                 sizeof( p_sys->sfmt_revert ) ) != 0 ) 
1181     {
1182         struct timeval now;
1183         struct timespec timeout;
1184         struct { vlc_mutex_t lock; vlc_cond_t cond; } w;
1185
1186         vlc_cond_init( p_aout, &w.cond );
1187         vlc_mutex_init( p_aout, &w.lock );
1188
1189         msg_Dbg( p_aout, STREAM_FORMAT_MSG( "stream format",
1190                                             p_sys->sfmt_revert ) );
1191
1192         err = AudioStreamAddPropertyListener( p_option->i_sid, 0,
1193                                           kAudioStreamPropertyPhysicalFormat,
1194                                           StreamListener, (void *)&w );
1195         if( err != noErr )
1196         {
1197             msg_Err( p_aout, "AudioStreamAddPropertyListener failed: [%4.4s]",
1198                      (char *)&err );
1199             vlc_mutex_destroy( &w.lock );
1200             vlc_cond_destroy( &w.cond );
1201             return( VLC_EGENERIC ); 
1202         }
1203
1204         vlc_mutex_lock( &w.lock );
1205
1206         msg_Dbg( p_aout, STREAM_FORMAT_MSG( "setting format",
1207                                             P_STREAMS[i_stream] ) );
1208
1209         err = AudioStreamSetProperty( p_option->i_sid, 0, 0,
1210                                       kAudioStreamPropertyPhysicalFormat,
1211                                       sizeof( P_STREAMS[i_stream] ),
1212                                       &P_STREAMS[i_stream] ); 
1213         if( err != noErr )
1214         {
1215             msg_Err( p_aout, "could not set the stream format: [%4.4s]",
1216                      (char *)&err );
1217             vlc_mutex_unlock( &w.lock );
1218             vlc_mutex_destroy( &w.lock );
1219             vlc_cond_destroy( &w.cond );
1220             return( VLC_EGENERIC );
1221         }
1222
1223         gettimeofday( &now, NULL );
1224         timeout.tv_sec = now.tv_sec;
1225         timeout.tv_nsec = (now.tv_usec + 100000) * 1000;
1226
1227         pthread_cond_timedwait( &w.cond.cond, &w.lock.mutex, &timeout );
1228         vlc_mutex_unlock( &w.lock );
1229
1230         if( GetStreamID( p_dev->devid, p_option->i_idx + 1, 
1231                          &p_option->i_sid ) )
1232         {
1233             msg_Err( p_aout, "GetStreamID(%ld, %ld) failed", 
1234                      p_option->i_dev, p_option->i_idx );
1235             vlc_mutex_destroy( &w.lock );
1236             vlc_cond_destroy( &w.cond );
1237             return( VLC_EGENERIC );
1238         }
1239
1240         err = AudioStreamRemovePropertyListener( p_option->i_sid, 0,
1241                 kAudioStreamPropertyPhysicalFormat, StreamListener );
1242         if( err != noErr )
1243         {
1244             msg_Err( p_aout, 
1245                     "AudioStreamRemovePropertyListener failed: [%4.4s]",
1246                     (char *)&err );
1247             vlc_mutex_destroy( &w.lock );
1248             vlc_cond_destroy( &w.cond );
1249             return( VLC_EGENERIC );
1250         }
1251
1252         vlc_mutex_destroy( &w.lock );
1253         vlc_cond_destroy( &w.cond );
1254
1255         p_sys->b_revert_sfmt = VLC_TRUE;
1256     }
1257
1258 #undef I_STREAMS
1259 #undef P_STREAMS
1260
1261     err = AudioDeviceAddPropertyListener( p_dev->devid, 0, FALSE,
1262                                           kAudioDevicePropertyDeviceIsAlive,
1263                                           DeviceListener, (void *)p_aout );
1264     if( err != noErr )
1265     {
1266         msg_Err( p_aout, "AudioDeviceAddPropertyListener failed: [%4.4s]",
1267                  (char *)&err );
1268         return( VLC_EGENERIC );
1269     } 
1270
1271     config_PutInt( p_aout, "coreaudio-dev", i_option );
1272
1273     p_sys->i_sel_opt = i_option;
1274     p_sys->devid = p_dev->devid;
1275
1276     return( VLC_SUCCESS );
1277
1278
1279 /*****************************************************************************
1280  * FreeDevice 
1281  *****************************************************************************/
1282 static void FreeDevice( aout_instance_t * p_aout ) 
1283 {
1284     OSStatus err;
1285
1286     struct aout_sys_t * p_sys = p_aout->output.p_sys;
1287
1288     if( p_sys->b_revert_sfmt )
1289     {
1290         struct aout_dev_t * p_dev;
1291         struct aout_option_t * p_option;
1292
1293         p_option = &p_sys->p_options[p_sys->i_sel_opt];
1294         p_dev = &p_sys->p_devices[p_option->i_dev];
1295
1296         msg_Dbg( p_aout, STREAM_FORMAT_MSG( "reverting to format",
1297                                             p_sys->sfmt_revert ) );
1298
1299         if( GetStreamID( p_dev->devid, p_option->i_idx + 1,
1300                          &p_option->i_sid ) )
1301         {
1302             msg_Err( p_aout, "GetStreamID(%ld, %ld) failed", 
1303                      p_option->i_dev, p_option->i_idx );
1304         }
1305         else
1306         {
1307             err = AudioStreamSetProperty( p_option->i_sid, 0, 0,
1308                                           kAudioStreamPropertyPhysicalFormat,
1309                                           sizeof( p_sys->sfmt_revert ),
1310                                           &p_sys->sfmt_revert ); 
1311             if( err != noErr )
1312             {
1313                 msg_Err( p_aout, "AudioStreamSetProperty revert format failed: [%4.4s]",
1314                          (char *)&err );
1315             }
1316         }
1317     }
1318
1319     err = AudioDeviceRemovePropertyListener( p_sys->devid, 0, FALSE,
1320                                              kAudioDevicePropertyDeviceIsAlive,
1321                                              DeviceListener );
1322     if( err != noErr )
1323     {
1324         msg_Err( p_aout, "AudioDeviceRemovePropertyListener failed: [%4.4s]",
1325                  (char *)&err );
1326     } 
1327 }
1328
1329 /*****************************************************************************
1330  * HardwareListener 
1331  *****************************************************************************/
1332 static OSStatus HardwareListener( AudioHardwarePropertyID inPropertyID,
1333                                   void * inClientData )
1334 {
1335     OSStatus err = noErr;
1336
1337     aout_instance_t * p_aout = (aout_instance_t *)inClientData;
1338     struct aout_sys_t * p_sys = p_aout->output.p_sys;
1339
1340     switch( inPropertyID )
1341     {
1342         case kAudioHardwarePropertyDevices:
1343         {
1344             UInt32 i_idx = 0;
1345             UInt32 i_sdx = 0;
1346             int i_option = -1;
1347
1348             if( p_sys->b_dev_alive )
1349             {
1350                 i_idx = p_sys->p_options[p_sys->i_sel_opt].i_idx;
1351                 i_sdx = p_sys->p_options[p_sys->i_sel_opt].i_sdx;
1352             }
1353
1354             FreeHardwareInfo( p_aout );
1355
1356             if( InitHardwareInfo( p_aout ) )
1357             {
1358                 msg_Err( p_aout, "InitHardwareInfo failed" );
1359                 break;
1360             }
1361
1362             if( p_sys->b_dev_alive )
1363             {
1364                 UInt32 i;
1365
1366                 for( i = 0; i < p_sys->i_options; i++ )
1367                 {
1368                     if( p_sys->p_devices[p_sys->p_options[i].i_dev].devid ==
1369                         p_sys->devid && p_sys->p_options[i].i_idx == i_idx &&
1370                         p_sys->p_options[i].i_sdx == i_sdx )
1371                     {
1372                         i_option = i;
1373                         break;
1374                     }
1375                 }
1376             }
1377
1378             var_Destroy( p_aout, "audio-device" );
1379             InitDeviceVar( p_aout, i_option, !p_sys->b_dev_alive );
1380         }
1381         break;
1382     }
1383
1384     return( err );
1385 }
1386
1387 /*****************************************************************************
1388  * DeviceListener 
1389  *****************************************************************************/
1390 static OSStatus DeviceListener( AudioDeviceID inDevice,
1391                                 UInt32 inChannel,
1392                                 Boolean isInput,
1393                                 AudioDevicePropertyID inPropertyID,
1394                                 void *inClientData )
1395 {
1396     UInt32 i_param_size;
1397     OSStatus err = noErr;
1398
1399     aout_instance_t * p_aout = (aout_instance_t *)inClientData;
1400     struct aout_sys_t * p_sys = p_aout->output.p_sys;
1401
1402     switch( inPropertyID )
1403     {
1404         case kAudioDevicePropertyDeviceIsAlive:
1405         {
1406             i_param_size = sizeof( p_sys->b_dev_alive );
1407             err = AudioDeviceGetProperty( p_sys->devid, 0, FALSE, 
1408                                           kAudioDevicePropertyDeviceIsAlive,
1409                                           &i_param_size, &p_sys->b_dev_alive );
1410             if( err != noErr )
1411             {
1412                 msg_Err( p_aout, "could not determine wether device is alive: %4.4s",
1413                          (char *)&err );
1414             }
1415         }
1416         break;
1417     }
1418
1419     return( err );
1420 }
1421
1422 /*****************************************************************************
1423  * StreamListener 
1424  *****************************************************************************/
1425 static OSStatus StreamListener( AudioStreamID inStream,
1426                                 UInt32 inChannel,
1427                                 AudioDevicePropertyID inPropertyID,
1428                                 void * inClientData )
1429 {
1430     OSStatus err = noErr;
1431
1432     struct { vlc_mutex_t lock; vlc_cond_t cond; } * w = inClientData;
1433
1434     switch( inPropertyID )
1435     {
1436         case kAudioStreamPropertyPhysicalFormat:
1437             vlc_mutex_lock( &w->lock );
1438             vlc_cond_signal( &w->cond );
1439             vlc_mutex_unlock( &w->lock ); 
1440             break;
1441
1442         default:
1443             break;
1444     }
1445
1446     return( err );
1447 }
1448
1449 /*****************************************************************************
1450  * InitDeviceVar
1451  *****************************************************************************/
1452 static void InitDeviceVar( aout_instance_t * p_aout, int i_option,
1453                            vlc_bool_t b_change )
1454
1455     UInt32 i;
1456     vlc_value_t val, text;
1457
1458     struct aout_sys_t * p_sys = p_aout->output.p_sys;
1459
1460     if( i_option == -1 || i_option >= (int)p_sys->i_options )
1461     {
1462         for( i = 0; i < p_sys->i_options; i++ )
1463         {
1464             if( p_sys->p_options[i].i_dev == p_sys->i_def_dev )
1465             {
1466                 i_option = i;
1467                 break;
1468             }
1469         }
1470     }
1471
1472     var_Create( p_aout, "audio-device", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
1473     text.psz_string = ADEV_TEXT;
1474     var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );
1475
1476     for( i = 0; i < p_sys->i_options; i++ )
1477     {
1478         text.psz_string = p_sys->p_options[i].sz_option;
1479         val.i_int = i;
1480         var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
1481
1482         if( !b_change && i == (UInt32)i_option )
1483         {
1484             p_sys->i_sel_opt = i;
1485             var_Set( p_aout, "audio-device", val );
1486             config_PutInt( p_aout, "coreaudio-dev", i_option );
1487         }
1488     }
1489
1490     var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart,
1491                      NULL );
1492
1493     if( b_change )
1494     {
1495         val.i_int = i_option;
1496         var_Set( p_aout, "audio-device", val );
1497     }
1498
1499     val.b_bool = VLC_TRUE;
1500     var_Set( p_aout, "intf-change", val );
1501 }