]> git.sesse.net Git - vlc/blob - modules/audio_output/coreaudio.c
8a79d001ad6b9d481e8371d560f3bf7dbbd44a41
[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.2 2003/05/04 15:02:42 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     current_date = p_sys->clock_diff +
590                    AudioConvertHostTimeToNanos( host_time.mHostTime ) / 1000;
591
592 #define B_SPDI (p_aout->output.output.i_format == VLC_FOURCC('s','p','d','i'))
593     p_buffer = aout_OutputNextBuffer( p_aout, current_date, B_SPDI );
594 #undef B_SPDI
595
596     if( p_buffer != NULL )
597     {
598         /* move data into output data buffer */
599         p_aout->p_vlc->pf_memcpy( outOutputData->mBuffers[ 0 ].mData, 
600                                   p_buffer->p_buffer, 
601                                   p_sys->i_buffer_size );
602
603         aout_BufferFree( p_buffer );
604     }
605     else
606     {
607         if( p_aout->output.output.i_format == VLC_FOURCC('f','l','3','2') )
608         {
609             UInt32 i, i_size = p_sys->i_buffer_size / sizeof(float);
610             float * p = (float *)outOutputData->mBuffers[ 0 ].mData;
611
612             for( i = 0; i < i_size; i++ )
613             {
614                 *p++ = 0.0;
615             }
616         }
617         else
618         {
619             memset( outOutputData->mBuffers[ 0 ].mData, 
620                     0, p_sys->i_buffer_size );
621         }
622     }
623
624     return( noErr );     
625 }
626
627 /*****************************************************************************
628  * InitHardwareInfo
629  *****************************************************************************/
630 static int InitHardwareInfo( aout_instance_t * p_aout )
631 {
632     OSStatus err;
633     UInt32 i, i_param_size;
634     AudioDeviceID devid_def; 
635     AudioDeviceID * p_devices;
636
637     struct aout_sys_t * p_sys = p_aout->output.p_sys;
638
639     vlc_mutex_lock( &p_sys->lock );
640
641     /* Get number of devices */
642     err = AudioHardwareGetPropertyInfo( kAudioHardwarePropertyDevices,
643                                         &i_param_size, NULL );
644     if( err != noErr )
645     {
646         msg_Err( p_aout, "AudioHardwareGetPropertyInfo failed: [%4.4s]",
647                  (char *)&err );
648         vlc_mutex_unlock( &p_sys->lock );
649         return( VLC_EGENERIC );
650     }
651
652     p_sys->i_devices = i_param_size / sizeof( AudioDeviceID );
653
654     if( p_sys->i_devices < 1 )
655     {
656         msg_Err( p_aout, "no devices found" );
657         vlc_mutex_unlock( &p_sys->lock );
658         return( VLC_EGENERIC );
659     }
660
661     msg_Dbg( p_aout, "system has [%ld] device(s)", p_sys->i_devices );
662
663     /* Allocate DeviceID array */
664     p_devices = (AudioDeviceID *)malloc( i_param_size );
665     if( p_devices == NULL )
666     {
667         msg_Err( p_aout, "out of memory" );
668         vlc_mutex_unlock( &p_sys->lock );
669         return( VLC_ENOMEM );
670     }
671
672     /* Populate DeviceID array */
673     err = AudioHardwareGetProperty( kAudioHardwarePropertyDevices,
674                                     &i_param_size, (void *)p_devices );
675     if( err != noErr )
676     {
677         msg_Err( p_aout, "AudioHardwareGetProperty failed: [%4.4s]",
678                  (char *)&err );
679         free( (void *)p_devices );
680         vlc_mutex_unlock( &p_sys->lock );
681         return( VLC_EGENERIC );
682     }
683
684     i_param_size = sizeof( AudioDeviceID );
685     err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice,
686                                     &i_param_size, (void *)&devid_def );
687     if( err != noErr )
688     {
689         msg_Err( p_aout, "AudioHardwareGetProperty failed: [%4.4s]",
690                  (char *)&err );
691         free( (void *)p_devices );
692         vlc_mutex_unlock( &p_sys->lock );
693         return( VLC_EGENERIC );
694     }
695
696     p_sys->p_devices = (struct aout_dev_t *)
697         malloc( sizeof( struct aout_dev_t ) * p_sys->i_devices ); 
698     if( p_sys->p_devices == NULL )
699     {
700         msg_Err( p_aout, "out of memory" );
701         free( (void *)p_devices );
702         vlc_mutex_unlock( &p_sys->lock );
703         return( VLC_ENOMEM );
704     }    
705
706     p_sys->i_options = 0;
707     p_sys->p_options = NULL;
708
709     for( i = 0; i < p_sys->i_devices; i++ )
710     {
711         p_sys->p_devices[i].devid = p_devices[i];
712
713         if( p_devices[i] == devid_def )
714         {
715             p_sys->i_def_dev = i;
716         }
717
718         if( InitDeviceInfo( i, p_aout ) )
719         {
720             UInt32 j;
721
722             msg_Err( p_aout, "InitDeviceInfo(%ld) failed", i );
723
724             for( j = 0; j < i; j++ )
725             {
726                 FreeDeviceInfo( j, p_aout );
727             }
728     
729             free( (void *)p_sys->p_devices );
730             free( (void *)p_devices );
731
732             vlc_mutex_unlock( &p_sys->lock );
733
734             return( VLC_EGENERIC );
735         }
736     }
737
738     free( (void *)p_devices );
739
740     p_sys->b_hwinfo = VLC_TRUE;
741
742     vlc_mutex_unlock( &p_sys->lock );
743
744     return( VLC_SUCCESS );
745 }
746
747 /*****************************************************************************
748  * InitDeviceInfo
749  *****************************************************************************/
750 static int InitDeviceInfo( UInt32 i_dev, aout_instance_t * p_aout ) 
751 {
752     OSStatus err;
753     UInt32 i, i_param_size;
754     AudioBufferList * p_buffer_list;
755
756     struct aout_sys_t * p_sys = p_aout->output.p_sys;
757     struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
758
759     /* Get length of device name */
760     err = AudioDeviceGetPropertyInfo( p_dev->devid, 0, FALSE, 
761                                       kAudioDevicePropertyDeviceName,
762                                       &i_param_size, NULL ); 
763     if( err != noErr )
764     {
765         msg_Err( p_aout, "AudioDeviceGetPropertyInfo failed: [%4.4s]",
766                  (char *)&err ); 
767         return( VLC_EGENERIC );
768     }
769
770     /* Allocate memory for device name */
771     p_dev->psz_device_name = (char *)malloc( i_param_size );
772     if( p_dev->psz_device_name == NULL )
773     {
774         msg_Err( p_aout, "out of memory" );
775         return( VLC_ENOMEM );
776     }
777
778     /* Get device name */
779     err = AudioDeviceGetProperty( p_dev->devid, 0, FALSE,
780                                   kAudioDevicePropertyDeviceName,
781                                   &i_param_size, p_dev->psz_device_name ); 
782     if( err != noErr )
783     {
784         msg_Err( p_aout, "AudioDeviceGetProperty failed: [%4.4s]",
785                  (char *)&err );
786         free( (void *)p_dev->psz_device_name );
787         return( VLC_EGENERIC );
788     }
789
790     msg_Dbg( p_aout, "device [%ld] has name [%s]",
791              i_dev, p_dev->psz_device_name );
792
793     err = AudioDeviceGetPropertyInfo( p_dev->devid, 0, FALSE,
794                                       kAudioDevicePropertyStreamConfiguration,
795                                       &i_param_size, NULL );
796     if( err != noErr )
797     {
798         msg_Err( p_aout, "AudioDeviceGetPropertyInfo failed: [%4.4s]",
799                  (char *)&err );
800         free( (void *)p_dev->psz_device_name );
801         return( VLC_EGENERIC );
802     }
803
804     p_buffer_list = (AudioBufferList *)malloc( i_param_size );
805     if( p_buffer_list == NULL )
806     {
807         msg_Err( p_aout, "out of memory" );
808         free( (void *)p_dev->psz_device_name );
809         return( VLC_ENOMEM );
810     }
811
812     err = AudioDeviceGetProperty( p_dev->devid, 0, FALSE,
813                                   kAudioDevicePropertyStreamConfiguration,
814                                   &i_param_size, p_buffer_list );
815     if( err != noErr )
816     {
817         msg_Err( p_aout, "AudioDeviceGetProperty failed: [%4.4s]",
818                  (char *)&err );
819         free( (void *)p_dev->psz_device_name );
820         free( (void *)p_buffer_list );
821         return( VLC_EGENERIC );
822     }
823
824     p_dev->i_streams = p_buffer_list->mNumberBuffers;
825     free( (void *)p_buffer_list );
826
827     msg_Dbg( p_aout, "device [%ld] has [%ld] streams", 
828              i_dev, p_dev->i_streams ); 
829
830     p_dev->pi_streams = (UInt32 *)malloc( p_dev->i_streams *
831                                           sizeof( *p_dev->pi_streams ) );
832     if( p_dev->pi_streams == NULL )
833     {
834         msg_Err( p_aout, "out of memory" );
835         free( (void *)p_dev->psz_device_name );
836         return( VLC_ENOMEM );
837     }
838
839     p_dev->pp_streams = (AudioStreamBasicDescription **) 
840                         malloc( p_dev->i_streams * 
841                                 sizeof( *p_dev->pp_streams ) );
842     if( p_dev->pp_streams == NULL )
843     {
844         msg_Err( p_aout, "out of memory" );
845         free( (void *)p_dev->psz_device_name );
846         free( (void *)p_dev->pi_streams );
847         return( VLC_ENOMEM );
848     } 
849
850     for( i = 0; i < p_dev->i_streams; i++ )
851     {
852         if( InitStreamInfo( i_dev, p_aout, i ) )
853         {
854             UInt32 j;
855
856             msg_Err( p_aout, "InitStreamInfo(%ld, %ld) failed", i_dev, i );
857
858             for( j = 0; j < i; j++ )
859             {
860                 FreeStreamInfo( i_dev, p_aout, j );
861             }
862
863             free( (void *)p_dev->psz_device_name );
864             free( (void *)p_dev->pi_streams );
865             free( (void *)p_dev->pp_streams );
866
867             return( VLC_EGENERIC );
868         }
869     }
870
871     return( VLC_SUCCESS );
872 }
873
874 /*****************************************************************************
875  * FreeDeviceInfo
876  *****************************************************************************/
877 static void FreeDeviceInfo( UInt32 i_dev, aout_instance_t * p_aout )
878 {
879     UInt32 i;
880
881     struct aout_sys_t * p_sys = p_aout->output.p_sys;
882     struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
883
884     for( i = 0; i < p_dev->i_streams; i++ )
885     {
886         FreeStreamInfo( i_dev, p_aout, i );
887     }
888
889     free( (void *)p_dev->pp_streams );
890     free( (void *)p_dev->pi_streams );
891     free( (void *)p_dev->psz_device_name );
892 }
893
894 /*****************************************************************************
895  * FreeHardwareInfo
896  *****************************************************************************/
897 static void FreeHardwareInfo( aout_instance_t * p_aout )
898 {
899     UInt32 i;
900
901     struct aout_sys_t * p_sys = p_aout->output.p_sys;
902
903     vlc_mutex_lock( &p_sys->lock );
904
905     if( !p_sys->b_hwinfo )
906     {
907         vlc_mutex_unlock( &p_sys->lock );
908         return;
909     }
910
911     for( i = 0; i < p_sys->i_devices; i++ )
912     {
913         FreeDeviceInfo( i, p_aout );
914     }
915
916     free( (void *)p_sys->p_options );
917     free( (void *)p_sys->p_devices );
918
919     p_sys->b_hwinfo = VLC_FALSE;
920
921     vlc_mutex_unlock( &p_sys->lock );
922 }
923
924 /*****************************************************************************
925  * GetStreamID 
926  *****************************************************************************/
927 static int GetStreamID( AudioDeviceID devid, UInt32 i_idx,
928                         AudioStreamID * p_sid )
929 {
930     OSStatus err;
931     UInt32 i_param_size;
932     AudioStreamID * p_stream_list;
933
934     err = AudioDeviceGetPropertyInfo( devid, 0, FALSE,
935                                       kAudioDevicePropertyStreams,
936                                       &i_param_size, NULL );
937     if( err != noErr )
938     {
939         return( VLC_EGENERIC );
940     }
941
942     p_stream_list = (AudioStreamID *)malloc( i_param_size );
943     if( p_stream_list == NULL )
944     {
945         return( VLC_ENOMEM );
946     }
947
948     err = AudioDeviceGetProperty( devid, 0, FALSE,
949                                   kAudioDevicePropertyStreams,
950                                   &i_param_size, p_stream_list );
951     if( err != noErr )
952     {
953         free( (void *)p_stream_list );
954         return( VLC_EGENERIC );
955     }
956
957     *p_sid = p_stream_list[i_idx - 1];
958
959     free( (void *)p_stream_list );
960
961     return( VLC_SUCCESS );
962 }
963
964 /*****************************************************************************
965  * InitStreamInfo
966  *****************************************************************************/
967 static int InitStreamInfo( UInt32 i_dev, aout_instance_t * p_aout,
968                            UInt32 i_idx )
969 {
970     OSStatus err;
971     AudioStreamID i_sid;
972     UInt32 i, j, i_param_size;
973
974     struct aout_sys_t * p_sys = p_aout->output.p_sys;
975     struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
976
977     if( GetStreamID( p_dev->devid, i_idx + 1, &i_sid ) )
978     {
979         msg_Err( p_aout, "GetStreamID(%ld, %ld) failed", i_dev, i_idx );
980         return( VLC_EGENERIC );
981     }
982
983     err = AudioStreamGetPropertyInfo( i_sid, 0,
984                                       kAudioStreamPropertyPhysicalFormats,
985                                       &i_param_size, NULL );
986     if( err != noErr )
987     {
988         msg_Err( p_aout, "AudioStreamGetPropertyInfo failed: [%4.4s]",
989                  (char *)&err );
990         return( VLC_EGENERIC );
991     }
992
993 #define P_STREAMS p_dev->pp_streams[i_idx]
994 #define I_STREAMS p_dev->pi_streams[i_idx]
995
996     I_STREAMS = i_param_size / sizeof( AudioStreamBasicDescription );
997
998     P_STREAMS = (AudioStreamBasicDescription *)malloc( i_param_size );
999     if( P_STREAMS == NULL )
1000     {
1001         msg_Err( p_aout, "out of memory" );
1002         return( VLC_ENOMEM );
1003     }
1004
1005     memset( P_STREAMS, 0, i_param_size );
1006
1007     err = AudioStreamGetProperty( i_sid, 0,
1008                                   kAudioStreamPropertyPhysicalFormats,
1009                                   &i_param_size, P_STREAMS );
1010     if( err != noErr )
1011     {
1012         msg_Err( p_aout, "AudioStreamGetProperty failed: [%4.4s]",
1013                  (char *)&err );
1014         free( (void *)P_STREAMS );
1015         return( VLC_EGENERIC );
1016     }
1017
1018     for( j = 0; j < N_AOUT_CLASSES; j++ )
1019     {
1020         vlc_bool_t b_found = 0;
1021
1022         for( i = 0; i < I_STREAMS; i++ )
1023         {
1024             if( j == 0 )
1025             {
1026                 msg_Dbg( p_aout, STREAM_FORMAT_MSG( "supported format",
1027                                                     P_STREAMS[i] ) );
1028             }
1029
1030             if( ( P_STREAMS[i].mFormatID == 'IAC3' ||
1031                   P_STREAMS[i].mFormatID == kAudioFormat60958AC3 ) &&
1032                 !AOUT_FMT_NON_LINEAR( &p_aout->output.output ) ) 
1033             {
1034                 continue;
1035             }
1036
1037             if( ( P_STREAMS[i].mFormatID != aout_classes[j].mFormatID ) ||
1038                 ( P_STREAMS[i].mChannelsPerFrame != 
1039                   aout_classes[j].mChannelsPerFrame ) )
1040             {
1041                 continue;
1042             }
1043
1044             b_found = 1;
1045             break;
1046         }
1047
1048         if( b_found )
1049         {
1050             p_sys->p_options = (struct aout_option_t *)
1051                                realloc( p_sys->p_options, 
1052                                         ( p_sys->i_options + 1 ) *
1053                                         sizeof( struct aout_option_t ) ); 
1054             if( p_sys->p_options == NULL )
1055             {
1056                 msg_Err( p_aout, "out of memory" );
1057                 free( (void *)P_STREAMS );
1058                 return( VLC_ENOMEM );
1059             }
1060
1061 #define AOUT_OPTION p_sys->p_options[p_sys->i_options]
1062
1063             snprintf( AOUT_OPTION.sz_option,
1064                       sizeof( AOUT_OPTION.sz_option ) / 
1065                       sizeof( AOUT_OPTION.sz_option[0] ) - 1,
1066                       "%ld: %s (%s)", 
1067                       p_sys->i_options,
1068                       p_dev->psz_device_name, 
1069                       aout_classes[j].psz_class );
1070
1071             AOUT_OPTION.i_sid = i_sid;
1072             AOUT_OPTION.i_dev = i_dev; 
1073             AOUT_OPTION.i_idx = i_idx;
1074             AOUT_OPTION.i_sdx = i;
1075             AOUT_OPTION.i_cdx = j;
1076
1077 #undef AOUT_OPTION
1078
1079             p_sys->i_options++;
1080         } 
1081     }
1082
1083 #undef I_STREAMS
1084 #undef P_STREAMS
1085
1086     return( VLC_SUCCESS );
1087 }
1088
1089 /*****************************************************************************
1090  * FreeStreamInfo
1091  *****************************************************************************/
1092 static void FreeStreamInfo( UInt32 i_dev, aout_instance_t * p_aout,
1093                             UInt32 i_idx )
1094 {
1095     struct aout_sys_t * p_sys = p_aout->output.p_sys;
1096     struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
1097
1098     free( (void *)p_dev->pp_streams[i_idx] );
1099 }
1100
1101 /*****************************************************************************
1102  * InitDevice 
1103  *****************************************************************************/
1104 static int InitDevice( aout_instance_t * p_aout ) 
1105 {
1106     OSStatus err;
1107     vlc_value_t val;
1108     unsigned int i_option;
1109     vlc_bool_t b_found = VLC_FALSE;
1110     UInt32 i, i_stream, i_param_size;
1111
1112     struct aout_dev_t * p_dev;
1113     struct aout_option_t * p_option;
1114     struct aout_sys_t * p_sys = p_aout->output.p_sys;
1115
1116     if( var_Get( p_aout, "audio-device", &val ) < 0 )
1117     {
1118         msg_Err( p_aout, "audio-device var does not exist" );
1119         return( VLC_ENOVAR );
1120     }
1121
1122     if( !sscanf( val.psz_string, "%d:", &i_option ) ||
1123         p_sys->i_options <= i_option )
1124     {
1125         i_option = 0;
1126     }
1127
1128     free( (void *)val.psz_string );
1129
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;
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_STRING | 
1470                                         VLC_VAR_HASCHOICE );
1471
1472     for( i = 0; i < p_sys->i_options; i++ )
1473     {
1474         val.psz_string = p_sys->p_options[i].sz_option;
1475         var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val );
1476
1477         if( !b_change && i == (UInt32)i_option )
1478         {
1479             p_sys->i_sel_opt = i;
1480             var_Set( p_aout, "audio-device", val );
1481             config_PutInt( p_aout, "coreaudio-dev", i_option );
1482         }
1483     }
1484
1485     var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart,
1486                      NULL );
1487
1488     if( b_change )
1489     {
1490         val.psz_string = p_sys->p_options[i_option].sz_option;
1491         var_Set( p_aout, "audio-device", val );
1492     }
1493
1494     val.b_bool = VLC_TRUE;
1495     var_Set( p_aout, "intf-change", val );
1496 }