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