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:
parent
d4352ac996
commit
d6607f5f23
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user