1 /*****************************************************************************
2 * aout_macosx.c : CoreAudio output plugin
3 *****************************************************************************
4 * Copyright (C) 2001 VideoLAN
5 * $Id: aout_macosx.c,v 1.15 2002/03/19 03:33:52 jlj 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 <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 *****************************************************************************/
44 typedef struct aout_sys_s
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_mutex_t mutex_lock; // pthread locks for sync of
56 vlc_cond_t cond_sync; // aout_Play and callback
59 /*****************************************************************************
61 *****************************************************************************/
62 static int aout_Open ( aout_thread_t *p_aout );
63 static int aout_SetFormat ( aout_thread_t *p_aout );
64 static int aout_GetBufInfo ( aout_thread_t *p_aout, int i_buffer_info );
65 static void aout_Play ( aout_thread_t *p_aout,
66 byte_t *buffer, int i_size );
67 static void aout_Close ( aout_thread_t *p_aout );
69 static int CABeginFormat ( aout_thread_t *p_aout );
70 static int CAEndFormat ( aout_thread_t *p_aout );
72 static OSStatus CAIOCallback ( AudioDeviceID inDevice,
73 const AudioTimeStamp *inNow,
74 const void *inInputData,
75 const AudioTimeStamp *inInputTime,
76 AudioBufferList *outOutputData,
77 const AudioTimeStamp *inOutputTime,
78 void *threadGlobals );
80 /*****************************************************************************
81 * Functions exported as capabilities. They are declared as static so that
82 * we don't pollute the namespace too much.
83 *****************************************************************************/
84 void _M( aout_getfunctions )( function_list_t * p_function_list )
86 p_function_list->functions.aout.pf_open = aout_Open;
87 p_function_list->functions.aout.pf_setformat = aout_SetFormat;
88 p_function_list->functions.aout.pf_getbufinfo = aout_GetBufInfo;
89 p_function_list->functions.aout.pf_play = aout_Play;
90 p_function_list->functions.aout.pf_close = aout_Close;
93 /*****************************************************************************
94 * aout_Open: opens a CoreAudio HAL device
95 *****************************************************************************/
96 static int aout_Open( aout_thread_t *p_aout )
101 /* allocate instance */
102 p_aout->p_sys = malloc( sizeof( aout_sys_t ) );
103 if( p_aout->p_sys == NULL )
105 intf_ErrMsg( "aout error: %s", strerror(ENOMEM) );
109 /* initialize members */
110 memset( p_aout->p_sys, 0, sizeof( aout_sys_t ) );
112 /* get the default output device */
113 ui_param_size = sizeof( p_aout->p_sys->device );
114 err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice,
116 (void *)&p_aout->p_sys->device );
120 intf_ErrMsg( "aout error: failed to get the device: %d", err );
124 /* get the buffer size that the device uses for IO */
125 ui_param_size = sizeof( p_aout->p_sys->ui_buffer_size );
126 err = AudioDeviceGetProperty( p_aout->p_sys->device, 0, false,
127 kAudioDevicePropertyBufferSize,
129 &p_aout->p_sys->ui_buffer_size );
133 intf_ErrMsg( "aout error: failed to get device buffer size: %d", err );
137 /* get a description of the data format used by the device */
138 ui_param_size = sizeof( p_aout->p_sys->s_dst_stream_format );
139 err = AudioDeviceGetProperty( p_aout->p_sys->device, 0, false,
140 kAudioDevicePropertyStreamFormat,
142 &p_aout->p_sys->s_dst_stream_format );
146 intf_ErrMsg( "aout error: failed to get dst stream format: %d", err );
150 if( p_aout->p_sys->s_dst_stream_format.mFormatID != kAudioFormatLinearPCM )
152 intf_ErrMsg( "aout error: kAudioFormatLinearPCM required" );
156 /* initialize mutex and cond */
157 vlc_mutex_init( &p_aout->p_sys->mutex_lock );
158 vlc_cond_init( &p_aout->p_sys->cond_sync );
160 /* initialize source stream format */
161 memcpy( &p_aout->p_sys->s_src_stream_format,
162 &p_aout->p_sys->s_dst_stream_format,
163 sizeof( p_aout->p_sys->s_src_stream_format ) );
165 if( CABeginFormat( p_aout ) )
167 intf_ErrMsg( "aout error: CABeginFormat failed" );
174 /*****************************************************************************
175 * aout_SetFormat: pretends to set the dsp output format
176 *****************************************************************************/
177 static int aout_SetFormat( aout_thread_t *p_aout )
179 if( CAEndFormat( p_aout ) )
181 intf_ErrMsg( "aout error: CAEndFormat failed" );
185 switch( p_aout->i_format )
188 intf_ErrMsg( "Audio format (Signed 8) not supported now,"
189 "please report stream" );
193 intf_ErrMsg( "Audio format (Unsigned 8) not supported now,"
194 "please report stream" );
197 case AOUT_FMT_S16_LE:
198 p_aout->p_sys->s_src_stream_format.mFormatFlags &=
199 ~kLinearPCMFormatFlagIsBigEndian;
200 p_aout->p_sys->s_src_stream_format.mFormatFlags |=
201 kLinearPCMFormatFlagIsSignedInteger;
204 case AOUT_FMT_S16_BE:
205 p_aout->p_sys->s_src_stream_format.mFormatFlags |=
206 kLinearPCMFormatFlagIsBigEndian;
207 p_aout->p_sys->s_src_stream_format.mFormatFlags |=
208 kLinearPCMFormatFlagIsSignedInteger;
211 case AOUT_FMT_U16_LE:
212 p_aout->p_sys->s_src_stream_format.mFormatFlags &=
213 ~kLinearPCMFormatFlagIsBigEndian;
214 p_aout->p_sys->s_src_stream_format.mFormatFlags &=
215 ~kLinearPCMFormatFlagIsSignedInteger;
218 case AOUT_FMT_U16_BE:
219 p_aout->p_sys->s_src_stream_format.mFormatFlags |=
220 kLinearPCMFormatFlagIsBigEndian;
221 p_aout->p_sys->s_src_stream_format.mFormatFlags &=
222 ~kLinearPCMFormatFlagIsSignedInteger;
226 intf_ErrMsg( "Audio format (0x%08x) not supported now,"
227 "please report stream", p_aout->i_format );
231 /* source format is not float */
232 p_aout->p_sys->s_src_stream_format.mFormatFlags &=
233 ~kLinearPCMFormatFlagIsFloat;
235 /* if destination format is float, take size diff into account */
236 if( p_aout->p_sys->s_dst_stream_format.mFormatFlags &
237 kLinearPCMFormatFlagIsFloat )
239 p_aout->p_sys->s_src_stream_format.mBytesPerPacket =
240 p_aout->p_sys->s_dst_stream_format.mBytesPerPacket / 2;
241 p_aout->p_sys->s_src_stream_format.mBytesPerFrame =
242 p_aout->p_sys->s_src_stream_format.mBytesPerFrame / 2;
243 p_aout->p_sys->s_src_stream_format.mBitsPerChannel =
244 p_aout->p_sys->s_src_stream_format.mBitsPerChannel / 2;
247 /* set sample rate and channels per frame */
248 p_aout->p_sys->s_src_stream_format.mSampleRate = p_aout->i_rate;
249 p_aout->p_sys->s_src_stream_format.mChannelsPerFrame = p_aout->i_channels;
251 if( CABeginFormat( p_aout ) )
253 intf_ErrMsg( "aout error: CABeginFormat failed" );
260 /*****************************************************************************
261 * aout_GetBufInfo: returns available bytes in buffer
262 *****************************************************************************/
263 static int aout_GetBufInfo( aout_thread_t *p_aout, int i_buffer_limit )
265 return( 0 ); /* send data as soon as possible */
268 /*****************************************************************************
269 * CAIOCallback : callback for audio output
270 *****************************************************************************/
271 static OSStatus CAIOCallback( AudioDeviceID inDevice,
272 const AudioTimeStamp *inNow,
273 const void *inInputData,
274 const AudioTimeStamp *inInputTime,
275 AudioBufferList *outOutputData,
276 const AudioTimeStamp *inOutputTime,
277 void *threadGlobals )
279 aout_sys_t *p_sys = (aout_sys_t *)threadGlobals;
281 /* see aout_Play below */
282 vlc_mutex_lock( &p_sys->mutex_lock );
283 vlc_cond_signal( &p_sys->cond_sync );
285 /* move data into output data buffer */
286 BlockMoveData( p_sys->p_buffer,
287 outOutputData->mBuffers[ 0 ].mData,
288 p_sys->ui_buffer_size );
290 vlc_mutex_unlock( &p_sys->mutex_lock );
295 /*****************************************************************************
296 * aout_Play: plays a sound
297 *****************************************************************************/
298 static void aout_Play( aout_thread_t *p_aout, byte_t *buffer, int i_size )
301 UInt32 ui_buffer_size = p_aout->p_sys->ui_buffer_size;
303 err = AudioConverterConvertBuffer( p_aout->p_sys->s_converter,
306 p_aout->p_sys->p_buffer );
310 intf_ErrMsg( "aout error: ConvertBuffer failed: %d", err );
314 * wait for a callback to occur (to flush the buffer), so aout_Play
315 * can't be called twice, losing the data we just wrote.
317 vlc_mutex_lock( &p_aout->p_sys->mutex_lock );
318 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 );
322 /*****************************************************************************
323 * aout_Close: closes the CoreAudio HAL device
324 *****************************************************************************/
325 static void aout_Close( aout_thread_t *p_aout )
327 if( CAEndFormat( p_aout ) )
329 intf_ErrMsg( "aout error: CAEndFormat failed" );
332 /* destroy lock and cond */
333 vlc_mutex_destroy( &p_aout->p_sys->mutex_lock );
334 vlc_cond_destroy( &p_aout->p_sys->cond_sync );
336 free( p_aout->p_sys );
339 /*****************************************************************************
340 * CABeginFormat: creates an AudioConverter
341 *****************************************************************************/
342 static int CABeginFormat( aout_thread_t *p_aout )
345 UInt32 ui_param_size;
347 if( p_aout->p_sys->b_format )
349 intf_ErrMsg( "aout error: CABeginFormat (b_format)" );
353 p_aout->p_sys->ui_buffer_size = 2 * 2 * sizeof(s16) *
354 ((s64)p_aout->i_rate * AOUT_BUFFER_DURATION) / 1000000;
356 /* set the buffer size that the device uses for IO */
357 ui_param_size = sizeof( p_aout->p_sys->ui_buffer_size );
358 err = AudioDeviceSetProperty( p_aout->p_sys->device, 0, 0, false,
359 kAudioDevicePropertyBufferSize,
361 &p_aout->p_sys->ui_buffer_size );
365 intf_ErrMsg( "aout error: AudioDeviceSetProperty failed: %d", err );
369 /* allocate audio buffer */
370 p_aout->p_sys->p_buffer = NewPtrClear( p_aout->p_sys->ui_buffer_size );
372 if( p_aout->p_sys->p_buffer == nil )
374 intf_ErrMsg( "aout error: failed to allocate audio buffer" );
378 /* create a new AudioConverter */
379 err = AudioConverterNew( &p_aout->p_sys->s_src_stream_format,
380 &p_aout->p_sys->s_dst_stream_format,
381 &p_aout->p_sys->s_converter );
385 intf_ErrMsg( "aout error: AudioConverterNew failed: %d", err );
386 DisposePtr( p_aout->p_sys->p_buffer );
391 err = AudioDeviceAddIOProc( p_aout->p_sys->device,
392 (AudioDeviceIOProc)CAIOCallback,
393 (void *)p_aout->p_sys );
397 intf_ErrMsg( "aout error: AudioDeviceAddIOProc failed: %d", err );
398 AudioConverterDispose( p_aout->p_sys->s_converter );
399 DisposePtr( p_aout->p_sys->p_buffer );
403 /* open the output */
404 err = AudioDeviceStart( p_aout->p_sys->device,
405 (AudioDeviceIOProc)CAIOCallback );
409 intf_ErrMsg( "aout error: AudioDeviceStart failed: %d", err );
410 AudioConverterDispose( p_aout->p_sys->s_converter );
411 DisposePtr( p_aout->p_sys->p_buffer );
415 p_aout->p_sys->b_format = 1;
420 /*****************************************************************************
421 * CAEndFormat: destroys the AudioConverter
422 *****************************************************************************/
423 static int CAEndFormat( aout_thread_t *p_aout )
427 if( !p_aout->p_sys->b_format )
429 intf_ErrMsg( "aout error: CAEndFormat (!b_format)" );
433 /* stop playing sound through the device */
434 err = AudioDeviceStop( p_aout->p_sys->device,
435 (AudioDeviceIOProc)CAIOCallback );
439 intf_ErrMsg( "aout error: AudioDeviceStop failed: %d", err );
443 /* remove the callback */
444 err = AudioDeviceRemoveIOProc( p_aout->p_sys->device,
445 (AudioDeviceIOProc)CAIOCallback );
449 intf_ErrMsg( "aout error: AudioDeviceRemoveIOProc failed: %d", err );
453 /* destroy the AudioConverter */
454 err = AudioConverterDispose( p_aout->p_sys->s_converter );
458 intf_ErrMsg( "aout error: AudioConverterDispose failed: %d", err );
462 /* release audio buffer */
463 DisposePtr( p_aout->p_sys->p_buffer );
465 p_aout->p_sys->b_format = 0;