10l: comparison of char* ptrs with string literals
[mplayer.git] / libao2 / ao_macosx.c
blob591fee18fba203c8035433b6690f2dfd3abd8324
1 /*
3 * ao_macosx.c
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)
13 * any later version.
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).
32 /* Change log:
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>
43 #include <stdio.h>
44 #include <string.h>
45 #include <stdlib.h>
46 #include <inttypes.h>
47 #include <pthread.h>
49 #include "config.h"
50 #include "mp_msg.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",
59 "macosx",
60 "Timothy J. Wood & Dan Christiansen & Chris Roccati",
64 LIBAO_EXTERN(macosx)
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 */
71 #define NUM_BUFS 32
73 typedef struct ao_macosx_s
75 /* AudioUnit */
76 AudioUnit theOutputUnit;
77 int packetSize;
78 int paused;
80 /* Ring-buffer */
81 /* does not need explicit synchronization, but needs to allocate
82 * (num_chunks + 1) * chunk_size memory to store num_chunks * chunk_size
83 * data */
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;
91 } ao_macosx_t;
93 static ao_macosx_t *ao = NULL;
95 /**
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;
105 return free;
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;
118 return used;
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;
136 return 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;
154 return len;
157 OSStatus theRenderProc(void *inRefCon, AudioUnitRenderActionFlags *inActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumFrames, AudioBufferList *ioData)
159 int amt=buf_used();
160 int req=(inNumFrames)*ao->packetSize;
162 if(amt>req)
163 amt=req;
165 if(amt)
166 read_buffer((unsigned char *)ioData->mBuffers[0].mData, amt);
167 else audio_pause();
168 ioData->mBuffers[0].mDataByteSize = amt;
170 return noErr;
173 static int control(int cmd,void *arg){
174 ao_control_vol_t *control_vol;
175 OSStatus err;
176 Float32 pan, vol;
177 switch (cmd) {
178 case AOCONTROL_GET_VOLUME:
179 control_vol = (ao_control_vol_t*)arg;
180 err = AudioUnitGetParameter(ao->theOutputUnit, kHALOutputParam_Volume, kAudioUnitScope_Global, 0, &vol);
182 if(err==0) {
183 // printf("GET VOL=%f\n", vol);
184 control_vol->left=control_vol->right=vol*100.0/4.0;
185 return CONTROL_TRUE;
187 else {
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);
196 if(err==0) {
197 // printf("SET VOL=%f\n", vol);
198 return CONTROL_TRUE;
200 else {
201 return CONTROL_FALSE;
203 /* Everything is currently unimplemented */
204 default:
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;
242 Component comp;
243 AURenderCallbackStruct renderCallback;
244 OSStatus err;
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){
255 case AF_FORMAT_8BIT:
256 inDesc.mBitsPerChannel=8;
257 break;
258 case AF_FORMAT_16BIT:
259 inDesc.mBitsPerChannel=16;
260 break;
261 case AF_FORMAT_24BIT:
262 inDesc.mBitsPerChannel=24;
263 break;
264 case AF_FORMAT_32BIT:
265 inDesc.mBitsPerChannel=32;
266 break;
267 default:
268 ao_msg(MSGT_AO, MSGL_WARN, "Unsupported format (0x%08x)\n", format);
269 return CONTROL_FALSE;
270 break;
273 if((format&AF_FORMAT_POINT_MASK)==AF_FORMAT_F) {
274 // float
275 inDesc.mFormatFlags = kAudioFormatFlagIsFloat|kAudioFormatFlagIsPacked;
277 else if((format&AF_FORMAT_SIGN_MASK)==AF_FORMAT_SI) {
278 // signed int
279 inDesc.mFormatFlags = kAudioFormatFlagIsSignedInteger|kAudioFormatFlagIsPacked;
281 else {
282 // unsigned int
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);
293 if (!aoIsCreated) {
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
301 if (comp == NULL) {
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
307 if (err) {
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);
314 if (err) {
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);
323 if (err) {
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);
331 if (err)
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));
355 if (err) {
356 ao_msg(MSGT_AO, MSGL_WARN, "Unable to set the render callback (err=%d)\n", err);
357 return CONTROL_FALSE;
360 reset();
362 return CONTROL_OK;
366 static int play(void* output_samples,int num_bytes,int flags)
368 int wrote=write_buffer(output_samples, num_bytes);
370 audio_resume();
371 return wrote;
374 /* set variables and buffer to initial state */
375 static void reset(void)
377 audio_pause();
378 /* reset ring-buffer state */
379 ao->buf_read_pos=0;
380 ao->buf_write_pos=0;
382 return;
386 /* return available space */
387 static int get_space(void)
389 return buf_free();
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)
405 int i;
406 OSErr status;
408 if (!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);
418 free(ao->buffer);
419 free(ao);
420 ao = NULL;
424 /* stop playing, keep buffers (for pause) */
425 static void audio_pause(void)
427 OSErr status=noErr;
429 /* stop callback */
430 status=AudioOutputUnitStop(ao->theOutputUnit);
431 if (status)
432 ao_msg(MSGT_AO,MSGL_WARN, "AudioOutputUnitStop returned %d\n",
433 (int)status);
434 ao->paused=1;
438 /* resume playing, after audio_pause() */
439 static void audio_resume(void)
441 if(ao->paused) {
442 OSErr status=noErr;
443 /* start callback */
444 status=AudioOutputUnitStart(ao->theOutputUnit);
445 if (status)
446 ao_msg(MSGT_AO,MSGL_WARN, "AudioOutputUnitStart returned %d\n",
447 (int)status);
448 ao->paused=0;