/* the Music Player Daemon (MPD) * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com) * This project's homepage is: http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "../output_api.h" #include #include #undef G_LOG_DOMAIN #define G_LOG_DOMAIN "osx" typedef struct _OsxData { AudioUnit au; GMutex *mutex; GCond *condition; char *buffer; size_t bufferSize; size_t pos; size_t len; int started; } OsxData; static OsxData *newOsxData(void) { OsxData *ret = g_new(OsxData, 1); ret->mutex = g_mutex_new(); ret->condition = g_cond_new(); ret->pos = 0; ret->len = 0; ret->started = 0; ret->buffer = NULL; ret->bufferSize = 0; return ret; } static bool osx_testDefault(void) { /*AudioUnit au; ComponentDescription desc; Component comp; desc.componentType = kAudioUnitType_Output; desc.componentSubType = kAudioUnitSubType_Output; desc.componentManufacturer = kAudioUnitManufacturer_Apple; desc.componentFlags = 0; desc.componentFlagsMask = 0; comp = FindNextComponent(NULL, &desc); if(!comp) { ERROR("Unable to open default OS X defice\n"); return -1; } if(OpenAComponent(comp, &au) != noErr) { ERROR("Unable to open default OS X defice\n"); return -1; } CloseComponent(au); */ return true; } static void * osx_initDriver(G_GNUC_UNUSED struct audio_output *audioOutput, G_GNUC_UNUSED const struct audio_format *audio_format, G_GNUC_UNUSED const struct config_param *param) { return newOsxData(); } static void freeOsxData(OsxData * od) { g_free(od->buffer); g_mutex_free(od->mutex); g_cond_free(od->condition); g_free(od); } static void osx_finishDriver(void *data) { OsxData *od = data; freeOsxData(od); } static void osx_dropBufferedAudio(void *data) { OsxData *od = data; g_mutex_lock(od->mutex); od->len = 0; g_mutex_unlock(od->mutex); } static void osx_closeDevice(void *data) { OsxData *od = data; g_mutex_lock(od->mutex); while (od->len) { g_cond_wait(od->condition, od->mutex); } g_mutex_unlock(od->mutex); if (od->started) { AudioOutputUnitStop(od->au); od->started = 0; } AudioUnitUninitialize(od->au); CloseComponent(od->au); } static OSStatus osx_render(void *vdata, G_GNUC_UNUSED AudioUnitRenderActionFlags * ioActionFlags, G_GNUC_UNUSED const AudioTimeStamp * inTimeStamp, G_GNUC_UNUSED UInt32 inBusNumber, G_GNUC_UNUSED UInt32 inNumberFrames, AudioBufferList * bufferList) { OsxData *od = (OsxData *) vdata; AudioBuffer *buffer = &bufferList->mBuffers[0]; size_t bufferSize = buffer->mDataByteSize; size_t bytesToCopy; size_t bytes; int curpos = 0; g_mutex_lock(od->mutex); bytesToCopy = MIN(od->len, bufferSize); bufferSize = bytesToCopy; od->len -= bytesToCopy; bytes = od->bufferSize - od->pos; if (bytesToCopy > bytes) { memcpy((unsigned char*)buffer->mData + curpos, od->buffer + od->pos, bytes); od->pos = 0; curpos += bytes; bytesToCopy -= bytes; } memcpy((unsigned char*)buffer->mData + curpos, od->buffer + od->pos, bytesToCopy); od->pos += bytesToCopy; if (od->pos >= od->bufferSize) od->pos = 0; g_mutex_unlock(od->mutex); g_cond_signal(od->condition); buffer->mDataByteSize = bufferSize; if (!bufferSize) { g_usleep(1000); } return 0; } static bool osx_openDevice(void *data, struct audio_format *audioFormat) { OsxData *od = data; ComponentDescription desc; Component comp; AURenderCallbackStruct callback; AudioStreamBasicDescription streamDesc; if (audioFormat->bits > 16) audioFormat->bits = 16; desc.componentType = kAudioUnitType_Output; desc.componentSubType = kAudioUnitSubType_DefaultOutput; desc.componentManufacturer = kAudioUnitManufacturer_Apple; desc.componentFlags = 0; desc.componentFlagsMask = 0; comp = FindNextComponent(NULL, &desc); if (comp == 0) { g_warning("Error finding OS X component\n"); return false; } if (OpenAComponent(comp, &od->au) != noErr) { g_warning("Unable to open OS X component\n"); return false; } if (AudioUnitInitialize(od->au) != 0) { CloseComponent(od->au); g_warning("Unable to initialize OS X audio unit\n"); return false; } callback.inputProc = osx_render; callback.inputProcRefCon = od; if (AudioUnitSetProperty(od->au, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &callback, sizeof(callback)) != 0) { AudioUnitUninitialize(od->au); CloseComponent(od->au); g_warning("unable to set callback for OS X audio unit\n"); return false; } streamDesc.mSampleRate = audioFormat->sample_rate; streamDesc.mFormatID = kAudioFormatLinearPCM; streamDesc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger; #if G_BYTE_ORDER == G_BIG_ENDIAN streamDesc.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian; #endif streamDesc.mBytesPerPacket = audio_format_frame_size(audioFormat); streamDesc.mFramesPerPacket = 1; streamDesc.mBytesPerFrame = streamDesc.mBytesPerPacket; streamDesc.mChannelsPerFrame = audioFormat->channels; streamDesc.mBitsPerChannel = audioFormat->bits; if (AudioUnitSetProperty(od->au, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamDesc, sizeof(streamDesc)) != 0) { AudioUnitUninitialize(od->au); CloseComponent(od->au); g_warning("Unable to set format on OS X device\n"); return false; } /* create a buffer of 1s */ od->bufferSize = (audioFormat->sample_rate) * audio_format_frame_size(audioFormat); od->buffer = g_realloc(od->buffer, od->bufferSize); od->pos = 0; od->len = 0; return true; } static size_t osx_play(void *data, const void *chunk, size_t size) { OsxData *od = data; size_t start, nbytes; if (!od->started) { int err; od->started = 1; err = AudioOutputUnitStart(od->au); if (err) { g_warning("unable to start audio output: %i\n", err); return 0; } } g_mutex_lock(od->mutex); while (od->len >= od->bufferSize) /* wait for some free space in the buffer */ g_cond_wait(od->condition, od->mutex); start = od->pos + od->len; if (start >= od->bufferSize) start -= od->bufferSize; nbytes = start < od->pos ? od->pos - start : od->bufferSize - start; assert(nbytes > 0); if (nbytes > size) nbytes = size; memcpy(od->buffer + start, chunk, nbytes); od->len += nbytes; g_mutex_unlock(od->mutex); return nbytes; } const struct audio_output_plugin osxPlugin = { .name = "osx", .test_default_device = osx_testDefault, .init = osx_initDriver, .finish = osx_finishDriver, .open = osx_openDevice, .play = osx_play, .cancel = osx_dropBufferedAudio, .close = osx_closeDevice, };