e3471c40a3622439a92ded60cd54cd68c87f1ca0
[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.23 2003/02/21 16:31:37 hartman 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         if( p_aout->output.output.i_format == VLC_FOURCC('f','l','3','2') )
472         {
473             int i;
474             int i_size = p_sys->i_buffer_size / sizeof(float);
475             
476             float * a = (float *)outOutputData->mBuffers[ 0 ].mData;
477             for ( i = 0 ; i < i_size ; i++ )
478                 *a++ = 0.0;
479         }
480         else
481         {
482             memset( outOutputData->mBuffers[ 0 ].mData, 
483                 0, p_sys->i_buffer_size );
484         }
485     }
486
487     return( noErr );     
488 }
489
490 /*****************************************************************************
491  * InitHardware 
492  *****************************************************************************/
493 static int InitHardware( aout_instance_t * p_aout )
494 {
495     OSStatus err;
496     UInt32 i, i_param_size;
497     AudioDeviceID * p_devices;
498
499     struct aout_sys_t * p_sys = p_aout->output.p_sys;
500
501     /* Get number of devices */
502     err = AudioHardwareGetPropertyInfo( kAudioHardwarePropertyDevices,
503                                         &i_param_size, NULL );
504     if( err != noErr )
505     {
506         msg_Err( p_aout, "AudioHardwareGetPropertyInfo failed: [%4.4s]",
507                  (char *)&err );
508         return( VLC_EGENERIC );
509     }
510
511     p_sys->i_devices = i_param_size / sizeof( AudioDeviceID );
512
513     if( p_sys->i_devices < 1 )
514     {
515         msg_Err( p_aout, "no devices found" );
516         return( VLC_EGENERIC );
517     }
518
519     msg_Dbg( p_aout, "system has [%ld] device(s)", p_sys->i_devices );
520
521     /* Allocate DeviceID array */
522     p_devices = (AudioDeviceID *)malloc( i_param_size );
523     if( p_devices == NULL )
524     {
525         msg_Err( p_aout, "out of memory" );
526         return( VLC_ENOMEM );
527     }
528
529     /* Populate DeviceID array */
530     err = AudioHardwareGetProperty( kAudioHardwarePropertyDevices,
531                                     &i_param_size, (void *)p_devices );
532     if( err != noErr )
533     {
534         msg_Err( p_aout, "AudioHardwareGetProperty failed: [%4.4s]",
535                  (char *)&err );
536         free( (void *)p_devices );
537         return( VLC_EGENERIC );
538     }
539
540     p_sys->p_devices = (struct aout_dev_t *)
541         malloc( sizeof( struct aout_dev_t ) * p_sys->i_devices ); 
542     if( p_sys->p_devices == NULL )
543     {
544         msg_Err( p_aout, "out of memory" );
545         free( (void *)p_devices );
546         return( VLC_ENOMEM );
547     }    
548
549     for( i = 0; i < p_sys->i_devices; i++ )
550     {
551         p_sys->p_devices[i].devid = p_devices[i];
552
553         if( InitDevice( i, p_aout ) )
554         {
555             UInt32 j;
556
557             msg_Err( p_aout, "InitDevice(%ld) failed", i );
558
559             for( j = 0; j < i; j++ )
560             {
561                 FreeDevice( j, p_aout );
562             }
563     
564             free( (void *)p_sys->p_devices );
565             free( (void *)p_devices );
566
567             return( VLC_EGENERIC );
568         }
569     }
570
571     free( (void *)p_devices );
572
573     return( VLC_SUCCESS );
574 }
575
576 /*****************************************************************************
577  * InitDevice
578  *****************************************************************************/
579 static int InitDevice( UInt32 i_dev, aout_instance_t * p_aout ) 
580 {
581     OSStatus err;
582     UInt32 i, i_param_size;
583     AudioBufferList *p_buffer_list;
584
585     struct aout_sys_t * p_sys = p_aout->output.p_sys;
586     struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
587
588     /* Get length of device name */
589     err = AudioDeviceGetPropertyInfo( p_dev->devid, 0, FALSE, 
590                                       kAudioDevicePropertyDeviceName,
591                                       &i_param_size, NULL ); 
592     if( err != noErr )
593     {
594         msg_Err( p_aout, "AudioDeviceGetPropertyInfo failed: [%4.4s]",
595                  (char *)&err ); 
596         return( VLC_EGENERIC );
597     }
598
599     /* Allocate memory for device name */
600     p_dev->psz_device_name = (char *)malloc( i_param_size );
601     if( p_dev->psz_device_name == NULL )
602     {
603         msg_Err( p_aout, "out of memory" );
604         return( VLC_ENOMEM );
605     }
606
607     /* Get device name */
608     err = AudioDeviceGetProperty( p_dev->devid, 0, FALSE,
609                                   kAudioDevicePropertyDeviceName,
610                                   &i_param_size, p_dev->psz_device_name ); 
611     if( err != noErr )
612     {
613         msg_Err( p_aout, "AudioDeviceGetProperty failed: [%4.4s]",
614                  (char *)&err );
615         free( (void *)p_dev->psz_device_name );
616         return( VLC_EGENERIC );
617     }
618
619     msg_Dbg( p_aout, "device [%ld] has name [%s]",
620              i_dev, p_dev->psz_device_name );
621
622     err = AudioDeviceGetPropertyInfo( p_dev->devid, 0, FALSE,
623                                       kAudioDevicePropertyStreamConfiguration,
624                                       &i_param_size, NULL );
625     if( err != noErr )
626     {
627         msg_Err( p_aout, "AudioDeviceGetPropertyInfo failed: [%4.4s]",
628                  (char *)&err );
629         free( (void *)p_dev->psz_device_name );
630         return( VLC_EGENERIC );
631     }
632
633     p_buffer_list = (AudioBufferList *)malloc( i_param_size );
634     if( p_buffer_list == NULL )
635     {
636         msg_Err( p_aout, "out of memory" );
637         free( (void *)p_dev->psz_device_name );
638         return( VLC_ENOMEM );
639     }
640
641     err = AudioDeviceGetProperty( p_dev->devid, 0, FALSE,
642                                   kAudioDevicePropertyStreamConfiguration,
643                                   &i_param_size, p_buffer_list );
644     if( err != noErr )
645     {
646         msg_Err( p_aout, "AudioDeviceGetProperty failed: [%4.4s]",
647                  (char *)&err );
648         free( (void *)p_dev->psz_device_name );
649         free( (void *)p_buffer_list );
650         return( VLC_EGENERIC );
651     }
652
653     p_dev->i_streams = p_buffer_list->mNumberBuffers;
654     free( (void *)p_buffer_list );
655
656     msg_Dbg( p_aout, "device [%ld] has [%ld] streams", 
657              i_dev, p_dev->i_streams ); 
658
659     p_dev->pp_streams = (AudioStreamBasicDescription **) 
660                         malloc( p_dev->i_streams * 
661                                 sizeof( *p_dev->pp_streams ) );
662     if( p_dev->pp_streams == NULL )
663     {
664         msg_Err( p_aout, "out of memory" );
665         free( (void *)p_dev->psz_device_name );
666         return( VLC_ENOMEM );
667     } 
668
669     for( i = 0; i < p_dev->i_streams; i++ )
670     {
671         if( InitStream( i_dev, p_aout, i ) )
672         {
673             UInt32 j;
674
675             msg_Err( p_aout, "InitStream(%ld, %ld) failed", i_dev, i );
676
677             for( j = 0; j < i; j++ )
678             {
679                 FreeStream( i_dev, p_aout, j );
680             }
681
682             free( (void *)p_dev->psz_device_name );
683             free( (void *)p_dev->pp_streams );
684
685             return( VLC_EGENERIC );
686         }
687     }
688
689     return( VLC_SUCCESS );
690 }
691
692 /*****************************************************************************
693  * FreeDevice 
694  *****************************************************************************/
695 static void FreeDevice( UInt32 i_dev, aout_instance_t * p_aout )
696 {
697     UInt32 i;
698
699     struct aout_sys_t * p_sys = p_aout->output.p_sys;
700     struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
701
702     for( i = 0; i < p_dev->i_streams; i++ )
703     {
704         FreeStream( i_dev, p_aout, i );
705     }
706
707     free( (void *)p_dev->pp_streams );
708     free( (void *)p_dev->psz_device_name );
709 }
710
711 /*****************************************************************************
712  * FreeHardware 
713  *****************************************************************************/
714 static void FreeHardware( aout_instance_t * p_aout )
715 {
716     UInt32 i;
717
718     struct aout_sys_t * p_sys = p_aout->output.p_sys;
719
720     for( i = 0; i < p_sys->i_devices; i++ )
721     {
722         FreeDevice( i, p_aout );
723     }
724
725     free( (void *)p_sys->p_options );
726     free( (void *)p_sys->p_devices );
727 }
728
729 /*****************************************************************************
730  * GetStreamID 
731  *****************************************************************************/
732 static int GetStreamID( AudioDeviceID devid, UInt32 i_idx,
733                         AudioStreamID * p_sid )
734 {
735     OSStatus err;
736     UInt32 i_param_size;
737     AudioStreamID * p_stream_list;
738
739     err = AudioDeviceGetPropertyInfo( devid, 0, FALSE,
740                                       kAudioDevicePropertyStreams,
741                                       &i_param_size, NULL );
742     if( err != noErr )
743     {
744         return( VLC_EGENERIC );
745     }
746
747     p_stream_list = (AudioStreamID *)malloc( i_param_size );
748     if( p_stream_list == NULL )
749     {
750         return( VLC_ENOMEM );
751     }
752
753     err = AudioDeviceGetProperty( devid, 0, FALSE,
754                                   kAudioDevicePropertyStreams,
755                                   &i_param_size, p_stream_list );
756     if( err != noErr )
757     {
758         free( (void *)p_stream_list );
759         return( VLC_EGENERIC );
760     }
761
762     *p_sid = p_stream_list[i_idx - 1];
763
764     free( (void *)p_stream_list );
765
766     return( VLC_SUCCESS );
767 }
768
769 /*****************************************************************************
770  * InitStream
771  *****************************************************************************/
772 static int InitStream( UInt32 i_dev, aout_instance_t *p_aout,
773                        UInt32 i_idx )
774 {
775     OSStatus err;
776     AudioStreamID i_sid;
777     UInt32 i, i_streams;
778     UInt32 j, i_param_size;
779
780     struct aout_sys_t * p_sys = p_aout->output.p_sys;
781     struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
782
783     if( GetStreamID( p_dev->devid, i_idx + 1, &i_sid ) )
784     {
785         msg_Err( p_aout, "GetStreamID(%ld, %ld) failed", i_dev, i_idx );
786         return( VLC_EGENERIC );
787     }
788
789     err = AudioStreamGetPropertyInfo( i_sid, 0,
790                                       kAudioStreamPropertyPhysicalFormats,
791                                       &i_param_size, NULL );
792     if( err != noErr )
793     {
794         msg_Err( p_aout, "AudioStreamGetPropertyInfo failed: [%4.4s]",
795                  (char *)&err );
796         return( VLC_EGENERIC );
797     }
798
799     i_streams = i_param_size / sizeof( AudioStreamBasicDescription );
800
801 #define P_STREAMS p_dev->pp_streams[i_idx]
802
803     P_STREAMS = (AudioStreamBasicDescription *)malloc( i_param_size );
804     if( P_STREAMS == NULL )
805     {
806         msg_Err( p_aout, "out of memory" );
807         return( VLC_ENOMEM );
808     }
809
810     err = AudioStreamGetProperty( i_sid, 0,
811                                   kAudioStreamPropertyPhysicalFormats,
812                                   &i_param_size, P_STREAMS );
813     if( err != noErr )
814     {
815         msg_Err( p_aout, "AudioStreamGetProperty failed: [%4.4s]",
816                  (char *)&err );
817         free( (void *)P_STREAMS );
818         return( VLC_EGENERIC );
819     }
820
821     for( j = 0; j < N_AOUT_CLASSES; j++ )
822     {
823         vlc_bool_t b_found = 0;
824         UInt32 i_channels = 0xFFFFFFFF;
825         UInt32 i_sdx = 0;
826
827         for( i = 0; i < i_streams; i++ )
828         {
829             if( j == 0 )
830             {
831                 msg_Dbg( p_aout, STREAM_FORMAT_MSG( "supported format",
832                                                     P_STREAMS[i] ) );
833             }
834
835             if( ( P_STREAMS[i].mFormatID != aout_classes[j].mFormatID ) ||
836                 ( P_STREAMS[i].mChannelsPerFrame < 
837                   aout_classes[j].mChannelsPerFrame ) )
838             {
839                 continue;
840             }
841
842             if( P_STREAMS[i].mChannelsPerFrame < i_channels )
843             {
844                 i_channels = P_STREAMS[i].mChannelsPerFrame;
845                 i_sdx = i;
846                 b_found = 1;
847             }
848         }
849
850         if( b_found )
851         {
852             p_sys->p_options = (struct aout_option_t *)
853                                realloc( p_sys->p_options, 
854                                         ( p_sys->i_options + 1 ) *
855                                         sizeof( struct aout_option_t ) ); 
856             if( p_sys->p_options == NULL )
857             {
858                 msg_Err( p_aout, "out of memory" );
859                 free( (void *)P_STREAMS );
860                 return( VLC_ENOMEM );
861             }
862
863 #define AOUT_OPTION p_sys->p_options[p_sys->i_options]
864
865             snprintf( AOUT_OPTION.sz_option,
866                       sizeof( AOUT_OPTION.sz_option ) / 
867                       sizeof( AOUT_OPTION.sz_option[0] ) - 1,
868                       "%ld: %s (%s)", 
869                       p_sys->i_options,
870                       p_dev->psz_device_name, 
871                       aout_classes[j].psz_class );
872
873             AOUT_OPTION.i_sid = i_sid;
874             AOUT_OPTION.i_dev = i_dev; 
875             AOUT_OPTION.i_idx = i_idx;
876             AOUT_OPTION.i_sdx = i_sdx;
877             AOUT_OPTION.i_cdx = j;
878
879 #undef AOUT_OPTION
880
881             p_sys->i_options++;
882         } 
883     }
884
885 #undef P_STREAMS
886
887     return( VLC_SUCCESS );
888 }
889
890 /*****************************************************************************
891  * FreeStream 
892  *****************************************************************************/
893 static void FreeStream( UInt32 i_dev, aout_instance_t *p_aout,
894                         UInt32 i_idx )
895 {
896     struct aout_sys_t * p_sys = p_aout->output.p_sys;
897     struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
898
899     free( (void *)p_dev->pp_streams[i_idx] );
900 }
901
902 /*****************************************************************************
903  * GetDevice 
904  *****************************************************************************/
905 static int GetDevice( aout_instance_t *p_aout, AudioDeviceID *p_devid ) 
906 {
907     OSStatus err;
908     vlc_value_t val;
909     unsigned int i_option;
910
911     struct aout_dev_t * p_dev;
912     struct aout_option_t * p_option;
913     struct aout_sys_t * p_sys = p_aout->output.p_sys;
914
915     if( var_Get( p_aout, "audio-device", &val ) < 0 )
916     {
917         msg_Err( p_aout, "audio-device var does not exist" );
918         return( VLC_ENOVAR );
919     }
920
921     if( !sscanf( val.psz_string, "%d:", &i_option ) ||
922         p_sys->i_options <= i_option )
923     {
924         i_option = 0;
925     }
926
927     free( (void *)val.psz_string );
928
929     p_option = &p_sys->p_options[i_option];
930     p_dev = &p_sys->p_devices[p_option->i_dev];
931
932 #define P_STREAMS p_dev->pp_streams[p_option->i_idx]
933
934     err = AudioStreamSetProperty( p_option->i_sid, 0, 0,
935                                   kAudioStreamPropertyPhysicalFormat,
936                                   sizeof( P_STREAMS[p_option->i_sdx] ),
937                                   &P_STREAMS[p_option->i_sdx] ); 
938     if( err != noErr )
939     {
940         msg_Err( p_aout, "AudioStreamSetProperty failed: [%4.4s]",
941                  (char *)&err );
942         return( VLC_EGENERIC );
943     }
944
945 #undef P_STREAMS
946
947     msg_Dbg( p_aout, "getting device [%ld]", p_option->i_dev );
948
949     config_PutInt( p_aout, "macosx-adev", i_option );
950
951     *p_devid = p_dev->devid;
952
953     return( VLC_SUCCESS );
954