Наше аудио приложение использует AUGraphs, содержащие блок микшера, конвертер и блок вывода. Это приложение для работы в реальном времени, поэтому производительность очень важна.
Была отмечена проблема, когда AUGraphStop () занимает 25 миллисекунд для завершения в главном потоке, и наш профилировщик показывает, что он проводит это время в спящем режиме. Кто-нибудь может объяснить, почему это происходит? Ожидание следующей точки пересечения нуля или еще один буфер для рендеринга?
Я пробовал несколько обходных путей, включая отправку одного кадра молчания рендеринга (и установку флага kAudioUnitRenderAction_OutputIsSilence) перед попыткой остановки и вызов AUGraphStop () внутри kAudioUnitRenderAction_PostRender, уведомляющего обратный вызов (это имеет смешанные результаты, и после чтения вокруг него не кажется рекомендуемым подходом).
Я пытался уменьшить количество кадров на фрагмент, но, похоже, это не имеет значения. Я также попытался удалить все аудиоустройства, кроме выходного узла, в попытке сузить проблему до конкретного модуля, но стоимость AUGraphStop () по-прежнему составляет 25 мс.
Вот как мы инициализируем график:
//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));
}
График запускается и останавливается с помощью AUGraphStart () и AUGraphStop (), ничего особенного. Вот стек вызовов, захваченный с помощью 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
Он тратит всю AUGraphStop () на сон!
Есть какие-нибудь подсказки?