For anyone who’s a) talked to me; b) followed by Twitter feed; you’ll know that OS X Leopards new AudioQueue Services is a brilliantly simple API, its very easy to use and understand. But, its also incredibly buggy and completely stops working with certain stream types. As a result of this I had to (somewhat angrily) write a wrapper for the output AudioUnit so I could continue to enjoy how simple things really were in implementation. So without further ado, here’s the header and implementation file. There is some documentation in the header section, however I make no guarantees about easy it will be for you to hook up, I wrote it specifically for my needs.
Header:
/*
* RAAudioQueue.h
* PlayerKit
*
* Created by Peter MacWhinnie on 5/31/08.
* Copyright 2008 Roundabout Software. All rights reserved.
*
*/
#include <AudioUnit/AudioUnit.h>
#include <CoreAudio/CoreAudio.h>
#ifndef RAAudioQueue_h
#define RAAudioQueue_h 1
/*!
@header RAAudioQueue
@abstract RAAudioQueue is a thin wrapper around AudioUnit_Output
@discussion RAAudioQueue is a paper thin wrapper around the output functionalities
of the AudioUnit framework. Its interface is designed to mimic leopards AudioQueue.
*/
/*!
@enum
@abstract RAAudioQueue errors
@discussion These are just errors that are specific to RAAudioQueue,
90% of the time the errors passed by the RAAudioQueue functions
are defined in a system framework, and not here.
@constant RAAudioQueueErrorCannotFindComponent This is returned if an output AudioUnit can’t be created
@constant RAAudioQueueErrorCannotCreateAudioQueue This is only returned if the memory cannot be allocated for an RAAudioQueueRef.
@constant RAAudioQueueErrorMissingParameters This is returned if you don’t pass in all the required parameters to a function.
*/
enum RAAudioQueueErrors
{
RAAudioQueueErrorCannotFindComponent = ‘Cmp?’,
RAAudioQueueErrorCannotCreateAudioQueue = ‘Mem?’,
RAAudioQueueErrorMissingParameters = ‘Pra?’,
};
/*!
@typedef RAAudioQueueRef
@abstract An Opaque RAAudioQueue struct
*/
typedef struct __RAAudioQueue * RAAudioQueueRef;
/*!
@typedef RAAudioQueueOutputProc
@abstract RAAudioQueueOutputProc is the template
@field audioQueue the sender
@field bufferCapacity the capacity of the AudioBuffer
@field buffer the AudioBuffer
@field userData data you passed to RAAudioQueue for this occassion.
*/
typedef void (*RAAudioQueueOutputProc)(RAAudioQueueRef audioQueue, UInt32 bufferCapacity, AudioBuffer *buffer, void *userData);
/*!
@function
@abstract Create a new RAAudioQueue
@discussion This method hides all the drudgery of opening the output audio unit.
@param inFormat the ASBD the RAAudioQueue is to use
@param inCallbackProc the callback to use when data is needed
@param inUserData userData that will be passed to you in the callback
@param outAudioQueue the resulting RAAudioQueue or 0 if there was an error.
*/
extern OSStatus RAAudioQueueNewOutput(AudioStreamBasicDescription *inFormat, RAAudioQueueOutputProc inCallbackProc, void *inUserData, RAAudioQueueRef *outAudioQueue);
/*!
@function
@abstract Dispose of an RAAudioQueue
@param audioQueue the RAAudioQueue to destroy
*/
extern OSStatus RAAudioQueueDispose(RAAudioQueueRef audioQueue);
/*!
@function
@abstract Start the specified audio queue
*/
extern OSStatus RAAudioQueueStart(RAAudioQueueRef audioQueue);
/*!
@function
@abstract Stop the specified audio queue
*/
extern OSStatus RAAudioQueueStop(RAAudioQueueRef audioQueue);
/*!
@function
@abstract RAAudioQueueSetProperty is a proxy for AudioUnitSetProperty
*/
extern OSStatus RAAudioQueueSetProperty(RAAudioQueueRef audioQueue, AudioUnitPropertyID inID, AudioUnitScope inScope, const void *inData, UInt32 inDataSize);
/*!
@function
@abstract RAAudioQueueGetProperty is a proxy for AudioUnitGetProperty
*/
extern OSStatus RAAudioQueueGetProperty(RAAudioQueueRef audioQueue, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void *outData, UInt32 *ioDataSize);
/*!
@function
@abstract RAAudioQueueSetParameter is a proxy for AudioUnitSetParameter
*/
extern OSStatus RAAudioQueueSetParameter(RAAudioQueueRef audioQueue, AudioUnitParameterID inID, AudioUnitScope inScope, AudioUnitElement inElement, AudioUnitParameterValue inValue, UInt32 inBufferOffsetInFrames);
/*!
@function
@abstract RAAudioQueueGetParameter is a proxy for AudioUnitGetParameter
*/
extern OSStatus RAAudioQueueGetParameter(RAAudioQueueRef audioQueue, AudioUnitParameterID inID, AudioUnitScope inScope, AudioUnitElement inElement, AudioUnitParameterValue *outValue);
/*!
@function
@abstract Set the volume level for the specified audio queue
*/
extern OSStatus RAAudioQueueSetVolume(RAAudioQueueRef audioQueue, float volume);
/*!
@function
@abstract Get the volume level for the specified audio queue
*/
extern OSStatus RAAudioQueueGetVolume(RAAudioQueueRef, float *volume);
#endif /* RAAudioQueue_h */
Implementation:
/*
* RAAudioQueue.c
* PlayerKit
*
* Created by Peter MacWhinnie on 5/31/08.
* Copyright 2008 Roundabout Software. All rights reserved.
*
*/
#include “RAAudioQueue.h”
#pragma mark Implementation
struct __RAAudioQueue
{
AudioUnit outputAudioUnit;
void *userData;
AudioStreamBasicDescription format;
RAAudioQueueOutputProc outputProc;
};
OSStatus __RAAudioQueueRenderCallback(void *userdata, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
{
RAAudioQueueRef self = (RAAudioQueueRef)userdata;
int i = 0;
while (i < ioData->mNumberBuffers)
{
AudioBuffer buffer = ioData->mBuffers[i];
self->outputProc(self, (inNumberFrames * self->format.mBytesPerPacket), &buffer, self->userData);
if(buffer.mDataByteSize == 0)
{
AudioOutputUnitStop(self->outputAudioUnit);
break;
}
i++;
}
return noErr;
}
#pragma mark -
#pragma mark Tools
const ComponentDescription __RAAudioQueue_componentDescription = {
/* componentType */ kAudioUnitType_Output,
/* componentSubType */ kAudioUnitSubType_HALOutput,
/* componentManufacturer */ kAudioUnitManufacturer_Apple,
/* componentFlags */ 0,
/* componentFlagsMask */ 0
};
OSStatus __RAAudioQueueGetOutputAudioUnit(AudioUnit *outputUnit)
{
Component component = FindNextComponent(NULL, (ComponentDescription *)&__RAAudioQueue_componentDescription);
if(!component) { return RAAudioQueueErrorCannotFindComponent; }
return (OSStatus)OpenAComponent(component, outputUnit);
}
#pragma mark -
#pragma mark Creation
OSStatus RAAudioQueueNewOutput(AudioStreamBasicDescription *inFormat, RAAudioQueueOutputProc inCallbackProc, void *inUserData, RAAudioQueueRef *outAudioQueue)
{
if(!inFormat || !inCallbackProc || !outAudioQueue) { return RAAudioQueueErrorMissingParameters; }
//Get output unit
AudioUnit outputUnit;
OSStatus error = __RAAudioQueueGetOutputAudioUnit(&outputUnit);
if(error != noErr) { return error; }
//Create an RAAudioQueueRef
RAAudioQueueRef audioQueue = malloc(sizeof(struct __RAAudioQueue));
if(!audioQueue) { return RAAudioQueueErrorCannotCreateAudioQueue; }
audioQueue->outputAudioUnit = outputUnit;
audioQueue->userData = inUserData;
audioQueue->outputProc = inCallbackProc;
audioQueue->format = *inFormat;
//Enable IO
UInt32 enableIO = 1;
error = AudioUnitSetProperty(outputUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableIO, sizeof(enableIO));
if(error != noErr) { return error; }
//Set the input format
error = AudioUnitSetProperty(outputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, inFormat, sizeof(*inFormat));
if(error != noErr) { return error; }
//Set the input callback
AURenderCallbackStruct inputCallbackStruct;
inputCallbackStruct.inputProc = __RAAudioQueueRenderCallback;
inputCallbackStruct.inputProcRefCon = audioQueue;
error = AudioUnitSetProperty(outputUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &inputCallbackStruct, sizeof(inputCallbackStruct));
if(error != noErr) { return error; }
//Initialize the AudioUnit
error = AudioUnitInitialize(outputUnit);
if(error != noErr) { return error; }
*outAudioQueue = audioQueue;
return noErr;
}
OSStatus RAAudioQueueDispose(RAAudioQueueRef audioQueue)
{
if(!audioQueue) { return RAAudioQueueErrorMissingParameters; }
audioQueue->userData = NULL;
audioQueue->outputProc = NULL;
AudioUnitUninitialize(audioQueue->outputAudioUnit);
OSStatus error = CloseComponent(audioQueue->outputAudioUnit);
free(audioQueue);
return error;
}
#pragma mark -
OSStatus RAAudioQueueStart(RAAudioQueueRef audioQueue)
{
if(!audioQueue) { return RAAudioQueueErrorMissingParameters; }
return (OSStatus)AudioOutputUnitStart(audioQueue->outputAudioUnit);
}
OSStatus RAAudioQueueStop(RAAudioQueueRef audioQueue)
{
if(!audioQueue) { return RAAudioQueueErrorMissingParameters; }
return (OSStatus)AudioOutputUnitStop(audioQueue->outputAudioUnit);
}
#pragma mark -
#pragma mark Parameters
OSStatus RAAudioQueueSetProperty(RAAudioQueueRef audioQueue, AudioUnitPropertyID inID, AudioUnitScope inScope, const void *inData, UInt32 inDataSize)
{
if(!audioQueue) { return RAAudioQueueErrorMissingParameters; }
return AudioUnitSetProperty(audioQueue->outputAudioUnit, inID, inScope, 0, inData, inDataSize);
}
OSStatus RAAudioQueueGetProperty(RAAudioQueueRef audioQueue, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void *outData, UInt32 *ioDataSize)
{
if(!audioQueue) { return RAAudioQueueErrorMissingParameters; }
return AudioUnitGetProperty(audioQueue->outputAudioUnit, inID, inScope, inElement, outData, ioDataSize);
}
#pragma mark -
OSStatus RAAudioQueueSetParameter(RAAudioQueueRef audioQueue, AudioUnitParameterID inID, AudioUnitScope inScope, AudioUnitElement inElement, AudioUnitParameterValue inValue, UInt32 inBufferOffsetInFrames)
{
if(!audioQueue) { return RAAudioQueueErrorMissingParameters; }
return AudioUnitSetParameter(audioQueue->outputAudioUnit, inID, inScope, inElement, inValue, inBufferOffsetInFrames);
}
OSStatus RAAudioQueueGetParameter(RAAudioQueueRef audioQueue, AudioUnitParameterID inID, AudioUnitScope inScope, AudioUnitElement inElement, AudioUnitParameterValue *outValue)
{
if(!audioQueue) { return RAAudioQueueErrorMissingParameters; }
return AudioUnitGetParameter(audioQueue->outputAudioUnit, inID, inScope, inElement, outValue);
}
#pragma mark -
OSStatus RAAudioQueueSetVolume(RAAudioQueueRef audioQueue, float volume)
{
return RAAudioQueueSetParameter(audioQueue, kHALOutputParam_Volume, kAudioUnitScope_Global, 0, volume, 0);
}
OSStatus RAAudioQueueGetVolume(RAAudioQueueRef audioQueue, float *volume)
{
return RAAudioQueueGetParameter(audioQueue, kHALOutputParam_Volume, kAudioUnitScope_Global, 0, (AudioUnitParameterValue *)&volume);
}
The above code may be used, modified, and redistributed in binary and source form, if all of the following conditions are met:
- Neither I, nor Roundabout Software is responsible for any damage this code might cause.
- Neither the name ‘Roundabout Software’ nor the name ‘Peter MacWhinnie’ may be used to endorse or promote products derived from the above source code without specific prior written permission.
That aside I would appreciate any improvements to this code be sent to me, this, however, is not a requirement.