]> git.sesse.net Git - vlc/blob - plugins/macosx/aout_macosx.c
MacOS X port : native QuickTime video output, courtesy of Florian G.
[vlc] / plugins / macosx / aout_macosx.c
1 /*****************************************************************************
2  * aout_darwin.c : Darwin audio output plugin
3  *****************************************************************************
4  * Copyright (C) 2001 VideoLAN
5  * $Id: aout_macosx.c,v 1.4 2001/10/08 16:20:25 massiot Exp $
6  *
7  * Authors: Colin Delacroix <colin@zoy.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  * 
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*
25  * 2001/03/21
26  * Status of audio under Darwin
27  * It currently works with 16 bits signed big endian mpeg 1 audio
28  * (and probably mpeg 2). This is the most common case.
29  * Note: ac3 decoder is currently broken under Darwin 
30  *
31  * TODO:
32  * Find little endian files and adapt output
33  * Find unsigned files and adapt output
34  * Find 8 bits files and adapt output
35  */
36  
37 #include "modules_inner.h"
38
39 /*****************************************************************************
40  * Preamble
41  *****************************************************************************/
42 #include "defs.h"
43
44 #include "config.h"
45 #include "common.h"                                     /* boolean_t, byte_t */
46 #include "threads.h"
47 #include "mtime.h"
48 #include "tests.h"
49
50 #include "audio_output.h"                                   /* aout_thread_t */
51
52 #include "intf_msg.h"                        /* intf_DbgMsg(), intf_ErrMsg() */
53 #include "main.h"
54
55 #include "modules.h"
56
57 #include <Carbon/Carbon.h>
58 #include <CoreAudio/AudioHardware.h>
59
60 #include <sys/fcntl.h>
61
62 /*
63  * Debug: to dump the output of the decoder directly to a file
64  * May disappear when AC3 decoder will work on Darwin
65  */
66 #define WRITE_AUDIO_OUTPUT_TO_FILE 0
67
68 /*****************************************************************************
69  * aout_sys_t: private audio output method descriptor
70  *****************************************************************************
71  * This structure is part of the audio output thread descriptor.
72  * It describes the Darwin specific properties of an output thread.
73  *****************************************************************************/
74 typedef struct aout_sys_s
75 {
76 #if WRITE_AUDIO_OUTPUT_TO_FILE
77     int           fd;                         // debug: fd to dump audio
78 #endif
79     AudioStreamBasicDescription deviceFormat; // info about the default device
80     AudioDeviceID device;                                       // the default device
81     Ptr                 p_Data;                     // ptr to the 32 bit float data
82     UInt32            ui_deviceBufferSize;        // audio device buffer size
83     vlc_mutex_t   mutex_lock;                 // pthread locks for sync of
84     vlc_cond_t    cond_sync;                  // aout_Play and callback
85 } aout_sys_t;
86
87 /*****************************************************************************
88  * Local prototypes.
89  *****************************************************************************/
90 static int     aout_Probe       ( probedata_t *p_data );
91 static int     aout_Open        ( aout_thread_t *p_aout );
92 static int     aout_SetFormat   ( aout_thread_t *p_aout );
93 static long    aout_GetBufInfo  ( aout_thread_t *p_aout, long l_buffer_info );
94 static void    aout_Play        ( aout_thread_t *p_aout,
95                                   byte_t *buffer, int i_size );
96 static void    aout_Close       ( aout_thread_t *p_aout );
97
98 static OSStatus appIOProc( AudioDeviceID inDevice, const AudioTimeStamp* inNow, 
99                     const void* inInputData, const AudioTimeStamp* inInputTime,
100                     AudioBufferList* outOutputData, 
101                     const AudioTimeStamp* inOutputTime, 
102                     void* threadGlobals );
103 static void Convert16BitIntegerTo32Float( Ptr p_in16BitDataPtr, Ptr p_out32BitDataPtr, 
104                                    UInt32 ui_totalBytes );
105 static void Convert16BitIntegerTo32FloatWithByteSwap( Ptr p_in16BitDataPtr, 
106                                                Ptr p_out32BitDataPtr, 
107                                                UInt32 p_totalBytes );
108 static void Convert8BitIntegerTo32Float( Ptr in8BitDataPtr, Ptr p_out32BitDataPtr, 
109                                   UInt32 ui_totalBytes );
110
111 /*****************************************************************************
112  * Functions exported as capabilities. They are declared as static so that
113  * we don't pollute the namespace too much.
114  *****************************************************************************/
115 void _M( aout_getfunctions )( function_list_t * p_function_list )
116 {
117     p_function_list->pf_probe = aout_Probe;
118     p_function_list->functions.aout.pf_open = aout_Open;
119     p_function_list->functions.aout.pf_setformat = aout_SetFormat;
120     p_function_list->functions.aout.pf_getbufinfo = aout_GetBufInfo;
121     p_function_list->functions.aout.pf_play = aout_Play;
122     p_function_list->functions.aout.pf_close = aout_Close;
123 }
124
125 /*****************************************************************************
126  * aout_Probe: probe the audio device and return a score
127  *****************************************************************************/
128 static int aout_Probe( probedata_t *p_data )
129 {
130     if( TestMethod( AOUT_METHOD_VAR, "macosx" ) )
131     {
132         return( 999 );
133     }
134
135     /* This plugin always works under OS X */
136     return( 100 );
137 }
138
139 /*****************************************************************************
140  * aout_Open: opens a HAL audio device
141  *****************************************************************************/
142 static int aout_Open( aout_thread_t *p_aout )
143 {
144     OSStatus        err = noErr;
145     UInt32                          ui_paramSize, ui_bufferSize;
146     AudioDeviceID               device = kAudioDeviceUnknown;
147     AudioStreamBasicDescription format;
148
149     /* Allocate structure */
150     p_aout->p_sys = malloc( sizeof( aout_sys_t ) );
151     if( p_aout->p_sys == NULL )
152     {
153         intf_ErrMsg("aout error: %s", strerror(ENOMEM) );
154         return( 1 );
155     }
156
157     /* Initialize some variables */
158     p_aout->i_format      = AOUT_FORMAT_DEFAULT;
159     p_aout->i_channels    = 1 + main_GetIntVariable( AOUT_STEREO_VAR,
160                                                   AOUT_STEREO_DEFAULT );
161     p_aout->l_rate        =     main_GetIntVariable( AOUT_RATE_VAR,
162                                                   AOUT_RATE_DEFAULT );
163
164     p_aout->p_sys->device = kAudioDeviceUnknown;
165     p_aout->p_sys->p_Data = nil;
166     
167     /*
168      * get the default output device for the HAL
169      * it is required to pass the size of the data to be returned
170      */
171     ui_paramSize = sizeof( p_aout->p_sys->device );     
172     err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice,
173                                     &ui_paramSize, (void *) &device );
174     
175     
176     if( err == noErr) 
177     {
178         /* 
179          * The values we get here are not used. We may find another method for
180          * insuring us that the audio device is working !
181          *
182          * First get the buffersize that the default device uses for IO
183          */
184         ui_paramSize = sizeof( p_aout->p_sys->ui_deviceBufferSize );
185         err = AudioDeviceGetProperty( device, 0, false, 
186                                       kAudioDevicePropertyBufferSize, 
187                                       &ui_paramSize, &ui_bufferSize);
188         if( err == noErr )
189         {
190             /* get a description of the data format used by the default device */
191             ui_paramSize = sizeof(p_aout->p_sys->deviceFormat); 
192             err = AudioDeviceGetProperty( device, 0, false, 
193                                           kAudioDevicePropertyStreamFormat, 
194                                           &ui_paramSize, &format);
195             if( err == noErr )
196             {
197                 if( format.mFormatID != kAudioFormatLinearPCM ) return paramErr;
198             
199                 /* everything is ok so fill in p_sys */
200                 p_aout->p_sys->device              = device;
201                 p_aout->p_sys->ui_deviceBufferSize = ui_bufferSize;
202                 p_aout->p_sys->deviceFormat        = format;
203             }
204         }
205     }
206
207     if (err != noErr) return err;
208
209     /* 
210      * Size calcul taken from audio_output.c we may change that file so we would
211      * not be forced to compute the same value twice
212      */
213     p_aout->p_sys->ui_deviceBufferSize = 
214       2 * 2 * sizeof(s16) * ((s64)p_aout->l_rate * AOUT_BUFFER_DURATION) / 1000000; 
215  
216     /* Allocate memory for audio */
217     p_aout->p_sys->p_Data = NewPtrClear( p_aout->p_sys->ui_deviceBufferSize );
218     if( p_aout->p_sys->p_Data == nil ) return paramErr;
219
220 #if WRITE_AUDIO_OUTPUT_TO_FILE
221     p_aout->p_sys->fd = open( "audio-darwin.pcm", O_RDWR|O_CREAT );
222     intf_WarnMsg( 3, "open(...) -> %d", p_aout->p_sys->fd );
223 #endif
224
225     vlc_cond_init( &p_aout->p_sys->cond_sync );
226     vlc_mutex_init( &p_aout->p_sys->mutex_lock );
227
228     return( 0 );
229 }
230
231 /*****************************************************************************
232  * aout_SetFormat: pretends to set the dsp output format
233  *****************************************************************************/
234 static int aout_SetFormat( aout_thread_t *p_aout )
235 {
236     OSStatus err = noErr;
237     UInt32       ui_paramSize, 
238              ui_bufferSize = p_aout->p_sys->ui_deviceBufferSize;
239     AudioStreamBasicDescription format;
240
241     /* set the buffersize that the default device uses for IO */
242     ui_paramSize = sizeof( ui_bufferSize );     
243     err = AudioDeviceSetProperty( p_aout->p_sys->device, 0, 0, false, 
244                                   kAudioDevicePropertyBufferSize, 
245                                   ui_paramSize, &ui_bufferSize);
246     if( err != noErr )
247     {
248         /* We have to tell the decoder to use audio device's buffer size  */
249         intf_ErrMsg( "aout : AudioDeviceSetProperty failed ( buffersize = %d ) -> %d",
250                      ui_bufferSize, err );
251         return( -1 );
252     }
253     else
254     {
255         p_aout->p_sys->ui_deviceBufferSize = ui_bufferSize;
256     
257         ui_paramSize = sizeof( format ); 
258         err = AudioDeviceGetProperty( p_aout->p_sys->device, 0, false, 
259                                       kAudioDevicePropertyStreamFormat, 
260                                       &ui_paramSize, &format);
261
262         if( err == noErr )
263         {
264             intf_DbgMsg( "audio output format is %i", p_aout->i_format );
265             
266             /*
267              * setting format.mFormatFlags to anything but the default value 
268              * doesn't seem to work. Can anybody explain that ??
269              */
270
271             switch( p_aout->i_format )
272             {
273                 case AOUT_FMT_U8:
274                     intf_ErrMsg( "Audio format (Unsigned 8) not supported now,"
275                                  "please report stream" );
276                     return( -1 );
277                     
278                 case AOUT_FMT_S16_LE:           /* Little endian signed 16 */
279                     intf_DbgMsg( "This means Little endian signed 16" );
280                     // format.mFormatFlags &= ~kLinearPCMFormatFlagIsBigEndian;
281                     intf_ErrMsg( "Audio format (LE Unsigned 16) not supported now,"
282                                  "please report stream" );
283                     return( -1 );
284                     
285                 case AOUT_FMT_S16_BE:              /* Big endian signed 16 */
286                     intf_DbgMsg( "This means big endian signed 16" );
287                     // format.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
288                     break;
289                     
290                 case AOUT_FMT_S8:
291                     intf_ErrMsg( "Audio format (Signed 8) not supported now,"
292                                  "please report stream" );
293                     return( -1 );
294                     
295                 case AOUT_FMT_U16_LE:                 /* Little endian U16 */
296                     // format.mFormatFlags &= ~kLinearPCMFormatFlagIsSignedInteger;
297                     intf_DbgMsg( "This means Little endian U16" );
298                     intf_ErrMsg( "Audio format (LE Unsigned 8) not supported now,"
299                                  "please report stream" );
300                     return( -1 );
301                     
302                 case AOUT_FMT_U16_BE:                    /* Big endian U16 */
303                     // format.mFormatFlags &= ~kLinearPCMFormatFlagIsSignedInteger;
304                     intf_ErrMsg( "Audio format (BE Unsigned 8) not supported now,"
305                                  "please report stream" );
306                     return( -1 );
307                     
308                     break;
309                 default:
310                     intf_DbgMsg( "This means Unknown aout format" );
311                     return( -1 );
312             }
313
314             /*
315              * It would have been nice to have these work (no more buffer
316              * convertion to float) but I couldn't manage to
317              */
318             // format.mFormatFlags &= ~kLinearPCMFormatFlagIsFloat;
319             // format.mFormatFlags |= kLinearPCMFormatFlagIsFloat;
320             // format.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
321
322             format.mSampleRate       = p_aout->l_rate;
323             format.mChannelsPerFrame = p_aout->i_channels;
324
325             err = AudioDeviceSetProperty( p_aout->p_sys->device, 0, 0, false, 
326                                           kAudioDevicePropertyStreamFormat, 
327                                           ui_paramSize, &format);
328             if( err != noErr )
329             {
330                 intf_ErrMsg( "aout : AudioDeviceSetProperty( mFormatFlags = %x, " 
331                              "mSampleRate = %f, mChannelsPerFrame = %d ) -> %d", 
332                              format.mFormatFlags, format.mSampleRate, 
333                              format.mChannelsPerFrame, err );
334                 return( -1 );
335             }
336         }
337     }
338
339     p_aout->i_latency = 0;
340
341     /* add callback */
342     err = AudioDeviceAddIOProc( p_aout->p_sys->device, 
343                                 (AudioDeviceIOProc)appIOProc, 
344                                 (void *)p_aout->p_sys );
345
346     /* open the output */
347     if( err == noErr )
348         err = AudioDeviceStart( p_aout->p_sys->device, (AudioDeviceIOProc)appIOProc );                  
349     
350     return( err );
351 }
352
353 /*****************************************************************************
354  * aout_GetBufInfo: returns available bytes in buffer
355  *****************************************************************************/
356 static long aout_GetBufInfo( aout_thread_t *p_aout, long l_buffer_limit )
357 {
358     return( 0 ); // Send data as soon as possible
359
360     /*
361      * Tune me ?
362      *
363        return (   p_aout->p_sys->p_Data
364              + p_aout->p_sys->sizeOfDataInMemory 
365              - p_aout->p_sys->currentDataLocationPtr 
366              - p_aout->p_sys->ui_deviceBufferSize );
367      */
368 }
369
370 /*****************************************************************************
371  * appIOProc : callback for audio output
372  *****************************************************************************/
373 static OSStatus appIOProc( AudioDeviceID  inDevice, const AudioTimeStamp*  inNow, 
374                     const void*  inInputData, const AudioTimeStamp*  inInputTime, 
375                     AudioBufferList*  outOutputData, const AudioTimeStamp* inOutputTime, 
376                     void* threadGlobals )
377 {
378     aout_sys_t* p_sys = threadGlobals;
379
380     /* see aout_Play below */
381     vlc_mutex_lock( &p_sys->mutex_lock );
382     vlc_cond_signal( &p_sys->cond_sync );
383     
384     /* move data into output data buffer */
385     BlockMoveData( p_sys->p_Data,
386                    outOutputData->mBuffers[ 0 ].mData, 
387                    p_sys->ui_deviceBufferSize );
388
389     vlc_mutex_unlock( &p_sys->mutex_lock );
390
391     return( noErr );     
392 }
393
394 /*****************************************************************************
395  * aout_Play: plays a sound
396  *****************************************************************************/
397 static void aout_Play( aout_thread_t *p_aout, byte_t *buffer, int i_size )
398 {
399 #if WRITE_AUDIO_OUTPUT_TO_FILE
400     write( p_aout->p_sys->fd, buffer, i_size );
401     intf_DbgMsg( "write() -> %d", write( p_aout->p_sys->fd, buffer, i_size ) );
402 #else
403     Convert16BitIntegerTo32Float( buffer, p_aout->p_sys->p_Data, i_size );
404     
405     /* 
406      * wait for a callback to occur (to flush the buffer), so aout_Play
407      * can't be called twice, losing the data we just wrote. 
408      */
409     vlc_cond_wait( &p_aout->p_sys->cond_sync, &p_aout->p_sys->mutex_lock );
410 #endif
411 }
412
413 /*****************************************************************************
414  * aout_Close: closes the dummy audio device
415  *****************************************************************************/
416 static void aout_Close( aout_thread_t *p_aout )
417 {
418     OSStatus    err = noErr;
419     
420     /* stop playing sound through the device */
421     err = AudioDeviceStop( p_aout->p_sys->device, 
422                            (AudioDeviceIOProc)appIOProc );                      
423     if( err == noErr )
424     {
425         /* remove the callback */
426         err = AudioDeviceRemoveIOProc( p_aout->p_sys->device, 
427                                       (AudioDeviceIOProc)appIOProc );           
428     }
429
430     DisposePtr( p_aout->p_sys->p_Data );
431  
432     return;
433 }
434
435 /*****************************************************************************
436  * Convert16BitIntegerTo32Float
437  *****************************************************************************/
438 static void Convert16BitIntegerTo32Float( Ptr p_in16BitDataPtr, Ptr p_out32BitDataPtr, 
439                                    UInt32 ui_totalBytes )
440 {
441     UInt32      i, ui_samples = ui_totalBytes / 2 /* each 16 bit sample is 2 bytes */;
442     SInt16      *p_s_inDataPtr = (SInt16 *) p_in16BitDataPtr;
443     Float32     *p_f_outDataPtr = (Float32 *) p_out32BitDataPtr;
444     
445     for( i = 0 ; i < ui_samples ; i++ )
446     {
447         *p_f_outDataPtr = (Float32)(*p_s_inDataPtr);
448         if( *p_f_outDataPtr > 0 )
449             *p_f_outDataPtr /= 32767.0;
450         else
451             *p_f_outDataPtr /= 32768.0;
452         p_f_outDataPtr++;
453         p_s_inDataPtr++;
454     }
455 }
456        
457 /*****************************************************************************
458  * Convert16BitIntegerTo32FloatWithByteSwap
459  *****************************************************************************/
460 static void Convert16BitIntegerTo32FloatWithByteSwap( Ptr p_in16BitDataPtr, 
461                                                Ptr p_out32BitDataPtr, 
462                                                UInt32 ui_totalBytes )
463 {
464     UInt32      i, ui_samples = ui_totalBytes / 2 /* each 16 bit sample is 2 bytes */;
465     SInt16      *p_s_inDataPtr = (SInt16 *) p_in16BitDataPtr;
466     Float32     *p_f_outDataPtr = (Float32 *) p_out32BitDataPtr;
467     
468     for( i = 0 ; i < ui_samples ; i++ )
469     {
470         *p_f_outDataPtr = (Float32)CFSwapInt16LittleToHost(*p_s_inDataPtr);
471         if( *p_f_outDataPtr > 0 )
472             *p_f_outDataPtr /= 32767.0;
473         else
474             *p_f_outDataPtr /= 32768.0;
475
476         p_f_outDataPtr++;
477         p_s_inDataPtr++;
478     }
479 }
480        
481 /*****************************************************************************
482  * Convert8BitIntegerTo32Float
483  *****************************************************************************/
484 static void Convert8BitIntegerTo32Float( Ptr p_in8BitDataPtr, Ptr p_out32BitDataPtr, 
485                                   UInt32 ui_totalBytes )
486 {
487     UInt32      i, ui_samples = ui_totalBytes;
488     SInt8       *p_c_inDataPtr = (SInt8 *)p_in8BitDataPtr;
489     Float32     *p_f_outDataPtr = (Float32 *)p_out32BitDataPtr;
490     
491     for( i = 0 ; i < ui_samples ; i++ )
492     {
493         *p_f_outDataPtr = (Float32)(*p_c_inDataPtr);
494         if( *p_f_outDataPtr > 0 )
495             *p_f_outDataPtr /= 32767.0;
496         else
497             *p_f_outDataPtr /= 32768.0;
498         
499         p_f_outDataPtr++;
500         p_c_inDataPtr++;
501     }
502 }