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