Advertisement

Apple CoreAudio - AUGraphStop() takes 25ms to complete

Started by March 24, 2011 07:21 PM
-1 comments, last by deadstar 13 years, 8 months ago
Hi,

Our audio app is using AUGraphs containing a mixer unit, a converter unit and an output unit. It's a realtime app, so performance is a high concern.

An issue has been flagged up where AUGraphStop() takes 25 milliseconds to complete on the main thread, and our profiler shows that it spends this time sleeping. Could anyone explain why this happens? Is it waiting for the next zero crossover point, or waiting for one more buffer to render?


I've tried several workarounds, including sending one render frame of silence (and setting the kAudioUnitRenderAction_OutputIsSilence flag) before attempting to stop, and calling AUGraphStop() inside the kAudioUnitRenderAction_PostRender notify callback (this has mixed results, and after reading around it doesn't seem a recommended approach).

Here's how we are initialising the graph:



//Configure converter/mixer/output unit descriptors
AudioComponentDescription OutputUnitDesc = { kAudioUnitType_Output ,kAudioUnitSubType_DefaultOutput, kAudioUnitManufacturer_Apple };
AudioComponentDescription MixerUnitDesc = { kAudioUnitType_Mixer, kAudioUnitSubType_MatrixMixer, kAudioUnitManufacturer_Apple };
AudioComponentDescription ConverterUnitDesc = { kAudioUnitType_FormatConverter, kAudioUnitSubType_AUConverter, kAudioUnitManufacturer_Apple };

//Create graph
CA_ERR(NewAUGraph(&mAudioGraph));

//Add converter/mixer/output nodes
CA_ERR(AUGraphAddNode(mAudioGraph, &OutputUnitDesc, &mOutputNode));
CA_ERR(AUGraphAddNode(mAudioGraph, &MixerUnitDesc, &mMixerNode));
CA_ERR(AUGraphAddNode(mAudioGraph, &ConverterUnitDesc, &mConverterNode));

//Connect nodes
CA_ERR(AUGraphConnectNodeInput(mAudioGraph, mConverterNode, 0, mMixerNode, 0));
CA_ERR(AUGraphConnectNodeInput(mAudioGraph, mMixerNode, 0, mOutputNode, 0));

//Open the graph (instantiates the units)
CA_ERR(AUGraphOpen(mAudioGraph));

//Get the created units
CA_ERR(AUGraphNodeInfo(mAudioGraph, mOutputNode, NULL, &mOutputUnit));
CA_ERR(AUGraphNodeInfo(mAudioGraph, mMixerNode, NULL, &mMixerUnit));
CA_ERR(AUGraphNodeInfo(mAudioGraph, mConverterNode, NULL, &mConverterUnit));

//Setup stream format description
mStreamDesc.mFormatID = kAudioFormatLinearPCM;
mStreamDesc.mFormatFlags = kLinearPCMFormatFlagIsPacked | kAudioFormatFlagIsSignedInteger;
mStreamDesc.mChannelsPerFrame = Header->nChannels;
mStreamDesc.mSampleRate = (Float64)Header->nSamplesPerSec;
mStreamDesc.mBitsPerChannel = Header->wBitsPerSample;
mStreamDesc.mBytesPerFrame = (Header->wBitsPerSample >> 3) * Header->nChannels;
mStreamDesc.mFramesPerPacket = 1;
mStreamDesc.mBytesPerPacket = mStreamDesc.mBytesPerFrame * mStreamDesc.mFramesPerPacket;
mStreamDesc.mReserved = 0;

//Set data endianness according to file type - TODO: Get endianness from header
AudioSystem::FileType FileType = mSample->GetFiletype();

if(FileType == AudioSystem::WAV)
mStreamDesc.mFormatFlags |= kAudioFormatFlagsNativeEndian;
else if(FileType == AudioSystem::OGG)
mStreamDesc.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;

