如何正确使用MIDIReadProc?

时间:2016-05-16 21:19:12

标签: core-audio coremidi

根据苹果公司的文档,它说:

  

因为您的MIDIReadProc回调是从一个单独的线程调用的,   使用提供的数据时请注意同步问题   这个回调。

这是否意味着,使用@synchronize为安全做线程阻塞?

或者这确实意味着可能会发生同步时序问题?

我目前正在尝试读取midi文件,并使用MIDIReadProc触发基于midi事件的软件合成器的音符开/关。我需要这是非常可靠和完美的及时。现在,我注意到当我使用这些midi事件并将音频写入缓冲区(全部来自MIDIReadProc)时,时间非常邋and,听起来根本没有。所以我想知道,从MIDIReadProc消费midi事件的“正确”方法是什么?

另外,MIDIReadProc是从midi文件中消费midi事件的唯一选择吗?

关于设置可以由我的合成器直接使用的虚拟端点,还有其他选择吗?如果是这样,那究竟如何运作?

2 个答案:

答案 0 :(得分:0)

如果您假定此格式的功能为midiReadProc

void midiReadProc(const MIDIPacketList *packetList, 
              void* readProcRefCon, 
              void* srcConnRefCon)
{
    MIDIPacket *packet = (MIDIPacket*)packetList->packet;

    int count = packetList->numPackets;
    for (int k=0; k<count; k++) {
        Byte midiStatus = packet->data[0];
        Byte midiChannel= midiStatus & 0x0F;
        Byte midiCommand = midiStatus >> 4;

    //parse MIDI messages, extract relevant information and pass it to the controller
    //controller must be visible from the midiReadProc
    }     
 packet = MIDIPacketNext(packet);
}

MIDI客户端必须在controller中声明,解释的MIDI事件从MIDI回调存储到controller,并在每个音频渲染周期由audioRenderCallback()读取。通过这种方式,您可以最大限度地减少对时间的不精确 音频缓冲区的长度,您可以在AudioUnit设置期间协商,使其与系统允许的时间一样短。

控制器可以是您定义的@interface myMidiSynthController : NSViewController,由MIDI通道矩阵和预先确定的每通道最大复音数组成,以及其他相关数据,如界面元素,每个活动的相位累加器语音,AudioComponentInstance等......基于midiReadProc()输入调整控制器大小是错误的。 RAM现在很便宜。

我正在使用这样的MIDI回调来处理来自MIDI设备的实时输入。关于MIDI文件的播放,如果你 想要处理任意复杂的流或文件,你也可能会遇到意外。 MIDI标准本身 具有定时功能,可以像MIDI硬件一样工作。一旦将整个文件读入内存,就可以 将您的数据转换为您想要的任何数据,并使用您自己的代码来控制声音合成。
请注意,不要使用任何会阻止音频渲染线程的代码(即在audioRenderCallback()内),或者对它进行内存管理。

答案 1 :(得分:0)

您可以使用AVAudioEngine.musicSequence并准备音频单位图表。然后使用MusicSequence API加载您的GM文件。像这样你不需要自己做时间。注意到目前为止我自己还没有这样做,但我理解它应该像这样工作。

在您实例化合成器音频单元后,您将其附加并连接到AVAudioEngine图表。

  

这是否意味着,使用@synchronize为安全做线程阻塞?

与你所说的相反是正确的:你当然不应该锁定实时线程。如果资源已被锁定,@synchronized指令将锁定。您可以考虑对实时线程使用无锁队列。另请参阅Four common mistakes in audio development

如果你必须使用CoreMIDI和MIDIReadProc,你可以直接从你的回调中调用MusicDeviceMIDIEvent将MIDI命令发送到合成器音频单元。