]> git.sesse.net Git - vlc/blob - modules/gui/macosx/aout.m
b0d14dcb1d794ffaae29f6899ec3309811d4d94c
[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.21 2003/01/15 00:49:49 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 /*****************************************************************************
45  * aout_class_t 
46  ****************************************************************************/
47 enum AudioDeviceClass
48 {
49     AudioDeviceClassA52     = 1 << 0,
50     AudioDeviceClassPCM2    = 1 << 1,
51     AudioDeviceClassPCM4    = 1 << 2,
52     AudioDeviceClassPCM6    = 1 << 3 
53 };
54
55 static struct aout_class_t
56 {
57     UInt32 mFormatID;
58     UInt32 mChannelsPerFrame;
59     enum AudioDeviceClass class;
60     const char *psz_class;
61 }
62 aout_classes[] =
63 {
64     { /* old A/52 format type */
65         'IAC3', 
66         0, 
67         AudioDeviceClassA52, 
68         "Digital A/52" 
69     },
70
71     { /* new A/52 format type */
72         kAudioFormat60958AC3, 
73         0, 
74         AudioDeviceClassA52, 
75         "Digital A/52"
76     },
77
78     {
79         kAudioFormatLinearPCM, 
80         2, 
81         AudioDeviceClassPCM2, 
82         "Stereo PCM"
83     },
84
85     {
86         kAudioFormatLinearPCM,
87         4,
88         AudioDeviceClassPCM4,
89         "4 Channel PCM"
90     },
91
92     {
93         kAudioFormatLinearPCM, 
94         6, 
95         AudioDeviceClassPCM6, 
96         "6 Channel PCM"
97     }
98 }; 
99
100 #define N_AOUT_CLASSES (sizeof(aout_classes)/sizeof(aout_classes[0]))
101
102 /*****************************************************************************
103  * aout_option_t
104  ****************************************************************************/
105 struct aout_option_t
106 {
107     char sz_option[64];
108     UInt32 i_dev, i_idx;
109     UInt32 i_sdx, i_cdx;
110     AudioStreamID i_sid;
111 };
112
113 /*****************************************************************************
114  * aout_dev_t
115  ****************************************************************************/
116 struct aout_dev_t
117 {
118     AudioDeviceID devid;
119     char *psz_device_name;
120     UInt32 i_streams;
121     AudioStreamBasicDescription ** pp_streams;
122 };
123
124 /*****************************************************************************
125  * aout_sys_t: private audio output method descriptor
126  *****************************************************************************
127  * This structure is part of the audio output thread descriptor.
128  * It describes the CoreAudio specific properties of an output thread.
129  *****************************************************************************/
130 struct aout_sys_t
131 {
132     UInt32                      i_devices;
133     struct aout_dev_t *         p_devices;
134     UInt32                      i_options;
135     struct aout_option_t *      p_options;   
136
137     AudioDeviceID               devid;
138     AudioStreamBasicDescription stream_format;
139
140     UInt32                      i_buffer_size;
141     mtime_t                     clock_diff;
142 };
143
144 /*****************************************************************************
145  * Local prototypes.
146  *****************************************************************************/
147 static int      InitHardware    ( aout_instance_t *p_aout );
148 static int      InitDevice      ( UInt32 i_dev, aout_instance_t *p_aout ); 
149 static void     FreeDevice      ( UInt32 i_dev, aout_instance_t *p_aout ); 
150 static void     FreeHardware    ( aout_instance_t *p_aout );
151 static int      GetDevice       ( aout_instance_t *p_aout, 
152                                   AudioDeviceID *p_devid );
153 static int      GetStreamID     ( AudioDeviceID devid, UInt32 i_idx,
154                                   AudioStreamID * p_sid );
155 static int      InitStream      ( UInt32 i_dev, aout_instance_t *p_aout,
156                                   UInt32 i_idx );
157 static void     FreeStream      ( UInt32 i_dev, aout_instance_t *p_aout,
158                                   UInt32 i_idx );
159
160 static void     Play            ( aout_instance_t *p_aout );
161
162 static OSStatus IOCallback      ( AudioDeviceID inDevice,
163                                   const AudioTimeStamp *inNow, 
164                                   const void *inInputData, 
165                                   const AudioTimeStamp *inInputTime,
166                                   AudioBufferList *outOutputData, 
167                                   const AudioTimeStamp *inOutputTime, 
168                                   void *threadGlobals );
169
170 /*****************************************************************************
171  * Open: open a CoreAudio HAL device
172  *****************************************************************************/
173 int E_(OpenAudio)( vlc_object_t * p_this )
174 {
175     OSStatus err;
176     vlc_value_t val;
177     UInt32 i, i_param_size;
178     struct aout_sys_t * p_sys;
179     aout_instance_t * p_aout = (aout_instance_t *)p_this;
180
181     /* Allocate structure */
182     p_sys = (struct aout_sys_t *)malloc( sizeof( struct aout_sys_t ) );
183     if( p_sys == NULL )
184     {
185         msg_Err( p_aout, "out of memory" );
186         return( VLC_ENOMEM );
187     }
188
189     memset( p_sys, 0, sizeof( struct aout_sys_t ) );
190     p_aout->output.p_sys = p_sys;
191
192     if( InitHardware( p_aout ) )
193     {
194         msg_Err( p_aout, "InitHardware failed" );
195         free( (void *)p_sys );
196         return( VLC_EGENERIC );
197     } 
198
199     if( var_Type( p_aout, "audio-device" ) == 0 )
200     {
201         UInt32 i_option = config_GetInt( p_aout, "macosx-adev" );
202
203         var_Create( p_aout, "audio-device", VLC_VAR_STRING | 
204                                             VLC_VAR_HASCHOICE );
205
206         for( i = 0; i < p_sys->i_options; i++ )
207         {
208             val.psz_string = p_sys->p_options[i].sz_option;
209             var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val );
210
211             if( i == i_option )
212             {
213                 var_Set( p_aout, "audio-device", val );
214             }
215         }
216
217         var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart,
218                          NULL );
219
220         val.b_bool = VLC_TRUE;
221         var_Set( p_aout, "intf-change", val );
222     }
223
224     /* Get requested device */
225     if( GetDevice( p_aout, &p_sys->devid ) )
226     {
227         msg_Err( p_aout, "GetDevice failed" );
228         FreeHardware( p_aout );
229         free( (void *)p_sys );
230         return( VLC_EGENERIC );
231     } 
232
233     p_aout->output.pf_play = Play;
234     aout_VolumeSoftInit( p_aout );
235
236     /* Get a description of the stream format */
237     i_param_size = sizeof( AudioStreamBasicDescription ); 
238     err = AudioDeviceGetProperty( p_sys->devid, 0, false, 
239                                   kAudioDevicePropertyStreamFormat,
240                                   &i_param_size, &p_sys->stream_format );
241     if( err != noErr )
242     {
243         msg_Err( p_aout, "failed to get stream format: [%4.4s]", 
244                  (char *)&err );
245         FreeHardware( p_aout );
246         free( (void *)p_sys );
247         return( VLC_EGENERIC );
248     }
249
250     /* Set the output sample rate */
251     p_aout->output.output.i_rate = 
252         (unsigned int)p_sys->stream_format.mSampleRate;
253
254     msg_Dbg( p_aout, "format: [%ld][%4.4s][%ld][%ld][%ld][%ld][%ld][%ld]",
255              (UInt32)p_sys->stream_format.mSampleRate,
256              (char *)&p_sys->stream_format.mFormatID,
257              p_sys->stream_format.mFormatFlags,
258              p_sys->stream_format.mBytesPerPacket,
259              p_sys->stream_format.mFramesPerPacket,
260              p_sys->stream_format.mBytesPerFrame,
261              p_sys->stream_format.mChannelsPerFrame,
262              p_sys->stream_format.mBitsPerChannel );                  
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( ( P_STREAMS[i].mFormatID != aout_classes[j].mFormatID ) ||
818                 ( P_STREAMS[i].mChannelsPerFrame < 
819                   aout_classes[j].mChannelsPerFrame ) )
820             {
821                 continue;
822             }
823
824             if( P_STREAMS[i].mChannelsPerFrame < i_channels )
825             {
826                 i_channels = P_STREAMS[i].mChannelsPerFrame;
827                 i_sdx = i;
828                 b_found = 1;
829             }
830         }
831
832         if( b_found )
833         {
834             p_sys->p_options = (struct aout_option_t *)
835                                realloc( p_sys->p_options, 
836                                         ( p_sys->i_options + 1 ) *
837                                         sizeof( struct aout_option_t ) ); 
838             if( p_sys->p_options == NULL )
839             {
840                 msg_Err( p_aout, "out of memory" );
841                 free( (void *)P_STREAMS );
842                 return( VLC_ENOMEM );
843             }
844
845 #define AOUT_OPTION p_sys->p_options[p_sys->i_options]
846
847             snprintf( AOUT_OPTION.sz_option,
848                       sizeof( AOUT_OPTION.sz_option ) / 
849                       sizeof( AOUT_OPTION.sz_option[0] ) - 1,
850                       "%ld: %s (%s)", 
851                       p_sys->i_options,
852                       p_dev->psz_device_name, 
853                       aout_classes[j].psz_class );
854
855             AOUT_OPTION.i_sid = i_sid;
856             AOUT_OPTION.i_dev = i_dev; 
857             AOUT_OPTION.i_idx = i_idx;
858             AOUT_OPTION.i_sdx = i_sdx;
859             AOUT_OPTION.i_cdx = j;
860
861 #undef AOUT_OPTION
862
863             p_sys->i_options++;
864         } 
865     }
866
867 #undef P_STREAMS
868
869     return( VLC_SUCCESS );
870 }
871
872 /*****************************************************************************
873  * FreeStream 
874  *****************************************************************************/
875 static void FreeStream( UInt32 i_dev, aout_instance_t *p_aout,
876                         UInt32 i_idx )
877 {
878     struct aout_sys_t * p_sys = p_aout->output.p_sys;
879     struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
880
881     free( (void *)p_dev->pp_streams[i_idx] );
882 }
883
884 /*****************************************************************************
885  * GetDevice 
886  *****************************************************************************/
887 static int GetDevice( aout_instance_t *p_aout, AudioDeviceID *p_devid ) 
888 {
889     OSStatus err;
890     vlc_value_t val;
891     unsigned int i_option;
892
893     struct aout_dev_t * p_dev;
894     struct aout_option_t * p_option;
895     struct aout_sys_t * p_sys = p_aout->output.p_sys;
896
897     if( var_Get( p_aout, "audio-device", &val ) < 0 )
898     {
899         msg_Err( p_aout, "audio-device var does not exist" );
900         return( VLC_ENOVAR );
901     }
902
903     if( !sscanf( val.psz_string, "%d:", &i_option ) ||
904         p_sys->i_options <= i_option )
905     {
906         i_option = 0;
907     }
908
909     free( (void *)val.psz_string );
910
911     p_option = &p_sys->p_options[i_option];
912     p_dev = &p_sys->p_devices[p_option->i_dev];
913
914 #define P_STREAMS p_dev->pp_streams[p_option->i_idx]
915
916     err = AudioStreamSetProperty( p_option->i_sid, 0, 0,
917                                   kAudioStreamPropertyPhysicalFormat,
918                                   sizeof( P_STREAMS[p_option->i_sdx] ),
919                                   &P_STREAMS[p_option->i_sdx] ); 
920     if( err != noErr )
921     {
922         msg_Err( p_aout, "AudioStreamSetProperty failed: [%4.4s]",
923                  (char *)&err );
924         return( VLC_EGENERIC );
925     }
926
927 #undef P_STREAMS
928
929     msg_Dbg( p_aout, "getting device [%ld]", p_option->i_dev );
930
931     config_PutInt( p_aout, "macosx-adev", i_option );
932
933     *p_devid = p_dev->devid;
934
935     return( VLC_SUCCESS );
936