//Configure number of input/output busses for mixer unit
int NumChannelsIn = Header->nChannels;
int NumChannelsOut = (int)AudioSystem::GetOutputChannelConfig();
CA_ERR(AudioUnitSetProperty(mMixerUnit, kAudioUnitProperty_BusCount, kAudioUnitScope_Input, 0, &NumChannelsIn, sizeof(u32)));
CA_ERR(AudioUnitSetProperty(mMixerUnit, kAudioUnitProperty_BusCount, kAudioUnitScope_Output, 0, &NumChannelsOut, sizeof(u32)));

//Set render callback
AURenderCallbackStruct callback = { AudioRenderCallback, this };
CA_ERR(AUGraphSetNodeInputCallback(mAudioGraph, mConverterNode, 0, &callback));

//Set stream format to something native to CoreAudio
AudioStreamBasicDescription OutputDesc = {0};
UInt32 Size = sizeof(AudioStreamBasicDescription);
CA_ERR(AudioUnitGetProperty(mMixerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &OutputDesc, &Size));

//Set num output channels
OutputDesc.mChannelsPerFrame = (int)AudioSystem::GetOutputChannelConfig();

//Set stream format
CA_ERR(AudioUnitSetProperty(mConverterUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &mStreamDesc, sizeof(AudioStreamBasicDescription)));
CA_ERR(AudioUnitSetProperty(mConverterUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &OutputDesc, sizeof(AudioStreamBasicDescription)));
CA_ERR(AudioUnitSetProperty(mMixerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &OutputDesc, sizeof(AudioStreamBasicDescription)));
CA_ERR(AudioUnitSetProperty(mMixerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &OutputDesc, sizeof(AudioStreamBasicDescription)));
CA_ERR(AudioUnitSetProperty(mOutputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &OutputDesc, sizeof(AudioStreamBasicDescription)));

//Initialise graph
CA_ERR(AUGraphInitialize(mAudioGraph));

//Set notification callback
CA_ERR(AUGraphAddRenderNotify(mAudioGraph, AudioNotifyCallback, this));

//Set global mixer volume
CA_ERR(AudioUnitSetParameter(mMixerUnit, kMatrixMixerParam_Volume, kAudioUnitScope_Global, 0xFFFFFFFF, 1.0, 0));

//Set input channel volumes
for(int i = 0; i < Header->nChannels; i++)
{
CA_ERR(AudioUnitSetParameter(mMixerUnit, kMatrixMixerParam_Volume, kAudioUnitScope_Input, i, 1.0, 0));
}

//Set output channel volumes
for(int i = 0; i < (int)AudioSystem::GetOutputChannelConfig(); i++)
{
CA_ERR(AudioUnitSetParameter(mMixerUnit, kMatrixMixerParam_Volume, kAudioUnitScope_Output, i, 1.0, 0));
}




The graph is started and stopped using AUGraphStart() and AUGraphStop(), nothing special. Here's the callstack captured using Shark Profiler:


| | | | | | | | | + 1.3%, AudioUnitGraph::Stop(), AudioToolbox
| | | | | | | | | | + 1.3%, AudioOutputUnitStop, AudioUnit
| | | | | | | | | | | + 1.3%, CallComponentDispatch, CarbonCore
| | | | | | | | | | | | + 1.3%, DefaultOutputAUEntry, CoreAudio
| | | | | | | | | | | | | + 1.3%, AUHALEntry, CoreAudio
| | | | | | | | | | | | | | + 1.3%, usleep$UNIX2003, libSystem.B.dylib
| | | | | | | | | | | | | | | + 1.3%, nanosleep$UNIX2003, libSystem.B.dylib
| | | | | | | | | | | | | | | | 1.3%, __semwait_signal, libSystem.B.dylib


It spends the entirety of AUGraphStop() just sleeping!

I've tried lowering the number of frames per slice, but it doesn't seem to make a difference. I've also tried removing all audio units except the output node, in an attempt to narrow the problem down to a specific unit, but the cost of AUGraphStop() still stands at 25ms.

Any clues?

"The right, man, in the wrong, place, can make all the dif-fer-rence in the world..." - GMan, Half-Life 2

A blog of my SEGA Megadrive development adventures: http://www.bigevilcorporation.co.uk

This topic is closed to new replies.

Advertisement