aboutsummaryrefslogtreecommitdiffstats
path: root/src/audioOutputs/audioOutput_osx.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/audioOutputs/audioOutput_osx.c')
-rw-r--r--src/audioOutputs/audioOutput_osx.c248
1 files changed, 245 insertions, 3 deletions
diff --git a/src/audioOutputs/audioOutput_osx.c b/src/audioOutputs/audioOutput_osx.c
index fc5ae13b9..ab31cc6f6 100644
--- a/src/audioOutputs/audioOutput_osx.c
+++ b/src/audioOutputs/audioOutput_osx.c
@@ -25,14 +25,76 @@
#include <CoreAudio/AudioHardware.h>
#include <stdlib.h>
+#include <pthread.h>
+
+#define BUFFER_SIZE 1024
typedef struct _OsxData {
- AudioDeviceID deviceID;
+ AudioDeviceID deviceID;
+ AudioStreamBasicDescription streamDesc;
+ pthread_mutex_t mutex;
+ pthread_cond_t condition;
+ Float32 buffer[BUFFER_SIZE];
+ int pos;
+ int len;
+ int go;
+ int started;
} OsxData;
+static void printError(OSStatus val) {
+ switch(val) {
+ case kAudioHardwareNoError:
+ ERROR("kAudioHardwareNoErr");
+ break;
+ case kAudioHardwareNotRunningError:
+ ERROR("kAudioHardwareNotRunningError");
+ break;
+ case kAudioHardwareUnspecifiedError:
+ ERROR("kAudioHardwareUnspecifiedError");
+ break;
+ case kAudioHardwareUnknownPropertyError:
+ ERROR("kAudioHardwareUnknownPropertyError");
+ break;
+ case kAudioHardwareBadPropertySizeError:
+ ERROR("kAudioHardwareBadPropertySizeError");
+ break;
+ case kAudioHardwareIllegalOperationError:
+ ERROR("kAudioHardwareIllegalOperationError");
+ break;
+ case kAudioHardwareBadDeviceError:
+ ERROR("kAudioHardwareBadDeviceError");
+ break;
+ case kAudioHardwareBadStreamError:
+ ERROR("kAudioHardwareBadStreamError");
+ break;
+ case kAudioHardwareUnsupportedOperationError:
+ ERROR("kAudioHardwareUnsupportedOperationError");
+ break;
+ case kAudioDeviceUnsupportedFormatError:
+ ERROR("kAudioDeviceUnsupportedFormatError");
+ break;
+ case kAudioDevicePermissionsError:
+ ERROR("kAudioDevicePermissionsError");
+ break;
+ default:
+ ERROR("unknown");
+ break;
+ }
+}
+
static OsxData * newOsxData() {
OsxData * ret = malloc(sizeof(OsxData));
+ ret->deviceID = kAudioDeviceUnknown;
+
+ pthread_mutex_init(&ret->mutex, NULL);
+ pthread_cond_init(&ret->condition, NULL);
+
+ ret->pos = 0;
+ ret->len = 0;
+ ret->go = 0;
+ ret->started = 0;
+
return ret;
}
@@ -79,18 +141,198 @@ static void osx_closeDevice(AudioOutput * audioOutput) {
audioOutput->open = 0;
}
+static OSStatus osx_IOProc(AudioDeviceID deviceID,
+ const AudioTimeStamp * inNow, const AudioBufferList *inData,
+ const AudioTimeStamp * inInputTime, AudioBufferList *outData,
+ const AudioTimeStamp * inOutputTime, void * vdata)
+{
+ OsxData * od = (OsxData *)vdata;
+ AudioBuffer * buffer = &outData->mBuffers[0];
+ int bufferSize = buffer->mDataByteSize/sizeof(Float32);
+ int floatsToCopy;
+ int curpos = 0;
+
+ DEBUG("entering IOProc\n");
+
+ pthread_mutex_lock(&od->mutex);
+
+ while((od->go || od->len) && bufferSize) {
+ while(od->go && od->len < bufferSize &&
+ od->len < BUFFER_SIZE)
+ {
+ pthread_cond_wait(&od->condition, &od->mutex);
+ }
+
+ floatsToCopy = od->len < bufferSize ? od->len : bufferSize;
+ bufferSize -= floatsToCopy;
+
+ if(od->pos+floatsToCopy > BUFFER_SIZE) {
+ int floats = BUFFER_SIZE-od->pos;
+ memcpy(buffer->mData+curpos, od->buffer+od->pos,
+ floats*sizeof(Float32));
+ od->len -= floats;
+ od->pos = 0;
+ curpos += floats;
+ floatsToCopy -= floats;
+ }
+
+ memcpy(buffer->mData+curpos, od->buffer+od->pos,
+ floatsToCopy*sizeof(Float32));
+ od->len -= floatsToCopy;
+ od->pos += floatsToCopy;
+ curpos += floatsToCopy;
+ }
+
+ if(bufferSize) {
+ memset(buffer->mData+curpos, 0, bufferSize*sizeof(Float32));
+ }
+
+ pthread_mutex_unlock(&od->mutex);
+ pthread_cond_signal(&od->condition);
+
+ DEBUG("exiting IOProc\n");
+
+ return 0;
+}
+
static int osx_openDevice(AudioOutput * audioOutput) {
- //OsxData * od = (OsxData *)audioOutput->data;
+ int err;
+ OsxData * od = (OsxData *)audioOutput->data;
+ UInt32 propertySize;
+ AudioFormat * audioFormat = &audioOutput->outAudioFormat;
+ UInt32 bufferByteCount = 8192;
+
+ propertySize = sizeof(od->deviceID);
+ err = AudioHardwareGetProperty(
+ kAudioHardwarePropertyDefaultOutputDevice,
+ &propertySize, &od->deviceID);
+ if(err || od->deviceID == kAudioDeviceUnknown) {
+ ERROR("Not able to get the default OS X device\n");
+ return -1;
+ }
+
+ od->streamDesc.mFormatID = kAudioFormatLinearPCM;
+ od->streamDesc.mSampleRate = audioFormat->sampleRate;
+ od->streamDesc.mFormatFlags = kLinearPCMFormatFlagIsFloat |
+ kLinearPCMFormatFlagIsBigEndian |
+ kLinearPCMFormatFlagIsPacked;
+ od->streamDesc.mBytesPerPacket = audioFormat->channels*sizeof(Float32);
+ od->streamDesc.mFramesPerPacket = 1;
+ od->streamDesc.mBytesPerFrame = audioFormat->channels*sizeof(Float32);
+ od->streamDesc.mChannelsPerFrame = audioFormat->channels;
+ od->streamDesc.mBitsPerChannel = 8 * sizeof(Float32);
+
+ audioFormat->bits = 16;
+
+ propertySize = sizeof(od->streamDesc);
+ err = AudioDeviceSetProperty(od->deviceID, 0, 0, false,
+ kAudioDevicePropertyStreamFormat,
+ propertySize, &od->streamDesc);
+ if(err) {
+ ERROR("unable to set format %i:%i:% on osx device\n",
+ (int)audioFormat->sampleRate,
+ (int)audioFormat->bits,
+ (int)audioFormat->channels);
+ return -1;
+ }
+
+ propertySize = sizeof(UInt32);
+ err = AudioDeviceSetProperty(od->deviceID, 0, 0, false,
+ kAudioDevicePropertyBufferSize,
+ propertySize, &bufferByteCount);
+
+ err = AudioDeviceAddIOProc(od->deviceID, osx_IOProc, od);
+ if(err) {
+ ERROR("error adding IOProc\n");
+ return -1;
+ }
+
+ od->go = 1;
+ od->pos = 0;
+ od->len = 0;
audioOutput->open = 1;
return 0;
}
+static void copyIntBufferToFloat(char * playChunk, int size, float * buffer,
+ int floats)
+{
+ /* this is for 16-bit audio only */
+ SInt16 * sample;
+
+ while(floats) {
+ sample = (SInt16 *)playChunk;
+ *buffer = *sample/32767.0;
+ playChunk += 2;
+ buffer++;
+ floats--;
+ }
+}
static int osx_play(AudioOutput * audioOutput, char * playChunk, int size) {
- //OsxData * od = (OsxData *)audioOutput->data;
+ OsxData * od = (OsxData *)audioOutput->data;
+ int floatsToCopy;
+
+ size /= 2;
+
+ DEBUG("entering osx_play\n");
+
+ pthread_mutex_lock(&od->mutex);
+
+ DEBUG("entering while loop\n");
+ while(size) {
+ DEBUG("iterating loop with size = %i\n", size);
+ while(od->len == BUFFER_SIZE) {
+ if(!od->started) {
+ OSStatus err = AudioDeviceStart(od->deviceID,
+ osx_IOProc);
+ DEBUG("start audio device\n");
+ if(err) {
+ printError(err);
+ ERROR(" error doing AudioDeviceStart "
+ "for osx device: %i\n",
+ (int)err);
+ pthread_mutex_unlock(&od->mutex);
+ return -1;
+ }
+ od->started = 1;
+ DEBUG("audio device started\n");
+ }
+
+ DEBUG("cond_wait\n");
+ pthread_cond_wait(&od->condition, &od->mutex);
+ }
+
+ floatsToCopy = BUFFER_SIZE - od->len;
+ floatsToCopy = floatsToCopy < size ? floatsToCopy : size;
+ size -= floatsToCopy;
+
+ if(od->pos+floatsToCopy > BUFFER_SIZE) {
+ int floats = BUFFER_SIZE-od->pos;
+ copyIntBufferToFloat(playChunk,
+ audioOutput->outAudioFormat.bits/8,
+ od->buffer,
+ floats);
+ od->pos = 0;
+ od->len += floats;
+ playChunk += floats*sizeof(Float32);
+ }
+
+ copyIntBufferToFloat(playChunk,
+ audioOutput->outAudioFormat.bits/8,
+ od->buffer,
+ floatsToCopy);
+ od->pos += floatsToCopy;
+ od->len += floatsToCopy;
+ playChunk += floatsToCopy*sizeof(Float32);
+ }
+
+ pthread_mutex_unlock(&od->mutex);
+ pthread_cond_signal(&od->condition);
+ DEBUG("exiting osx_play\n");
return 0;
}