1 /*****************************************************************************
2 * aout_macosx.m: CoreAudio output plugin
3 *****************************************************************************
4 * Copyright (C) 2001 VideoLAN
5 * $Id: aout_macosx.m,v 1.9 2002/07/31 20:56:52 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; // Play and callback
58 mtime_t clock_diff; // diff between system clock & audio
61 /*****************************************************************************
63 *****************************************************************************/
64 static int SetFormat ( aout_thread_t * );
65 static int GetBufInfo ( aout_thread_t *, int );
66 static void Play ( aout_thread_t *, byte_t *, int );
68 static int CABeginFormat ( aout_thread_t * );
69 static int CAEndFormat ( aout_thread_t * );
71 static OSStatus CAIOCallback ( AudioDeviceID inDevice,
72 const AudioTimeStamp *inNow,
73 const void *inInputData,
74 const AudioTimeStamp *inInputTime,
75 AudioBufferList *outOutputData,
76 const AudioTimeStamp *inOutputTime,
77 void *threadGlobals );
79 /*****************************************************************************
80 * OpenAudio: opens a CoreAudio HAL device
81 *****************************************************************************/
82 int E_(OpenAudio) ( vlc_object_t *p_this )
84 aout_thread_t * p_aout = (aout_thread_t *)p_this;
88 /* allocate instance */
89 p_aout->p_sys = malloc( sizeof( aout_sys_t ) );
90 if( p_aout->p_sys == NULL )
92 msg_Err( p_aout, "out of memory" );
96 /* initialize members */
97 memset( p_aout->p_sys, 0, sizeof( aout_sys_t ) );
99 /* get the default output device */
100 ui_param_size = sizeof( p_aout->p_sys->device );
101 err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice,
103 (void *)&p_aout->p_sys->device );
107 msg_Err( p_aout, "failed to get the device: %d", err );
111 /* get the buffer size that the device uses for IO */
112 ui_param_size = sizeof( p_aout->p_sys->ui_buffer_size );
113 err = AudioDeviceGetProperty( p_aout->p_sys->device, 0, false,
114 kAudioDevicePropertyBufferSize,
116 &p_aout->p_sys->ui_buffer_size );
120 msg_Err( p_aout, "failed to get device buffer size: %d", err );
124 /* get a description of the data format used by the device */
125 ui_param_size = sizeof( p_aout->p_sys->s_dst_stream_format );
126 err = AudioDeviceGetProperty( p_aout->p_sys->device, 0, false,
127 kAudioDevicePropertyStreamFormat,
129 &p_aout->p_sys->s_dst_stream_format );
133 msg_Err( p_aout, "failed to get dst stream format: %d", err );
137 if( p_aout->p_sys->s_dst_stream_format.mFormatID != kAudioFormatLinearPCM )
139 msg_Err( p_aout, "kAudioFormatLinearPCM required" );
143 /* initialize mutex and cond */
144 vlc_mutex_init( p_aout, &p_aout->p_sys->mutex_lock );
145 vlc_cond_init( p_aout, &p_aout->p_sys->cond_sync );
147 /* initialize source stream format */
148 memcpy( &p_aout->p_sys->s_src_stream_format,
149 &p_aout->p_sys->s_dst_stream_format,
150 sizeof( p_aout->p_sys->s_src_stream_format ) );
152 if( CABeginFormat( p_aout ) )
154 msg_Err( p_aout, "CABeginFormat failed" );
158 p_aout->pf_setformat = SetFormat;
159 p_aout->pf_getbufinfo = GetBufInfo;
160 p_aout->pf_play = Play;
165 /*****************************************************************************
166 * SetFormat: pretends to set the dsp output format
167 *****************************************************************************/
168 static int SetFormat( aout_thread_t *p_aout )
170 if( CAEndFormat( p_aout ) )
172 msg_Err( p_aout, "CAEndFormat failed" );
176 switch( p_aout->i_format )
180 "Signed 8 not supported yet, please report stream" );
185 "Unsigned 8 not supported yet, please report stream" );
188 case AOUT_FMT_S16_LE:
189 p_aout->p_sys->s_src_stream_format.mFormatFlags &=
190 ~kLinearPCMFormatFlagIsBigEndian;
191 p_aout->p_sys->s_src_stream_format.mFormatFlags |=
192 kLinearPCMFormatFlagIsSignedInteger;
195 case AOUT_FMT_S16_BE:
196 p_aout->p_sys->s_src_stream_format.mFormatFlags |=
197 kLinearPCMFormatFlagIsBigEndian;
198 p_aout->p_sys->s_src_stream_format.mFormatFlags |=
199 kLinearPCMFormatFlagIsSignedInteger;
202 case AOUT_FMT_U16_LE:
203 p_aout->p_sys->s_src_stream_format.mFormatFlags &=
204 ~kLinearPCMFormatFlagIsBigEndian;
205 p_aout->p_sys->s_src_stream_format.mFormatFlags &=
206 ~kLinearPCMFormatFlagIsSignedInteger;
209 case AOUT_FMT_U16_BE:
210 p_aout->p_sys->s_src_stream_format.mFormatFlags |=
211 kLinearPCMFormatFlagIsBigEndian;
212 p_aout->p_sys->s_src_stream_format.mFormatFlags &=
213 ~kLinearPCMFormatFlagIsSignedInteger;
217 msg_Err( p_aout, "audio format (0x%08x) not supported now,"
218 "please report stream", p_aout->i_format );
222 /* source format is not float */
223 p_aout->p_sys->s_src_stream_format.mFormatFlags &=
224 ~kLinearPCMFormatFlagIsFloat;
226 /* if destination format is float, take size diff into account */
227 if( p_aout->p_sys->s_dst_stream_format.mFormatFlags &
228 kLinearPCMFormatFlagIsFloat )
230 p_aout->p_sys->s_src_stream_format.mBytesPerPacket =
231 p_aout->p_sys->s_dst_stream_format.mBytesPerPacket / 2;
232 p_aout->p_sys->s_src_stream_format.mBytesPerFrame =
233 p_aout->p_sys->s_src_stream_format.mBytesPerFrame / 2;
234 p_aout->p_sys->s_src_stream_format.mBitsPerChannel =
235 p_aout->p_sys->s_src_stream_format.mBitsPerChannel / 2;
238 /* set sample rate and channels per frame */
239 p_aout->p_sys->s_src_stream_format.mSampleRate = p_aout->i_rate;
240 p_aout->p_sys->s_src_stream_format.mChannelsPerFrame = p_aout->i_channels;
242 if( CABeginFormat( p_aout ) )
244 msg_Err( p_aout, "CABeginFormat failed" );
251 /*****************************************************************************
252 * GetBufInfo: returns available bytes in buffer
253 *****************************************************************************/
254 static int GetBufInfo( aout_thread_t *p_aout, int i_buffer_limit )
256 return( 0 ); /* send data as soon as possible */
259 /*****************************************************************************
260 * CAIOCallback : callback for audio output
261 *****************************************************************************/
262 static OSStatus CAIOCallback( AudioDeviceID inDevice,
263 const AudioTimeStamp *inNow,
264 const void *inInputData,
265 const AudioTimeStamp *inInputTime,
266 AudioBufferList *outOutputData,
267 const AudioTimeStamp *inOutputTime,
268 void *threadGlobals )
270 aout_thread_t *p_aout = (aout_thread_t *)threadGlobals;
271 aout_sys_t *p_sys = p_aout->p_sys;
273 AudioTimeStamp host_time;
275 host_time.mFlags = kAudioTimeStampHostTimeValid;
276 AudioDeviceTranslateTime( inDevice, inOutputTime, &host_time );
277 //intf_Msg( "%lld", AudioConvertHostTimeToNanos(host_time.mHostTime) / 1000 + p_aout->p_sys->clock_diff - p_aout->date );
278 p_aout->date = p_aout->p_sys->clock_diff + AudioConvertHostTimeToNanos(host_time.mHostTime) / 1000;
280 /* move data into output data buffer */
281 if( p_sys->b_buffer_data )
283 BlockMoveData( p_sys->p_buffer,
284 outOutputData->mBuffers[ 0 ].mData,
285 p_sys->ui_buffer_size );
289 memset(outOutputData->mBuffers[ 0 ].mData, 0, p_sys->ui_buffer_size);
290 //X msg_Warn( p_aout, "audio output is starving, expect glitches" );
294 vlc_mutex_lock( &p_sys->mutex_lock );
295 p_sys->b_buffer_data = 0;
296 vlc_cond_signal( &p_sys->cond_sync );
297 vlc_mutex_unlock( &p_sys->mutex_lock );
302 /*****************************************************************************
304 *****************************************************************************/
305 static void Play( aout_thread_t *p_aout, byte_t *buffer, int i_size )
308 UInt32 ui_buffer_size = p_aout->p_sys->ui_buffer_size;
311 * wait for a callback to occur (to flush the buffer), so Play
312 * can't be called twice, losing the data we just wrote.
314 vlc_mutex_lock( &p_aout->p_sys->mutex_lock );
315 if ( p_aout->p_sys->b_buffer_data )
317 vlc_cond_wait( &p_aout->p_sys->cond_sync, &p_aout->p_sys->mutex_lock );
319 vlc_mutex_unlock( &p_aout->p_sys->mutex_lock );
321 err = AudioConverterConvertBuffer( p_aout->p_sys->s_converter,
324 p_aout->p_sys->p_buffer );
328 msg_Err( p_aout, "ConvertBuffer failed: %d", err );
332 p_aout->p_sys->b_buffer_data = 1;
336 /*****************************************************************************
337 * CloseAudio: closes the CoreAudio HAL device
338 *****************************************************************************/
339 void E_(CloseAudio) ( vlc_object_t *p_this )
341 aout_thread_t * p_aout = (aout_thread_t *)p_this;
343 if( CAEndFormat( p_aout ) )
345 msg_Err( p_aout, "CAEndFormat failed" );
348 /* destroy lock and cond */
349 vlc_mutex_destroy( &p_aout->p_sys->mutex_lock );
350 vlc_cond_destroy( &p_aout->p_sys->cond_sync );
352 free( p_aout->p_sys );
355 /*****************************************************************************
356 * CABeginFormat: creates an AudioConverter
357 *****************************************************************************/
358 static int CABeginFormat( aout_thread_t *p_aout )
361 UInt32 ui_param_size;
363 if( p_aout->p_sys->b_format )
365 msg_Err( p_aout, "CABeginFormat (b_format)" );
369 p_aout->p_sys->ui_buffer_size = 2 * 2 * sizeof(s16) *
370 ((s64)p_aout->i_rate * AOUT_BUFFER_DURATION) / 1000000;
372 /* set the buffer size that the device uses for IO */
373 ui_param_size = sizeof( p_aout->p_sys->ui_buffer_size );
374 err = AudioDeviceSetProperty( p_aout->p_sys->device, 0, 0, false,
375 kAudioDevicePropertyBufferSize,
377 &p_aout->p_sys->ui_buffer_size );
378 //p_aout->i_latency = p_aout->p_sys->ui_buffer_size / 2;
382 msg_Err( p_aout, "AudioDeviceSetProperty failed: %d", err );
386 /* allocate audio buffer */
387 p_aout->p_sys->p_buffer = NewPtrClear( p_aout->p_sys->ui_buffer_size );
389 if( p_aout->p_sys->p_buffer == nil )
391 msg_Err( p_aout, "failed to allocate audio buffer" );
395 /* create a new AudioConverter */
396 err = AudioConverterNew( &p_aout->p_sys->s_src_stream_format,
397 &p_aout->p_sys->s_dst_stream_format,
398 &p_aout->p_sys->s_converter );
402 msg_Err( p_aout, "AudioConverterNew failed: %d", err );
403 DisposePtr( p_aout->p_sys->p_buffer );
408 err = AudioDeviceAddIOProc( p_aout->p_sys->device,
409 (AudioDeviceIOProc)CAIOCallback,
414 msg_Err( p_aout, "AudioDeviceAddIOProc failed: %d", err );
415 AudioConverterDispose( p_aout->p_sys->s_converter );
416 DisposePtr( p_aout->p_sys->p_buffer );
420 /* open the output */
421 err = AudioDeviceStart( p_aout->p_sys->device,
422 (AudioDeviceIOProc)CAIOCallback );
426 msg_Err( p_aout, "AudioDeviceStart failed: %d", err );
427 AudioConverterDispose( p_aout->p_sys->s_converter );
428 DisposePtr( p_aout->p_sys->p_buffer );
432 /* Let's pray for the following operation to be atomic... */
433 p_aout->p_sys->clock_diff = mdate()
434 - AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()) / 1000
435 + (mtime_t)p_aout->p_sys->ui_buffer_size / 4 * 1000000 / (mtime_t)p_aout->i_rate
436 + p_aout->p_vlc->i_desync;
438 p_aout->p_sys->b_format = 1;
443 /*****************************************************************************
444 * CAEndFormat: destroys the AudioConverter
445 *****************************************************************************/
446 static int CAEndFormat( aout_thread_t *p_aout )
450 if( !p_aout->p_sys->b_format )
452 msg_Err( p_aout, "CAEndFormat (!b_format)" );
456 /* stop playing sound through the device */
457 err = AudioDeviceStop( p_aout->p_sys->device,
458 (AudioDeviceIOProc)CAIOCallback );
462 msg_Err( p_aout, "AudioDeviceStop failed: %d", err );
466 /* remove the callback */
467 err = AudioDeviceRemoveIOProc( p_aout->p_sys->device,
468 (AudioDeviceIOProc)CAIOCallback );
472 msg_Err( p_aout, "AudioDeviceRemoveIOProc failed: %d", err );
476 /* destroy the AudioConverter */
477 err = AudioConverterDispose( p_aout->p_sys->s_converter );
481 msg_Err( p_aout, "AudioConverterDispose failed: %d", err );
485 /* release audio buffer */
486 DisposePtr( p_aout->p_sys->p_buffer );
488 p_aout->p_sys->b_format = 0;