发送到同一通道的连续 MIDI 消息是乱码

Consecutive MIDI messages sent to same channel are garbled

本文关键字:消息 MIDI 连续 通道      更新时间:2023-10-16

我正在编写一个简单的C++命令行实用程序,通过Roland UM-One Mk 2 USB接口将MIDI消息从我的Mac(MacOS 10.12)发送到Behringer Powerplay P16-M混音器。 我正在使用rtmidi实时 MIDI API。

当我向同一频道发送两条连续的 MIDI 消息时,两条消息似乎被混音器弄乱了。当我向备用频道发送两条连续消息时,它工作正常。

例如,为了将通道 1 电平设置为 midi 0 (-51dB),将通道 1 平移到 midi 64(居中),我理解我应该发送以下两条消息:0xB0 0x07 0x00xB0 0x0A 0x40.当我一个接一个地发送这些消息时,混音器上的 LED 意外地指示通道 1 电平约为 64,并且平移保持不变——就像我0xB0 0x07 0x40发送单条消息一样。相反,如果我将两条消息与另一个频道的消息穿插在一起,或者睡眠时间长得离谱(任何超过 900 毫秒的时间都可以),LED 会指示预期的设置:电平为零,平移居中。

我做错了什么?为什么我不能向同一频道发送两条连续的消息?我发送消息的速度是否太快?这是一个rtmidi问题吗?是混音器有问题吗?

这是一个最小但完整的工作示例,用于演示该问题。(我用XCode针对CoreMIDI,CoreAudio和CoreFoundation框架编译它。

#include <iostream>
#include <vector>
#include <unistd.h> // (for usleep)
#include "RtMidi.h"
// define some midi messages
static std::vector<unsigned char> ch1vol {0xB0 , 0x07 , 0x0}; //  set ch 1 volume to midi 0 (-51dB)
static std::vector<unsigned char> ch1pan {0xB0 , 0x0A , 0x40}; // set ch 1 pan to 64
static std::vector<unsigned char> ch2vol {0xB1 , 0x07 , 0x0}; // set ch 2 volume to midi 0 (-51dB)
static std::vector<unsigned char> ch2pan {0xB1 , 0x0A , 0x40}; // set ch 2 pan to 64
#define SLEEPMSEC( milliseconds ) usleep( (unsigned long) (milliseconds * 1000.0) )
int main(int argc, const char * argv[]) {
RtMidiOut * midiOut = new RtMidiOut(); // create an RtMidiOut
// are there any midi ports available?
if (!midiOut->getPortCount()) {
std::cout << "*** No midi ports found. Goodbye." << std::endl;
exit(1);
}
// open the desired midi device
std::string portName = midiOut->getPortName(0);
if (portName != "Powerplay 16") {
std::cout << "*** Can't find requested midi device. Goodbye." << std::endl;
exit(1);
}
midiOut->openPort(0); // open the midi device to receive output
switch(argc) {
case 2:
// This works as expected
// Result: channels 1 and 2 volumes are set to 0; the panning in both channels is centered)
midiOut->sendMessage( &ch1vol ); // set ch 1 volume to 0
midiOut->sendMessage( &ch2vol ); // set ch 2 volume to 0
midiOut->sendMessage( &ch1pan ); // set ch 1 pan to center
midiOut->sendMessage( &ch2pan ); // set ch 2 pan to center
break;
case 3:
// This does NOT work as expected
// Result: channels 1 and 2 volumes are set to 64; the panning in both channels is  unchanged
midiOut->sendMessage( &ch1vol ); // set ch 1 volume to 0
midiOut->sendMessage( &ch1pan ); // set ch 1 pan to center
midiOut->sendMessage( &ch2vol ); // set ch 2 volume to 0
midiOut->sendMessage( &ch2pan ); // set ch 2 pan to center
break;
case 4:
// Introduce a sleep in between same-channel calls.
// This DOES work as expected (but with an unacceptable delay)
midiOut->sendMessage( &ch1vol ); // set ch 1 volume to 0
SLEEPMSEC(900);
midiOut->sendMessage( &ch1pan ); // set ch 1 pan to center
midiOut->sendMessage( &ch2vol ); // set ch 2 volume to 0
SLEEPMSEC(900);
midiOut->sendMessage( &ch2pan ); // set ch 2 pan to center
break;
default:
std::cout << "Usage: testmidi ARG [ARG [ARG]]" << std::endl;
std::cout << "Perform a midi test according to the number of arguments." << std::endl;
std::cout << "Be sure to reset the device manually after each run." << std::endl;
break;
}
}

正如@Kurt Revis所建议的那样,问题出在混音器上。

做了什么:我使用 MIDI 监视器来验证我的外发消息是否正确。所以我的代码没问题。我将电缆连接到不同的MIDI设备(Roland Juno合成器),并向其发送了一堆重复的信息:没问题。我的结论是Mac,我的代码,UM-ONE接口和电缆都可以。这就离开了混音器。我尝试了第二台Behringer Powerplay P16-M,得到了完全相同的结果。因此,这似乎是混合器的设计/制造问题。

我的结论是:Behringer Powerplay P16-M 混音器无法正确处理按顺序发送到同一通道的传入 MIDI 消息,除非它们都具有相同的控制更改类型。

解决方法:经过大量实验,我想出了以下建议:

  1. 向同一通道发送连续(但不同)的控制更改消息时,请在消息之间插入至少 500 毫秒的延迟。例如,如果您向某个频道发送一堆音量消息,然后向该频道发送一堆平移消息,则没有问题。如果要将一堆交错的音量和平移消息发送到同一通道,则需要在每条消息之间插入延迟。

  2. 重置混音器时,通道 0("主")需要特殊处理。发送到它的消息需要通过插入至少 500 毫秒的延迟来与发送到其他通道的消息"隔离"。

[编辑]更好的解决方案:与其插入延迟,不如向设备发送 MIDI 音高弯曲消息(例如,发送0xE0 0x0 0x0)。尽管P16-M没有任何用于音高弯曲消息的记录,但它显然足以改变现状并使设备与消息流恢复同步。无论如何,对我有用。