Bug 742160 - Import AudioUnit cubeb backend. r=doublec

This commit is contained in:
Matthew Gregan 2012-04-16 15:04:42 +12:00
parent c6d2de46e7
commit b5111a2ca4
2 changed files with 261 additions and 2 deletions

View File

@ -45,14 +45,20 @@ include $(DEPTH)/config/autoconf.mk
MODULE = cubeb
LIBRARY_NAME = cubeb
FORCE_STATIC_LIB= 1
ifeq (WINNT,$(OS_TARGET))
ifeq ($(OS_TARGET),WINNT)
VISIBILITY_FLAGS =
endif
ifeq (WINNT,$(OS_TARGET))
ifeq ($(OS_TARGET),WINNT)
CSRCS = \
cubeb_winmm.c \
$(NULL)
endif
ifeq ($(OS_TARGET),Darwin)
CSRCS = \
cubeb_audiounit.c \
$(NULL)
endif
include $(topsrcdir)/config/rules.mk

View File

@ -0,0 +1,253 @@
/*
* Copyright © 2011 Mozilla Foundation
*
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
#undef NDEBUG
#include <assert.h>
#include <pthread.h>
#include <stdlib.h>
#include <CoreServices/CoreServices.h>
#include <AudioUnit/AudioUnit.h>
#include "cubeb/cubeb.h"
#define NBUFS 4
struct cubeb_stream {
AudioUnit unit;
cubeb_data_callback data_callback;
cubeb_state_callback state_callback;
void * user_ptr;
AudioStreamBasicDescription sample_spec;
pthread_mutex_t mutex;
uint64_t frames_played;
uint64_t frames_queued;
int shutdown;
int draining;
};
static OSStatus
audio_unit_output_callback(void * user_ptr, AudioUnitRenderActionFlags * flags,
AudioTimeStamp const * tstamp, UInt32 bus, UInt32 nframes,
AudioBufferList * bufs)
{
cubeb_stream * stm;
unsigned char * buf;
long got;
OSStatus r;
assert(bufs->mNumberBuffers == 1);
buf = bufs->mBuffers[0].mData;
stm = user_ptr;
pthread_mutex_lock(&stm->mutex);
if (stm->draining || stm->shutdown) {
pthread_mutex_unlock(&stm->mutex);
if (stm->draining) {
r = AudioOutputUnitStop(stm->unit);
assert(r == 0);
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
}
return noErr;
}
pthread_mutex_unlock(&stm->mutex);
got = stm->data_callback(stm, stm->user_ptr, buf, nframes);
pthread_mutex_lock(&stm->mutex);
if (got < 0) {
// XXX handle this case.
assert(false);
pthread_mutex_unlock(&stm->mutex);
return noErr;
}
if (got < nframes) {
size_t got_bytes = got * stm->sample_spec.mBytesPerFrame;
size_t rem_bytes = (nframes - got) * stm->sample_spec.mBytesPerFrame;
stm->draining = 1;
memset(buf + got_bytes, 0, rem_bytes);
}
stm->frames_played = stm->frames_queued;
stm->frames_queued += got;
pthread_mutex_unlock(&stm->mutex);
return noErr;
}
int
cubeb_init(cubeb ** context, char const * context_name)
{
*context = (void *) 0xdeadbeef;
return CUBEB_OK;
}
void
cubeb_destroy(cubeb * ctx)
{
assert(ctx == (void *) 0xdeadbeef);
}
int
cubeb_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
cubeb_stream_params stream_params, unsigned int latency,
cubeb_data_callback data_callback, cubeb_state_callback state_callback,
void * user_ptr)
{
AudioStreamBasicDescription ss;
ComponentDescription desc;
cubeb_stream * stm;
Component comp;
AURenderCallbackStruct input;
unsigned int buffer_size;
OSStatus r;
assert(context == (void *) 0xdeadbeef);
*stream = NULL;
if (stream_params.rate < 1 || stream_params.rate > 192000 ||
stream_params.channels < 1 || stream_params.channels > 32 ||
latency < 1 || latency > 2000) {
return CUBEB_ERROR_INVALID_FORMAT;
}
memset(&ss, 0, sizeof(ss));
ss.mFormatFlags = 0;
switch (stream_params.format) {
case CUBEB_SAMPLE_S16LE:
ss.mBitsPerChannel = 16;
ss.mFormatFlags |= kAudioFormatFlagIsSignedInteger;
break;
case CUBEB_SAMPLE_S16BE:
ss.mBitsPerChannel = 16;
ss.mFormatFlags |= kAudioFormatFlagIsSignedInteger |
kAudioFormatFlagIsBigEndian;
break;
case CUBEB_SAMPLE_FLOAT32LE:
ss.mBitsPerChannel = 32;
ss.mFormatFlags |= kAudioFormatFlagIsFloat;
break;
case CUBEB_SAMPLE_FLOAT32BE:
ss.mBitsPerChannel = 32;
ss.mFormatFlags |= kAudioFormatFlagIsFloat |
kAudioFormatFlagIsBigEndian;
break;
default:
return CUBEB_ERROR_INVALID_FORMAT;
}
ss.mFormatID = kAudioFormatLinearPCM;
ss.mFormatFlags |= kLinearPCMFormatFlagIsPacked;
ss.mSampleRate = stream_params.rate;
ss.mChannelsPerFrame = stream_params.channels;
ss.mBytesPerFrame = (ss.mBitsPerChannel / 8) * ss.mChannelsPerFrame;
ss.mFramesPerPacket = 1;
ss.mBytesPerPacket = ss.mBytesPerFrame * ss.mFramesPerPacket;
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_DefaultOutput;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
comp = FindNextComponent(NULL, &desc);
assert(comp);
stm = calloc(1, sizeof(*stm));
assert(stm);
stm->data_callback = data_callback;
stm->state_callback = state_callback;
stm->user_ptr = user_ptr;
stm->sample_spec = ss;
r = pthread_mutex_init(&stm->mutex, NULL);
assert(r == 0);
stm->frames_played = 0;
stm->frames_queued = 0;
r = OpenAComponent(comp, &stm->unit);
assert(r == 0);
input.inputProc = audio_unit_output_callback;
input.inputProcRefCon = stm;
r = AudioUnitSetProperty(stm->unit, kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Global, 0, &input, sizeof(input));
assert(r == 0);
r = AudioUnitSetProperty(stm->unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
0, &ss, sizeof(ss));
assert(r == 0);
buffer_size = ss.mSampleRate / 1000.0 * latency * ss.mBytesPerFrame / NBUFS;
if (buffer_size % ss.mBytesPerFrame != 0) {
buffer_size += ss.mBytesPerFrame - (buffer_size % ss.mBytesPerFrame);
}
assert(buffer_size % ss.mBytesPerFrame == 0);
r = AudioUnitInitialize(stm->unit);
assert(r == 0);
*stream = stm;
return CUBEB_OK;
}
void
cubeb_stream_destroy(cubeb_stream * stm)
{
OSStatus r;
stm->shutdown = 1;
r = AudioOutputUnitStop(stm->unit);
assert(r == 0);
r = AudioUnitUninitialize(stm->unit);
assert(r == 0);
r = CloseComponent(stm->unit);
assert(r == 0);
r = pthread_mutex_destroy(&stm->mutex);
assert(r == 0);
free(stm);
}
int
cubeb_stream_start(cubeb_stream * stm)
{
OSStatus r;
r = AudioOutputUnitStart(stm->unit);
assert(r == 0);
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED);
return CUBEB_OK;
}
int
cubeb_stream_stop(cubeb_stream * stm)
{
OSStatus r;
r = AudioOutputUnitStop(stm->unit);
assert(r == 0);
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
return CUBEB_OK;
}
int
cubeb_stream_get_position(cubeb_stream * stm, uint64_t * position)
{
pthread_mutex_lock(&stm->mutex);
*position = stm->frames_played;
pthread_mutex_unlock(&stm->mutex);
return CUBEB_OK;
}