从音频单元的渲染线程调用 MusicDeviceMIDIEvent

Calling MusicDeviceMIDIEvent from the audio unit's render thread

关于 MusicDeviceMIDIEvent,有一件事我不明白。在我见过的每一个例子中(搜索 Github 和 Apple 例子)它总是从主线程中使用。现在,为了使用样本偏移参数,文档指出:

inOffsetSampleFrame: If you are scheduling the MIDI Event from the audio unit's render thread, then you can supply a sample offset that the audio unit may apply when applying that event in its next audio unit render. This allows you to schedule to the sample, the time when a MIDI command is applied and is particularly important when starting new notes. If you are not scheduling in the audio unit's render thread, then you should set this value to 0

尽管如此,即使在最简单的情况下,您只有一个采样器音频单元和一个 io 单元,您如何从音频单元的渲染线程安排 MIDI 事件,因为采样器不允许渲染回调即使它会(或者如果你使用 io 的回调只是为了点击),它也会感觉很糟糕,因为渲染回调不适用于安排 MIDI 事件?

如何从音频单元的渲染线程正确调用此函数?

renderNotify 回调是从渲染线程进行调度的完美场所。您甚至可以在 MusicDevice 本身上设置 renderNotify。这是它在 AUSampler 上的样子。

OSStatus status = AudioUnitAddRenderNotify(sampler, renderNotify, sampler);

在这个例子中,我通过 inRefCon 参数将采样器作为参考传递进来,并且我只是每 44100 个样本发送一个 note-on(144) 到 note 64,但是在应用程序中,你将传递一个 c 结构到inRefCon 对您的 MIDI 设备的引用,以及您进行调度所需的所有值。注意检查预渲染的渲染标志。

static OSStatus renderNotify(void                         * inRefCon,
                             AudioUnitRenderActionFlags   * ioActionFlags,
                             const AudioTimeStamp         * inTimeStamp,
                             UInt32                       inBusNumber,
                             UInt32                       inNumberFrames,
                             AudioBufferList              * ioData) {

    AudioUnit sampler = inRefCon;
    if (ioActionFlags & kAudioUnitRenderAction_PreRender) {
        for (int i = 0; i < inNumberFrames; i++) {
            if (fmod(inTimeStamp->mSampleTime + i, 44000) == 0) {
                MusicDeviceMIDIEvent(sampler,144, 64, 127, i); // i is the offset from render start, so use it for offset argument.
            }
        }
    }

    return noErr;
}