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

当一次演奏太多音符或演奏音符的速度太快时,音频会短暂地退出,然后再完全退出。然后,采样器将无法使用,直到从SoundFont重新加载预设为止。仅在某些乐器上会发生这种情况(特别是预设88、90和94,在常规MIDI中分别是Pad 1(新年龄),Pad 3(polysynth)和Pad 7(光晕))

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

我最好的猜测是,“Pad”乐器使用的采样时间更长,因此快速/联合演奏许多音符会比AudioToolbox占用更多的内存。

以前是否有人遇到过此问题,如果是,是否有办法避免音频丢失,或至少检测到它们以优雅地处理它们?

一些细节:

  • SoundFont约为150 MB,包含许多与常规MIDI
  • 一致的不同预设/乐器
    AUSampler的
  • 位于效果单元链的顶部,这些效果单元连接到AUMultiChannelMixer(me.masterMixerUnit)。 audioOutputCallback设置为RemoteIO单元
  • 的输出元素的输入回调

    回溯(在`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;
    }
    

    最佳答案

    如果在正常情况下音质良好,我猜这可能不是由您的AU链中的编程错误引起的,而是由OS无法足够快地处理该块引起的。

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

    如果发现确实存在性能下降的问题,那么您应该做的第一件事就是增加缓冲区大小。之后,您应该尝试以几种基本方式优化代码。通过Instruments(即探查器)运行算法,并查找任何明显的瓶颈。避免在render回调内部分配对象;这将对系统造成额外的压力。

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

    10-07 19:39
    查看更多