Исключение звука AUSampler

Я разрабатываю приложение для iPad, которое использует AUSampler AudioUnit в сочетании с другими AudioUnit для воспроизведения звука. AUSampler загружает пресет из файла SoundFont2.

Когда одновременно воспроизводится слишком много нот или ноты воспроизводятся слишком быстро, звук начинает ненадолго пропадать, а затем полностью пропадает. Затем сэмплер становится непригодным для использования до тех пор, пока пресет не будет перезагружен из SoundFont. Это происходит только с некоторыми инструментами (в частности, с пресетами 88, 90 и 94, которые в General MIDI являются пэдом 1 (новый век), пэдом 3 (полисинт) и пэдом 7 (гало) соответственно).

Включив точку останова All Exceptions в XCode, я вижу, что проблема возникает, когда AudioToolbox пытается создать новую VoiceZone для воспроизводимой ноты. Однако это исключение перехватывается где-то ниже в стеке вызовов ниже моего кода, и AudioUnitRender не возвращает никаких ошибок.

Мое лучшее предположение состоит в том, что инструменты «пэд» используют более длинные сэмплы, и поэтому быстрое воспроизведение многих нот / в сочетании требует больше памяти, чем доступно для AudioToolbox.

Кто-нибудь сталкивался с этой проблемой раньше, и если да, то есть ли способ избежать пропадания звука или, по крайней мере, обнаружить их, чтобы изящно с ними справиться?

Некоторые детали:

  • SoundFont весит около 150 МБ и содержит множество различных пресетов/инструментов, соответствующих стандарту General MIDI.
  • AUSampler находится во главе цепочки блоков эффектов, которые соединяются в AUMultiChannelMixer (me.masterMixerUnit). audioOutputCallback устанавливается как входной обратный вызов выходного элемента модуля RemoteIO.

Backtrace (на AURemoteIO::IOThread):

* thread #14: tid = 0x3203, 0x368c8498 libc++abi.dylib`__cxa_throw, stop reason = breakpoint 1.2
frame #0: 0x368c8498 libc++abi.dylib`__cxa_throw
frame #1: 0x35d46a60 AudioToolbox`VoiceZone::operator new(unsigned long) + 340
frame #2: 0x35d46b6a AudioToolbox`VoiceZone::NewVoiceZone(SamplerNote*, ZoneState*, float, float, unsigned long) + 86
frame #3: 0x35d3f846 AudioToolbox`SamplerNote::Configure(InstrumentState*) + 1106
frame #4: 0x35d3ff2c AudioToolbox`non-virtual thunk to SamplerNote::Attack(MusicDeviceNoteParams const&) + 24
frame #5: 0x35d551ea AudioToolbox`SynthNote::AttackNote(SynthPartElement*, SynthGroupElement*, unsigned long, unsigned long long, unsigned long, MusicDeviceNoteParams const&) + 58
frame #6: 0x35d5485a AudioToolbox`SynthGroupElement::NoteOn(SynthNote*, SynthPartElement*, unsigned long, unsigned long, MusicDeviceNoteParams const&) + 70
frame #7: 0x35d3d220 AudioToolbox`SamplerElement::StartNote(SynthPartElement*, unsigned long, unsigned long, MusicDeviceNoteParams const&) + 344
frame #8: 0x35d3c4fa AudioToolbox`Sampler::RealTimeStartNote(SynthGroupElement*, unsigned long, unsigned long, MusicDeviceNoteParams const&) + 94
frame #9: 0x35d530a0 AudioToolbox`AUInstrumentBase::PerformEvents(AudioTimeStamp const&) + 164
frame #10: 0x35d5313c AudioToolbox`AUInstrumentBase::Render(unsigned long&, AudioTimeStamp const&, unsigned long) + 20
frame #11: 0x35d3c564 AudioToolbox`Sampler::Render(unsigned long&, AudioTimeStamp const&, unsigned long) + 68
frame #12: 0x35c2435a AudioToolbox`AUBase::DoRenderBus(unsigned long&, AudioTimeStamp const&, unsigned long, AUOutputElement*, unsigned long, AudioBufferList&) + 210
frame #13: 0x35c24184 AudioToolbox`AUBase::DoRender(unsigned long&, AudioTimeStamp const&, unsigned long, unsigned long, AudioBufferList&) + 496
frame #14: 0x35c23f8a AudioToolbox`AUMethodRender(void*, unsigned long*, AudioTimeStamp const*, unsigned long, unsigned long, AudioBufferList*) + 50

... render methods of other AudioUnits in chain ...

frame #32: 0x00081aaa SoundBrush 2`audioOutputCallback + 194 at SoundEngine.mm:1375

Исключение возникает во время вызова AudioUnitRender в моем обратном вызове ниже:

static OSStatus audioOutputCallback(void                       *inRefCon,
                                    AudioUnitRenderActionFlags *ioActionFlags,
                                    const AudioTimeStamp       *inTimeStamp,
                                    UInt32                      inBusNumber,
                                    UInt32                      inNumberFrames,
                                    AudioBufferList            *ioData)
{
    //get a reference to the SoundEngine
    SoundEngine *me = (__bridge SoundEngine *)inRefCon;

    // render the sound
    OSStatus err = AudioUnitRender(me.masterMixerUnit,
                                   ioActionFlags,
                                   inTimeStamp,
                                   kAudioUnitBus_Output,
                                   inNumberFrames,
                                   ioData);
    if (err != noErr) NSLog(@"AudioUnitRender failed [%ld]", err);

    // ... do some processing ...

    return err;
}

person Jayson    schedule 10.12.2012    source источник


Ответы (1)


Если качество звука хорошее в нормальных условиях, я предполагаю, что выпадения вызваны не ошибкой программирования в вашей цепочке AU, а скорее тем, что ОС не может достаточно быстро обработать блок.

Самый простой способ обнаружить пропадание звука — получить системное время в микросекундах до начала обработки, а затем снова измерить его после завершения. Глядя на частоту дискретизации и размер буфера, вы сможете определить, смогли ли вы доставить сэмплы за заданный промежуток времени.

Если вы обнаружите, что у вас действительно есть выпадения, вызванные производительностью, первое, что вы должны сделать, это увеличить размер буфера. После этого вы должны попытаться оптимизировать свой код несколькими основными способами. Запустите алгоритм через Instruments (то есть профилировщик) и найдите любые очевидные узкие места. Избегайте выделения объектов внутри обратного вызова рендеринга; это вызовет дополнительную нагрузку на систему.

Также я бы посоветовал запустить код обработки на настольном компьютере, чтобы убедиться, что проблема действительно вызвана производительностью iPad.

person Nik Reiman    schedule 11.12.2012
comment
Спасибо, я проведу более сложный анализ производительности и посмотрю, что получится. - person Jayson; 13.12.2012