1 /*****************************************************************************
2 * aout_darwin.c : Darwin audio output plugin
3 *****************************************************************************
4 * Copyright (C) 2001 VideoLAN
5 * $Id: aout_macosx.c,v 1.3 2001/07/12 20:44:52 reno Exp $
7 * Authors: Colin Delacroix <colin@zoy.org>
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.
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.
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 *****************************************************************************/
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
32 * Find little endian files and adapt output
33 * Find unsigned files and adapt output
34 * Find 8 bits files and adapt output
37 #define MODULE_NAME macosx
38 #include "modules_inner.h"
40 /*****************************************************************************
42 *****************************************************************************/
46 #include "common.h" /* boolean_t, byte_t */
51 #include "audio_output.h" /* aout_thread_t */
53 #include "intf_msg.h" /* intf_DbgMsg(), intf_ErrMsg() */
58 #include <Carbon/Carbon.h>
59 #include <CoreAudio/AudioHardware.h>
61 #include <sys/fcntl.h>
64 * Debug: to dump the output of the decoder directly to a file
65 * May disappear when AC3 decoder will work on Darwin
67 #define WRITE_AUDIO_OUTPUT_TO_FILE 0
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
77 #if WRITE_AUDIO_OUTPUT_TO_FILE
78 int fd; // debug: fd to dump audio
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
88 /*****************************************************************************
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 );
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 );
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 )
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;
126 /*****************************************************************************
127 * aout_Probe: probe the audio device and return a score
128 *****************************************************************************/
129 static int aout_Probe( probedata_t *p_data )
131 if( TestMethod( AOUT_METHOD_VAR, "macosx" ) )
136 /* This plugin always works under OS X */
140 /*****************************************************************************
141 * aout_Open: opens a HAL audio device
142 *****************************************************************************/
143 static int aout_Open( aout_thread_t *p_aout )
145 OSStatus err = noErr;
146 UInt32 ui_paramSize, ui_bufferSize;
147 AudioDeviceID device = kAudioDeviceUnknown;
148 AudioStreamBasicDescription format;
150 /* Allocate structure */
151 p_aout->p_sys = malloc( sizeof( aout_sys_t ) );
152 if( p_aout->p_sys == NULL )
154 intf_ErrMsg("aout error: %s", strerror(ENOMEM) );
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,
165 p_aout->p_sys->device = kAudioDeviceUnknown;
166 p_aout->p_sys->p_Data = nil;
169 * get the default output device for the HAL
170 * it is required to pass the size of the data to be returned
172 ui_paramSize = sizeof( p_aout->p_sys->device );
173 err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice,
174 &ui_paramSize, (void *) &device );
180 * The values we get here are not used. We may find another method for
181 * insuring us that the audio device is working !
183 * First get the buffersize that the default device uses for IO
185 ui_paramSize = sizeof( p_aout->p_sys->ui_deviceBufferSize );
186 err = AudioDeviceGetProperty( device, 0, false,
187 kAudioDevicePropertyBufferSize,
188 &ui_paramSize, &ui_bufferSize);
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);
198 if( format.mFormatID != kAudioFormatLinearPCM ) return paramErr;
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;
208 if (err != noErr) return err;
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
214 p_aout->p_sys->ui_deviceBufferSize =
215 2 * 2 * sizeof(s16) * ((s64)p_aout->l_rate * AOUT_BUFFER_DURATION) / 1000000;
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;
221 #if WRITE_AUDIO_OUTPUT_TO_FILE
222 p_aout->p_sys->fd = open( "audio-darwin.pcm", O_RDWR|O_CREAT );
223 intf_WarnMsg( 3, "open(...) -> %d", p_aout->p_sys->fd );
226 vlc_cond_init( &p_aout->p_sys->cond_sync );
227 vlc_mutex_init( &p_aout->p_sys->mutex_lock );
232 /*****************************************************************************
233 * aout_SetFormat: pretends to set the dsp output format
234 *****************************************************************************/
235 static int aout_SetFormat( aout_thread_t *p_aout )
237 OSStatus err = noErr;
239 ui_bufferSize = p_aout->p_sys->ui_deviceBufferSize;
240 AudioStreamBasicDescription format;
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);
249 /* We have to tell the decoder to use audio device's buffer size */
250 intf_ErrMsg( "aout : AudioDeviceSetProperty failed ( buffersize = %d ) -> %d",
251 ui_bufferSize, err );
256 p_aout->p_sys->ui_deviceBufferSize = ui_bufferSize;
258 ui_paramSize = sizeof( format );
259 err = AudioDeviceGetProperty( p_aout->p_sys->device, 0, false,
260 kAudioDevicePropertyStreamFormat,
261 &ui_paramSize, &format);
265 intf_DbgMsg( "audio output format is %i", p_aout->i_format );
268 * setting format.mFormatFlags to anything but the default value
269 * doesn't seem to work. Can anybody explain that ??
272 switch( p_aout->i_format )
275 intf_ErrMsg( "Audio format (Unsigned 8) not supported now,"
276 "please report stream" );
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" );
286 case AOUT_FMT_S16_BE: /* Big endian signed 16 */
287 intf_DbgMsg( "This means big endian signed 16" );
288 // format.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
292 intf_ErrMsg( "Audio format (Signed 8) not supported now,"
293 "please report stream" );
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" );
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" );
311 intf_DbgMsg( "This means Unknown aout format" );
316 * It would have been nice to have these work (no more buffer
317 * convertion to float) but I couldn't manage to
319 // format.mFormatFlags &= ~kLinearPCMFormatFlagIsFloat;
320 // format.mFormatFlags |= kLinearPCMFormatFlagIsFloat;
321 // format.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
323 format.mSampleRate = p_aout->l_rate;
324 format.mChannelsPerFrame = p_aout->i_channels;
326 err = AudioDeviceSetProperty( p_aout->p_sys->device, 0, 0, false,
327 kAudioDevicePropertyStreamFormat,
328 ui_paramSize, &format);
331 intf_ErrMsg( "aout : AudioDeviceSetProperty( mFormatFlags = %x, "
332 "mSampleRate = %f, mChannelsPerFrame = %d ) -> %d",
333 format.mFormatFlags, format.mSampleRate,
334 format.mChannelsPerFrame, err );
340 p_aout->i_latency = 0;
343 err = AudioDeviceAddIOProc( p_aout->p_sys->device,
344 (AudioDeviceIOProc)appIOProc,
345 (void *)p_aout->p_sys );
347 /* open the output */
349 err = AudioDeviceStart( p_aout->p_sys->device, (AudioDeviceIOProc)appIOProc );
354 /*****************************************************************************
355 * aout_GetBufInfo: returns available bytes in buffer
356 *****************************************************************************/
357 static long aout_GetBufInfo( aout_thread_t *p_aout, long l_buffer_limit )
359 return( 0 ); // Send data as soon as possible
364 return ( p_aout->p_sys->p_Data
365 + p_aout->p_sys->sizeOfDataInMemory
366 - p_aout->p_sys->currentDataLocationPtr
367 - p_aout->p_sys->ui_deviceBufferSize );
371 /*****************************************************************************
372 * appIOProc : callback for audio output
373 *****************************************************************************/
374 OSStatus appIOProc( AudioDeviceID inDevice, const AudioTimeStamp* inNow,
375 const void* inInputData, const AudioTimeStamp* inInputTime,
376 AudioBufferList* outOutputData, const AudioTimeStamp* inOutputTime,
377 void* threadGlobals )
379 aout_sys_t* p_sys = threadGlobals;
381 /* see aout_Play below */
382 vlc_mutex_lock( &p_sys->mutex_lock );
383 vlc_cond_signal( &p_sys->cond_sync );
385 /* move data into output data buffer */
386 BlockMoveData( p_sys->p_Data,
387 outOutputData->mBuffers[ 0 ].mData,
388 p_sys->ui_deviceBufferSize );
390 vlc_mutex_unlock( &p_sys->mutex_lock );
395 /*****************************************************************************
396 * aout_Play: plays a sound
397 *****************************************************************************/
398 static void aout_Play( aout_thread_t *p_aout, byte_t *buffer, int i_size )
400 #if WRITE_AUDIO_OUTPUT_TO_FILE
401 write( p_aout->p_sys->fd, buffer, i_size );
402 intf_DbgMsg( "write() -> %d", write( p_aout->p_sys->fd, buffer, i_size ) );
404 Convert16BitIntegerTo32Float( buffer, p_aout->p_sys->p_Data, i_size );
407 * wait for a callback to occur (to flush the buffer), so aout_Play
408 * can't be called twice, losing the data we just wrote.
410 vlc_cond_wait( &p_aout->p_sys->cond_sync, &p_aout->p_sys->mutex_lock );
414 /*****************************************************************************
415 * aout_Close: closes the dummy audio device
416 *****************************************************************************/
417 static void aout_Close( aout_thread_t *p_aout )
419 OSStatus err = noErr;
421 /* stop playing sound through the device */
422 err = AudioDeviceStop( p_aout->p_sys->device,
423 (AudioDeviceIOProc)appIOProc );
426 /* remove the callback */
427 err = AudioDeviceRemoveIOProc( p_aout->p_sys->device,
428 (AudioDeviceIOProc)appIOProc );
431 DisposePtr( p_aout->p_sys->p_Data );
436 /*****************************************************************************
437 * Convert16BitIntegerTo32Float
438 *****************************************************************************/
439 void Convert16BitIntegerTo32Float( Ptr p_in16BitDataPtr, Ptr p_out32BitDataPtr,
440 UInt32 ui_totalBytes )
442 UInt32 i, ui_samples = ui_totalBytes / 2 /* each 16 bit sample is 2 bytes */;
443 SInt16 *p_s_inDataPtr = (SInt16 *) p_in16BitDataPtr;
444 Float32 *p_f_outDataPtr = (Float32 *) p_out32BitDataPtr;
446 for( i = 0 ; i < ui_samples ; i++ )
448 *p_f_outDataPtr = (Float32)(*p_s_inDataPtr);
449 if( *p_f_outDataPtr > 0 )
450 *p_f_outDataPtr /= 32767.0;
452 *p_f_outDataPtr /= 32768.0;
458 /*****************************************************************************
459 * Convert16BitIntegerTo32FloatWithByteSwap
460 *****************************************************************************/
461 void Convert16BitIntegerTo32FloatWithByteSwap( Ptr p_in16BitDataPtr,
462 Ptr p_out32BitDataPtr,
463 UInt32 ui_totalBytes )
465 UInt32 i, ui_samples = ui_totalBytes / 2 /* each 16 bit sample is 2 bytes */;
466 SInt16 *p_s_inDataPtr = (SInt16 *) p_in16BitDataPtr;
467 Float32 *p_f_outDataPtr = (Float32 *) p_out32BitDataPtr;
469 for( i = 0 ; i < ui_samples ; i++ )
471 *p_f_outDataPtr = (Float32)CFSwapInt16LittleToHost(*p_s_inDataPtr);
472 if( *p_f_outDataPtr > 0 )
473 *p_f_outDataPtr /= 32767.0;
475 *p_f_outDataPtr /= 32768.0;
482 /*****************************************************************************
483 * Convert8BitIntegerTo32Float
484 *****************************************************************************/
485 void Convert8BitIntegerTo32Float( Ptr p_in8BitDataPtr, Ptr p_out32BitDataPtr,
486 UInt32 ui_totalBytes )
488 UInt32 i, ui_samples = ui_totalBytes;
489 SInt8 *p_c_inDataPtr = (SInt8 *)p_in8BitDataPtr;
490 Float32 *p_f_outDataPtr = (Float32 *)p_out32BitDataPtr;
492 for( i = 0 ; i < ui_samples ; i++ )
494 *p_f_outDataPtr = (Float32)(*p_c_inDataPtr);
495 if( *p_f_outDataPtr > 0 )
496 *p_f_outDataPtr /= 32767.0;
498 *p_f_outDataPtr /= 32768.0;