Audio Queue Offline Render to use filter on PCM playback
Audio Queue Offline Render to use filter on PCM playback
- Subject: Audio Queue Offline Render to use filter on PCM playback
- From: Steven Winston <email@hidden>
- Date: Tue, 11 Aug 2009 14:48:13 -0700
So here's what I'm trying to do: offline render using Audio Queue
Services, instead of writing the PCM data to a file, modify the data
(apply a filter), play it out via RemoteIO. The most I can seem to do
is output "spurts" of data, or something that sounds like a sin wave.
Has anyone had any success with this?
Attached is my current implementation (minus filter, it's just an
Audio Queue Service to PCM to RemoteIO kind of thing). Any pointing
towards the right direction from the sages that read this list would
be very much appreciated!
// AudioQueueServices.cpp
// SteveIPhoneEngine
//
// Created by Steve Winston on 2/12/09.
// Copyright 2009 __MyCompanyName__. All rights reserved.
//
OSStatus AudioQueueServicesMgr::AUPlaybackCallback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp
*inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
AudioBufferList *ioData) {
AudioQueueServicesMgr *THIS = (AudioQueueServicesMgr *) inRefCon;
if(THIS->AmDone) {
//can have anynumber of input buffers so
UInt32 *frameBuffer = (UInt32*)ioData->mBuffers[0].mData;
if(inBusNumber == 0) {
for(UInt32 i = 0; i < inNumberFrames ; i++) {
frameBuffer[i] =
((UInt32*)THIS->captureABL.mBuffers[0].mData)[i];
THIS->AmDone = FALSE;
}
}
}
return noErr;
}
void AudioQueueServicesMgr::TimerCallback(CFRunLoopTimerRef timer,
void *inRefCon) {
AudioQueueServicesMgr *THIS = (AudioQueueServicesMgr *) inRefCon;
if(THIS->AmDone == FALSE) {
UInt32 reqFrames = (THIS->mBufferByteSize / 2) /
THIS->captureFormat.mBytesPerFrame;
OSStatus work = AudioQueueOfflineRender(THIS->mQueue,
&THIS->ts, THIS->captureBuffer, reqFrames);
if(work != 0)
return;
THIS->captureABL.mBuffers[0].mData = THIS->captureBuffer->mAudioData;
THIS->captureABL.mBuffers[0].mDataByteSize =
THIS->captureBuffer->mAudioDataByteSize;
UInt32 writeFrames =
THIS->captureABL.mBuffers[0].mDataByteSize /
THIS->captureFormat.mBytesPerFrame;
THIS->ts.mSampleTime += writeFrames;
THIS->AmDone = TRUE;
}
}
void AudioQueueServicesMgr::QueueCallback( void * inUserData,
AudioQueueRef inAQ, AudioQueueBufferRef inCompleteAQBuffer) {
OSStatus result = noErr;
AudioQueueServicesMgr *THIS = (AudioQueueServicesMgr *)inUserData;
if(DisposeBuffer(inAQ, THIS->mBuffersToDispose, inCompleteAQBuffer))
return;
UInt32 nPackets = 0;
if((CurFileInfo->mFileDataInQueue) && (THIS->mFileInfo.size() == 1) &&
(!THIS->mStopAtEnd))
nPackets = THIS->GetNumPacketsToRead(CurFileInfo);
else
{
UInt32 numBytes;
while (nPackets == 0) {
nPackets = THIS->GetNumPacketsToRead(CurFileInfo);
result = AudioFileReadPackets(CurFileInfo->mAFID, false, &numBytes,
THIS->mPacketDescs, THIS->mCurrentPacket, &nPackets,
inCompleteAQBuffer->mAudioData);
inCompleteAQBuffer->mAudioDataByteSize = numBytes;
if(nPackets == 0) {
THIS->mCurrentPacket = 0;
if(CurFileInfo->mLoadAtOnce)
CurFileInfo->mFileDataInQueue = true;
UInt32 theNextFileIndex = (THIS->mCurrentFileIndex<
THIS->mFileInfo.size()-1) ? THIS->mCurrentFileIndex + 1 : 0;
if(theNextFileIndex == 0 && THIS->mStopAtEnd) {
result = AudioQueueStop(inAQ, false);
return;
}
SInt8 theQueueState = GetQueueStateForNextBuffer(CurFileInfo,
THIS->mFileInfo[theNextFileIndex]);
if(theNextFileIndex != THIS->mCurrentFileIndex) {
result = AudioFileClose(CurFileInfo->mAFID);
THIS->mCurrentFileIndex = theNextFileIndex;
result = LoadFileDataInfo(CurFileInfo->mFilePath, CurFileInfo->mAFID,
CurFileInfo->mFileFormat, CurFileInfo->mFileDataSize);
}
switch (theQueueState) {
case kQueueState_ResizeBuffer:
inCompleteAQBuffer->mAudioDataByteSize = CurFileInfo->mFileDataSize;
AttachNewCookie(inAQ, CurFileInfo);
break;
case kQueueState_NeedNewCookie:
AttachNewCookie(inAQ, CurFileInfo);
break;
case kQueueState_NeedNewBuffers:
THIS->mBuffersToDispose.push_back(inCompleteAQBuffer);
THIS->SetupBuffers(CurFileInfo);
break;
case kQueueState_NeedNewQueue:
THIS->mMakeNewQueueWhenStopped = true;
AudioQueueStop(inAQ, false);
break;
}}}}
AudioQueueEnqueueBuffer(inAQ, inCompleteAQBuffer, (THIS->mPacketDescs
? nPackets : 0), THIS->mPacketDescs);
if(CurFileInfo->mLoadAtOnce)
CurFileInfo->mFileDataInQueue = true;
THIS->mCurrentPacket += nPackets;
}
OSStatus AudioQueueServicesMgr::SetupAudioUnit( _FileInfo *inFileInfo ) {
OSStatus status;
AudioComponentDescription desc;
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_RemoteIO;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
AudioComponent outputComponent = AudioComponentFindNext(NULL, &desc);
status = AudioComponentInstanceNew(outputComponent, &audioUnit);
if(status)
return status;
UInt32 flag = 1; //set kAudioUnitScope_Output to on
status = AudioUnitSetProperty(audioUnit,
kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, kOutputBus,
&flag, sizeof(flag));
if(status)
return status;
AURenderCallbackStruct callbackStruct;
callbackStruct.inputProc = AUPlaybackCallback;
callbackStruct.inputProcRefCon = this;
status = AudioUnitSetProperty(audioUnit,
kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global,
kOutputBus, &callbackStruct, sizeof(callbackStruct));
if(status)
return status;
AudioStreamBasicDescription outputFormat;
outputFormat.mSampleRate = inFileInfo->mFileFormat.mSampleRate; //44100.00;
outputFormat.mFormatID = inFileInfo->mFileFormat.mFormatID;
//kAudioFormatLinearPCM;
outputFormat.mFormatFlags = inFileInfo->mFileFormat.mFormatFlags;
//kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
outputFormat.mFramesPerPacket =
inFileInfo->mFileFormat.mFramesPerPacket; //1;
outputFormat.mChannelsPerFrame =
inFileInfo->mFileFormat.mChannelsPerFrame; //2;
outputFormat.mBitsPerChannel =
inFileInfo->mFileFormat.mBitsPerChannel; //16;
outputFormat.mBytesPerPacket = inFileInfo->mFileFormat.mBytesPerPacket; //4;
outputFormat.mBytesPerFrame = inFileInfo->mFileFormat.mBytesPerFrame; //4;
AudioUnitSetProperty(audioUnit, kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output, 0, &outputFormat, sizeof(outputFormat));
status = AudioUnitInitialize(audioUnit);
return status;
}
OSStatus AudioQueueServicesMgr::SetupQueue(_FileInfo *inFileInfo) {
UInt32 size = 0;
OSStatus result = AudioQueueNewOutput(&inFileInfo->mFileFormat,
QueueCallback, this, CFRunLoopGetCurrent(), kCFRunLoopCommonModes, 0,
&mQueue);
size = sizeof(UInt32);
result = AudioFileGetProperty(inFileInfo->mAFID,
kAudioFilePropertyMagicCookieData, &size, NULL);
if(!result && size) {
char* cookie = new char [size];
AudioFileGetProperty(inFileInfo->mAFID,
kAudioFilePropertyMagicCookieData, &size, cookie);
AudioQueueSetProperty(mQueue, kAudioQueueProperty_MagicCookie, cookie, size);
delete [] cookie;
}
AudioQueueAddPropertyListener(mQueue, kAudioQueueProperty_IsRunning,
QueueStoppedProc, this);
mMakeNewQueueWhenStopped = false;
result = SetVolume(mVolume);
return result;
}
OSStatus AudioQueueServicesMgr::SetupBuffers (_FileInfo *inFileInfo) {
OSStatus result = 0;
int numBuffersToQueue = kNumBuffer;
UInt32 maxPacketSize;
UInt32 size = sizeof(maxPacketSize);
result = AudioFileGetProperty(inFileInfo->mAFID,
kAudioFilePropertyPacketSizeUpperBound, &size, &maxPacketSize);
bool isFormatVBR = (inFileInfo->mFileFormat.mBytesPerPacket == 0 ||
inFileInfo->mFileFormat.mFramesPerPacket == 0);
CalculateBytesForTime(inFileInfo->mFileFormat, maxPacketSize, 1.0,
&mBufferByteSize, &mNumPacketsToRead);
if((mBufferByteSize * numBuffersToQueue) > inFileInfo->mFileDataSize)
inFileInfo->mLoadAtOnce = true;
if(inFileInfo->mLoadAtOnce){
UInt64 theFileNumPackets;
size = sizeof(UInt64);
result = AudioFileGetProperty(inFileInfo->mAFID,
kAudioFilePropertyAudioDataPacketCount, &size, &theFileNumPackets);
mNumPacketsToRead = (UInt32)theFileNumPackets;
mBufferByteSize = inFileInfo->mFileDataSize;
numBuffersToQueue = kNumBuffer;
} else
mNumPacketsToRead = mBufferByteSize / maxPacketSize;
if(isFormatVBR)
mPacketDescs = new AudioStreamPacketDescription[mNumPacketsToRead];
else
mPacketDescs = NULL;
if(acl != NULL)
free(acl);
acl = NULL;
if(AudioFileGetProperty(inFileInfo->mAFID,
kAudioFilePropertyChannelLayout, &size, NULL) == noErr) {
acl = (AudioChannelLayout *)malloc(size);
AudioFileGetProperty(inFileInfo->mAFID,
kAudioFilePropertyChannelLayout, &size, acl);
AudioQueueSetProperty(mQueue, kAudioFilePropertyChannelLayout, acl, size);
}
captureFormat.mSampleRate = inFileInfo->mFileFormat.mSampleRate;
captureFormat.SetAUCanonical(inFileInfo->mFileFormat.mChannelsPerFrame, true);
for(int i = 0; i < numBuffersToQueue; ++i) {
AudioQueueAllocateBuffer(mQueue, mBufferByteSize, &mBuffers[i]);
if(inFileInfo->mLoadAtOnce)
inFileInfo->mFileDataInQueue = true;
}
AudioQueueSetOfflineRenderFormat(mQueue, &captureFormat, acl);
result = AudioQueueAllocateBuffer(mQueue, mBufferByteSize / 2, &captureBuffer);
if(result != 0) {
free(acl);
delete [] mPacketDescs;
return result;
}
captureABL.mNumberBuffers = 1;
captureABL.mBuffers[0].mData = captureBuffer->mAudioData;
captureABL.mBuffers[0].mNumberChannels = captureFormat.mChannelsPerFrame;
CFRunLoopTimerContext info;
info.version = 0;
info.info = this;
info.retain = NULL;
info.release = NULL;
info.copyDescription = NULL;
RenderTimer = CFRunLoopTimerCreate(NULL,
CFAbsoluteTimeGetCurrent(), (CFTimeInterval)0.25f, 0, 0,
TimerCallback, &info);
return result;
}
OSStatus AudioQueueServicesMgr::Start() {
if(isPlaying())
Stop(false);
OSStatus result = AudioQueueStart(mQueue, NULL);
if(result != 0)
return result;
ts.mFlags = kAudioTimeStampSampleTimeValid;
ts.mSampleTime = 0;
result = AudioQueueOfflineRender(mQueue, &ts, captureBuffer, 0);
if(result != 0)
return result;
for(int i = 0; i < kNumBuffer; ++i)
QueueCallback(this, mQueue, mBuffers[i]);
CFRunLoopAddTimer(CFRunLoopGetCurrent(), RenderTimer,
kCFRunLoopDefaultMode);
result = AudioOutputUnitStart(audioUnit);
return result;
}
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Coreaudio-api mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden