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