]> git.sesse.net Git - vlc/blob - plugins/macosx/aout_macosx.c
* ./src/misc/modules_plugin.h: kludge to allow the ALSA module to be
[vlc] / plugins / macosx / aout_macosx.c
1 /*****************************************************************************
2  * aout_macosx.c : CoreAudio output plugin
3  *****************************************************************************
4  * Copyright (C) 2001 VideoLAN
5  * $Id: aout_macosx.c,v 1.15 2002/03/19 03:33:52 jlj Exp $
6  *
7  * Authors: Colin Delacroix <colin@zoy.org>
8  *          Jon Lech Johansen <jon-vl@nanocrew.net>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  * 
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <string.h>
29
30 #include <videolan/vlc.h>
31
32 #include "audio_output.h"                                   /* aout_thread_t */
33
34 #include <Carbon/Carbon.h>
35 #include <CoreAudio/AudioHardware.h>
36 #include <AudioToolbox/AudioConverter.h>
37
38 /*****************************************************************************
39  * aout_sys_t: private audio output method descriptor
40  *****************************************************************************
41  * This structure is part of the audio output thread descriptor.
42  * It describes the CoreAudio specific properties of an output thread.
43  *****************************************************************************/
44 typedef struct aout_sys_s
45 {
46     AudioDeviceID       device;         // the audio device
47     AudioConverterRef   s_converter;    // the AudioConverter
48     int                 b_format;       // format begun 
49
50     AudioStreamBasicDescription s_src_stream_format;
51     AudioStreamBasicDescription s_dst_stream_format;
52
53     Ptr                     p_buffer;       // ptr to the 32 bit float data
54     UInt32                  ui_buffer_size; // audio device buffer size
55     vlc_mutex_t         mutex_lock;     // pthread locks for sync of
56     vlc_cond_t          cond_sync;      // aout_Play and callback
57 } aout_sys_t;
58
59 /*****************************************************************************
60  * Local prototypes.
61  *****************************************************************************/
62 static int      aout_Open       ( aout_thread_t *p_aout );
63 static int      aout_SetFormat  ( aout_thread_t *p_aout );
64 static int      aout_GetBufInfo ( aout_thread_t *p_aout, int i_buffer_info );
65 static void     aout_Play       ( aout_thread_t *p_aout,
66                                   byte_t *buffer, int i_size );
67 static void     aout_Close      ( aout_thread_t *p_aout );
68
69 static int      CABeginFormat   ( aout_thread_t *p_aout );
70 static int      CAEndFormat     ( aout_thread_t *p_aout );
71
72 static OSStatus CAIOCallback    ( AudioDeviceID inDevice,
73                                   const AudioTimeStamp *inNow, 
74                                   const void *inInputData, 
75                                   const AudioTimeStamp *inInputTime,
76                                   AudioBufferList *outOutputData, 
77                                   const AudioTimeStamp *inOutputTime, 
78                                   void *threadGlobals );
79
80 /*****************************************************************************
81  * Functions exported as capabilities. They are declared as static so that
82  * we don't pollute the namespace too much.
83  *****************************************************************************/
84 void _M( aout_getfunctions )( function_list_t * p_function_list )
85 {
86     p_function_list->functions.aout.pf_open = aout_Open;
87     p_function_list->functions.aout.pf_setformat = aout_SetFormat;
88     p_function_list->functions.aout.pf_getbufinfo = aout_GetBufInfo;
89     p_function_list->functions.aout.pf_play = aout_Play;
90     p_function_list->functions.aout.pf_close = aout_Close;
91 }
92
93 /*****************************************************************************
94  * aout_Open: opens a CoreAudio HAL device
95  *****************************************************************************/
96 static int aout_Open( aout_thread_t *p_aout )
97 {
98     OSStatus err;
99     UInt32 ui_param_size;
100
101     /* allocate instance */
102     p_aout->p_sys = malloc( sizeof( aout_sys_t ) );
103     if( p_aout->p_sys == NULL )
104     {
105         intf_ErrMsg( "aout error: %s", strerror(ENOMEM) );
106         return( 1 );
107     }
108
109     /* initialize members */
110     memset( p_aout->p_sys, 0, sizeof( aout_sys_t ) );
111
112     /* get the default output device */
113     ui_param_size = sizeof( p_aout->p_sys->device );    
114     err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice,
115                                     &ui_param_size, 
116                                     (void *)&p_aout->p_sys->device );
117
118     if( err != noErr ) 
119     {
120         intf_ErrMsg( "aout error: failed to get the device: %d", err );
121         return( -1 );
122     }
123
124     /* get the buffer size that the device uses for IO */
125     ui_param_size = sizeof( p_aout->p_sys->ui_buffer_size );
126     err = AudioDeviceGetProperty( p_aout->p_sys->device, 0, false, 
127                                   kAudioDevicePropertyBufferSize, 
128                                   &ui_param_size,
129                                   &p_aout->p_sys->ui_buffer_size );
130
131     if( err != noErr )
132     {
133         intf_ErrMsg( "aout error: failed to get device buffer size: %d", err );
134         return( -1 );
135     }
136
137     /* get a description of the data format used by the device */
138     ui_param_size = sizeof( p_aout->p_sys->s_dst_stream_format ); 
139     err = AudioDeviceGetProperty( p_aout->p_sys->device, 0, false, 
140                                   kAudioDevicePropertyStreamFormat, 
141                                   &ui_param_size,
142                                   &p_aout->p_sys->s_dst_stream_format );
143
144     if( err != noErr )
145     {
146         intf_ErrMsg( "aout error: failed to get dst stream format: %d", err );
147         return( -1 );
148     }
149
150     if( p_aout->p_sys->s_dst_stream_format.mFormatID != kAudioFormatLinearPCM )
151     {
152         intf_ErrMsg( "aout error: kAudioFormatLinearPCM required" );
153         return( -1 );
154     }
155
156     /* initialize mutex and cond */
157     vlc_mutex_init( &p_aout->p_sys->mutex_lock );
158     vlc_cond_init( &p_aout->p_sys->cond_sync );
159
160     /* initialize source stream format */
161     memcpy( &p_aout->p_sys->s_src_stream_format,
162             &p_aout->p_sys->s_dst_stream_format,
163             sizeof( p_aout->p_sys->s_src_stream_format ) );
164
165     if( CABeginFormat( p_aout ) )
166     {
167         intf_ErrMsg( "aout error: CABeginFormat failed" );
168         return( -1 );
169     }
170
171     return( 0 );
172 }
173
174 /*****************************************************************************
175  * aout_SetFormat: pretends to set the dsp output format
176  *****************************************************************************/
177 static int aout_SetFormat( aout_thread_t *p_aout )
178 {
179     if( CAEndFormat( p_aout ) )
180     {
181         intf_ErrMsg( "aout error: CAEndFormat failed" );
182         return( -1 );
183     }
184
185     switch( p_aout->i_format )
186     {
187         case AOUT_FMT_S8:
188             intf_ErrMsg( "Audio format (Signed 8) not supported now,"
189                          "please report stream" );
190             return( -1 );
191                     
192         case AOUT_FMT_U8:
193             intf_ErrMsg( "Audio format (Unsigned 8) not supported now,"
194                          "please report stream" );
195             return( -1 );
196
197         case AOUT_FMT_S16_LE:
198             p_aout->p_sys->s_src_stream_format.mFormatFlags &=
199                 ~kLinearPCMFormatFlagIsBigEndian;
200             p_aout->p_sys->s_src_stream_format.mFormatFlags |=
201                 kLinearPCMFormatFlagIsSignedInteger;
202             break;
203
204         case AOUT_FMT_S16_BE:
205             p_aout->p_sys->s_src_stream_format.mFormatFlags |=
206                 kLinearPCMFormatFlagIsBigEndian;
207             p_aout->p_sys->s_src_stream_format.mFormatFlags |=
208                 kLinearPCMFormatFlagIsSignedInteger;
209             break;
210
211         case AOUT_FMT_U16_LE:
212             p_aout->p_sys->s_src_stream_format.mFormatFlags &=
213                 ~kLinearPCMFormatFlagIsBigEndian;
214             p_aout->p_sys->s_src_stream_format.mFormatFlags &=
215                 ~kLinearPCMFormatFlagIsSignedInteger;
216             break;
217                     
218         case AOUT_FMT_U16_BE:
219             p_aout->p_sys->s_src_stream_format.mFormatFlags |=
220                 kLinearPCMFormatFlagIsBigEndian;
221             p_aout->p_sys->s_src_stream_format.mFormatFlags &=
222                 ~kLinearPCMFormatFlagIsSignedInteger;
223             break;
224                     
225         default:
226             intf_ErrMsg( "Audio format (0x%08x) not supported now,"
227                          "please report stream", p_aout->i_format );
228             return( -1 );
229     }
230
231     /* source format is not float */
232     p_aout->p_sys->s_src_stream_format.mFormatFlags &=
233         ~kLinearPCMFormatFlagIsFloat;
234
235     /* if destination format is float, take size diff into account */
236     if( p_aout->p_sys->s_dst_stream_format.mFormatFlags & 
237         kLinearPCMFormatFlagIsFloat )
238     {
239         p_aout->p_sys->s_src_stream_format.mBytesPerPacket =
240             p_aout->p_sys->s_dst_stream_format.mBytesPerPacket / 2;
241         p_aout->p_sys->s_src_stream_format.mBytesPerFrame =
242             p_aout->p_sys->s_src_stream_format.mBytesPerFrame / 2;
243         p_aout->p_sys->s_src_stream_format.mBitsPerChannel =
244             p_aout->p_sys->s_src_stream_format.mBitsPerChannel / 2;
245     }
246
247     /* set sample rate and channels per frame */
248     p_aout->p_sys->s_src_stream_format.mSampleRate = p_aout->i_rate; 
249     p_aout->p_sys->s_src_stream_format.mChannelsPerFrame = p_aout->i_channels;
250
251     if( CABeginFormat( p_aout ) )
252     {
253         intf_ErrMsg( "aout error: CABeginFormat failed" );
254         return( -1 );
255     }
256
257     return( 0 );
258 }
259
260 /*****************************************************************************
261  * aout_GetBufInfo: returns available bytes in buffer
262  *****************************************************************************/
263 static int aout_GetBufInfo( aout_thread_t *p_aout, int i_buffer_limit )
264 {
265     return( 0 ); /* send data as soon as possible */
266 }
267
268 /*****************************************************************************
269  * CAIOCallback : callback for audio output
270  *****************************************************************************/
271 static OSStatus CAIOCallback( AudioDeviceID inDevice,
272                               const AudioTimeStamp *inNow, 
273                               const void *inInputData,
274                               const AudioTimeStamp *inInputTime, 
275                               AudioBufferList *outOutputData,
276                               const AudioTimeStamp *inOutputTime, 
277                               void *threadGlobals )
278 {
279     aout_sys_t *p_sys = (aout_sys_t *)threadGlobals;
280
281     /* see aout_Play below */
282     vlc_mutex_lock( &p_sys->mutex_lock );
283     vlc_cond_signal( &p_sys->cond_sync );
284     
285     /* move data into output data buffer */
286     BlockMoveData( p_sys->p_buffer,
287                    outOutputData->mBuffers[ 0 ].mData, 
288                    p_sys->ui_buffer_size );
289
290     vlc_mutex_unlock( &p_sys->mutex_lock );
291
292     return( noErr );     
293 }
294
295 /*****************************************************************************
296  * aout_Play: plays a sound
297  *****************************************************************************/
298 static void aout_Play( aout_thread_t *p_aout, byte_t *buffer, int i_size )
299 {
300     OSStatus err;
301     UInt32 ui_buffer_size = p_aout->p_sys->ui_buffer_size;
302
303     err = AudioConverterConvertBuffer( p_aout->p_sys->s_converter,
304                                        i_size, buffer,
305                                        &ui_buffer_size,
306                                        p_aout->p_sys->p_buffer );
307
308     if( err != noErr )
309     {
310         intf_ErrMsg( "aout error: ConvertBuffer failed: %d", err );
311     }
312
313     /* 
314      * wait for a callback to occur (to flush the buffer), so aout_Play
315      * can't be called twice, losing the data we just wrote. 
316      */
317     vlc_mutex_lock( &p_aout->p_sys->mutex_lock );
318     vlc_cond_wait( &p_aout->p_sys->cond_sync, &p_aout->p_sys->mutex_lock );
319     vlc_mutex_unlock( &p_aout->p_sys->mutex_lock );
320 }
321
322 /*****************************************************************************
323  * aout_Close: closes the CoreAudio HAL device
324  *****************************************************************************/
325 static void aout_Close( aout_thread_t *p_aout )
326 {
327     if( CAEndFormat( p_aout ) )
328     {
329         intf_ErrMsg( "aout error: CAEndFormat failed" );
330     }
331
332     /* destroy lock and cond */
333     vlc_mutex_destroy( &p_aout->p_sys->mutex_lock );
334     vlc_cond_destroy( &p_aout->p_sys->cond_sync );
335
336     free( p_aout->p_sys );
337 }
338
339 /*****************************************************************************
340  * CABeginFormat: creates an AudioConverter 
341  *****************************************************************************/
342 static int CABeginFormat( aout_thread_t *p_aout )
343 {
344     OSStatus err;
345     UInt32 ui_param_size;
346
347     if( p_aout->p_sys->b_format )
348     {
349         intf_ErrMsg( "aout error: CABeginFormat (b_format)" );
350         return( 1 );
351     }
352
353     p_aout->p_sys->ui_buffer_size = 2 * 2 * sizeof(s16) * 
354         ((s64)p_aout->i_rate * AOUT_BUFFER_DURATION) / 1000000; 
355
356     /* set the buffer size that the device uses for IO */
357     ui_param_size = sizeof( p_aout->p_sys->ui_buffer_size );    
358     err = AudioDeviceSetProperty( p_aout->p_sys->device, 0, 0, false, 
359                                   kAudioDevicePropertyBufferSize, 
360                                   ui_param_size,
361                                   &p_aout->p_sys->ui_buffer_size );
362
363     if( err != noErr )
364     {
365         intf_ErrMsg( "aout error: AudioDeviceSetProperty failed: %d", err );
366         return( 1 );
367     }
368
369     /* allocate audio buffer */ 
370     p_aout->p_sys->p_buffer = NewPtrClear( p_aout->p_sys->ui_buffer_size );
371
372     if( p_aout->p_sys->p_buffer == nil )
373     {
374         intf_ErrMsg( "aout error: failed to allocate audio buffer" );
375         return( 1 );
376     }
377
378     /* create a new AudioConverter */
379     err = AudioConverterNew( &p_aout->p_sys->s_src_stream_format,
380                              &p_aout->p_sys->s_dst_stream_format,
381                              &p_aout->p_sys->s_converter );
382
383     if( err != noErr )
384     {
385         intf_ErrMsg( "aout error: AudioConverterNew failed: %d", err );
386         DisposePtr( p_aout->p_sys->p_buffer );
387         return( 1 );
388     }
389
390     /* add callback */
391     err = AudioDeviceAddIOProc( p_aout->p_sys->device, 
392                                 (AudioDeviceIOProc)CAIOCallback, 
393                                 (void *)p_aout->p_sys );
394
395     if( err != noErr )
396     {
397         intf_ErrMsg( "aout error: AudioDeviceAddIOProc failed: %d", err );
398         AudioConverterDispose( p_aout->p_sys->s_converter );
399         DisposePtr( p_aout->p_sys->p_buffer );
400         return( 1 );
401     } 
402
403     /* open the output */
404     err = AudioDeviceStart( p_aout->p_sys->device,
405                             (AudioDeviceIOProc)CAIOCallback );
406
407     if( err != noErr )
408     {
409         intf_ErrMsg( "aout error: AudioDeviceStart failed: %d", err );
410         AudioConverterDispose( p_aout->p_sys->s_converter );
411         DisposePtr( p_aout->p_sys->p_buffer );
412         return( 1 );
413     }
414
415     p_aout->p_sys->b_format = 1;
416
417     return( 0 );
418 }
419
420 /*****************************************************************************
421  * CAEndFormat: destroys the AudioConverter 
422  *****************************************************************************/
423 static int CAEndFormat( aout_thread_t *p_aout )
424 {
425     OSStatus err; 
426
427     if( !p_aout->p_sys->b_format )
428     {
429         intf_ErrMsg( "aout error: CAEndFormat (!b_format)" );
430         return( 1 );
431     }
432
433     /* stop playing sound through the device */
434     err = AudioDeviceStop( p_aout->p_sys->device,
435                            (AudioDeviceIOProc)CAIOCallback ); 
436
437     if( err != noErr )
438     {
439         intf_ErrMsg( "aout error: AudioDeviceStop failed: %d", err );
440         return( 1 );
441     }
442
443     /* remove the callback */
444     err = AudioDeviceRemoveIOProc( p_aout->p_sys->device,
445                                    (AudioDeviceIOProc)CAIOCallback ); 
446
447     if( err != noErr )
448     {
449         intf_ErrMsg( "aout error: AudioDeviceRemoveIOProc failed: %d", err );
450         return( 1 );
451     }
452
453     /* destroy the AudioConverter */
454     err = AudioConverterDispose( p_aout->p_sys->s_converter );
455
456     if( err != noErr )
457     {
458         intf_ErrMsg( "aout error: AudioConverterDispose failed: %d", err );
459         return( 1 );
460     }
461
462     /* release audio buffer */
463     DisposePtr( p_aout->p_sys->p_buffer );
464
465     p_aout->p_sys->b_format = 0;
466
467     return( 0 );
468 }