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 $
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 /*****************************************************************************
39 *****************************************************************************/
42 #include <videolan/vlc.h>
44 #include "audio_output.h" /* aout_thread_t */
48 #include <Carbon/Carbon.h>
49 #include <CoreAudio/AudioHardware.h>
51 #include <sys/fcntl.h>
54 * Debug: to dump the output of the decoder directly to a file
55 * May disappear when AC3 decoder will work on Darwin
57 #define WRITE_AUDIO_OUTPUT_TO_FILE 0
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
67 #if WRITE_AUDIO_OUTPUT_TO_FILE
68 int fd; // debug: fd to dump audio
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
78 /*****************************************************************************
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 );
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 );
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 )
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;
116 /*****************************************************************************
117 * aout_Probe: probe the audio device and return a score
118 *****************************************************************************/
119 static int aout_Probe( probedata_t *p_data )
121 if( TestMethod( AOUT_METHOD_VAR, "macosx" ) )
126 /* This plugin always works under OS X */
130 /*****************************************************************************
131 * aout_Open: opens a HAL audio device
132 *****************************************************************************/
133 static int aout_Open( aout_thread_t *p_aout )
135 OSStatus err = noErr;
136 UInt32 ui_paramSize, ui_bufferSize;
137 AudioDeviceID device = kAudioDeviceUnknown;
138 AudioStreamBasicDescription format;
140 /* Allocate structure */
141 p_aout->p_sys = malloc( sizeof( aout_sys_t ) );
142 if( p_aout->p_sys == NULL )
144 intf_ErrMsg("aout error: %s", strerror(ENOMEM) );
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,
155 p_aout->p_sys->device = kAudioDeviceUnknown;
156 p_aout->p_sys->p_Data = nil;
159 * get the default output device for the HAL
160 * it is required to pass the size of the data to be returned
162 ui_paramSize = sizeof( p_aout->p_sys->device );
163 err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice,
164 &ui_paramSize, (void *) &device );
170 * The values we get here are not used. We may find another method for
171 * insuring us that the audio device is working !
173 * First get the buffersize that the default device uses for IO
175 ui_paramSize = sizeof( p_aout->p_sys->ui_deviceBufferSize );
176 err = AudioDeviceGetProperty( device, 0, false,
177 kAudioDevicePropertyBufferSize,
178 &ui_paramSize, &ui_bufferSize);
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);
188 if( format.mFormatID != kAudioFormatLinearPCM ) return paramErr;
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;
198 if (err != noErr) return err;
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
204 p_aout->p_sys->ui_deviceBufferSize =
205 2 * 2 * sizeof(s16) * ((s64)p_aout->l_rate * AOUT_BUFFER_DURATION) / 1000000;
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;
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 );
216 vlc_cond_init( &p_aout->p_sys->cond_sync );
217 vlc_mutex_init( &p_aout->p_sys->mutex_lock );
222 /*****************************************************************************
223 * aout_SetFormat: pretends to set the dsp output format
224 *****************************************************************************/
225 static int aout_SetFormat( aout_thread_t *p_aout )
227 OSStatus err = noErr;
229 ui_bufferSize = p_aout->p_sys->ui_deviceBufferSize;
230 AudioStreamBasicDescription format;
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);
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 );
246 p_aout->p_sys->ui_deviceBufferSize = ui_bufferSize;
248 ui_paramSize = sizeof( format );
249 err = AudioDeviceGetProperty( p_aout->p_sys->device, 0, false,
250 kAudioDevicePropertyStreamFormat,
251 &ui_paramSize, &format);
255 intf_DbgMsg( "audio output format is %i", p_aout->i_format );
258 * setting format.mFormatFlags to anything but the default value
259 * doesn't seem to work. Can anybody explain that ??
262 switch( p_aout->i_format )
265 intf_ErrMsg( "Audio format (Unsigned 8) not supported now,"
266 "please report stream" );
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" );
276 case AOUT_FMT_S16_BE: /* Big endian signed 16 */
277 intf_DbgMsg( "This means big endian signed 16" );
278 // format.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
282 intf_ErrMsg( "Audio format (Signed 8) not supported now,"
283 "please report stream" );
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" );
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" );
301 intf_DbgMsg( "This means Unknown aout format" );
306 * It would have been nice to have these work (no more buffer
307 * convertion to float) but I couldn't manage to
309 // format.mFormatFlags &= ~kLinearPCMFormatFlagIsFloat;
310 // format.mFormatFlags |= kLinearPCMFormatFlagIsFloat;
311 // format.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
313 format.mSampleRate = p_aout->l_rate;
314 format.mChannelsPerFrame = p_aout->i_channels;
316 err = AudioDeviceSetProperty( p_aout->p_sys->device, 0, 0, false,
317 kAudioDevicePropertyStreamFormat,
318 ui_paramSize, &format);
321 intf_ErrMsg( "aout : AudioDeviceSetProperty( mFormatFlags = %x, "
322 "mSampleRate = %f, mChannelsPerFrame = %d ) -> %d",
323 format.mFormatFlags, format.mSampleRate,
324 format.mChannelsPerFrame, err );
330 p_aout->i_latency = 0;
333 err = AudioDeviceAddIOProc( p_aout->p_sys->device,
334 (AudioDeviceIOProc)appIOProc,
335 (void *)p_aout->p_sys );
337 /* open the output */
339 err = AudioDeviceStart( p_aout->p_sys->device, (AudioDeviceIOProc)appIOProc );
344 /*****************************************************************************
345 * aout_GetBufInfo: returns available bytes in buffer
346 *****************************************************************************/
347 static long aout_GetBufInfo( aout_thread_t *p_aout, long l_buffer_limit )
349 return( 0 ); // Send data as soon as possible
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 );
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 )
369 aout_sys_t* p_sys = threadGlobals;
371 /* see aout_Play below */
372 vlc_mutex_lock( &p_sys->mutex_lock );
373 vlc_cond_signal( &p_sys->cond_sync );
375 /* move data into output data buffer */
376 BlockMoveData( p_sys->p_Data,
377 outOutputData->mBuffers[ 0 ].mData,
378 p_sys->ui_deviceBufferSize );
380 vlc_mutex_unlock( &p_sys->mutex_lock );
385 /*****************************************************************************
386 * aout_Play: plays a sound
387 *****************************************************************************/
388 static void aout_Play( aout_thread_t *p_aout, byte_t *buffer, int i_size )
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 ) );
394 Convert16BitIntegerTo32Float( buffer, p_aout->p_sys->p_Data, i_size );
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.
400 vlc_cond_wait( &p_aout->p_sys->cond_sync, &p_aout->p_sys->mutex_lock );
404 /*****************************************************************************
405 * aout_Close: closes the dummy audio device
406 *****************************************************************************/
407 static void aout_Close( aout_thread_t *p_aout )
409 OSStatus err = noErr;
411 /* stop playing sound through the device */
412 err = AudioDeviceStop( p_aout->p_sys->device,
413 (AudioDeviceIOProc)appIOProc );
416 /* remove the callback */
417 err = AudioDeviceRemoveIOProc( p_aout->p_sys->device,
418 (AudioDeviceIOProc)appIOProc );
421 DisposePtr( p_aout->p_sys->p_Data );
426 /*****************************************************************************
427 * Convert16BitIntegerTo32Float
428 *****************************************************************************/
429 static void Convert16BitIntegerTo32Float( Ptr p_in16BitDataPtr, Ptr p_out32BitDataPtr,
430 UInt32 ui_totalBytes )
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;
436 for( i = 0 ; i < ui_samples ; i++ )
438 *p_f_outDataPtr = (Float32)(*p_s_inDataPtr);
439 if( *p_f_outDataPtr > 0 )
440 *p_f_outDataPtr /= 32767.0;
442 *p_f_outDataPtr /= 32768.0;
448 /*****************************************************************************
449 * Convert16BitIntegerTo32FloatWithByteSwap
450 *****************************************************************************/
451 static void Convert16BitIntegerTo32FloatWithByteSwap( Ptr p_in16BitDataPtr,
452 Ptr p_out32BitDataPtr,
453 UInt32 ui_totalBytes )
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;
459 for( i = 0 ; i < ui_samples ; i++ )
461 *p_f_outDataPtr = (Float32)CFSwapInt16LittleToHost(*p_s_inDataPtr);
462 if( *p_f_outDataPtr > 0 )
463 *p_f_outDataPtr /= 32767.0;
465 *p_f_outDataPtr /= 32768.0;
472 /*****************************************************************************
473 * Convert8BitIntegerTo32Float
474 *****************************************************************************/
475 static void Convert8BitIntegerTo32Float( Ptr p_in8BitDataPtr, Ptr p_out32BitDataPtr,
476 UInt32 ui_totalBytes )
478 UInt32 i, ui_samples = ui_totalBytes;
479 SInt8 *p_c_inDataPtr = (SInt8 *)p_in8BitDataPtr;
480 Float32 *p_f_outDataPtr = (Float32 *)p_out32BitDataPtr;
482 for( i = 0 ; i < ui_samples ; i++ )
484 *p_f_outDataPtr = (Float32)(*p_c_inDataPtr);
485 if( *p_f_outDataPtr > 0 )
486 *p_f_outDataPtr /= 32767.0;
488 *p_f_outDataPtr /= 32768.0;