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.

by Peter on 06/07/2008, in Software,

Name *:

Mail *:

Website:

*: required field.
Copyright © 2007-2008, Peter MacWhinnie. All Fancy Rights Reserved. Boring ones too.