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, 0 comments

The other day I decided to class-dump QTKit in hopes that QTMovie would have a real update-in-place method [1]. Well first of all, class-dump doesn’t work with 64 and 32 bit universal binaries so I didn’t actually use class-dump but rather o-tool [2].
Unfortunately QTKit doesn’t have some hidden update-in-place method. But! that isn’t to say it doesn’t have a bunch of really useful stuff that should be public for the sake of mine, and as a result everyone else’s sanity.
The following are just a few of the hidden methods, they’re simply the ones I found the most immediately useful.

@property float treble;
@property float bass;

The treble and bass properties do exactly what you think they do, they change the bass and treble levels on the receiving movie. The value scale is (oddly) between -8.0 and 8.0, 0.0 being default.

@property float balance;

Balance, just like the treble and bass properties, does exactly what you think it does. The value scale is between -1.0 and 1.0, 0.0 being default.

@property float gain;

I can’t say I am entirely sure about this property, it appears to be an alias for volume. The value scale is 0.0 to 1.0, 1.0 being default.

- (BOOL)isDRMAuthorized;
- (BOOL)isDRMProtected;

By far the most amusing of the hidden methods. I could see this being fairly useful.

@property BOOL saveable;

As far as I can tell this is just a convenience method that doesn’t really seem to do anything all that useful.

@property (copy) NSDictionary *annotations;

This has to be the most useful of the properties I found. ‘annotations’ is dictionary which contains all of the metadata associated with the file you’ve loaded into a QTMovie.

@property (copy) NSString *copyright;

This is likely another convenience method, the same information is available thru -attributeForKey: if you don’t feel like using private methods.

There we plenty of other private methods, many of which were convenience methods. The list above is simply of the methods I found immediately useful, there are dozens more.
If you wish to view the dump in its entirety the dump file is available here [3].


[1]: Yes, I am aware of -updateMovieFile, it doesn’t work for anything other then MOV files.
[2]: Okay, if you hadn’t guessed by now I dumped QTKit on Leopard.
[3]: Code dump generated with “otool -o -V /System/Library/Frameworks/QTKit.framework/Versions/A/QTKit > QTKit.dump”.

by Peter on 05/08/2008, in Cocoa, Software, 0 comments

In a move of what could only be described as absolute laziness, I am releasing the Transbar source into the general public.

It is under the MIT license:

/*
 * Copyright (c) 2008 Peter MacWhinnie
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

Transbar Page

by Peter on 04/29/2008, in Software, 0 comments

Before reading this post its important to keep in mind that this is nothing more then a massive brain dump. Its likely that there are spelling and grammatical errors, inaccuracies, unfounded claims, insults, and all around lack of polish. So with that in mind do enjoy this bunch of text, I enjoyed writing it.
In addition to all that, one must keep in mind that I’ve used Objective-C and the Cocoa SDK interchangeably in some places, so you’ll need to use your imagination a bit.

read more…

by Peter on 03/28/2008, in Software, 0 comments
Copyright © 2007-2008, Peter MacWhinnie. All Fancy Rights Reserved. Boring ones too.