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