使用 RtMIDI,MIDISendSysex 不向虚拟端口发送事件

Posted

技术标签:

【中文标题】使用 RtMIDI,MIDISendSysex 不向虚拟端口发送事件【英文标题】:Using RtMIDI, MIDISendSysex sends no events to virtual ports 【发布时间】:2014-02-20 12:32:15 【问题描述】:

刚开始在 Xcode 中使用 RtMIDI 来制作一些 MIDI 的原型。它工作正常,但遇到了一个绊脚石。

使用示例 midiout.cpp 而不进行任何修改:

如果我将 MIDI 事件发送到现有的 MIDI 端口,则所有事件都可以正常发送。 如果我将 MIDI 事件发送到创建的虚拟端口,所有事件 除了 sysex 事件都会发送。

查看库代码,除了 sysex 之外的所有 MIDI 事件都使用 os 调用 MIDIReceived 发送(在 OSX 上)。 Sysex 事件使用 MIDISendSysex 发送。这是应该的。

现在,没有抛出任何错误,一切都按原样执行,MIDISendSysex 调用没有失败——只是没有 sysex 事件到达目的地。他们只是消失在黑洞中!

还有其他人遇到过这个问题或有任何帮助、建议、解决方法吗?

谢谢,

(Xcode 4.6.2、OSX 10.9.1,使用 MIDIMonitor 和 MIDIPipe 监控 MIDI 端口上的流量,两者都显示 sysex 事件未从 midiout.cpp 到达虚拟端口的相同结果)

好的,这是进行实际发送的 sendMessage 路由:



    void MidiOutCore :: sendMessage( std::vector *message )
    
    // ------------------------------------------------------------------------
      // We use the MIDISendSysex() function to asynchronously send sysex
      // messages.  Otherwise, we use a single CoreMidi MIDIPacket.
    // ------------------------------------------------------------------------
    // error handling code removed for brevity

      MIDITimeStamp timeStamp = AudioGetCurrentHostTime();
      CoreMidiData *data = static_cast (apiData_);
      OSStatus result;

    // ------------------------------------------------------------------------
    // IF EVENT IS SYSEX
    // ------------------------------------------------------------------------

      if ( message->at(0) == 0xF0 )                // sysex start byte
        while ( sysexBuffer != 0 ) usleep( 1000 );  // sleep 1 ms

       sysexBuffer = new char[nBytes];

       // Copy data to buffer.
       for ( unsigned int i=0; iat(i);

       // build sysex request
       data->sysexreq.destination = data->destinationId;        // destinaiondId is valid endpointref
       data->sysexreq.data = (Byte *)sysexBuffer;
       data->sysexreq.bytesToSend = nBytes;
       data->sysexreq.complete = 0;
       data->sysexreq.completionProc = sysexCompletionProc;
       data->sysexreq.completionRefCon = &(data->sysexreq);

       // send the data
       // this works when we are connected to a 'real' MIDI port/device, but fails on a virtual port
       // destinationId is an endpointref and valid and id is good
       // tried to use data->endpoint (also an endpointref with id) but doesn't send either
       result = MIDISendSysex( &(data->sysexreq) );
       return;
      

    // ------------------------------------------------------------------------
    // IF EVENT IS NOT SYSEX
    // ------------------------------------------------------------------------

      MIDIPacketList packetList;
      MIDIPacket *packet = MIDIPacketListInit( &packetList );
      packet = MIDIPacketListAdd( &packetList, sizeof(packetList), packet, timeStamp, nBytes, (const Byte *) &message->at( 0 ) );

      // Send to any destinations that may have connected to us.
      // this sends to virtual MIDI ports
      if ( data->endpoint ) 
        result = MIDIReceived( data->endpoint, &packetList );
        if ( result != noErr ) 
          errorString_ = "MidiOutCore::sendMessage: error sending MIDI to virtual destinations.";
          RtMidi::error( RtError::WARNING, errorString_ );
        
      

      // And send to an explicit destination port if we're connected.
      // this sends to regular real MIDI devices we are connected to, not virtual ports
      if ( connected_ ) 
        result = MIDISend( data->port, data->destinationId, &packetList );
        if ( result != noErr ) 
          errorString_ = "MidiOutCore::sendMessage: error sending MIDI message to port.";
          RtMidi::error( RtError::WARNING, errorString_ );
        
      

    

【问题讨论】:

请给我们看示例代码。 嗨 - 这是使用库附带的 midiout.cpp 文件。我不确定我应该发布多少?示例程序只是让用户选择一个现有端口,或创建一个虚拟端口,并为每个端口触发一些示例 MIDI 事件。将在几秒钟内将链接放到 rtmidi... rtmidi 在这里:music.mcgill.ca/~gary/rtmidi 下载:music.mcgill.ca/~gary/rtmidi/release/rtmidi-2.0.1.tar.gz。正如我所说,我的代码有问题,所以我返回并使用库附带的示例程序检查了行为,这显示了相同的行为。 midiout.cpp:dl.dropboxusercontent.com/u/4211158/code/midiout.cppRtMidi.cpp:dl.dropboxusercontent.com/u/4211158/code/RtMidi.cppRtMidi.h:dl.dropboxusercontent.com/u/4211158/code/RtMidi.hRtError.h:dl.dropboxusercontent.com/u/4211158/code/RtError.h 有趣的是 - 非 sysex 事件以两种方式发送 - 如果连接到现有端口,rtmidi 使用 MIDISend,但如果有端点(虚拟端口)也使用MIDI 收到。如果我注释掉 MIDIReceived 发送者,那么常规事件也不会出现在虚拟端口上。因此,这似乎与 MIDISendSysex 未显示在虚拟端口上有关 - 似乎它只想发送到常规 MIDI 端口而不是虚拟端口。正在调查... 【参考方案1】:

跟进说明 - 这是 RTMidi 中的一个错误,应该在下一个版本中修复。这与 CoreMIDI 中的 MIDISendSysex() 调用不发送到虚拟端口有关,它只会在真实端口上工作。

我已经更新了我的本地 RTMIDI 副本以使用 MIDIReceived() 处理 sysex 事件,正如 CoreAdio 邮件列表中此线程所推荐的那样: http://lists.apple.com/archives/coreaudio-api/2006/Jan/msg00236.html

它现在将 sysex 发送到虚拟端口并按预期运行。

【讨论】:

实际上并非 100% 正确。 Sysex 可以通过虚拟端口传输。接收方需要符合 sysex 规则。所以需要正确设置标题。知道这一点是因为几个月前我制作了一个虚拟 midi 端口插件,它接收 sysex 并在需要时发送回以传输歌曲文本字符。当您使用ableton 进行测试时,请考虑您使用的DAW 在某些情况下无法发送开箱即用的sysex。是的,在虚拟端口上,发送者和接收者被转过身来。所以 MIDISendSysex 在这种情况下是没用的, MIDIReceived() 是要走的路..正确! 这在 7 年后仍然是一个问题!使用 Linux。

以上是关于使用 RtMIDI,MIDISendSysex 不向虚拟端口发送事件的主要内容,如果未能解决你的问题,请参考以下文章

如何明智地分配静态 RtMidi 回调对象?

无法编译 rtmidi 测试 cmidiin.cpp 文件,非法指令

从回调中发出信号

尝试编译 RtAudio 时出现未定义的符号

实时 MIDI 输入和音频同步

如何强制 MIDI 设备报告控制状态?