1 /*****************************************************************************
2 * aout_macosx.c : CoreAudio output plugin
3 *****************************************************************************
4 * Copyright (C) 2001 VideoLAN
5 * $Id: aout_macosx.m,v 1.4 2002/05/20 10:44:18 massiot Exp $
7 * Authors: Colin Delacroix <colin@zoy.org>
8 * Jon Lech Johansen <jon-vl@nanocrew.net>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
23 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
30 #include <videolan/vlc.h>
32 #include "audio_output.h" /* aout_thread_t */
34 #include <Carbon/Carbon.h>
35 #include <CoreAudio/AudioHardware.h>
36 #include <CoreAudio/HostTime.h>
37 #include <AudioToolbox/AudioConverter.h>
39 /*****************************************************************************
40 * aout_sys_t: private audio output method descriptor
41 *****************************************************************************
42 * This structure is part of the audio output thread descriptor.
43 * It describes the CoreAudio specific properties of an output thread.
44 *****************************************************************************/
45 typedef struct aout_sys_s
47 AudioDeviceID device; // the audio device
48 AudioConverterRef s_converter; // the AudioConverter
49 int b_format; // format begun
51 AudioStreamBasicDescription s_src_stream_format;
52 AudioStreamBasicDescription s_dst_stream_format;
54 Ptr p_buffer; // ptr to the 32 bit float data
55 UInt32 ui_buffer_size; // audio device buffer size
56 boolean_t b_buffer_data; // available buffer data?
57 vlc_mutex_t mutex_lock; // pthread locks for sync of
58 vlc_cond_t cond_sync; // aout_Play and callback
59 mtime_t clock_diff; // diff between system clock & audio
62 /*****************************************************************************
64 *****************************************************************************/
65 static int aout_Open ( aout_thread_t *p_aout );
66 static int aout_SetFormat ( aout_thread_t *p_aout );
67 static int aout_GetBufInfo ( aout_thread_t *p_aout, int i_buffer_info );
68 static void aout_Play ( aout_thread_t *p_aout,
69 byte_t *buffer, int i_size );
70 static void aout_Close ( aout_thread_t *p_aout );
72 static int CABeginFormat ( aout_thread_t *p_aout );
73 static int CAEndFormat ( aout_thread_t *p_aout );
75 static OSStatus CAIOCallback ( AudioDeviceID inDevice,
76 const AudioTimeStamp *inNow,
77 const void *inInputData,
78 const AudioTimeStamp *inInputTime,
79 AudioBufferList *outOutputData,
80 const AudioTimeStamp *inOutputTime,
81 void *threadGlobals );
83 /*****************************************************************************
84 * Functions exported as capabilities. They are declared as static so that
85 * we don't pollute the namespace too much.
86 *****************************************************************************/
87 void _M( aout_getfunctions )( function_list_t * p_function_list )
89 p_function_list->functions.aout.pf_open = aout_Open;
90 p_function_list->functions.aout.pf_setformat = aout_SetFormat;
91 p_function_list->functions.aout.pf_getbufinfo = aout_GetBufInfo;
92 p_function_list->functions.aout.pf_play = aout_Play;
93 p_function_list->functions.aout.pf_close = aout_Close;
96 /*****************************************************************************
97 * aout_Open: opens a CoreAudio HAL device
98 *****************************************************************************/
99 static int aout_Open( aout_thread_t *p_aout )
102 UInt32 ui_param_size;
104 /* allocate instance */
105 p_aout->p_sys = malloc( sizeof( aout_sys_t ) );
106 if( p_aout->p_sys == NULL )
108 intf_ErrMsg( "aout error: %s", strerror(ENOMEM) );
112 /* initialize members */
113 memset( p_aout->p_sys, 0, sizeof( aout_sys_t ) );
115 /* get the default output device */
116 ui_param_size = sizeof( p_aout->p_sys->device );
117 err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice,
119 (void *)&p_aout->p_sys->device );
123 intf_ErrMsg( "aout error: failed to get the device: %d", err );
127 /* get the buffer size that the device uses for IO */
128 ui_param_size = sizeof( p_aout->p_sys->ui_buffer_size );
129 err = AudioDeviceGetProperty( p_aout->p_sys->device, 0, false,
130 kAudioDevicePropertyBufferSize,
132 &p_aout->p_sys->ui_buffer_size );
136 intf_ErrMsg( "aout error: failed to get device buffer size: %d", err );
140 /* get a description of the data format used by the device */
141 ui_param_size = sizeof( p_aout->p_sys->s_dst_stream_format );
142 err = AudioDeviceGetProperty( p_aout->p_sys->device, 0, false,
143 kAudioDevicePropertyStreamFormat,
145 &p_aout->p_sys->s_dst_stream_format );
149 intf_ErrMsg( "aout error: failed to get dst stream format: %d", err );
153 if( p_aout->p_sys->s_dst_stream_format.mFormatID != kAudioFormatLinearPCM )
155 intf_ErrMsg( "aout error: kAudioFormatLinearPCM required" );
159 /* initialize mutex and cond */
160 vlc_mutex_init( &p_aout->p_sys->mutex_lock );
161 vlc_cond_init( &p_aout->p_sys->cond_sync );
163 /* initialize source stream format */
164 memcpy( &p_aout->p_sys->s_src_stream_format,
165 &p_aout->p_sys->s_dst_stream_format,
166 sizeof( p_aout->p_sys->s_src_stream_format ) );
168 if( CABeginFormat( p_aout ) )
170 intf_ErrMsg( "aout error: CABeginFormat failed" );
177 /*****************************************************************************
178 * aout_SetFormat: pretends to set the dsp output format
179 *****************************************************************************/
180 static int aout_SetFormat( aout_thread_t *p_aout )
182 if( CAEndFormat( p_aout ) )
184 intf_ErrMsg( "aout error: CAEndFormat failed" );
188 switch( p_aout->i_format )
191 intf_ErrMsg( "Audio format (Signed 8) not supported now,"
192 "please report stream" );
196 intf_ErrMsg( "Audio format (Unsigned 8) not supported now,"
197 "please report stream" );
200 case AOUT_FMT_S16_LE:
201 p_aout->p_sys->s_src_stream_format.mFormatFlags &=
202 ~kLinearPCMFormatFlagIsBigEndian;
203 p_aout->p_sys->s_src_stream_format.mFormatFlags |=
204 kLinearPCMFormatFlagIsSignedInteger;
207 case AOUT_FMT_S16_BE:
208 p_aout->p_sys->s_src_stream_format.mFormatFlags |=
209 kLinearPCMFormatFlagIsBigEndian;
210 p_aout->p_sys->s_src_stream_format.mFormatFlags |=
211 kLinearPCMFormatFlagIsSignedInteger;
214 case AOUT_FMT_U16_LE:
215 p_aout->p_sys->s_src_stream_format.mFormatFlags &=
216 ~kLinearPCMFormatFlagIsBigEndian;
217 p_aout->p_sys->s_src_stream_format.mFormatFlags &=
218 ~kLinearPCMFormatFlagIsSignedInteger;
221 case AOUT_FMT_U16_BE:
222 p_aout->p_sys->s_src_stream_format.mFormatFlags |=
223 kLinearPCMFormatFlagIsBigEndian;
224 p_aout->p_sys->s_src_stream_format.mFormatFlags &=
225 ~kLinearPCMFormatFlagIsSignedInteger;
229 intf_ErrMsg( "Audio format (0x%08x) not supported now,"
230 "please report stream", p_aout->i_format );
234 /* source format is not float */
235 p_aout->p_sys->s_src_stream_format.mFormatFlags &=
236 ~kLinearPCMFormatFlagIsFloat;
238 /* if destination format is float, take size diff into account */
239 if( p_aout->p_sys->s_dst_stream_format.mFormatFlags &
240 kLinearPCMFormatFlagIsFloat )
242 p_aout->p_sys->s_src_stream_format.mBytesPerPacket =
243 p_aout->p_sys->s_dst_stream_format.mBytesPerPacket / 2;
244 p_aout->p_sys->s_src_stream_format.mBytesPerFrame =
245 p_aout->p_sys->s_src_stream_format.mBytesPerFrame / 2;
246 p_aout->p_sys->s_src_stream_format.mBitsPerChannel =
247 p_aout->p_sys->s_src_stream_format.mBitsPerChannel / 2;
250 /* set sample rate and channels per frame */
251 p_aout->p_sys->s_src_stream_format.mSampleRate = p_aout->i_rate;
252 p_aout->p_sys->s_src_stream_format.mChannelsPerFrame = p_aout->i_channels;
254 if( CABeginFormat( p_aout ) )
256 intf_ErrMsg( "aout error: CABeginFormat failed" );
263 /*****************************************************************************
264 * aout_GetBufInfo: returns available bytes in buffer
265 *****************************************************************************/
266 static int aout_GetBufInfo( aout_thread_t *p_aout, int i_buffer_limit )
268 return( 0 ); /* send data as soon as possible */
271 /*****************************************************************************
272 * CAIOCallback : callback for audio output
273 *****************************************************************************/
274 static OSStatus CAIOCallback( AudioDeviceID inDevice,
275 const AudioTimeStamp *inNow,
276 const void *inInputData,
277 const AudioTimeStamp *inInputTime,
278 AudioBufferList *outOutputData,
279 const AudioTimeStamp *inOutputTime,
280 void *threadGlobals )
282 aout_thread_t *p_aout = (aout_thread_t *)threadGlobals;
283 aout_sys_t *p_sys = p_aout->p_sys;
285 AudioTimeStamp host_time;
287 host_time.mFlags = kAudioTimeStampHostTimeValid;
288 AudioDeviceTranslateTime( inDevice, inOutputTime, &host_time );
289 //intf_Msg( "%lld", AudioConvertHostTimeToNanos(host_time.mHostTime) / 1000 + p_aout->p_sys->clock_diff - p_aout->date );
290 p_aout->date = p_aout->p_sys->clock_diff + AudioConvertHostTimeToNanos(host_time.mHostTime) / 1000;
292 /* move data into output data buffer */
293 if( p_sys->b_buffer_data )
295 BlockMoveData( p_sys->p_buffer,
296 outOutputData->mBuffers[ 0 ].mData,
297 p_sys->ui_buffer_size );
301 memset(outOutputData->mBuffers[ 0 ].mData, 0, p_sys->ui_buffer_size);
302 intf_WarnMsg(1, "aout warning: audio output is starving, expect glitches");
305 /* see aout_Play below */
306 vlc_mutex_lock( &p_sys->mutex_lock );
307 p_sys->b_buffer_data = 0;
308 vlc_cond_signal( &p_sys->cond_sync );
309 vlc_mutex_unlock( &p_sys->mutex_lock );
314 /*****************************************************************************
315 * aout_Play: plays a sound
316 *****************************************************************************/
317 static void aout_Play( aout_thread_t *p_aout, byte_t *buffer, int i_size )
320 UInt32 ui_buffer_size = p_aout->p_sys->ui_buffer_size;
323 * wait for a callback to occur (to flush the buffer), so aout_Play
324 * can't be called twice, losing the data we just wrote.
326 vlc_mutex_lock( &p_aout->p_sys->mutex_lock );
327 if ( p_aout->p_sys->b_buffer_data )
329 vlc_cond_wait( &p_aout->p_sys->cond_sync, &p_aout->p_sys->mutex_lock );
331 vlc_mutex_unlock( &p_aout->p_sys->mutex_lock );
333 err = AudioConverterConvertBuffer( p_aout->p_sys->s_converter,
336 p_aout->p_sys->p_buffer );
340 intf_ErrMsg( "aout error: ConvertBuffer failed: %d", err );
344 p_aout->p_sys->b_buffer_data = 1;
348 /*****************************************************************************
349 * aout_Close: closes the CoreAudio HAL device
350 *****************************************************************************/
351 static void aout_Close( aout_thread_t *p_aout )
353 if( CAEndFormat( p_aout ) )
355 intf_ErrMsg( "aout error: CAEndFormat failed" );
358 /* destroy lock and cond */
359 vlc_mutex_destroy( &p_aout->p_sys->mutex_lock );
360 vlc_cond_destroy( &p_aout->p_sys->cond_sync );
362 free( p_aout->p_sys );
365 /*****************************************************************************
366 * CABeginFormat: creates an AudioConverter
367 *****************************************************************************/
368 static int CABeginFormat( aout_thread_t *p_aout )
371 UInt32 ui_param_size;
373 if( p_aout->p_sys->b_format )
375 intf_ErrMsg( "aout error: CABeginFormat (b_format)" );
379 p_aout->p_sys->ui_buffer_size = 2 * 2 * sizeof(s16) *
380 ((s64)p_aout->i_rate * AOUT_BUFFER_DURATION) / 1000000;
382 /* set the buffer size that the device uses for IO */
383 ui_param_size = sizeof( p_aout->p_sys->ui_buffer_size );
384 err = AudioDeviceSetProperty( p_aout->p_sys->device, 0, 0, false,
385 kAudioDevicePropertyBufferSize,
387 &p_aout->p_sys->ui_buffer_size );
388 //p_aout->i_latency = p_aout->p_sys->ui_buffer_size / 2;
392 intf_ErrMsg( "aout error: AudioDeviceSetProperty failed: %d", err );
396 /* allocate audio buffer */
397 p_aout->p_sys->p_buffer = NewPtrClear( p_aout->p_sys->ui_buffer_size );
399 if( p_aout->p_sys->p_buffer == nil )
401 intf_ErrMsg( "aout error: failed to allocate audio buffer" );
405 /* create a new AudioConverter */
406 err = AudioConverterNew( &p_aout->p_sys->s_src_stream_format,
407 &p_aout->p_sys->s_dst_stream_format,
408 &p_aout->p_sys->s_converter );
412 intf_ErrMsg( "aout error: AudioConverterNew failed: %d", err );
413 DisposePtr( p_aout->p_sys->p_buffer );
418 err = AudioDeviceAddIOProc( p_aout->p_sys->device,
419 (AudioDeviceIOProc)CAIOCallback,
424 intf_ErrMsg( "aout error: AudioDeviceAddIOProc failed: %d", err );
425 AudioConverterDispose( p_aout->p_sys->s_converter );
426 DisposePtr( p_aout->p_sys->p_buffer );
430 /* open the output */
431 err = AudioDeviceStart( p_aout->p_sys->device,
432 (AudioDeviceIOProc)CAIOCallback );
436 intf_ErrMsg( "aout error: AudioDeviceStart failed: %d", err );
437 AudioConverterDispose( p_aout->p_sys->s_converter );
438 DisposePtr( p_aout->p_sys->p_buffer );
442 /* Let's pray for the following operation to be atomic... */
443 p_aout->p_sys->clock_diff = mdate()
444 - AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()) / 1000
445 + (mtime_t)p_aout->p_sys->ui_buffer_size / 4 * 1000000 / (mtime_t)p_aout->i_rate
448 p_aout->p_sys->b_format = 1;
453 /*****************************************************************************
454 * CAEndFormat: destroys the AudioConverter
455 *****************************************************************************/
456 static int CAEndFormat( aout_thread_t *p_aout )
460 if( !p_aout->p_sys->b_format )
462 intf_ErrMsg( "aout error: CAEndFormat (!b_format)" );
466 /* stop playing sound through the device */
467 err = AudioDeviceStop( p_aout->p_sys->device,
468 (AudioDeviceIOProc)CAIOCallback );
472 intf_ErrMsg( "aout error: AudioDeviceStop failed: %d", err );
476 /* remove the callback */
477 err = AudioDeviceRemoveIOProc( p_aout->p_sys->device,
478 (AudioDeviceIOProc)CAIOCallback );
482 intf_ErrMsg( "aout error: AudioDeviceRemoveIOProc failed: %d", err );
486 /* destroy the AudioConverter */
487 err = AudioConverterDispose( p_aout->p_sys->s_converter );
491 intf_ErrMsg( "aout error: AudioConverterDispose failed: %d", err );
495 /* release audio buffer */
496 DisposePtr( p_aout->p_sys->p_buffer );
498 p_aout->p_sys->b_format = 0;