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