1 /*****************************************************************************
2 * aout_macosx.m: CoreAudio output plugin
3 *****************************************************************************
4 * Copyright (C) 2001 VideoLAN
5 * $Id: aout_macosx.m,v 1.8 2002/07/20 18:01:42 sam 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 *****************************************************************************/
33 #include <Carbon/Carbon.h>
34 #include <CoreAudio/AudioHardware.h>
35 #include <CoreAudio/HostTime.h>
36 #include <AudioToolbox/AudioConverter.h>
38 /*****************************************************************************
39 * aout_sys_t: private audio output method descriptor
40 *****************************************************************************
41 * This structure is part of the audio output thread descriptor.
42 * It describes the CoreAudio specific properties of an output thread.
43 *****************************************************************************/
46 AudioDeviceID device; // the audio device
47 AudioConverterRef s_converter; // the AudioConverter
48 int b_format; // format begun
50 AudioStreamBasicDescription s_src_stream_format;
51 AudioStreamBasicDescription s_dst_stream_format;
53 Ptr p_buffer; // ptr to the 32 bit float data
54 UInt32 ui_buffer_size; // audio device buffer size
55 vlc_bool_t b_buffer_data; // available buffer data?
56 vlc_mutex_t mutex_lock; // pthread locks for sync of
57 vlc_cond_t cond_sync; // aout_Play and callback
58 mtime_t clock_diff; // diff between system clock & audio
61 /*****************************************************************************
63 *****************************************************************************/
64 static int aout_Open ( aout_thread_t *p_aout );
65 static int aout_SetFormat ( aout_thread_t *p_aout );
66 static int aout_GetBufInfo ( aout_thread_t *p_aout, int i_buffer_info );
67 static void aout_Play ( aout_thread_t *p_aout,
68 byte_t *buffer, int i_size );
69 static void aout_Close ( aout_thread_t *p_aout );
71 static int CABeginFormat ( aout_thread_t *p_aout );
72 static int CAEndFormat ( aout_thread_t *p_aout );
74 static OSStatus CAIOCallback ( AudioDeviceID inDevice,
75 const AudioTimeStamp *inNow,
76 const void *inInputData,
77 const AudioTimeStamp *inInputTime,
78 AudioBufferList *outOutputData,
79 const AudioTimeStamp *inOutputTime,
80 void *threadGlobals );
82 /*****************************************************************************
83 * Functions exported as capabilities. They are declared as static so that
84 * we don't pollute the namespace too much.
85 *****************************************************************************/
86 void _M( aout_getfunctions )( function_list_t * p_function_list )
88 p_function_list->functions.aout.pf_open = aout_Open;
89 p_function_list->functions.aout.pf_setformat = aout_SetFormat;
90 p_function_list->functions.aout.pf_getbufinfo = aout_GetBufInfo;
91 p_function_list->functions.aout.pf_play = aout_Play;
92 p_function_list->functions.aout.pf_close = aout_Close;
95 /*****************************************************************************
96 * aout_Open: opens a CoreAudio HAL device
97 *****************************************************************************/
98 static int aout_Open( aout_thread_t *p_aout )
101 UInt32 ui_param_size;
103 /* allocate instance */
104 p_aout->p_sys = malloc( sizeof( aout_sys_t ) );
105 if( p_aout->p_sys == NULL )
107 msg_Err( p_aout, "out of memory" );
111 /* initialize members */
112 memset( p_aout->p_sys, 0, sizeof( aout_sys_t ) );
114 /* get the default output device */
115 ui_param_size = sizeof( p_aout->p_sys->device );
116 err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice,
118 (void *)&p_aout->p_sys->device );
122 msg_Err( p_aout, "failed to get the device: %d", err );
126 /* get the buffer size that the device uses for IO */
127 ui_param_size = sizeof( p_aout->p_sys->ui_buffer_size );
128 err = AudioDeviceGetProperty( p_aout->p_sys->device, 0, false,
129 kAudioDevicePropertyBufferSize,
131 &p_aout->p_sys->ui_buffer_size );
135 msg_Err( p_aout, "failed to get device buffer size: %d", err );
139 /* get a description of the data format used by the device */
140 ui_param_size = sizeof( p_aout->p_sys->s_dst_stream_format );
141 err = AudioDeviceGetProperty( p_aout->p_sys->device, 0, false,
142 kAudioDevicePropertyStreamFormat,
144 &p_aout->p_sys->s_dst_stream_format );
148 msg_Err( p_aout, "failed to get dst stream format: %d", err );
152 if( p_aout->p_sys->s_dst_stream_format.mFormatID != kAudioFormatLinearPCM )
154 msg_Err( p_aout, "kAudioFormatLinearPCM required" );
158 /* initialize mutex and cond */
159 vlc_mutex_init( p_aout, &p_aout->p_sys->mutex_lock );
160 vlc_cond_init( p_aout, &p_aout->p_sys->cond_sync );
162 /* initialize source stream format */
163 memcpy( &p_aout->p_sys->s_src_stream_format,
164 &p_aout->p_sys->s_dst_stream_format,
165 sizeof( p_aout->p_sys->s_src_stream_format ) );
167 if( CABeginFormat( p_aout ) )
169 msg_Err( p_aout, "CABeginFormat failed" );
176 /*****************************************************************************
177 * aout_SetFormat: pretends to set the dsp output format
178 *****************************************************************************/
179 static int aout_SetFormat( aout_thread_t *p_aout )
181 if( CAEndFormat( p_aout ) )
183 msg_Err( p_aout, "CAEndFormat failed" );
187 switch( p_aout->i_format )
191 "Signed 8 not supported yet, please report stream" );
196 "Unsigned 8 not supported yet, please report stream" );
199 case AOUT_FMT_S16_LE:
200 p_aout->p_sys->s_src_stream_format.mFormatFlags &=
201 ~kLinearPCMFormatFlagIsBigEndian;
202 p_aout->p_sys->s_src_stream_format.mFormatFlags |=
203 kLinearPCMFormatFlagIsSignedInteger;
206 case AOUT_FMT_S16_BE:
207 p_aout->p_sys->s_src_stream_format.mFormatFlags |=
208 kLinearPCMFormatFlagIsBigEndian;
209 p_aout->p_sys->s_src_stream_format.mFormatFlags |=
210 kLinearPCMFormatFlagIsSignedInteger;
213 case AOUT_FMT_U16_LE:
214 p_aout->p_sys->s_src_stream_format.mFormatFlags &=
215 ~kLinearPCMFormatFlagIsBigEndian;
216 p_aout->p_sys->s_src_stream_format.mFormatFlags &=
217 ~kLinearPCMFormatFlagIsSignedInteger;
220 case AOUT_FMT_U16_BE:
221 p_aout->p_sys->s_src_stream_format.mFormatFlags |=
222 kLinearPCMFormatFlagIsBigEndian;
223 p_aout->p_sys->s_src_stream_format.mFormatFlags &=
224 ~kLinearPCMFormatFlagIsSignedInteger;
228 msg_Err( p_aout, "audio format (0x%08x) not supported now,"
229 "please report stream", p_aout->i_format );
233 /* source format is not float */
234 p_aout->p_sys->s_src_stream_format.mFormatFlags &=
235 ~kLinearPCMFormatFlagIsFloat;
237 /* if destination format is float, take size diff into account */
238 if( p_aout->p_sys->s_dst_stream_format.mFormatFlags &
239 kLinearPCMFormatFlagIsFloat )
241 p_aout->p_sys->s_src_stream_format.mBytesPerPacket =
242 p_aout->p_sys->s_dst_stream_format.mBytesPerPacket / 2;
243 p_aout->p_sys->s_src_stream_format.mBytesPerFrame =
244 p_aout->p_sys->s_src_stream_format.mBytesPerFrame / 2;
245 p_aout->p_sys->s_src_stream_format.mBitsPerChannel =
246 p_aout->p_sys->s_src_stream_format.mBitsPerChannel / 2;
249 /* set sample rate and channels per frame */
250 p_aout->p_sys->s_src_stream_format.mSampleRate = p_aout->i_rate;
251 p_aout->p_sys->s_src_stream_format.mChannelsPerFrame = p_aout->i_channels;
253 if( CABeginFormat( p_aout ) )
255 msg_Err( p_aout, "CABeginFormat failed" );
262 /*****************************************************************************
263 * aout_GetBufInfo: returns available bytes in buffer
264 *****************************************************************************/
265 static int aout_GetBufInfo( aout_thread_t *p_aout, int i_buffer_limit )
267 return( 0 ); /* send data as soon as possible */
270 /*****************************************************************************
271 * CAIOCallback : callback for audio output
272 *****************************************************************************/
273 static OSStatus CAIOCallback( AudioDeviceID inDevice,
274 const AudioTimeStamp *inNow,
275 const void *inInputData,
276 const AudioTimeStamp *inInputTime,
277 AudioBufferList *outOutputData,
278 const AudioTimeStamp *inOutputTime,
279 void *threadGlobals )
281 aout_thread_t *p_aout = (aout_thread_t *)threadGlobals;
282 aout_sys_t *p_sys = p_aout->p_sys;
284 AudioTimeStamp host_time;
286 host_time.mFlags = kAudioTimeStampHostTimeValid;
287 AudioDeviceTranslateTime( inDevice, inOutputTime, &host_time );
288 //intf_Msg( "%lld", AudioConvertHostTimeToNanos(host_time.mHostTime) / 1000 + p_aout->p_sys->clock_diff - p_aout->date );
289 p_aout->date = p_aout->p_sys->clock_diff + AudioConvertHostTimeToNanos(host_time.mHostTime) / 1000;
291 /* move data into output data buffer */
292 if( p_sys->b_buffer_data )
294 BlockMoveData( p_sys->p_buffer,
295 outOutputData->mBuffers[ 0 ].mData,
296 p_sys->ui_buffer_size );
300 memset(outOutputData->mBuffers[ 0 ].mData, 0, p_sys->ui_buffer_size);
301 //X msg_Warn( p_aout, "audio output is starving, expect glitches" );
304 /* see aout_Play below */
305 vlc_mutex_lock( &p_sys->mutex_lock );
306 p_sys->b_buffer_data = 0;
307 vlc_cond_signal( &p_sys->cond_sync );
308 vlc_mutex_unlock( &p_sys->mutex_lock );
313 /*****************************************************************************
314 * aout_Play: play a sound
315 *****************************************************************************/
316 static void aout_Play( aout_thread_t *p_aout, byte_t *buffer, int i_size )
319 UInt32 ui_buffer_size = p_aout->p_sys->ui_buffer_size;
322 * wait for a callback to occur (to flush the buffer), so aout_Play
323 * can't be called twice, losing the data we just wrote.
325 vlc_mutex_lock( &p_aout->p_sys->mutex_lock );
326 if ( p_aout->p_sys->b_buffer_data )
328 vlc_cond_wait( &p_aout->p_sys->cond_sync, &p_aout->p_sys->mutex_lock );
330 vlc_mutex_unlock( &p_aout->p_sys->mutex_lock );
332 err = AudioConverterConvertBuffer( p_aout->p_sys->s_converter,
335 p_aout->p_sys->p_buffer );
339 msg_Err( p_aout, "ConvertBuffer failed: %d", err );
343 p_aout->p_sys->b_buffer_data = 1;
347 /*****************************************************************************
348 * aout_Close: closes the CoreAudio HAL device
349 *****************************************************************************/
350 static void aout_Close( aout_thread_t *p_aout )
352 if( CAEndFormat( p_aout ) )
354 msg_Err( p_aout, "CAEndFormat failed" );
357 /* destroy lock and cond */
358 vlc_mutex_destroy( &p_aout->p_sys->mutex_lock );
359 vlc_cond_destroy( &p_aout->p_sys->cond_sync );
361 free( p_aout->p_sys );
364 /*****************************************************************************
365 * CABeginFormat: creates an AudioConverter
366 *****************************************************************************/
367 static int CABeginFormat( aout_thread_t *p_aout )
370 UInt32 ui_param_size;
372 if( p_aout->p_sys->b_format )
374 msg_Err( p_aout, "CABeginFormat (b_format)" );
378 p_aout->p_sys->ui_buffer_size = 2 * 2 * sizeof(s16) *
379 ((s64)p_aout->i_rate * AOUT_BUFFER_DURATION) / 1000000;
381 /* set the buffer size that the device uses for IO */
382 ui_param_size = sizeof( p_aout->p_sys->ui_buffer_size );
383 err = AudioDeviceSetProperty( p_aout->p_sys->device, 0, 0, false,
384 kAudioDevicePropertyBufferSize,
386 &p_aout->p_sys->ui_buffer_size );
387 //p_aout->i_latency = p_aout->p_sys->ui_buffer_size / 2;
391 msg_Err( p_aout, "AudioDeviceSetProperty failed: %d", err );
395 /* allocate audio buffer */
396 p_aout->p_sys->p_buffer = NewPtrClear( p_aout->p_sys->ui_buffer_size );
398 if( p_aout->p_sys->p_buffer == nil )
400 msg_Err( p_aout, "failed to allocate audio buffer" );
404 /* create a new AudioConverter */
405 err = AudioConverterNew( &p_aout->p_sys->s_src_stream_format,
406 &p_aout->p_sys->s_dst_stream_format,
407 &p_aout->p_sys->s_converter );
411 msg_Err( p_aout, "AudioConverterNew failed: %d", err );
412 DisposePtr( p_aout->p_sys->p_buffer );
417 err = AudioDeviceAddIOProc( p_aout->p_sys->device,
418 (AudioDeviceIOProc)CAIOCallback,
423 msg_Err( p_aout, "AudioDeviceAddIOProc failed: %d", err );
424 AudioConverterDispose( p_aout->p_sys->s_converter );
425 DisposePtr( p_aout->p_sys->p_buffer );
429 /* open the output */
430 err = AudioDeviceStart( p_aout->p_sys->device,
431 (AudioDeviceIOProc)CAIOCallback );
435 msg_Err( p_aout, "AudioDeviceStart failed: %d", err );
436 AudioConverterDispose( p_aout->p_sys->s_converter );
437 DisposePtr( p_aout->p_sys->p_buffer );
441 /* Let's pray for the following operation to be atomic... */
442 p_aout->p_sys->clock_diff = mdate()
443 - AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()) / 1000
444 + (mtime_t)p_aout->p_sys->ui_buffer_size / 4 * 1000000 / (mtime_t)p_aout->i_rate
445 + p_aout->p_vlc->i_desync;
447 p_aout->p_sys->b_format = 1;
452 /*****************************************************************************
453 * CAEndFormat: destroys the AudioConverter
454 *****************************************************************************/
455 static int CAEndFormat( aout_thread_t *p_aout )
459 if( !p_aout->p_sys->b_format )
461 msg_Err( p_aout, "CAEndFormat (!b_format)" );
465 /* stop playing sound through the device */
466 err = AudioDeviceStop( p_aout->p_sys->device,
467 (AudioDeviceIOProc)CAIOCallback );
471 msg_Err( p_aout, "AudioDeviceStop failed: %d", err );
475 /* remove the callback */
476 err = AudioDeviceRemoveIOProc( p_aout->p_sys->device,
477 (AudioDeviceIOProc)CAIOCallback );
481 msg_Err( p_aout, "AudioDeviceRemoveIOProc failed: %d", err );
485 /* destroy the AudioConverter */
486 err = AudioConverterDispose( p_aout->p_sys->s_converter );
490 msg_Err( p_aout, "AudioConverterDispose failed: %d", err );
494 /* release audio buffer */
495 DisposePtr( p_aout->p_sys->p_buffer );
497 p_aout->p_sys->b_format = 0;