I previously stated that I had a fix for the AudioOutputUnitStart -66681 error, then updated the post stating I’d failed.
Today, Sijo asked if I had any idea about how to fix the problem. Unfortunately I don’t…. But I haven’t had that error for a long time, so I figured I might as well post my audio setup code here (along with the callbacks). Hopefully it’s a useful comparison. It works, and is reasonably documented (IMO). It’s not my exact code, it’s a couple of classes copied & pasted, so it’ll have to be adapted somewhat. Let me know if there are any problems.
First, the AudioSourceInterface. Implement this to provide the audio samples.
#pragma once
#include <Coreaudio/CoreAudioTypes.h> // All this for SInt16....
class AudioSourceInterface
{
public:
virtual void audioRequestedFloat(float* output, int numSamples, int numChannels) = 0;
virtual void audioRequestedInt(SInt16* output, int numSamples, int numChannels) = 0;
};
Now the AudioEngine base class. First the header, which is later subclassed for the iphone implementation.
#pragma once
#include <AudioUnit/AudioUnit.h>
class AudioSourceInterface;
class AudioEngine
{
public:
AudioEngine();
virtual ~AudioEngine() {}
void setup(AudioSourceInterface* audioSource);
void exit();
protected:
AudioUnit mAudioUnit;
AudioSourceInterface* mAudioSource;
float mSampleRate;
virtual void createAudioUnit() = 0;
virtual void enableAudioUnit() {}
void setupAudioUnit();
void setupIntAudioStream(AudioStreamBasicDescription* audioFormat) const;
void setupFloatAudioStream(AudioStreamBasicDescription* audioFormat) const;
static OSStatus floatRenderer(void* inRefCon,
AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList* ioData);
static OSStatus intRenderer(void* inRefCon,
AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList* ioData);
};
And the AudioEngine cpp body…
#include "AudioEngine.h"
#include <AudioUnit/AudioUnit.h>
#include <AudioUnit/AudioComponent.h>
#include "AudioSourceInterface.h"
#include "Core.h"
AudioEngine::AudioEngine():
mSampleRate(kSampleRate)
{
}
void AudioEngine::setup(AudioSourceInterface* audioSource)
{
mAudioSource = audioSource;
createAudioUnit();
enableAudioUnit();
setupAudioUnit();
}
void AudioEngine::setupAudioUnit()
{
// We tell the Output Unit what format we're going to supply data to it
// this is necessary if you're providing data through an input callback
// AND you want the DefaultOutputUnit to do any format conversions
// necessary from your format to the device's format.
AudioStreamBasicDescription audioFormat;
setupIntAudioStream(&audioFormat);
OSStatus err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input, 0, &audioFormat, sizeof(audioFormat));
assert(err == noErr);
// Initialize unit
err = AudioUnitInitialize(mAudioUnit);
assert(err == noErr);
// Set render callback
AURenderCallbackStruct input;
input.inputProc = intRenderer;
input.inputProcRefCon = this;
err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input, 0, &input, sizeof(input));
assert(err == noErr);
// Start the rendering
// The DefaultOutputUnit will do any format conversions to the format of the default device
err = AudioOutputUnitStart(mAudioUnit);
assert(err == noErr);
// we call the CFRunLoopRunInMode to service any notifications that the audio
// system has to deal with
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 2, false);
}
void AudioEngine::exit()
{
OSStatus err = AudioOutputUnitStop(mAudioUnit);
assert(err == noErr);
err = AudioUnitUninitialize(mAudioUnit);
assert(err == noErr);
err = AudioComponentInstanceDispose(mAudioUnit);
assert(err == noErr);
}
void AudioEngine::setupIntAudioStream(AudioStreamBasicDescription* audioFormat) const
{
memset(audioFormat, 0, sizeof(audioFormat));
audioFormat->mSampleRate = mSampleRate;
audioFormat->mFormatID = kAudioFormatLinearPCM;
audioFormat->mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
audioFormat->mFramesPerPacket = 1;
audioFormat->mChannelsPerFrame = 1;
audioFormat->mBitsPerChannel = 16;
audioFormat->mBytesPerPacket = 2;
audioFormat->mBytesPerFrame = 2;
}
void AudioEngine::setupFloatAudioStream(AudioStreamBasicDescription* audioFormat) const
{
memset(audioFormat, 0, sizeof(audioFormat));
audioFormat->mSampleRate = mSampleRate;
audioFormat->mFormatID = kAudioFormatLinearPCM;
audioFormat->mFormatFlags = kAudioFormatFlagIsFloat | kLinearPCMFormatFlagIsNonInterleaved;
audioFormat->mFramesPerPacket = 1;
audioFormat->mChannelsPerFrame = 2;
audioFormat->mBitsPerChannel = 32;
audioFormat->mBytesPerPacket = 4;
audioFormat->mBytesPerFrame = 4;
}
OSStatus AudioEngine::floatRenderer(void* inRefCon,
AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList* ioData)
{
assert(inNumberFrames <= kAudioBufferSizeFloats);
AudioEngine* self = (AudioEngine*)inRefCon;
float* buffer = static_cast<float*>(ioData->mBuffers[0].mData);
uint bufferSize = ioData->mBuffers[0].mDataByteSize;
// Get synth sound data for one channel
self->mAudioSource->audioRequestedFloat(buffer, inNumberFrames, 1);
// Duplicate single channel across all channels
for (UInt32 channel = 1; channel < ioData->mNumberBuffers; channel++)
memcpy (ioData->mBuffers[channel].mData, buffer, bufferSize);
return noErr;
}
OSStatus AudioEngine::intRenderer(void* inRefCon,
AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList* ioData)
{
assert(inNumberFrames <= kAudioBufferSizeFloats);
AudioEngine* self = (AudioEngine*)inRefCon;
SInt16* buffer = static_cast<sint16*>(ioData->mBuffers[0].mData);
// Get synth sound data for one channel
self->mAudioSource->audioRequestedInt(buffer, inNumberFrames, 1);
// Duplicate single channel across all channels
uint bufferSize = ioData->mBuffers[0].mDataByteSize;
for (UInt32 channel = 1; channel < ioData->mNumberBuffers; channel++)
memcpy (ioData->mBuffers[channel].mData, buffer, bufferSize);
return noErr;
}
The AudioEngineIPhoneImpl header.
#pragma once
#include "AudioEngine.h"
class AudioEngineIPhoneImpl: public AudioEngine
{
protected:
void enableAudioUnit();
void createAudioUnit();
};
And the AudioEngineIPhoneImpl body.
#include "AudioEngineIPhoneImpl.h"
#include <AudioUnit/AudioComponent.h>
// http://michael.tyson.id.au/2008/11/04/using-remoteio-audio-unit/
const int kOutputBus = 0;
const int kInputBus = 1;
void AudioEngineIPhoneImpl::createAudioUnit()
{
// Create the audio component instance
AudioComponentDescription desc;
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_RemoteIO;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
AudioComponent comp = AudioComponentFindNext(NULL, &desc);
assert(comp);
OSStatus err = AudioComponentInstanceNew(comp, &mAudioUnit);
assert(err == noErr);
}
void AudioEngineIPhoneImpl::enableAudioUnit()
{
// Disable input - flag must be 0 to enable larger speakers,
// otherwise small ear speaker is used.
UInt32 flag = 0;
OSStatus err = AudioUnitSetProperty(mAudioUnit, kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Input, kInputBus, &flag, sizeof(flag));
assert(err == noErr);
// Enable IO for output
flag = 1;
err = AudioUnitSetProperty(mAudioUnit, kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Output, kOutputBus, &flag, sizeof(flag));
assert(err == noErr);
}
Hopefully all that is a good comparison!