9b89e9a8bc795facf472e4c0ae6ac086b6a12b09
[vlc.git] / 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.22 2003/01/21 00:47:43 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 };
61
62 static struct aout_class_t
63 {
64     UInt32 mFormatID;
65     UInt32 mChannelsPerFrame;
66     enum AudioDeviceClass class;
67     const char *psz_class;
68 }
69 aout_classes[] =
70 {
71     { /* old A/52 format type */
72         'IAC3', 
73         0, 
74         AudioDeviceClassA52, 
75         "Digital A/52" 
76     },
77
78     { /* new A/52 format type */
79         kAudioFormat60958AC3, 
80         0, 
81         AudioDeviceClassA52, 
82         "Digital A/52"
83     },
84
85     {
86         kAudioFormatLinearPCM, 
87         2, 
88         AudioDeviceClassPCM2, 
89         "Stereo PCM"
90     },
91
92     {
93         kAudioFormatLinearPCM,
94         4,
95         AudioDeviceClassPCM4,
96         "4 Channel PCM"
97     },
98
99     {
100         kAudioFormatLinearPCM, 
101         6, 
102         AudioDeviceClassPCM6, 
103         "6 Channel PCM"
104     }
105 }; 
106
107 #define N_AOUT_CLASSES (sizeof(aout_classes)/sizeof(aout_classes[0]))
108
109 /*****************************************************************************
110  * aout_option_t
111  ****************************************************************************/
112 struct aout_option_t
113 {
114     char sz_option[64];
115     UInt32 i_dev, i_idx;
116     UInt32 i_sdx, i_cdx;
117     AudioStreamID i_sid;
118 };
119
120 /*****************************************************************************
121  * aout_dev_t
122  ****************************************************************************/
123 struct aout_dev_t
124 {
125     AudioDeviceID devid;
126     char *psz_device_name;
127     UInt32 i_streams;
128     AudioStreamBasicDescription ** pp_streams;
129 };
130
131 /*****************************************************************************
132  * aout_sys_t: private audio output method descriptor
133  *****************************************************************************
134  * This structure is part of the audio output thread descriptor.
135  * It describes the CoreAudio specific properties of an output thread.
136  *****************************************************************************/
137 struct aout_sys_t
138 {
139     UInt32                      i_devices;
140     struct aout_dev_t *         p_devices;
141     UInt32                      i_options;
142     struct aout_option_t *      p_options;   
143
144     AudioDeviceID               devid;
145     AudioStreamBasicDescription stream_format;
146
147     UInt32                      i_buffer_size;
148     mtime_t                     clock_diff;
149 };
150
151 /*****************************************************************************
152  * Local prototypes.
153  *****************************************************************************/
154 static int      InitHardware    ( aout_instance_t *p_aout );
155 static int      InitDevice      ( UInt32 i_dev, aout_instance_t *p_aout ); 
156 static void     FreeDevice      ( UInt32 i_dev, aout_instance_t *p_aout ); 
157 static void     FreeHardware    ( aout_instance_t *p_aout );
158 static int      GetDevice       ( aout_instance_t *p_aout, 
159                                   AudioDeviceID *p_devid );
160 static int      GetStreamID     ( AudioDeviceID devid, UInt32 i_idx,
161                                   AudioStreamID * p_sid );
162 static int      InitStream      ( UInt32 i_dev, aout_instance_t *p_aout,
163                                   UInt32 i_idx );
164 static void     FreeStream      ( UInt32 i_dev, aout_instance_t *p_aout,
165                                   UInt32 i_idx );
166
167 static void     Play            ( aout_instance_t *p_aout );
168
169 static OSStatus IOCallback      ( AudioDeviceID inDevice,
170                                   const AudioTimeStamp *inNow, 
171                                   const void *inInputData, 
172                                   const AudioTimeStamp *inInputTime,
173                                   AudioBufferList *outOutputData, 
174                                   const AudioTimeStamp *inOutputTime, 
175                                   void *threadGlobals );
176
177 /*****************************************************************************
178  * Open: open a CoreAudio HAL device
179  *****************************************************************************/
180 int E_(OpenAudio)( vlc_object_t * p_this )
181 {
182     OSStatus err;
183     vlc_value_t val;
184     UInt32 i, i_param_size;
185     struct aout_sys_t * p_sys;
186     aout_instance_t * p_aout = (aout_instance_t *)p_this;
187
188     /* Allocate structure */
189     p_sys = (struct aout_sys_t *)malloc( sizeof( struct aout_sys_t ) );
190     if( p_sys == NULL )
191     {
192         msg_Err( p_aout, "out of memory" );
193         return( VLC_ENOMEM );
194     }
195
196     memset( p_sys, 0, sizeof( struct aout_sys_t ) );
197     p_aout->output.p_sys = p_sys;
198
199     if( InitHardware( p_aout ) )
200     {
201         msg_Err( p_aout, "InitHardware failed" );
202         free( (void *)p_sys );
203         return( VLC_EGENERIC );
204     } 
205
206     if( var_Type( p_aout, "audio-device" ) == 0 )
207     {
208         UInt32 i_option = config_GetInt( p_aout, "macosx-adev" );
209
210         var_Create( p_aout, "audio-device", VLC_VAR_STRING | 
211                                             VLC_VAR_HASCHOICE );
212
213         for( i = 0; i < p_sys->i_options; i++ )
214         {
215             val.psz_string = p_sys->p_options[i].sz_option;
216             var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val );
217
218             if( i == i_option )
219             {
220                 var_Set( p_aout, "audio-device", val );
221             }
222         }
223
224         var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart,
225                          NULL );
226
227         val.b_bool = VLC_TRUE;
228         var_Set( p_aout, "intf-change", val );
229     }
230
231     /* Get requested device */
232     if( GetDevice( p_aout, &p_sys->devid ) )
233     {
234         msg_Err( p_aout, "GetDevice failed" );
235         FreeHardware( p_aout );
236         free( (void *)p_sys );
237         return( VLC_EGENERIC );
238     } 
239
240     p_aout->output.pf_play = Play;
241     aout_VolumeSoftInit( p_aout );
242
243     /* Get a description of the stream format */
244     i_param_size = sizeof( AudioStreamBasicDescription ); 
245     err = AudioDeviceGetProperty( p_sys->devid, 0, false, 
246                                   kAudioDevicePropertyStreamFormat,
247                                   &i_param_size, &p_sys->stream_format );
248     if( err != noErr )
249     {
250         msg_Err( p_aout, "failed to get stream format: [%4.4s]", 
251                  (char *)&err );
252         FreeHardware( p_aout );
253         free( (void *)p_sys );
254         return( VLC_EGENERIC );
255     }
256
257     /* Set the output sample rate */
258     p_aout->output.output.i_rate = 
259         (unsigned int)p_sys->stream_format.mSampleRate;
260
261     msg_Dbg( p_aout, STREAM_FORMAT_MSG( "using format",
262                                         p_sys->stream_format ) );
263
264     /* Get the buffer size */
265     i_param_size = sizeof( p_sys->i_buffer_size );
266     err = AudioDeviceGetProperty( p_sys->devid, 0, false, 
267                                   kAudioDevicePropertyBufferSize, 
268                                   &i_param_size, &p_sys->i_buffer_size );
269     if( err != noErr )
270     {
271         msg_Err( p_aout, "failed to get buffer size: [%4.4s]", 
272                  (char *)&err );
273         FreeHardware( p_aout );
274         free( (void *)p_sys );
275         return( VLC_EGENERIC );
276     }
277
278     msg_Dbg( p_aout, "device buffer size: [%ld]", p_sys->i_buffer_size );
279
280     /* If we do AC3 over SPDIF, set buffer size to one AC3 frame */
281     if( ( p_sys->stream_format.mFormatID == kAudioFormat60958AC3 ||
282           p_sys->stream_format.mFormatID == 'IAC3' ) &&
283         p_sys->i_buffer_size != AOUT_SPDIF_SIZE )
284     {
285         p_sys->i_buffer_size = AOUT_SPDIF_SIZE;
286         i_param_size = sizeof( p_sys->i_buffer_size );
287         err = AudioDeviceSetProperty( p_sys->devid, 0, 0, false,
288                                       kAudioDevicePropertyBufferSize,
289                                       i_param_size, &p_sys->i_buffer_size );
290         if( err != noErr )
291         {
292             msg_Err( p_aout, "failed to set buffer size: [%4.4s]", 
293                      (char *)&err );
294             FreeHardware( p_aout );
295             free( (void *)p_sys );
296             return( VLC_EGENERIC );
297         }
298
299         msg_Dbg( p_aout, "device buffer size set to: [%ld]", 
300                  p_sys->i_buffer_size );
301     }
302
303     switch( p_sys->stream_format.mFormatID )
304     {
305     case kAudioFormatLinearPCM:
306         p_aout->output.output.i_format = VLC_FOURCC('f','l','3','2');
307
308         if( p_sys->stream_format.mChannelsPerFrame == 6 )
309         {
310             p_aout->output.output.i_physical_channels =
311                 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
312                 AOUT_CHAN_CENTER | AOUT_CHAN_REARRIGHT |
313                 AOUT_CHAN_REARLEFT | AOUT_CHAN_LFE;
314         }
315         else if( p_sys->stream_format.mChannelsPerFrame == 4 )
316         {
317             p_aout->output.output.i_physical_channels =
318                 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
319                 AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
320         }
321         else
322         {
323             p_aout->output.output.i_physical_channels =
324                 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
325         }
326
327         p_aout->output.i_nb_samples = (int)( p_sys->i_buffer_size /
328                                       p_sys->stream_format.mBytesPerFrame );
329         break;
330
331     case 'IAC3':
332     case kAudioFormat60958AC3:
333         p_aout->output.output.i_format = VLC_FOURCC('s','p','d','i');
334         p_aout->output.output.i_bytes_per_frame = AOUT_SPDIF_SIZE;
335         p_aout->output.output.i_frame_length = A52_FRAME_NB;
336         p_aout->output.i_nb_samples = p_aout->output.output.i_frame_length;
337         break;
338
339     default:
340         msg_Err( p_aout, "unknown hardware format: [%4.4s]", 
341                  (char *)&p_sys->stream_format.mFormatID );
342         FreeHardware( p_aout );
343         free( (void *)p_sys );
344         return( VLC_EGENERIC );
345     }
346
347     /* Set buffer frame size */
348     i_param_size = sizeof( p_aout->output.i_nb_samples );
349     err = AudioDeviceSetProperty( p_sys->devid, 0, 0, false,
350                                   kAudioDevicePropertyBufferFrameSize,
351                                   i_param_size,
352                                   &p_aout->output.i_nb_samples );
353     if( err != noErr )
354     {
355         msg_Err( p_aout, "failed to set buffer frame size: [%4.4s]", 
356                  (char *)&err );
357         FreeHardware( p_aout );
358         free( (void *)p_sys );
359         return( VLC_EGENERIC );
360     }
361
362     msg_Dbg( p_aout, "device buffer frame size set to: [%d]",
363              p_aout->output.i_nb_samples );
364
365     /* Add callback */
366     err = AudioDeviceAddIOProc( p_sys->devid,
367                                 (AudioDeviceIOProc)IOCallback,
368                                 (void *)p_aout );
369     if( err != noErr )
370     {
371         msg_Err( p_aout, "AudioDeviceAddIOProc failed: [%4.4s]",
372                  (char *)&err );
373         FreeHardware( p_aout );
374         free( (void *)p_sys );
375         return( VLC_EGENERIC );
376     }
377  
378     /* Start device */
379     err = AudioDeviceStart( p_sys->devid, (AudioDeviceIOProc)IOCallback ); 
380     if( err != noErr )
381     {
382         msg_Err( p_aout, "AudioDeviceStart failed: [%4.4s]",
383                  (char *)&err );
384         FreeHardware( p_aout );
385         free( (void *)p_sys );
386         return( VLC_EGENERIC );
387     }
388
389     /* Let's pray for the following operation to be atomic... */
390     p_sys->clock_diff = - (mtime_t)
391         AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000; 
392     p_sys->clock_diff += mdate();
393
394     return( VLC_SUCCESS );
395 }
396
397 /*****************************************************************************
398  * Close: close the CoreAudio HAL device
399  *****************************************************************************/
400 void E_(CloseAudio)( aout_instance_t * p_aout )
401 {
402     OSStatus err; 
403     struct aout_sys_t * p_sys = p_aout->output.p_sys;
404
405     /* Stop device */
406     err = AudioDeviceStop( p_sys->devid, (AudioDeviceIOProc)IOCallback ); 
407     if( err != noErr )
408     {
409         msg_Err( p_aout, "AudioDeviceStop failed: [%4.4s]", (char *)&err );
410     }
411
412     /* Remove callback */
413     err = AudioDeviceRemoveIOProc( p_sys->devid,
414                                    (AudioDeviceIOProc)IOCallback );
415     if( err != noErr )
416     {
417         msg_Err( p_aout, "AudioDeviceRemoveIOProc failed: [%4.4s]",
418                  (char *)&err );
419     }
420
421     FreeHardware( p_aout );
422
423     free( p_sys );
424 }
425
426 /*****************************************************************************
427  * Play: nothing to do
428  *****************************************************************************/
429 static void Play( aout_instance_t * p_aout )
430 {
431 }
432
433 /*****************************************************************************
434  * IOCallback: callback for audio output
435  *****************************************************************************/
436 static OSStatus IOCallback( AudioDeviceID inDevice,
437                             const AudioTimeStamp *inNow, 
438                             const void *inInputData,
439                             const AudioTimeStamp *inInputTime, 
440                             AudioBufferList *outOutputData,
441                             const AudioTimeStamp *inOutputTime, 
442                             void *threadGlobals )
443 {
444     aout_buffer_t * p_buffer;
445     AudioTimeStamp  host_time;
446     mtime_t         current_date;
447
448     aout_instance_t * p_aout = (aout_instance_t *)threadGlobals;
449     struct aout_sys_t * p_sys = p_aout->output.p_sys;
450
451     host_time.mFlags = kAudioTimeStampHostTimeValid;
452     AudioDeviceTranslateTime( inDevice, inOutputTime, &host_time );
453     current_date = p_sys->clock_diff +
454                    AudioConvertHostTimeToNanos( host_time.mHostTime ) / 1000;
455
456 #define B_SPDI (p_aout->output.output.i_format == VLC_FOURCC('s','p','d','i'))
457     p_buffer = aout_OutputNextBuffer( p_aout, current_date, B_SPDI );
458 #undef B_SPDI
459
460     if( p_buffer != NULL )
461     {
462         /* move data into output data buffer */
463         BlockMoveData( p_buffer->p_buffer, 
464                        outOutputData->mBuffers[ 0 ].mData, 
465                        p_sys->i_buffer_size );
466
467         aout_BufferFree( p_buffer );
468     }
469     else
470     {
471         memset( outOutputData->mBuffers[ 0 ].mData, 
472                 0, p_sys->i_buffer_size );
473     }
474
475     return( noErr );     
476 }
477
478 /*****************************************************************************
479  * InitHardware 
480  *****************************************************************************/
481 static int InitHardware( aout_instance_t * p_aout )
482 {
483     OSStatus err;
484     UInt32 i, i_param_size;
485     AudioDeviceID * p_devices;
486
487     struct aout_sys_t * p_sys = p_aout->output.p_sys;
488
489     /* Get number of devices */
490     err = AudioHardwareGetPropertyInfo( kAudioHardwarePropertyDevices,
491                                         &i_param_size, NULL );
492     if( err != noErr )
493     {
494         msg_Err( p_aout, "AudioHardwareGetPropertyInfo failed: [%4.4s]",
495                  (char *)&err );
496         return( VLC_EGENERIC );
497     }
498
499     p_sys->i_devices = i_param_size / sizeof( AudioDeviceID );
500
501     if( p_sys->i_devices < 1 )
502     {
503         msg_Err( p_aout, "no devices found" );
504         return( VLC_EGENERIC );
505     }
506
507     msg_Dbg( p_aout, "system has [%ld] device(s)", p_sys->i_devices );
508
509     /* Allocate DeviceID array */
510     p_devices = (AudioDeviceID *)malloc( i_param_size );
511     if( p_devices == NULL )
512     {
513         msg_Err( p_aout, "out of memory" );
514         return( VLC_ENOMEM );
515     }
516
517     /* Populate DeviceID array */
518     err = AudioHardwareGetProperty( kAudioHardwarePropertyDevices,
519                                     &i_param_size, (void *)p_devices );
520     if( err != noErr )
521     {
522         msg_Err( p_aout, "AudioHardwareGetProperty failed: [%4.4s]",
523                  (char *)&err );
524         free( (void *)p_devices );
525         return( VLC_EGENERIC );
526     }
527
528     p_sys->p_devices = (struct aout_dev_t *)
529         malloc( sizeof( struct aout_dev_t ) * p_sys->i_devices ); 
530     if( p_sys->p_devices == NULL )
531     {
532         msg_Err( p_aout, "out of memory" );
533         free( (void *)p_devices );
534         return( VLC_ENOMEM );
535     }    
536
537     for( i = 0; i < p_sys->i_devices; i++ )
538     {
539         p_sys->p_devices[i].devid = p_devices[i];
540
541         if( InitDevice( i, p_aout ) )
542         {
543             UInt32 j;
544
545             msg_Err( p_aout, "InitDevice(%ld) failed", i );
546
547             for( j = 0; j < i; j++ )
548             {
549                 FreeDevice( j, p_aout );
550             }
551     
552             free( (void *)p_sys->p_devices );
553             free( (void *)p_devices );
554
555             return( VLC_EGENERIC );
556         }
557     }
558
559     free( (void *)p_devices );
560
561     return( VLC_SUCCESS );
562 }
563
564 /*****************************************************************************
565  * InitDevice
566  *****************************************************************************/
567 static int InitDevice( UInt32 i_dev, aout_instance_t * p_aout ) 
568 {
569     OSStatus err;
570     UInt32 i, i_param_size;
571     AudioBufferList *p_buffer_list;
572
573     struct aout_sys_t * p_sys = p_aout->output.p_sys;
574     struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
575
576     /* Get length of device name */
577     err = AudioDeviceGetPropertyInfo( p_dev->devid, 0, FALSE, 
578                                       kAudioDevicePropertyDeviceName,
579                                       &i_param_size, NULL ); 
580     if( err != noErr )
581     {
582         msg_Err( p_aout, "AudioDeviceGetPropertyInfo failed: [%4.4s]",
583                  (char *)&err ); 
584         return( VLC_EGENERIC );
585     }
586
587     /* Allocate memory for device name */
588     p_dev->psz_device_name = (char *)malloc( i_param_size );
589     if( p_dev->psz_device_name == NULL )
590     {
591         msg_Err( p_aout, "out of memory" );
592         return( VLC_ENOMEM );
593     }
594
595     /* Get device name */
596     err = AudioDeviceGetProperty( p_dev->devid, 0, FALSE,
597                                   kAudioDevicePropertyDeviceName,
598                                   &i_param_size, p_dev->psz_device_name ); 
599     if( err != noErr )
600     {
601         msg_Err( p_aout, "AudioDeviceGetProperty failed: [%4.4s]",
602                  (char *)&err );
603         free( (void *)p_dev->psz_device_name );
604         return( VLC_EGENERIC );
605     }
606
607     msg_Dbg( p_aout, "device [%ld] has name [%s]",
608              i_dev, p_dev->psz_device_name );
609
610     err = AudioDeviceGetPropertyInfo( p_dev->devid, 0, FALSE,
611                                       kAudioDevicePropertyStreamConfiguration,
612                                       &i_param_size, NULL );
613     if( err != noErr )
614     {
615         msg_Err( p_aout, "AudioDeviceGetPropertyInfo failed: [%4.4s]",
616                  (char *)&err );
617         free( (void *)p_dev->psz_device_name );
618         return( VLC_EGENERIC );
619     }
620
621     p_buffer_list = (AudioBufferList *)malloc( i_param_size );
622     if( p_buffer_list == NULL )
623     {
624         msg_Err( p_aout, "out of memory" );
625         free( (void *)p_dev->psz_device_name );
626         return( VLC_ENOMEM );
627     }
628
629     err = AudioDeviceGetProperty( p_dev->devid, 0, FALSE,
630                                   kAudioDevicePropertyStreamConfiguration,
631                                   &i_param_size, p_buffer_list );
632     if( err != noErr )
633     {
634         msg_Err( p_aout, "AudioDeviceGetProperty failed: [%4.4s]",
635                  (char *)&err );
636         free( (void *)p_dev->psz_device_name );
637         free( (void *)p_buffer_list );
638         return( VLC_EGENERIC );
639     }
640
641     p_dev->i_streams = p_buffer_list->mNumberBuffers;
642     free( (void *)p_buffer_list );
643
644     msg_Dbg( p_aout, "device [%ld] has [%ld] streams", 
645              i_dev, p_dev->i_streams ); 
646
647     p_dev->pp_streams = (AudioStreamBasicDescription **) 
648                         malloc( p_dev->i_streams * 
649                                 sizeof( *p_dev->pp_streams ) );
650     if( p_dev->pp_streams == NULL )
651     {
652         msg_Err( p_aout, "out of memory" );
653         free( (void *)p_dev->psz_device_name );
654         return( VLC_ENOMEM );
655     } 
656
657     for( i = 0; i < p_dev->i_streams; i++ )
658     {
659         if( InitStream( i_dev, p_aout, i ) )
660         {
661             UInt32 j;
662
663             msg_Err( p_aout, "InitStream(%ld, %ld) failed", i_dev, i );
664
665             for( j = 0; j < i; j++ )
666             {
667                 FreeStream( i_dev, p_aout, j );
668             }
669
670             free( (void *)p_dev->psz_device_name );
671             free( (void *)p_dev->pp_streams );
672
673             return( VLC_EGENERIC );
674         }
675     }
676
677     return( VLC_SUCCESS );
678 }
679
680 /*****************************************************************************
681  * FreeDevice 
682  *****************************************************************************/
683 static void FreeDevice( UInt32 i_dev, aout_instance_t * p_aout )
684 {
685     UInt32 i;
686
687     struct aout_sys_t * p_sys = p_aout->output.p_sys;
688     struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
689
690     for( i = 0; i < p_dev->i_streams; i++ )
691     {
692         FreeStream( i_dev, p_aout, i );
693     }
694
695     free( (void *)p_dev->pp_streams );
696     free( (void *)p_dev->psz_device_name );
697 }
698
699 /*****************************************************************************
700  * FreeHardware 
701  *****************************************************************************/
702 static void FreeHardware( aout_instance_t * p_aout )
703 {
704     UInt32 i;
705
706     struct aout_sys_t * p_sys = p_aout->output.p_sys;
707
708     for( i = 0; i < p_sys->i_devices; i++ )
709     {
710         FreeDevice( i, p_aout );
711     }
712
713     free( (void *)p_sys->p_options );
714     free( (void *)p_sys->p_devices );
715 }
716
717 /*****************************************************************************
718  * GetStreamID 
719  *****************************************************************************/
720 static int GetStreamID( AudioDeviceID devid, UInt32 i_idx,
721                         AudioStreamID * p_sid )
722 {
723     OSStatus err;
724     UInt32 i_param_size;
725     AudioStreamID * p_stream_list;
726
727     err = AudioDeviceGetPropertyInfo( devid, 0, FALSE,
728                                       kAudioDevicePropertyStreams,
729                                       &i_param_size, NULL );
730     if( err != noErr )
731     {
732         return( VLC_EGENERIC );
733     }
734
735     p_stream_list = (AudioStreamID *)malloc( i_param_size );
736     if( p_stream_list == NULL )
737     {
738         return( VLC_ENOMEM );
739     }
740
741     err = AudioDeviceGetProperty( devid, 0, FALSE,
742                                   kAudioDevicePropertyStreams,
743                                   &i_param_size, p_stream_list );
744     if( err != noErr )
745     {
746         free( (void *)p_stream_list );
747         return( VLC_EGENERIC );
748     }
749
750     *p_sid = p_stream_list[i_idx - 1];
751
752     free( (void *)p_stream_list );
753
754     return( VLC_SUCCESS );
755 }
756
757 /*****************************************************************************
758  * InitStream
759  *****************************************************************************/
760 static int InitStream( UInt32 i_dev, aout_instance_t *p_aout,
761                        UInt32 i_idx )
762 {
763     OSStatus err;
764     AudioStreamID i_sid;
765     UInt32 i, i_streams;
766     UInt32 j, i_param_size;
767
768     struct aout_sys_t * p_sys = p_aout->output.p_sys;
769     struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
770
771     if( GetStreamID( p_dev->devid, i_idx + 1, &i_sid ) )
772     {
773         msg_Err( p_aout, "GetStreamID(%ld, %ld) failed", i_dev, i_idx );
774         return( VLC_EGENERIC );
775     }
776
777     err = AudioStreamGetPropertyInfo( i_sid, 0,
778                                       kAudioStreamPropertyPhysicalFormats,
779                                       &i_param_size, NULL );
780     if( err != noErr )
781     {
782         msg_Err( p_aout, "AudioStreamGetPropertyInfo failed: [%4.4s]",
783                  (char *)&err );
784         return( VLC_EGENERIC );
785     }
786
787     i_streams = i_param_size / sizeof( AudioStreamBasicDescription );
788
789 #define P_STREAMS p_dev->pp_streams[i_idx]
790
791     P_STREAMS = (AudioStreamBasicDescription *)malloc( i_param_size );
792     if( P_STREAMS == NULL )
793     {
794         msg_Err( p_aout, "out of memory" );
795         return( VLC_ENOMEM );
796     }
797
798     err = AudioStreamGetProperty( i_sid, 0,
799                                   kAudioStreamPropertyPhysicalFormats,
800                                   &i_param_size, P_STREAMS );
801     if( err != noErr )
802     {
803         msg_Err( p_aout, "AudioStreamGetProperty failed: [%4.4s]",
804                  (char *)&err );
805         free( (void *)P_STREAMS );
806         return( VLC_EGENERIC );
807     }
808
809     for( j = 0; j < N_AOUT_CLASSES; j++ )
810     {
811         vlc_bool_t b_found = 0;
812         UInt32 i_channels = 0xFFFFFFFF;
813         UInt32 i_sdx = 0;
814
815         for( i = 0; i < i_streams; i++ )
816         {
817             if( j == 0 )
818             {
819                 msg_Dbg( p_aout, STREAM_FORMAT_MSG( "supported format",
820                                                     P_STREAMS[i] ) );
821             }
822
823             if( ( P_STREAMS[i].mFormatID != aout_classes[j].mFormatID ) ||
824                 ( P_STREAMS[i].mChannelsPerFrame < 
825                   aout_classes[j].mChannelsPerFrame ) )
826             {
827                 continue;
828             }
829
830             if( P_STREAMS[i].mChannelsPerFrame < i_channels )
831             {
832                 i_channels = P_STREAMS[i].mChannelsPerFrame;
833                 i_sdx = i;
834                 b_found = 1;
835             }
836         }
837
838         if( b_found )
839         {
840             p_sys->p_options = (struct aout_option_t *)
841                                realloc( p_sys->p_options, 
842                                         ( p_sys->i_options + 1 ) *
843                                         sizeof( struct aout_option_t ) ); 
844             if( p_sys->p_options == NULL )
845             {
846                 msg_Err( p_aout, "out of memory" );
847                 free( (void *)P_STREAMS );
848                 return( VLC_ENOMEM );
849             }
850
851 #define AOUT_OPTION p_sys->p_options[p_sys->i_options]
852
853             snprintf( AOUT_OPTION.sz_option,
854                       sizeof( AOUT_OPTION.sz_option ) / 
855                       sizeof( AOUT_OPTION.sz_option[0] ) - 1,
856                       "%ld: %s (%s)", 
857                       p_sys->i_options,
858                       p_dev->psz_device_name, 
859                       aout_classes[j].psz_class );
860
861             AOUT_OPTION.i_sid = i_sid;
862             AOUT_OPTION.i_dev = i_dev; 
863             AOUT_OPTION.i_idx = i_idx;
864             AOUT_OPTION.i_sdx = i_sdx;
865             AOUT_OPTION.i_cdx = j;
866
867 #undef AOUT_OPTION
868
869             p_sys->i_options++;
870         } 
871     }
872
873 #undef P_STREAMS
874
875     return( VLC_SUCCESS );
876 }
877
878 /*****************************************************************************
879  * FreeStream 
880  *****************************************************************************/
881 static void FreeStream( UInt32 i_dev, aout_instance_t *p_aout,
882                         UInt32 i_idx )
883 {
884     struct aout_sys_t * p_sys = p_aout->output.p_sys;
885     struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
886
887     free( (void *)p_dev->pp_streams[i_idx] );
888 }
889
890 /*****************************************************************************
891  * GetDevice 
892  *****************************************************************************/
893 static int GetDevice( aout_instance_t *p_aout, AudioDeviceID *p_devid ) 
894 {
895     OSStatus err;
896     vlc_value_t val;
897     unsigned int i_option;
898
899     struct aout_dev_t * p_dev;
900     struct aout_option_t * p_option;
901     struct aout_sys_t * p_sys = p_aout->output.p_sys;
902
903     if( var_Get( p_aout, "audio-device", &val ) < 0 )
904     {
905         msg_Err( p_aout, "audio-device var does not exist" );
906         return( VLC_ENOVAR );
907     }
908
909     if( !sscanf( val.psz_string, "%d:", &i_option ) ||
910         p_sys->i_options <= i_option )
911     {
912         i_option = 0;
913     }
914
915     free( (void *)val.psz_string );
916
917     p_option = &p_sys->p_options[i_option];
918     p_dev = &p_sys->p_devices[p_option->i_dev];
919
920 #define P_STREAMS p_dev->pp_streams[p_option->i_idx]
921
922     err = AudioStreamSetProperty( p_option->i_sid, 0, 0,
923                                   kAudioStreamPropertyPhysicalFormat,
924                                   sizeof( P_STREAMS[p_option->i_sdx] ),
925                                   &P_STREAMS[p_option->i_sdx] ); 
926     if( err != noErr )
927     {
928         msg_Err( p_aout, "AudioStreamSetProperty failed: [%4.4s]",
929                  (char *)&err );
930         return( VLC_EGENERIC );
931     }
932
933 #undef P_STREAMS
934
935     msg_Dbg( p_aout, "getting device [%ld]", p_option->i_dev );
936
937     config_PutInt( p_aout, "macosx-adev", i_option );
938
939     *p_devid = p_dev->devid;
940
941     return( VLC_SUCCESS );
942