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