playing audio in OS X now works, but far from finished

still need to implement closing the audio device

git-svn-id: https://svn.musicpd.org/mpd/trunk@3086 09075e82-0dd4-0310-85a5-a0d7c8717e4f
This commit is contained in:
Warren Dukes 2005-03-16 04:46:41 +00:00
parent d4352ac996
commit d6607f5f23

View File

@ -20,73 +20,28 @@
#ifdef HAVE_OSX #ifdef HAVE_OSX
#include "../conf.h" #include <AudioUnit/AudioUnit.h>
#include "../log.h"
#include <CoreAudio/AudioHardware.h>
#include <stdlib.h> #include <stdlib.h>
#include <pthread.h> #include <pthread.h>
#define BUFFER_SIZE 1024 #include "../log.h"
#define BUFFER_SIZE 8192
typedef struct _OsxData { typedef struct _OsxData {
AudioDeviceID deviceID; AudioUnit au;
AudioStreamBasicDescription streamDesc;
pthread_mutex_t mutex; pthread_mutex_t mutex;
pthread_cond_t condition; pthread_cond_t condition;
Float32 buffer[BUFFER_SIZE]; char buffer[BUFFER_SIZE];
int pos; int pos;
int len; int len;
int go; int go;
int started; int started;
} OsxData; } 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() { static OsxData * newOsxData() {
OsxData * ret = malloc(sizeof(OsxData)); OsxData * ret = malloc(sizeof(OsxData));
ret->deviceID = kAudioDeviceUnknown;
pthread_mutex_init(&ret->mutex, NULL); pthread_mutex_init(&ret->mutex, NULL);
pthread_cond_init(&ret->condition, NULL); pthread_cond_init(&ret->condition, NULL);
@ -99,18 +54,29 @@ static OsxData * newOsxData() {
} }
static int osx_testDefault() { static int osx_testDefault() {
int err; /*AudioUnit au;
AudioDeviceID deviceID; ComponentDescription desc;
UInt32 propertySize = sizeof(deviceID); Component comp;
err = AudioHardwareGetProperty( desc.componentType = kAudioUnitType_Output;
kAudioHardwarePropertyDefaultOutputDevice, desc.componentSubType = kAudioUnitSubType_Output;
&propertySize, &deviceID); desc.componentManufacturer = kAudioUnitManufacturer_Apple;
if(err || deviceID == kAudioDeviceUnknown) { desc.componentFlags = 0;
WARNING("Not able to get the default OS X device\n"); desc.componentFlagsMask = 0;
comp = FindNextComponent(NULL, &desc);
if(!comp) {
ERROR("Unable to open default OS X defice\n");
return -1; return -1;
} }
if(OpenAComponent(comp, &au) != noErr) {
ERROR("Unable to open default OS X defice\n");
return -1;
}
CloseComponent(au);*/
return 0; return 0;
} }
@ -141,109 +107,132 @@ static void osx_closeDevice(AudioOutput * audioOutput) {
audioOutput->open = 0; audioOutput->open = 0;
} }
static OSStatus osx_IOProc(AudioDeviceID deviceID, static OSStatus osx_render(void * vdata,
const AudioTimeStamp * inNow, const AudioBufferList *inData, AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp * inInputTime, AudioBufferList *outData, const AudioTimeStamp * inTimeStamp,
const AudioTimeStamp * inOutputTime, void * vdata) UInt32 inBusNumber, UInt32 inNumberFrames,
AudioBufferList *bufferList)
{ {
OsxData * od = (OsxData *)vdata; OsxData * od = (OsxData *)vdata;
AudioBuffer * buffer = &outData->mBuffers[0]; AudioBuffer * buffer = &bufferList->mBuffers[0];
int bufferSize = buffer->mDataByteSize/sizeof(Float32); int bufferSize = buffer->mDataByteSize;
int floatsToCopy; int bytesToCopy;
int curpos = 0; int curpos = 0;
DEBUG("entering IOProc\n");
pthread_mutex_lock(&od->mutex); pthread_mutex_lock(&od->mutex);
while((od->go || od->len) && bufferSize) { while((od->go || od->len) && bufferSize) {
while(od->go && od->len < bufferSize && while(od->go && od->len < bufferSize &&
od->len < BUFFER_SIZE) od->len < BUFFER_SIZE)
{ {
pthread_cond_signal(&od->condition);
pthread_cond_wait(&od->condition, &od->mutex); pthread_cond_wait(&od->condition, &od->mutex);
} }
floatsToCopy = od->len < bufferSize ? od->len : bufferSize; bytesToCopy = od->len < bufferSize ? od->len : bufferSize;
bufferSize -= floatsToCopy; bufferSize -= bytesToCopy;
od->len -= bytesToCopy;
if(od->pos+floatsToCopy > BUFFER_SIZE) { if(od->pos+bytesToCopy > BUFFER_SIZE) {
int floats = BUFFER_SIZE-od->pos; int bytes = BUFFER_SIZE-od->pos;
memcpy(buffer->mData+curpos, od->buffer+od->pos, memcpy(buffer->mData+curpos, od->buffer+od->pos, bytes);
floats*sizeof(Float32));
od->len -= floats;
od->pos = 0; od->pos = 0;
curpos += floats; curpos += bytes;
floatsToCopy -= floats; bytesToCopy -= bytes;
} }
memcpy(buffer->mData+curpos, od->buffer+od->pos, assert(bytesToCopy);
floatsToCopy*sizeof(Float32)); memcpy(buffer->mData+curpos, od->buffer+od->pos, bytesToCopy);
od->len -= floatsToCopy; od->pos += bytesToCopy;
od->pos += floatsToCopy; curpos += bytesToCopy;
curpos += floatsToCopy;
if(od->pos >= BUFFER_SIZE) od->pos = 0;
} }
if(bufferSize) { if(bufferSize) {
memset(buffer->mData+curpos, 0, bufferSize*sizeof(Float32)); memset(buffer->mData+curpos, 0, bufferSize);
} }
pthread_mutex_unlock(&od->mutex);
pthread_cond_signal(&od->condition); pthread_cond_signal(&od->condition);
pthread_mutex_unlock(&od->mutex);
DEBUG("exiting IOProc\n");
return 0; return 0;
} }
static int osx_openDevice(AudioOutput * audioOutput) { static int osx_openDevice(AudioOutput * audioOutput) {
int err;
OsxData * od = (OsxData *)audioOutput->data; OsxData * od = (OsxData *)audioOutput->data;
UInt32 propertySize; ComponentDescription desc;
Component comp;
AURenderCallbackStruct callback;
AudioFormat * audioFormat = &audioOutput->outAudioFormat; AudioFormat * audioFormat = &audioOutput->outAudioFormat;
UInt32 bufferByteCount = 8192; AudioStreamBasicDescription streamDesc;
propertySize = sizeof(od->deviceID); desc.componentType = kAudioUnitType_Output;
err = AudioHardwareGetProperty( desc.componentSubType = kAudioUnitSubType_DefaultOutput;
kAudioHardwarePropertyDefaultOutputDevice, desc.componentManufacturer = kAudioUnitManufacturer_Apple;
&propertySize, &od->deviceID); desc.componentFlags = 0;
if(err || od->deviceID == kAudioDeviceUnknown) { desc.componentFlagsMask = 0;
ERROR("Not able to get the default OS X device\n");
DEBUG("finding component\n");
comp = FindNextComponent(NULL, &desc);
if(comp == 0) {
ERROR("Error finding OS X component\n");
return -1; return -1;
} }
od->streamDesc.mFormatID = kAudioFormatLinearPCM; DEBUG("opening component\n");
od->streamDesc.mSampleRate = audioFormat->sampleRate; if(OpenAComponent(comp, &od->au) != noErr) {
od->streamDesc.mFormatFlags = kLinearPCMFormatFlagIsFloat | ERROR("Unable to open OS X component\n");
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; return -1;
} }
propertySize = sizeof(UInt32); DEBUG("initializing au\n");
err = AudioDeviceSetProperty(od->deviceID, 0, 0, false, if(AudioUnitInitialize(od->au) != 0) {
kAudioDevicePropertyBufferSize, CloseComponent(od->au);
propertySize, &bufferByteCount); ERROR("Unable to initialuze OS X audio unit\n");
return -1;
}
err = AudioDeviceAddIOProc(od->deviceID, osx_IOProc, od); callback.inputProc = osx_render;
callback.inputProcRefCon = od;
DEBUG("set callback\n");
if(AudioUnitSetProperty(od->au, kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input, 0,
&callback, sizeof(callback)) != 0)
{
AudioUnitUninitialize(od->au);
CloseComponent(od->au);
ERROR("unable to set callbak for OS X audio unit\n");
return -1;
}
streamDesc.mSampleRate = audioFormat->sampleRate;
streamDesc.mFormatID = kAudioFormatLinearPCM;
streamDesc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger |
kLinearPCMFormatFlagIsBigEndian;
streamDesc.mBytesPerPacket = audioFormat->channels*audioFormat->bits/8;
streamDesc.mFramesPerPacket = 1;
streamDesc.mBytesPerFrame = streamDesc.mBytesPerPacket;
streamDesc.mChannelsPerFrame = audioFormat->channels;
streamDesc.mBitsPerChannel = audioFormat->bits;
DEBUG("set format\n");
if(AudioUnitSetProperty(od->au, kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input, 0,
&streamDesc, sizeof(streamDesc)) != 0)
{
AudioUnitUninitialize(od->au);
CloseComponent(od->au);
ERROR("Unable to set format on OS X device\n");
return -1;
}
DEBUG("start\n");
int err = AudioOutputUnitStart(od->au);
if(err) { if(err) {
ERROR("error adding IOProc\n"); ERROR("unable to start audio output: %i\n", err);
return -1; return -1;
} }
@ -253,86 +242,54 @@ static int osx_openDevice(AudioOutput * audioOutput) {
audioOutput->open = 1; audioOutput->open = 1;
DEBUG("opened OS X device\n");
return 0; 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) { static int osx_play(AudioOutput * audioOutput, char * playChunk, int size) {
OsxData * od = (OsxData *)audioOutput->data; OsxData * od = (OsxData *)audioOutput->data;
int floatsToCopy; int bytesToCopy;
int curpos;
size /= 2;
DEBUG("entering osx_play\n");
pthread_mutex_lock(&od->mutex); pthread_mutex_lock(&od->mutex);
DEBUG("entering while loop\n"); curpos = od->pos+od->len;
while(size) { if(curpos >= BUFFER_SIZE) curpos -= BUFFER_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"); while(size) {
while(od->len >= BUFFER_SIZE) {
if(curpos!=od->pos) {
DEBUG("%i != %i\n", curpos, od->pos);
abort();
}
pthread_cond_signal(&od->condition);
pthread_cond_wait(&od->condition, &od->mutex); pthread_cond_wait(&od->condition, &od->mutex);
} }
floatsToCopy = BUFFER_SIZE - od->len; bytesToCopy = BUFFER_SIZE - od->len;
floatsToCopy = floatsToCopy < size ? floatsToCopy : size; bytesToCopy = bytesToCopy < size ? bytesToCopy : size;
size -= floatsToCopy; size -= bytesToCopy;
od->len += bytesToCopy;
if(od->pos+floatsToCopy > BUFFER_SIZE) { if(curpos+bytesToCopy > BUFFER_SIZE) {
int floats = BUFFER_SIZE-od->pos; int bytes = BUFFER_SIZE-curpos;
copyIntBufferToFloat(playChunk, memcpy(od->buffer+curpos, playChunk, bytes);
audioOutput->outAudioFormat.bits/8, curpos = 0;
od->buffer, playChunk += bytes;
floats); bytesToCopy -= bytes;
od->pos = 0;
od->len += floats;
playChunk += floats*sizeof(Float32);
} }
copyIntBufferToFloat(playChunk, assert(bytesToCopy);
audioOutput->outAudioFormat.bits/8, memcpy(od->buffer+curpos, playChunk, bytesToCopy);
od->buffer, curpos += bytesToCopy;
floatsToCopy); playChunk += bytesToCopy;
od->pos += floatsToCopy;
od->len += floatsToCopy; if(curpos >= BUFFER_SIZE) curpos = 0;
playChunk += floatsToCopy*sizeof(Float32);
} }
pthread_mutex_unlock(&od->mutex);
pthread_cond_signal(&od->condition); pthread_cond_signal(&od->condition);
pthread_mutex_unlock(&od->mutex);
DEBUG("exiting osx_play\n");
return 0; return 0;
} }