1 /* the Music Player Daemon (MPD)
2 * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
3 * This project's homepage is: http://www.musicpd.org
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include "../audioOutput.h"
23 #include <AudioUnit/AudioUnit.h>
29 typedef struct _OsxData {
31 pthread_mutex_t mutex;
32 pthread_cond_t condition;
40 static OsxData *newOsxData()
42 OsxData *ret = xmalloc(sizeof(OsxData));
44 pthread_mutex_init(&ret->mutex, NULL);
45 pthread_cond_init(&ret->condition, NULL);
56 static int osx_testDefault()
59 ComponentDescription desc;
62 desc.componentType = kAudioUnitType_Output;
63 desc.componentSubType = kAudioUnitSubType_Output;
64 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
65 desc.componentFlags = 0;
66 desc.componentFlagsMask = 0;
68 comp = FindNextComponent(NULL, &desc);
70 ERROR("Unable to open default OS X defice\n");
74 if(OpenAComponent(comp, &au) != noErr) {
75 ERROR("Unable to open default OS X defice\n");
79 CloseComponent(au); */
84 static int osx_initDriver(AudioOutput * audioOutput, ConfigParam * param)
86 OsxData *od = newOsxData();
88 audioOutput->data = od;
93 static void freeOsxData(OsxData * od)
97 pthread_mutex_destroy(&od->mutex);
98 pthread_cond_destroy(&od->condition);
102 static void osx_finishDriver(AudioOutput * audioOutput)
104 OsxData *od = (OsxData *) audioOutput->data;
108 static void osx_dropBufferedAudio(AudioOutput * audioOutput)
110 OsxData *od = (OsxData *) audioOutput->data;
112 pthread_mutex_lock(&od->mutex);
114 pthread_mutex_unlock(&od->mutex);
117 static void osx_closeDevice(AudioOutput * audioOutput)
119 OsxData *od = (OsxData *) audioOutput->data;
121 pthread_mutex_lock(&od->mutex);
123 pthread_cond_wait(&od->condition, &od->mutex);
125 pthread_mutex_unlock(&od->mutex);
128 AudioOutputUnitStop(od->au);
132 CloseComponent(od->au);
133 AudioUnitUninitialize(od->au);
135 audioOutput->open = 0;
138 static OSStatus osx_render(void *vdata,
139 AudioUnitRenderActionFlags * ioActionFlags,
140 const AudioTimeStamp * inTimeStamp,
141 UInt32 inBusNumber, UInt32 inNumberFrames,
142 AudioBufferList * bufferList)
144 OsxData *od = (OsxData *) vdata;
145 AudioBuffer *buffer = &bufferList->mBuffers[0];
146 int bufferSize = buffer->mDataByteSize;
150 /*DEBUG("osx_render: enter : %i\n", (int)bufferList->mNumberBuffers);
151 DEBUG("osx_render: ioActionFlags: %p\n", ioActionFlags);
153 if(*ioActionFlags & kAudioUnitRenderAction_PreRender) {
154 DEBUG("prerender\n");
156 if(*ioActionFlags & kAudioUnitRenderAction_PostRender) {
157 DEBUG("post render\n");
159 if(*ioActionFlags & kAudioUnitRenderAction_OutputIsSilence) {
160 DEBUG("post render\n");
162 if(*ioActionFlags & kAudioOfflineUnitRenderAction_Preflight) {
163 DEBUG("prefilight\n");
165 if(*ioActionFlags & kAudioOfflineUnitRenderAction_Render) {
168 if(*ioActionFlags & kAudioOfflineUnitRenderAction_Complete) {
173 /* while(bufferSize) {
174 DEBUG("osx_render: lock\n"); */
175 pthread_mutex_lock(&od->mutex);
177 DEBUG("%i:%i\n", bufferSize, od->len);
178 while(od->go && od->len < bufferSize &&
179 od->len < od->bufferSize)
181 DEBUG("osx_render: wait\n");
182 pthread_cond_wait(&od->condition, &od->mutex);
186 bytesToCopy = od->len < bufferSize ? od->len : bufferSize;
187 bufferSize = bytesToCopy;
188 od->len -= bytesToCopy;
190 if (od->pos + bytesToCopy > od->bufferSize) {
191 int bytes = od->bufferSize - od->pos;
192 memcpy(buffer->mData + curpos, od->buffer + od->pos, bytes);
195 bytesToCopy -= bytes;
198 memcpy(buffer->mData + curpos, od->buffer + od->pos, bytesToCopy);
199 od->pos += bytesToCopy;
200 curpos += bytesToCopy;
202 if (od->pos >= od->bufferSize)
204 /* DEBUG("osx_render: unlock\n"); */
205 pthread_mutex_unlock(&od->mutex);
206 pthread_cond_signal(&od->condition);
209 buffer->mDataByteSize = bufferSize;
215 /* DEBUG("osx_render: leave\n"); */
219 static int osx_openDevice(AudioOutput * audioOutput)
221 OsxData *od = (OsxData *) audioOutput->data;
222 ComponentDescription desc;
224 AURenderCallbackStruct callback;
225 AudioFormat *audioFormat = &audioOutput->outAudioFormat;
226 AudioStreamBasicDescription streamDesc;
228 desc.componentType = kAudioUnitType_Output;
229 desc.componentSubType = kAudioUnitSubType_DefaultOutput;
230 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
231 desc.componentFlags = 0;
232 desc.componentFlagsMask = 0;
234 comp = FindNextComponent(NULL, &desc);
236 ERROR("Error finding OS X component\n");
240 if (OpenAComponent(comp, &od->au) != noErr) {
241 ERROR("Unable to open OS X component\n");
245 if (AudioUnitInitialize(od->au) != 0) {
246 CloseComponent(od->au);
247 ERROR("Unable to initialize OS X audio unit\n");
251 callback.inputProc = osx_render;
252 callback.inputProcRefCon = od;
254 if (AudioUnitSetProperty(od->au, kAudioUnitProperty_SetRenderCallback,
255 kAudioUnitScope_Input, 0,
256 &callback, sizeof(callback)) != 0) {
257 AudioUnitUninitialize(od->au);
258 CloseComponent(od->au);
259 ERROR("unable to set callback for OS X audio unit\n");
263 streamDesc.mSampleRate = audioFormat->sampleRate;
264 streamDesc.mFormatID = kAudioFormatLinearPCM;
265 streamDesc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
266 #ifdef WORDS_BIGENDIAN
267 streamDesc.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
270 streamDesc.mBytesPerPacket =
271 audioFormat->channels * audioFormat->bits / 8;
272 streamDesc.mFramesPerPacket = 1;
273 streamDesc.mBytesPerFrame = streamDesc.mBytesPerPacket;
274 streamDesc.mChannelsPerFrame = audioFormat->channels;
275 streamDesc.mBitsPerChannel = audioFormat->bits;
277 if (AudioUnitSetProperty(od->au, kAudioUnitProperty_StreamFormat,
278 kAudioUnitScope_Input, 0,
279 &streamDesc, sizeof(streamDesc)) != 0) {
280 AudioUnitUninitialize(od->au);
281 CloseComponent(od->au);
282 ERROR("Unable to set format on OS X device\n");
286 /* create a buffer of 1s */
287 od->bufferSize = (audioFormat->sampleRate) *
288 (audioFormat->bits >> 3) * (audioFormat->channels);
289 od->buffer = xrealloc(od->buffer, od->bufferSize);
294 audioOutput->open = 1;
299 static int osx_play(AudioOutput * audioOutput, char *playChunk, int size)
301 OsxData *od = (OsxData *) audioOutput->data;
305 /* DEBUG("osx_play: enter\n"); */
310 err = AudioOutputUnitStart(od->au);
312 ERROR("unable to start audio output: %i\n", err);
317 pthread_mutex_lock(&od->mutex);
320 /* DEBUG("osx_play: lock\n"); */
321 curpos = od->pos + od->len;
322 if (curpos >= od->bufferSize)
323 curpos -= od->bufferSize;
325 bytesToCopy = od->bufferSize < size ? od->bufferSize : size;
327 while (od->len > od->bufferSize - bytesToCopy) {
328 /* DEBUG("osx_play: wait\n"); */
329 pthread_cond_wait(&od->condition, &od->mutex);
332 bytesToCopy = od->bufferSize - od->len;
333 bytesToCopy = bytesToCopy < size ? bytesToCopy : size;
335 od->len += bytesToCopy;
337 if (curpos + bytesToCopy > od->bufferSize) {
338 int bytes = od->bufferSize - curpos;
339 memcpy(od->buffer + curpos, playChunk, bytes);
342 bytesToCopy -= bytes;
345 memcpy(od->buffer + curpos, playChunk, bytesToCopy);
346 curpos += bytesToCopy;
347 playChunk += bytesToCopy;
350 /* DEBUG("osx_play: unlock\n"); */
351 pthread_mutex_unlock(&od->mutex);
353 /* DEBUG("osx_play: leave\n"); */
357 AudioOutputPlugin osxPlugin = {
364 osx_dropBufferedAudio,
366 NULL, /* sendMetadataFunc */
373 DISABLED_AUDIO_OUTPUT_PLUGIN(osxPlugin)