]> git.sesse.net Git - vlc/blob - plugins/darwin/aout_darwin.c
e883fc5d14a6c2b075d724506f77b1b138c40f66
[vlc] / plugins / darwin / aout_darwin.c
1 /*****************************************************************************
2  * aout_darwin.c : Darwin audio output plugin
3  *****************************************************************************
4  * Copyright (C) 2001 VideoLAN
5  *
6  * Authors: 
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  * 
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
21  *****************************************************************************/
22
23 #define MODULE_NAME darwin
24 #include "modules_inner.h"
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include "defs.h"
30
31 #include "config.h"
32 #include "common.h"                                     /* boolean_t, byte_t */
33 #include "threads.h"
34 #include "mtime.h"
35 #include "tests.h"
36
37 #include "audio_output.h"                                   /* aout_thread_t */
38
39 #include "intf_msg.h"                        /* intf_DbgMsg(), intf_ErrMsg() */
40 #include "main.h"
41
42 #include "modules.h"
43
44 #include <Carbon/Carbon.h>
45 #include <CoreAudio/AudioHardware.h>
46
47 #include <sys/fcntl.h>
48
49 #define WRITE_AUDIO_OUTPUT_TO_FILE 0
50
51 /*****************************************************************************
52  * aout_sys_t: private audio output method descriptor
53  *****************************************************************************
54  * This structure is part of the audio output thread descriptor.
55  * It describes the Darwin specific properties of an output thread.
56  *****************************************************************************/
57 typedef struct aout_sys_s
58 {
59 #if WRITE_AUDIO_OUTPUT_TO_FILE
60     int           fd;                 // debug
61 #endif
62     // unsigned long sizeOfDataInMemory; // size in bytes of the 32 bit float data stored in memory
63     Ptr                 p_Data;     // Ptr to the 32 bit float data stored in memory
64     // Ptr                      currentDataLocationPtr; // location of the next chunk of data to send to the HAL
65     AudioDeviceID device;                                   // the default device
66     UInt32            ui_deviceBufferSize;   // bufferSize returned by kAudioDevicePropertyBufferSize
67     AudioStreamBasicDescription deviceFormat; // info about the default device
68     vlc_mutex_t   mutex_lock;
69     vlc_cond_t    cond_sync;
70 } aout_sys_t;
71
72 /*****************************************************************************
73  * Local prototypes.
74  *****************************************************************************/
75 static int     aout_Probe       ( probedata_t *p_data );
76 static int     aout_Open        ( aout_thread_t *p_aout );
77 static int     aout_SetFormat   ( aout_thread_t *p_aout );
78 static long    aout_GetBufInfo  ( aout_thread_t *p_aout, long l_buffer_info );
79 static void    aout_Play        ( aout_thread_t *p_aout,
80                                   byte_t *buffer, int i_size );
81 static void    aout_Close       ( aout_thread_t *p_aout );
82
83 OSStatus appIOProc( AudioDeviceID  inDevice, const AudioTimeStamp*  inNow, 
84                     const void*  inInputData, const AudioTimeStamp*  inInputTime, 
85                     AudioBufferList*  outOutputData, const AudioTimeStamp* inOutputTime, 
86                     void* appGlobals );
87 void Convert16BitIntegerTo32Float( Ptr in16BitDataPtr, Ptr out32BitDataPtr, UInt32 totalBytes );
88 void Convert16BitIntegerTo32FloatWithByteSwap( Ptr in16BitDataPtr, Ptr out32BitDataPtr, UInt32 totalBytes );
89 void Convert8BitIntegerTo32Float( Ptr in8BitDataPtr, Ptr out32BitDataPtr, UInt32 totalBytes );
90
91 /*****************************************************************************
92  * Functions exported as capabilities. They are declared as static so that
93  * we don't pollute the namespace too much.
94  *****************************************************************************/
95 void _M( aout_getfunctions )( function_list_t * p_function_list )
96 {
97     p_function_list->pf_probe = aout_Probe;
98     p_function_list->functions.aout.pf_open = aout_Open;
99     p_function_list->functions.aout.pf_setformat = aout_SetFormat;
100     p_function_list->functions.aout.pf_getbufinfo = aout_GetBufInfo;
101     p_function_list->functions.aout.pf_play = aout_Play;
102     p_function_list->functions.aout.pf_close = aout_Close;
103 }
104
105 /*****************************************************************************
106  * aout_Probe: probe the audio device and return a score
107  *****************************************************************************/
108 static int aout_Probe( probedata_t *p_data )
109 {
110     if( TestMethod( AOUT_METHOD_VAR, "darwin" ) )
111     {
112         return( 999 );
113     }
114
115     /* The Darwin plugin always works under Darwin or MacOS X */
116     return( 100 );
117 }
118
119 /*****************************************************************************
120  * aout_Open: opens a HAL audio device
121  *****************************************************************************/
122 static int aout_Open( aout_thread_t *p_aout )
123 {
124     OSStatus        err = noErr;
125     UInt32                          count, bufferSize;
126     AudioDeviceID               device = kAudioDeviceUnknown;
127     AudioStreamBasicDescription format;
128
129     /* Allocate structure */
130     p_aout->p_sys = malloc( sizeof( aout_sys_t ) );
131     if( p_aout->p_sys == NULL )
132     {
133         intf_ErrMsg("aout error: %s", strerror(ENOMEM) );
134         return( 1 );
135     }
136
137     /* Initialize some variables */
138     p_aout->i_format   = AOUT_FORMAT_DEFAULT;
139     p_aout->i_channels = 1 + main_GetIntVariable( AOUT_STEREO_VAR,
140                                                   AOUT_STEREO_DEFAULT );
141     p_aout->l_rate     =     main_GetIntVariable( AOUT_RATE_VAR,
142                                                   AOUT_RATE_DEFAULT );
143     p_aout->p_sys->device                 = kAudioDeviceUnknown;
144     p_aout->p_sys->p_Data         = nil;
145     // p_aout->p_sys->currentDataLocationPtr = nil;
146     
147     // get the default output device for the HAL
148     // it is required to pass the size of the data to be returned
149     count = sizeof( p_aout->p_sys->device );    
150     err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice,  
151                                     &count, (void *) &device);
152     
153     if( err == noErr) 
154     {
155         // get the buffersize that the default device uses for IO
156         // it is required to pass the size of the data to be returned
157         count = sizeof(p_aout->p_sys->ui_deviceBufferSize);     
158         err = AudioDeviceGetProperty( device, 0, false, 
159                                       kAudioDevicePropertyBufferSize, 
160                                       &count, &bufferSize);
161         if( err == noErr )
162         {
163             // get a description of the data format used by the default device
164             // it is required to pass the size of the data to be returned
165             count = sizeof(p_aout->p_sys->deviceFormat); 
166             err = AudioDeviceGetProperty( device, 0, false, 
167                                           kAudioDevicePropertyStreamFormat, 
168                                           &count, &format);
169             if( err == noErr )
170             {
171                 if( format.mFormatID != kAudioFormatLinearPCM ) return paramErr;
172             
173                 // everything is ok so fill in p_sys
174                 p_aout->p_sys->device           = device;
175                 p_aout->p_sys->ui_deviceBufferSize = bufferSize;
176                 p_aout->p_sys->deviceFormat     = format;
177             }
178         }
179     }
180
181     if (err != noErr) return err;
182
183     p_aout->p_sys->ui_deviceBufferSize = 2 * 2 * sizeof(s16) 
184                                         * ((s64)p_aout->l_rate * AOUT_BUFFER_DURATION) / 1000000; 
185     // p_aout->p_sys->sizeOfDataInMemory = p_aout->p_sys->ui_deviceBufferSize; 
186
187     p_aout->p_sys->p_Data = NewPtrClear( p_aout->p_sys->ui_deviceBufferSize );
188     if( p_aout->p_sys->p_Data == nil ) return paramErr;
189
190 #if WRITE_AUDIO_OUTPUT_TO_FILE
191     p_aout->p_sys->fd = open( "/Users/bofh/audio-darwin.pcm", O_RDWR|O_CREAT );
192     intf_ErrMsg( "open(...) -> %d", p_aout->p_sys->fd );
193 #endif
194
195     vlc_cond_init( &p_aout->p_sys->cond_sync );
196     vlc_mutex_init( &p_aout->p_sys->mutex_lock );
197     
198     return( 0 );
199 }
200
201 /*****************************************************************************
202  * aout_SetFormat: pretends to set the dsp output format
203  *****************************************************************************/
204 static int aout_SetFormat( aout_thread_t *p_aout )
205 {
206     OSStatus err = noErr;
207     UInt32       count, 
208              bufferSize = p_aout->p_sys->ui_deviceBufferSize;
209     AudioStreamBasicDescription format;
210
211     // get the buffersize that the default device uses for IO
212     // it is required to pass the size of the data to be returned
213     count = sizeof( bufferSize );       
214     err = AudioDeviceSetProperty( p_aout->p_sys->device, 0, 0, false, 
215                                   kAudioDevicePropertyBufferSize, 
216                                   count, &bufferSize);
217     intf_ErrMsg( "AudioDeviceSetProperty( buffersize = %d ) -> %d", bufferSize, err );
218
219     if( err == noErr )
220     {
221         p_aout->p_sys->ui_deviceBufferSize = bufferSize;
222     
223         // get a description of the data format used by the default device
224         // it is required to pass the size of the data to be returned
225         count = sizeof( format ); 
226         /*
227         err = AudioDeviceGetProperty( p_aout->p_sys->device, 0, false, 
228                                       kAudioDevicePropertyStreamFormat, 
229                                       &count, &format);
230         */
231         if( err == noErr )
232         {
233             // intf_ErrMsg( "audio output format is %i", p_aout->i_format );
234             if( format.mFormatID != kAudioFormatLinearPCM ) return paramErr;
235
236             switch( p_aout->i_format )
237             {
238                 case AOUT_FMT_U8:
239                     break;
240                 case AOUT_FMT_S16_LE:           /* Little endian signed 16 */
241                     // intf_ErrMsg( "This means Little endian signed 16" );
242                     break; 
243                 case AOUT_FMT_S16_BE:              /* Big endian signed 16 */
244                     // intf_ErrMsg( "This means Big endian signed 16" );
245                     // format.mFormatFlags &= ~kLinearPCMFormatFlagIsFloat;
246                     // format.mFormatFlags |= kLinearPCMFormatFlagIsFloat;
247                     // format.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
248                     // format.mFormatFlags &= ~kLinearPCMFormatFlagIsBigEndian;
249                     // format.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
250                     break; 
251                 case AOUT_FMT_S8:
252                     break; 
253                 case AOUT_FMT_U16_LE:                 /* Little endian U16 */
254                     // intf_ErrMsg( "This means Little endian U16" );
255                     break; 
256                 case AOUT_FMT_U16_BE:                    /* Big endian U16 */
257                     // intf_ErrMsg( "This means Big endian U16" );
258                     break;
259                 default:
260                     ; // intf_ErrMsg( "This means Unknown aout format" );
261             }
262
263             format.mSampleRate = p_aout->l_rate;
264             format.mChannelsPerFrame = p_aout->i_channels;
265             err = AudioDeviceSetProperty( p_aout->p_sys->device, 0, 0, false, 
266                                           kAudioDevicePropertyStreamFormat, 
267                                           count, &format);
268             /*
269             intf_ErrMsg( "AudioDeviceSetProperty( mFormatFlags = %x, " 
270                                                  "mSampleRate = %f, "
271                                                  "mChannelsPerFrame = %d ) " 
272                                                  "-> %d", 
273                                                   format.mFormatFlags, 
274                                                   format.mSampleRate, 
275                                                   format.mChannelsPerFrame, 
276                                                   err );
277             */
278         }
279     }
280
281     err = AudioDeviceAddIOProc( p_aout->p_sys->device, 
282                                 (AudioDeviceIOProc)appIOProc, 
283                                 (void *)p_aout->p_sys );
284
285     if( err == noErr )
286         err = AudioDeviceStart( p_aout->p_sys->device, (AudioDeviceIOProc)appIOProc );                  
287     
288     return( err );
289 }
290
291 /*****************************************************************************
292  * aout_GetBufInfo: returns available bytes in buffer
293  *****************************************************************************/
294 static long aout_GetBufInfo( aout_thread_t *p_aout, long l_buffer_limit )
295 {
296     return( 0 ); // Send data as soon as possible
297
298 /*
299  * Tune me ?
300  *
301     return (   p_aout->p_sys->p_Data
302              + p_aout->p_sys->sizeOfDataInMemory 
303              - p_aout->p_sys->currentDataLocationPtr 
304              - p_aout->p_sys->ui_deviceBufferSize );
305 */
306 }
307
308 /*****************************************************************************
309  * appIOProc : callback for audio output
310  *****************************************************************************/
311 OSStatus appIOProc( AudioDeviceID  inDevice, const AudioTimeStamp*  inNow, 
312                     const void*  inInputData, const AudioTimeStamp*  inInputTime, 
313                     AudioBufferList*  outOutputData, const AudioTimeStamp* inOutputTime, 
314                     void* appGlobals )
315 {
316     aout_sys_t* p_sys    = appGlobals;
317
318     vlc_mutex_lock( &p_sys->mutex_lock );
319     vlc_cond_signal( &p_sys->cond_sync );
320     
321     // move data into output data buffer
322     BlockMoveData( p_sys->p_Data,
323                    outOutputData->mBuffers[ 0 ].mData, 
324                    p_sys->ui_deviceBufferSize );
325
326     vlc_mutex_unlock( &p_sys->mutex_lock );
327
328     return( noErr );     
329 }
330
331 /*****************************************************************************
332  * aout_Play: plays a sound
333  *****************************************************************************/
334 static void aout_Play( aout_thread_t *p_aout, byte_t *buffer, int i_size )
335 {
336 #if WRITE_AUDIO_OUTPUT_TO_FILE
337     write( p_aout->p_sys->fd, buffer, i_size );
338     // intf_ErrMsg( "write() -> %d", write( p_aout->p_sys->fd, buffer, i_size ) );
339 #else
340     Convert16BitIntegerTo32Float( buffer, p_aout->p_sys->p_Data, i_size );
341     vlc_cond_wait( &p_aout->p_sys->cond_sync, &p_aout->p_sys->mutex_lock );
342 #endif
343 }
344
345 /*****************************************************************************
346  * aout_Close: closes the dummy audio device
347  *****************************************************************************/
348 static void aout_Close( aout_thread_t *p_aout )
349 {
350     OSStatus    err = noErr;
351     
352     // stop playing sound through the device
353     err = AudioDeviceStop( p_aout->p_sys->device, (AudioDeviceIOProc)appIOProc );                       
354     if (err != noErr) return;
355
356     // remove the IO proc from the device
357     err = AudioDeviceRemoveIOProc( p_aout->p_sys->device, (AudioDeviceIOProc)appIOProc );               
358     if (err != noErr) return;
359
360     // vlc_cond_signal( &p_aout->p_sys->cond_sync );
361     DisposePtr( p_aout->p_sys->p_Data );
362  
363     return;
364 }
365
366 /*****************************************************************************
367  * Convert16BitIntegerTo32Float
368  *****************************************************************************/
369 void Convert16BitIntegerTo32Float( Ptr in16BitDataPtr, Ptr out32BitDataPtr, 
370                                    UInt32 totalBytes )
371 {
372     UInt32      i, samples = totalBytes / 2 /* each 16 bit sample is 2 bytes */;
373     SInt16      *inDataPtr = (SInt16 *) in16BitDataPtr;
374     Float32     *outDataPtr = (Float32 *) out32BitDataPtr;
375     
376     for( i = 0 ; i < samples ; i++ )
377     {
378         *outDataPtr = (Float32)(*inDataPtr);
379         if( *outDataPtr > 0 )
380             *outDataPtr /= 32767.0;
381         else
382             *outDataPtr /= 32768.0;
383         outDataPtr++;
384         inDataPtr++;
385     }
386 }
387        
388 /*****************************************************************************
389  * Convert16BitIntegerTo32FloatWithByteSwap
390  *****************************************************************************/
391 void Convert16BitIntegerTo32FloatWithByteSwap( Ptr in16BitDataPtr, 
392                                                Ptr out32BitDataPtr, 
393                                                UInt32 totalBytes )
394 {
395     UInt32      i, samples = totalBytes / 2 /* each 16 bit sample is 2 bytes */;
396     SInt16      *inDataPtr = (SInt16 *) in16BitDataPtr;
397     Float32     *outDataPtr = (Float32 *) out32BitDataPtr;
398     
399     for( i = 0 ; i < samples ; i++ )
400     {
401         *outDataPtr = (Float32)CFSwapInt16LittleToHost(*inDataPtr);
402         if( *outDataPtr > 0 )
403             *outDataPtr /= 32767.0;
404         else
405             *outDataPtr /= 32768.0;
406
407         outDataPtr++;
408         inDataPtr++;
409     }
410 }
411        
412 /*****************************************************************************
413  * Convert8BitIntegerTo32Float
414  *****************************************************************************/
415 void Convert8BitIntegerTo32Float( Ptr in8BitDataPtr, Ptr out32BitDataPtr, 
416                                   UInt32 totalBytes )
417 {
418     UInt32      i, samples = totalBytes;
419     SInt8       *inDataPtr = (SInt8 *) in8BitDataPtr;
420     Float32     *outDataPtr = (Float32 *) out32BitDataPtr;
421     
422     for( i = 0 ; i < samples ; i++ )
423     {
424         *outDataPtr = (Float32)(*inDataPtr);
425         if( *outDataPtr > 0 )
426             *outDataPtr /= 32767.0;
427         else
428             *outDataPtr /= 32768.0;
429         
430         outDataPtr++;
431         inDataPtr++;
432     }
433 }