1 /*****************************************************************************
2 * aout_darwin.c : Darwin audio output plugin
3 *****************************************************************************
4 * Copyright (C) 2001 VideoLAN
5 * $Id: aout_macosx.c,v 1.4 2001/10/08 16:20:25 massiot 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 #include "modules_inner.h"
39 /*****************************************************************************
41 *****************************************************************************/
45 #include "common.h" /* boolean_t, byte_t */
50 #include "audio_output.h" /* aout_thread_t */
52 #include "intf_msg.h" /* intf_DbgMsg(), intf_ErrMsg() */
57 #include <Carbon/Carbon.h>
58 #include <CoreAudio/AudioHardware.h>
60 #include <sys/fcntl.h>
63 * Debug: to dump the output of the decoder directly to a file
64 * May disappear when AC3 decoder will work on Darwin
66 #define WRITE_AUDIO_OUTPUT_TO_FILE 0
68 /*****************************************************************************
69 * aout_sys_t: private audio output method descriptor
70 *****************************************************************************
71 * This structure is part of the audio output thread descriptor.
72 * It describes the Darwin specific properties of an output thread.
73 *****************************************************************************/
74 typedef struct aout_sys_s
76 #if WRITE_AUDIO_OUTPUT_TO_FILE
77 int fd; // debug: fd to dump audio
79 AudioStreamBasicDescription deviceFormat; // info about the default device
80 AudioDeviceID device; // the default device
81 Ptr p_Data; // ptr to the 32 bit float data
82 UInt32 ui_deviceBufferSize; // audio device buffer size
83 vlc_mutex_t mutex_lock; // pthread locks for sync of
84 vlc_cond_t cond_sync; // aout_Play and callback
87 /*****************************************************************************
89 *****************************************************************************/
90 static int aout_Probe ( probedata_t *p_data );
91 static int aout_Open ( aout_thread_t *p_aout );
92 static int aout_SetFormat ( aout_thread_t *p_aout );
93 static long aout_GetBufInfo ( aout_thread_t *p_aout, long l_buffer_info );
94 static void aout_Play ( aout_thread_t *p_aout,
95 byte_t *buffer, int i_size );
96 static void aout_Close ( aout_thread_t *p_aout );
98 static OSStatus appIOProc( AudioDeviceID inDevice, const AudioTimeStamp* inNow,
99 const void* inInputData, const AudioTimeStamp* inInputTime,
100 AudioBufferList* outOutputData,
101 const AudioTimeStamp* inOutputTime,
102 void* threadGlobals );
103 static void Convert16BitIntegerTo32Float( Ptr p_in16BitDataPtr, Ptr p_out32BitDataPtr,
104 UInt32 ui_totalBytes );
105 static void Convert16BitIntegerTo32FloatWithByteSwap( Ptr p_in16BitDataPtr,
106 Ptr p_out32BitDataPtr,
107 UInt32 p_totalBytes );
108 static void Convert8BitIntegerTo32Float( Ptr in8BitDataPtr, Ptr p_out32BitDataPtr,
109 UInt32 ui_totalBytes );
111 /*****************************************************************************
112 * Functions exported as capabilities. They are declared as static so that
113 * we don't pollute the namespace too much.
114 *****************************************************************************/
115 void _M( aout_getfunctions )( function_list_t * p_function_list )
117 p_function_list->pf_probe = aout_Probe;
118 p_function_list->functions.aout.pf_open = aout_Open;
119 p_function_list->functions.aout.pf_setformat = aout_SetFormat;
120 p_function_list->functions.aout.pf_getbufinfo = aout_GetBufInfo;
121 p_function_list->functions.aout.pf_play = aout_Play;
122 p_function_list->functions.aout.pf_close = aout_Close;
125 /*****************************************************************************
126 * aout_Probe: probe the audio device and return a score
127 *****************************************************************************/
128 static int aout_Probe( probedata_t *p_data )
130 if( TestMethod( AOUT_METHOD_VAR, "macosx" ) )
135 /* This plugin always works under OS X */
139 /*****************************************************************************
140 * aout_Open: opens a HAL audio device
141 *****************************************************************************/
142 static int aout_Open( aout_thread_t *p_aout )
144 OSStatus err = noErr;
145 UInt32 ui_paramSize, ui_bufferSize;
146 AudioDeviceID device = kAudioDeviceUnknown;
147 AudioStreamBasicDescription format;
149 /* Allocate structure */
150 p_aout->p_sys = malloc( sizeof( aout_sys_t ) );
151 if( p_aout->p_sys == NULL )
153 intf_ErrMsg("aout error: %s", strerror(ENOMEM) );
157 /* Initialize some variables */
158 p_aout->i_format = AOUT_FORMAT_DEFAULT;
159 p_aout->i_channels = 1 + main_GetIntVariable( AOUT_STEREO_VAR,
160 AOUT_STEREO_DEFAULT );
161 p_aout->l_rate = main_GetIntVariable( AOUT_RATE_VAR,
164 p_aout->p_sys->device = kAudioDeviceUnknown;
165 p_aout->p_sys->p_Data = nil;
168 * get the default output device for the HAL
169 * it is required to pass the size of the data to be returned
171 ui_paramSize = sizeof( p_aout->p_sys->device );
172 err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice,
173 &ui_paramSize, (void *) &device );
179 * The values we get here are not used. We may find another method for
180 * insuring us that the audio device is working !
182 * First get the buffersize that the default device uses for IO
184 ui_paramSize = sizeof( p_aout->p_sys->ui_deviceBufferSize );
185 err = AudioDeviceGetProperty( device, 0, false,
186 kAudioDevicePropertyBufferSize,
187 &ui_paramSize, &ui_bufferSize);
190 /* get a description of the data format used by the default device */
191 ui_paramSize = sizeof(p_aout->p_sys->deviceFormat);
192 err = AudioDeviceGetProperty( device, 0, false,
193 kAudioDevicePropertyStreamFormat,
194 &ui_paramSize, &format);
197 if( format.mFormatID != kAudioFormatLinearPCM ) return paramErr;
199 /* everything is ok so fill in p_sys */
200 p_aout->p_sys->device = device;
201 p_aout->p_sys->ui_deviceBufferSize = ui_bufferSize;
202 p_aout->p_sys->deviceFormat = format;
207 if (err != noErr) return err;
210 * Size calcul taken from audio_output.c we may change that file so we would
211 * not be forced to compute the same value twice
213 p_aout->p_sys->ui_deviceBufferSize =
214 2 * 2 * sizeof(s16) * ((s64)p_aout->l_rate * AOUT_BUFFER_DURATION) / 1000000;
216 /* Allocate memory for audio */
217 p_aout->p_sys->p_Data = NewPtrClear( p_aout->p_sys->ui_deviceBufferSize );
218 if( p_aout->p_sys->p_Data == nil ) return paramErr;
220 #if WRITE_AUDIO_OUTPUT_TO_FILE
221 p_aout->p_sys->fd = open( "audio-darwin.pcm", O_RDWR|O_CREAT );
222 intf_WarnMsg( 3, "open(...) -> %d", p_aout->p_sys->fd );
225 vlc_cond_init( &p_aout->p_sys->cond_sync );
226 vlc_mutex_init( &p_aout->p_sys->mutex_lock );
231 /*****************************************************************************
232 * aout_SetFormat: pretends to set the dsp output format
233 *****************************************************************************/
234 static int aout_SetFormat( aout_thread_t *p_aout )
236 OSStatus err = noErr;
238 ui_bufferSize = p_aout->p_sys->ui_deviceBufferSize;
239 AudioStreamBasicDescription format;
241 /* set the buffersize that the default device uses for IO */
242 ui_paramSize = sizeof( ui_bufferSize );
243 err = AudioDeviceSetProperty( p_aout->p_sys->device, 0, 0, false,
244 kAudioDevicePropertyBufferSize,
245 ui_paramSize, &ui_bufferSize);
248 /* We have to tell the decoder to use audio device's buffer size */
249 intf_ErrMsg( "aout : AudioDeviceSetProperty failed ( buffersize = %d ) -> %d",
250 ui_bufferSize, err );
255 p_aout->p_sys->ui_deviceBufferSize = ui_bufferSize;
257 ui_paramSize = sizeof( format );
258 err = AudioDeviceGetProperty( p_aout->p_sys->device, 0, false,
259 kAudioDevicePropertyStreamFormat,
260 &ui_paramSize, &format);
264 intf_DbgMsg( "audio output format is %i", p_aout->i_format );
267 * setting format.mFormatFlags to anything but the default value
268 * doesn't seem to work. Can anybody explain that ??
271 switch( p_aout->i_format )
274 intf_ErrMsg( "Audio format (Unsigned 8) not supported now,"
275 "please report stream" );
278 case AOUT_FMT_S16_LE: /* Little endian signed 16 */
279 intf_DbgMsg( "This means Little endian signed 16" );
280 // format.mFormatFlags &= ~kLinearPCMFormatFlagIsBigEndian;
281 intf_ErrMsg( "Audio format (LE Unsigned 16) not supported now,"
282 "please report stream" );
285 case AOUT_FMT_S16_BE: /* Big endian signed 16 */
286 intf_DbgMsg( "This means big endian signed 16" );
287 // format.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
291 intf_ErrMsg( "Audio format (Signed 8) not supported now,"
292 "please report stream" );
295 case AOUT_FMT_U16_LE: /* Little endian U16 */
296 // format.mFormatFlags &= ~kLinearPCMFormatFlagIsSignedInteger;
297 intf_DbgMsg( "This means Little endian U16" );
298 intf_ErrMsg( "Audio format (LE Unsigned 8) not supported now,"
299 "please report stream" );
302 case AOUT_FMT_U16_BE: /* Big endian U16 */
303 // format.mFormatFlags &= ~kLinearPCMFormatFlagIsSignedInteger;
304 intf_ErrMsg( "Audio format (BE Unsigned 8) not supported now,"
305 "please report stream" );
310 intf_DbgMsg( "This means Unknown aout format" );
315 * It would have been nice to have these work (no more buffer
316 * convertion to float) but I couldn't manage to
318 // format.mFormatFlags &= ~kLinearPCMFormatFlagIsFloat;
319 // format.mFormatFlags |= kLinearPCMFormatFlagIsFloat;
320 // format.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
322 format.mSampleRate = p_aout->l_rate;
323 format.mChannelsPerFrame = p_aout->i_channels;
325 err = AudioDeviceSetProperty( p_aout->p_sys->device, 0, 0, false,
326 kAudioDevicePropertyStreamFormat,
327 ui_paramSize, &format);
330 intf_ErrMsg( "aout : AudioDeviceSetProperty( mFormatFlags = %x, "
331 "mSampleRate = %f, mChannelsPerFrame = %d ) -> %d",
332 format.mFormatFlags, format.mSampleRate,
333 format.mChannelsPerFrame, err );
339 p_aout->i_latency = 0;
342 err = AudioDeviceAddIOProc( p_aout->p_sys->device,
343 (AudioDeviceIOProc)appIOProc,
344 (void *)p_aout->p_sys );
346 /* open the output */
348 err = AudioDeviceStart( p_aout->p_sys->device, (AudioDeviceIOProc)appIOProc );
353 /*****************************************************************************
354 * aout_GetBufInfo: returns available bytes in buffer
355 *****************************************************************************/
356 static long aout_GetBufInfo( aout_thread_t *p_aout, long l_buffer_limit )
358 return( 0 ); // Send data as soon as possible
363 return ( p_aout->p_sys->p_Data
364 + p_aout->p_sys->sizeOfDataInMemory
365 - p_aout->p_sys->currentDataLocationPtr
366 - p_aout->p_sys->ui_deviceBufferSize );
370 /*****************************************************************************
371 * appIOProc : callback for audio output
372 *****************************************************************************/
373 static OSStatus appIOProc( AudioDeviceID inDevice, const AudioTimeStamp* inNow,
374 const void* inInputData, const AudioTimeStamp* inInputTime,
375 AudioBufferList* outOutputData, const AudioTimeStamp* inOutputTime,
376 void* threadGlobals )
378 aout_sys_t* p_sys = threadGlobals;
380 /* see aout_Play below */
381 vlc_mutex_lock( &p_sys->mutex_lock );
382 vlc_cond_signal( &p_sys->cond_sync );
384 /* move data into output data buffer */
385 BlockMoveData( p_sys->p_Data,
386 outOutputData->mBuffers[ 0 ].mData,
387 p_sys->ui_deviceBufferSize );
389 vlc_mutex_unlock( &p_sys->mutex_lock );
394 /*****************************************************************************
395 * aout_Play: plays a sound
396 *****************************************************************************/
397 static void aout_Play( aout_thread_t *p_aout, byte_t *buffer, int i_size )
399 #if WRITE_AUDIO_OUTPUT_TO_FILE
400 write( p_aout->p_sys->fd, buffer, i_size );
401 intf_DbgMsg( "write() -> %d", write( p_aout->p_sys->fd, buffer, i_size ) );
403 Convert16BitIntegerTo32Float( buffer, p_aout->p_sys->p_Data, i_size );
406 * wait for a callback to occur (to flush the buffer), so aout_Play
407 * can't be called twice, losing the data we just wrote.
409 vlc_cond_wait( &p_aout->p_sys->cond_sync, &p_aout->p_sys->mutex_lock );
413 /*****************************************************************************
414 * aout_Close: closes the dummy audio device
415 *****************************************************************************/
416 static void aout_Close( aout_thread_t *p_aout )
418 OSStatus err = noErr;
420 /* stop playing sound through the device */
421 err = AudioDeviceStop( p_aout->p_sys->device,
422 (AudioDeviceIOProc)appIOProc );
425 /* remove the callback */
426 err = AudioDeviceRemoveIOProc( p_aout->p_sys->device,
427 (AudioDeviceIOProc)appIOProc );
430 DisposePtr( p_aout->p_sys->p_Data );
435 /*****************************************************************************
436 * Convert16BitIntegerTo32Float
437 *****************************************************************************/
438 static void Convert16BitIntegerTo32Float( Ptr p_in16BitDataPtr, Ptr p_out32BitDataPtr,
439 UInt32 ui_totalBytes )
441 UInt32 i, ui_samples = ui_totalBytes / 2 /* each 16 bit sample is 2 bytes */;
442 SInt16 *p_s_inDataPtr = (SInt16 *) p_in16BitDataPtr;
443 Float32 *p_f_outDataPtr = (Float32 *) p_out32BitDataPtr;
445 for( i = 0 ; i < ui_samples ; i++ )
447 *p_f_outDataPtr = (Float32)(*p_s_inDataPtr);
448 if( *p_f_outDataPtr > 0 )
449 *p_f_outDataPtr /= 32767.0;
451 *p_f_outDataPtr /= 32768.0;
457 /*****************************************************************************
458 * Convert16BitIntegerTo32FloatWithByteSwap
459 *****************************************************************************/
460 static void Convert16BitIntegerTo32FloatWithByteSwap( Ptr p_in16BitDataPtr,
461 Ptr p_out32BitDataPtr,
462 UInt32 ui_totalBytes )
464 UInt32 i, ui_samples = ui_totalBytes / 2 /* each 16 bit sample is 2 bytes */;
465 SInt16 *p_s_inDataPtr = (SInt16 *) p_in16BitDataPtr;
466 Float32 *p_f_outDataPtr = (Float32 *) p_out32BitDataPtr;
468 for( i = 0 ; i < ui_samples ; i++ )
470 *p_f_outDataPtr = (Float32)CFSwapInt16LittleToHost(*p_s_inDataPtr);
471 if( *p_f_outDataPtr > 0 )
472 *p_f_outDataPtr /= 32767.0;
474 *p_f_outDataPtr /= 32768.0;
481 /*****************************************************************************
482 * Convert8BitIntegerTo32Float
483 *****************************************************************************/
484 static void Convert8BitIntegerTo32Float( Ptr p_in8BitDataPtr, Ptr p_out32BitDataPtr,
485 UInt32 ui_totalBytes )
487 UInt32 i, ui_samples = ui_totalBytes;
488 SInt8 *p_c_inDataPtr = (SInt8 *)p_in8BitDataPtr;
489 Float32 *p_f_outDataPtr = (Float32 *)p_out32BitDataPtr;
491 for( i = 0 ; i < ui_samples ; i++ )
493 *p_f_outDataPtr = (Float32)(*p_c_inDataPtr);
494 if( *p_f_outDataPtr > 0 )
495 *p_f_outDataPtr /= 32767.0;
497 *p_f_outDataPtr /= 32768.0;