2016-05-16 231 views
0

根据苹果的文档,它说:如何正确使用MIDIReadProc?

因为你MIDIReadProc回调是从单独的线程调用, 使用 回调提供的数据时,要注意的同步问题。

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

或者这是否意味着同步计时问题可能会发生?

我正在尝试读取midi文件,并使用MIDIReadProc触发基于MIDI事件的软件合成器的音符/音符。我需要这是非常可靠和完美的时间。现在,我注意到,当我使用这些midi事件并将音频写入缓冲区(全部由MIDIReadProc完成)时,时序非常渺茫,听起来没有任何问题。所以我想知道,从MIDIReadProc中消耗midi事件的“正确”方式是什么?

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

就设置可以被我的合成器直接消耗的虚拟端点而言,还有其他选择吗?如果是这样,那究竟是如何工作的?

回答

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事件被存储到controller从MIDI回调,读取的每个音频渲染周期上的audioRenderCallback()。通过这种方式,您可以最小化音频缓冲区的时间不精确度,您可以在AudioUnit设置期间协商音频缓冲区的长度,使其与系统允许的一样短。

控制器可以是你定义一个@interface myMidiSynthController : NSViewController,由MIDI信道的矩阵和的预先确定的最大复音每通道,其中诸如接口元件,相位累加器为每个活动的语音等相关数据, AudioComponentInstance等...根据midiReadProc()输入调整控制器大小是错误的。 RAM现在很便宜。

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

+0

但是您没有提及如何“将它传递给控制器​​”,以便它可以在特殊的midiReadProc高优先级线程之外处理。显然,我需要将这些数据包存储到主线程可以使用并转换为音频的数据结构中。如何正确地做到这一点?理想情况下,我想要一个多维数组,因此我可以执行数据包[midiChannel] [currentChannelPacketIndex] = *数据包...但我不知道如何分配给定通道上的总共多少个数据包,以便我可以正确分配该存储的内存。 – patrick

+0

@patrick,你的假设“主线程可以消耗某些东西并转化为音频”或者似乎有措辞问题,或者不完全确切。只要在实时渲染线程上完成音频制作/消费,主线程就只关注GUI更新,这是时间选择最有效的选择。关于将MIDI生成的数据传递给控制器​​,如果您只是将其传递给控制器​​,您仍然需要将数据包解码为渲染线程将理解的内容。你的问题相当广泛,我会尽快调整我的答案。 – user3078414

+0

“关于播放MIDI文件,如果您想处理任意复杂的流或文件,您可能会遇到意外。” - 那是我的问题。我正尝试传输一个185bpm的midi文件,其中最快的音符是第16个,这听起来像是一场绝对的灾难.. – patrick

0

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

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

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

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

如果您必须使用CoreMIDI和MIDIReadProc,则可以通过在回调中调用MusicDeviceMIDIEvent来将MIDI命令发送到合成器音频单元。