AUSampler Audio Drop-out

时间:2012-12-10 23:14:00

标签: ios midi audiounit audiotoolbox

我正在开发一个iPad应用程序,它使用AUSampler AudioUnit与其他AudioUnit一起播放音频。 AUSampler从SoundFont2文件加载预设。

如果一次播放太多音符或音符播放太快,音频会在完全退出之前开始短暂丢失。然后,采样器将无法使用,直到从SoundFont重新加载预设。这种情况只发生在一些乐器上(特别是预设88,90和94,它们在通用MIDI中分别是Pad 1(新时代),Pad 3(polysynth)和Pad 7(halo))

通过打开XCode中的All Exceptions断点,我可以看到当AudioToolbox尝试为正在播放的音符创建新的VoiceZone时会出现问题。但是,在我的代码下面的调用堆栈中,此异常被捕获到较低的位置,并且AudioUnitRender不会返回任何错误。

我最好的猜测是“pad”乐器使用更长的样本,因此快速/同时播放许多音符会占用比AudioToolbox更多的内存。

以前有没有人遇到过这个问题,如果有的话,有没有办法避免音频丢失,或至少检测它们以便优雅地处理它们?

一些细节:

  • SoundFont大约150 MB,包含许多符合通用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;
}

1 个答案:

答案 0 :(得分:1)

如果在正常条件下音质良好,我猜测丢失不是由AU链中的编程错误引起的,而是由于操作系统无法足够快地处理该块。

检测音频丢失的最简单方法是在开始处理之前以微秒为单位获取系统时间,然后在完成后再次测量它。通过查看采样率和缓冲区大小,您应该能够确定是否能够在给定的时间内交付样本。

如果您发现自己确实因性能而导致辍学,那么您应该做的第一件事就是增加缓冲区大小。在此之后,您应该尝试以几种基本方式优化代码。通过Instruments(即分析器)运行算法,并寻找任何明显的瓶颈。避免在render回调中进行对象分配;这将对系统造成额外的压力。

此外,我建议在台式计算机上运行处理代码,以确保问题确实是由iPad的性能引起的。