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