//
// AudioRenderer.mm
// HighwayDash
//
// Created by John Ryland on 18/02/2016.
// Copyright © 2016 John Ryland. All rights reserved.
//
#import "AudioRenderer.h"
#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>
struct AudioRenderer::Pimpl
{
// private implementation details here
AudioComponentInstance m_audioUnit;
int m_channels;
int m_bytesPerChannel;
int m_sampleRate;
//AudioBuffer m_buffer;
};
AudioRenderer::AudioRenderer()
: m_pimpl(std::unique_ptr<Pimpl>(new Pimpl()))
{
}
AudioRenderer::~AudioRenderer()
{
AudioUnitUninitialize(m_pimpl->m_audioUnit);
}
static void checkStatus(int status)
{
if (status)
printf("Audio Error %d\n", status);
}
// Callback when the audioUnit needs to fill buffers with audio data
static OSStatus playbackCallback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData) {
// TODO: check what happens for stereo or other cases
for (int i=0; i < ioData->mNumberBuffers; i++)
{
AudioBuffer buffer = ioData->mBuffers[i];
AudioRenderer* _this = static_cast<AudioRenderer*>(inRefCon);
_this->renderSamples(buffer.mData, inNumberFrames * 2); // _this->m_pimpl->m_channels);
}
return noErr;
}
void AudioRenderer::initialize(SampleRate a_rate, BitsPerChannel a_bitsPerChannel, Channels a_channels)
{
OSStatus status;
// Save parameters
m_pimpl->m_bytesPerChannel = a_bitsPerChannel / 8;
m_pimpl->m_channels = a_channels;
m_pimpl->m_sampleRate = a_rate;
// Describe audio component
AudioComponentDescription desc;
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_RemoteIO;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
// Get component
AudioComponent audioComponent = AudioComponentFindNext(NULL, &desc);
// Get audio units
status = AudioComponentInstanceNew(audioComponent, &m_pimpl->m_audioUnit);
checkStatus(status);
const int kOutputBus = 0;
// Enable IO for playback
UInt32 flag = 1;
status = AudioUnitSetProperty(m_pimpl->m_audioUnit,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Output,
kOutputBus,
&flag,
sizeof(flag));
checkStatus(status);
// Describe format
AudioStreamBasicDescription audioFormat;
audioFormat.mSampleRate = float(a_rate);
audioFormat.mFormatID = kAudioFormatLinearPCM;
audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
audioFormat.mFramesPerPacket = 1;
audioFormat.mChannelsPerFrame = a_channels;
audioFormat.mBitsPerChannel = a_bitsPerChannel;
audioFormat.mBytesPerPacket = m_pimpl->m_bytesPerChannel * a_channels;
audioFormat.mBytesPerFrame = m_pimpl->m_bytesPerChannel * a_channels;
// Apply format
status = AudioUnitSetProperty(m_pimpl->m_audioUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
kOutputBus,
&audioFormat,
sizeof(audioFormat));
checkStatus(status);
// Set output callback
AURenderCallbackStruct callbackStruct;
callbackStruct.inputProc = playbackCallback;
callbackStruct.inputProcRefCon = this;
status = AudioUnitSetProperty(m_pimpl->m_audioUnit,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Global,
kOutputBus,
&callbackStruct,
sizeof(callbackStruct));
checkStatus(status);
// Initialise
status = AudioUnitInitialize(m_pimpl->m_audioUnit);
checkStatus(status);
}
void AudioRenderer::start()
{
checkStatus(AudioOutputUnitStart(m_pimpl->m_audioUnit));
}
void AudioRenderer::stop()
{
checkStatus(AudioOutputUnitStop(m_pimpl->m_audioUnit));
}