5 * Original Copyright (C) Timothy J. Wood - Aug 2000
7 * This file is part of libao, a cross-platform library. See
8 * README for a history of this source code.
10 * libao 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, or (at your option)
15 * libao 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 along
21 * with libao; if not, write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 * The MacOS X CoreAudio framework doesn't mesh as simply as some
27 * simpler frameworks do. This is due to the fact that CoreAudio pulls
28 * audio samples rather than having them pushed at it (which is nice
29 * when you are wanting to do good buffering of audio).
34 * 14/5-2003: Ported to MPlayer libao2 by Dan Christiansen
36 * AC-3 and MPEG audio passthrough is possible, but I don't have
37 * access to a sound card that supports it.
40 #include <CoreServices/CoreServices.h>
41 #include <AudioUnit/AudioUnit.h>
42 #include <AudioToolbox/AudioToolbox.h>
52 #include "audio_out.h"
53 #include "audio_out_internal.h"
54 #include "libaf/af_format.h"
56 static ao_info_t info
=
58 "Darwin/Mac OS X native audio output",
60 "Timothy J. Wood & Dan Christiansen & Chris Roccati",
66 /* Prefix for all mp_msg() calls */
67 #define ao_msg(a, b, c...) mp_msg(a, b, "AO: [macosx] " c)
69 /* This is large, but best (maybe it should be even larger).
70 * CoreAudio supposedly has an internal latency in the order of 2ms */
73 typedef struct ao_macosx_s
76 AudioUnit theOutputUnit
;
81 /* does not need explicit synchronization, but needs to allocate
82 * (num_chunks + 1) * chunk_size memory to store num_chunks * chunk_size
84 unsigned char *buffer
;
85 unsigned int buffer_len
; ///< must always be (num_chunks + 1) * chunk_size
86 unsigned int num_chunks
;
87 unsigned int chunk_size
;
89 unsigned int buf_read_pos
;
90 unsigned int buf_write_pos
;
93 static ao_macosx_t
*ao
= NULL
;
96 * \brief return number of free bytes in the buffer
97 * may only be called by mplayer's thread
98 * \return minimum number of free bytes in buffer, value may change between
99 * two immediately following calls, and the real number of free bytes
100 * might actually be larger!
102 static int buf_free(void) {
103 int free
= ao
->buf_read_pos
- ao
->buf_write_pos
- ao
->chunk_size
;
104 if (free
< 0) free
+= ao
->buffer_len
;
109 * \brief return number of buffered bytes
110 * may only be called by playback thread
111 * \return minimum number of buffered bytes, value may change between
112 * two immediately following calls, and the real number of buffered bytes
113 * might actually be larger!
115 static int buf_used(void) {
116 int used
= ao
->buf_write_pos
- ao
->buf_read_pos
;
117 if (used
< 0) used
+= ao
->buffer_len
;
122 * \brief add data to ringbuffer
124 static int write_buffer(unsigned char* data
, int len
){
125 int first_len
= ao
->buffer_len
- ao
->buf_write_pos
;
126 int free
= buf_free();
127 if (len
> free
) len
= free
;
128 if (first_len
> len
) first_len
= len
;
129 // till end of buffer
130 memcpy (&ao
->buffer
[ao
->buf_write_pos
], data
, first_len
);
131 if (len
> first_len
) { // we have to wrap around
132 // remaining part from beginning of buffer
133 memcpy (ao
->buffer
, &data
[first_len
], len
- first_len
);
135 ao
->buf_write_pos
= (ao
->buf_write_pos
+ len
) % ao
->buffer_len
;
140 * \brief remove data from ringbuffer
142 static int read_buffer(unsigned char* data
,int len
){
143 int first_len
= ao
->buffer_len
- ao
->buf_read_pos
;
144 int buffered
= buf_used();
145 if (len
> buffered
) len
= buffered
;
146 if (first_len
> len
) first_len
= len
;
147 // till end of buffer
148 memcpy (data
, &ao
->buffer
[ao
->buf_read_pos
], first_len
);
149 if (len
> first_len
) { // we have to wrap around
150 // remaining part from beginning of buffer
151 memcpy (&data
[first_len
], ao
->buffer
, len
- first_len
);
153 ao
->buf_read_pos
= (ao
->buf_read_pos
+ len
) % ao
->buffer_len
;
157 OSStatus
theRenderProc(void *inRefCon
, AudioUnitRenderActionFlags
*inActionFlags
, const AudioTimeStamp
*inTimeStamp
, UInt32 inBusNumber
, UInt32 inNumFrames
, AudioBufferList
*ioData
)
160 int req
=(inNumFrames
)*ao
->packetSize
;
166 read_buffer((unsigned char *)ioData
->mBuffers
[0].mData
, amt
);
168 ioData
->mBuffers
[0].mDataByteSize
= amt
;
173 static int control(int cmd
,void *arg
){
174 ao_control_vol_t
*control_vol
;
178 case AOCONTROL_GET_VOLUME
:
179 control_vol
= (ao_control_vol_t
*)arg
;
180 err
= AudioUnitGetParameter(ao
->theOutputUnit
, kHALOutputParam_Volume
, kAudioUnitScope_Global
, 0, &vol
);
183 // printf("GET VOL=%f\n", vol);
184 control_vol
->left
=control_vol
->right
=vol
*100.0/4.0;
188 return CONTROL_FALSE
;
191 case AOCONTROL_SET_VOLUME
:
192 control_vol
= (ao_control_vol_t
*)arg
;
194 vol
=(control_vol
->left
+control_vol
->right
)*4.0/200.0;
195 err
= AudioUnitSetParameter(ao
->theOutputUnit
, kHALOutputParam_Volume
, kAudioUnitScope_Global
, 0, vol
, 0);
197 // printf("SET VOL=%f\n", vol);
201 return CONTROL_FALSE
;
203 /* Everything is currently unimplemented */
205 return CONTROL_FALSE
;
211 static void print_format(const char* str
,AudioStreamBasicDescription
*f
){
212 uint32_t flags
=(uint32_t) f
->mFormatFlags
;
213 ao_msg(MSGT_AO
,MSGL_V
, "%s %7.1fHz %dbit [%c%c%c%c] %s %s %s%s%s%s\n",
214 str
, f
->mSampleRate
, f
->mBitsPerChannel
,
215 (int)(f
->mFormatID
& 0xff000000) >> 24,
216 (int)(f
->mFormatID
& 0x00ff0000) >> 16,
217 (int)(f
->mFormatID
& 0x0000ff00) >> 8,
218 (int)(f
->mFormatID
& 0x000000ff) >> 0,
219 (flags
&kAudioFormatFlagIsFloat
) ? "float" : "int",
220 (flags
&kAudioFormatFlagIsBigEndian
) ? "BE" : "LE",
221 (flags
&kAudioFormatFlagIsSignedInteger
) ? "S" : "U",
222 (flags
&kAudioFormatFlagIsPacked
) ? " packed" : "",
223 (flags
&kAudioFormatFlagIsAlignedHigh
) ? " aligned" : "",
224 (flags
&kAudioFormatFlagIsNonInterleaved
) ? " ni" : "" );
226 ao_msg(MSGT_AO
,MSGL_DBG2
, "%5d mBytesPerPacket\n",
227 (int)f
->mBytesPerPacket
);
228 ao_msg(MSGT_AO
,MSGL_DBG2
, "%5d mFramesPerPacket\n",
229 (int)f
->mFramesPerPacket
);
230 ao_msg(MSGT_AO
,MSGL_DBG2
, "%5d mBytesPerFrame\n",
231 (int)f
->mBytesPerFrame
);
232 ao_msg(MSGT_AO
,MSGL_DBG2
, "%5d mChannelsPerFrame\n",
233 (int)f
->mChannelsPerFrame
);
238 static int init(int rate
,int channels
,int format
,int flags
)
240 AudioStreamBasicDescription inDesc
, outDesc
;
241 ComponentDescription desc
;
243 AURenderCallbackStruct renderCallback
;
245 UInt32 size
, maxFrames
;
246 int aoIsCreated
= ao
!= NULL
;
248 if (!aoIsCreated
) ao
= malloc(sizeof(ao_macosx_t
));
250 // Build Description for the input format
251 inDesc
.mSampleRate
=rate
;
252 inDesc
.mFormatID
=kAudioFormatLinearPCM
;
253 inDesc
.mChannelsPerFrame
=channels
;
254 switch(format
&AF_FORMAT_BITS_MASK
){
256 inDesc
.mBitsPerChannel
=8;
258 case AF_FORMAT_16BIT
:
259 inDesc
.mBitsPerChannel
=16;
261 case AF_FORMAT_24BIT
:
262 inDesc
.mBitsPerChannel
=24;
264 case AF_FORMAT_32BIT
:
265 inDesc
.mBitsPerChannel
=32;
268 ao_msg(MSGT_AO
, MSGL_WARN
, "Unsupported format (0x%08x)\n", format
);
269 return CONTROL_FALSE
;
273 if((format
&AF_FORMAT_POINT_MASK
)==AF_FORMAT_F
) {
275 inDesc
.mFormatFlags
= kAudioFormatFlagIsFloat
|kAudioFormatFlagIsPacked
;
277 else if((format
&AF_FORMAT_SIGN_MASK
)==AF_FORMAT_SI
) {
279 inDesc
.mFormatFlags
= kAudioFormatFlagIsSignedInteger
|kAudioFormatFlagIsPacked
;
283 inDesc
.mFormatFlags
= kAudioFormatFlagIsPacked
;
286 if((format
&AF_FORMAT_END_MASK
)==AF_FORMAT_BE
)
287 inDesc
.mFormatFlags
|= kAudioFormatFlagIsBigEndian
;
289 inDesc
.mFramesPerPacket
= 1;
290 ao
->packetSize
= inDesc
.mBytesPerPacket
= inDesc
.mBytesPerFrame
= inDesc
.mFramesPerPacket
*channels
*(inDesc
.mBitsPerChannel
/8);
291 print_format("source: ",&inDesc
);
294 desc
.componentType
= kAudioUnitType_Output
;
295 desc
.componentSubType
= kAudioUnitSubType_DefaultOutput
;
296 desc
.componentManufacturer
= kAudioUnitManufacturer_Apple
;
297 desc
.componentFlags
= 0;
298 desc
.componentFlagsMask
= 0;
300 comp
= FindNextComponent(NULL
, &desc
); //Finds an component that meets the desc spec's
302 ao_msg(MSGT_AO
, MSGL_WARN
, "Unable to find Output Unit component\n");
303 return CONTROL_FALSE
;
306 err
= OpenAComponent(comp
, &(ao
->theOutputUnit
)); //gains access to the services provided by the component
308 ao_msg(MSGT_AO
, MSGL_WARN
, "Unable to open Output Unit component (err=%d)\n", err
);
309 return CONTROL_FALSE
;
312 // Initialize AudioUnit
313 err
= AudioUnitInitialize(ao
->theOutputUnit
);
315 ao_msg(MSGT_AO
, MSGL_WARN
, "Unable to initialize Output Unit component (err=%d)\n", err
);
316 return CONTROL_FALSE
;
320 size
= sizeof(AudioStreamBasicDescription
);
321 err
= AudioUnitSetProperty(ao
->theOutputUnit
, kAudioUnitProperty_StreamFormat
, kAudioUnitScope_Input
, 0, &inDesc
, size
);
324 ao_msg(MSGT_AO
, MSGL_WARN
, "Unable to set the input format (err=%d)\n", err
);
325 return CONTROL_FALSE
;
328 size
= sizeof(UInt32
);
329 err
= AudioUnitGetProperty(ao
->theOutputUnit
, kAudioDevicePropertyBufferSize
, kAudioUnitScope_Input
, 0, &maxFrames
, &size
);
333 ao_msg(MSGT_AO
,MSGL_WARN
, "AudioUnitGetProperty returned %d when getting kAudioDevicePropertyBufferSize\n", (int)err
);
334 return CONTROL_FALSE
;
337 ao
->chunk_size
= maxFrames
;//*inDesc.mBytesPerFrame;
339 ao_data
.samplerate
= inDesc
.mSampleRate
;
340 ao_data
.channels
= inDesc
.mChannelsPerFrame
;
341 ao_data
.bps
= ao_data
.samplerate
* inDesc
.mBytesPerFrame
;
342 ao_data
.outburst
= ao
->chunk_size
;
343 ao_data
.buffersize
= ao_data
.bps
;
345 ao
->num_chunks
= (ao_data
.bps
+ao
->chunk_size
-1)/ao
->chunk_size
;
346 ao
->buffer_len
= (ao
->num_chunks
+ 1) * ao
->chunk_size
;
347 ao
->buffer
= aoIsCreated
? realloc(ao
->buffer
,(ao
->num_chunks
+ 1)*ao
->chunk_size
)
348 : calloc(ao
->num_chunks
+ 1, ao
->chunk_size
);
350 ao_msg(MSGT_AO
,MSGL_V
, "using %5d chunks of %d bytes (buffer len %d bytes)\n", (int)ao
->num_chunks
, (int)ao
->chunk_size
, (int)ao
->buffer_len
);
352 renderCallback
.inputProc
= theRenderProc
;
353 renderCallback
.inputProcRefCon
= 0;
354 err
= AudioUnitSetProperty(ao
->theOutputUnit
, kAudioUnitProperty_SetRenderCallback
, kAudioUnitScope_Input
, 0, &renderCallback
, sizeof(AURenderCallbackStruct
));
356 ao_msg(MSGT_AO
, MSGL_WARN
, "Unable to set the render callback (err=%d)\n", err
);
357 return CONTROL_FALSE
;
366 static int play(void* output_samples
,int num_bytes
,int flags
)
368 int wrote
=write_buffer(output_samples
, num_bytes
);
374 /* set variables and buffer to initial state */
375 static void reset(void)
378 /* reset ring-buffer state */
386 /* return available space */
387 static int get_space(void)
393 /* return delay until audio is played */
394 static float get_delay(void)
396 int buffered
= ao
->buffer_len
- ao
->chunk_size
- buf_free(); // could be less
397 // inaccurate, should also contain the data buffered e.g. by the OS
398 return (float)(buffered
)/(float)ao_data
.bps
;
402 /* unload plugin and deregister from coreaudio */
403 static void uninit(int immed
)
409 long long timeleft
=(1000000LL*buf_used())/ao_data
.bps
;
410 ao_msg(MSGT_AO
,MSGL_DBG2
, "%d bytes left @%d bps (%ld usec)\n", buf_used(), ao_data
.bps
, (int)timeleft
);
411 usec_sleep((int)timeleft
);
414 AudioOutputUnitStop(ao
->theOutputUnit
);
415 AudioUnitUninitialize(ao
->theOutputUnit
);
416 CloseComponent(ao
->theOutputUnit
);
424 /* stop playing, keep buffers (for pause) */
425 static void audio_pause(void)
430 status
=AudioOutputUnitStop(ao
->theOutputUnit
);
432 ao_msg(MSGT_AO
,MSGL_WARN
, "AudioOutputUnitStop returned %d\n",
438 /* resume playing, after audio_pause() */
439 static void audio_resume(void)
444 status
=AudioOutputUnitStart(ao
->theOutputUnit
);
446 ao_msg(MSGT_AO
,MSGL_WARN
, "AudioOutputUnitStart returned %d\n",