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