发送到同一通道的连续 MIDI 信息出现乱码
Consecutive MIDI messages sent to same channel are garbled
我正在编写一个简单的 C++ 命令行实用程序,用于将 MIDI 消息从我的 Mac (MacOS 10.12) 通过罗兰 UM-One Mk 2 USB 接口。我正在使用 rtmidi
realtime MIDI API.
当我向同一个通道发送两个连续的 MIDI 消息时,这两个消息似乎被调音台弄乱了。当我向备用频道发送两个连续的消息时,它工作正常。
例如,为了将通道 1 电平设置为 midi 0 (-51dB) 并将通道 1 声像设置为 midi 64(居中),我知道我应该发送这两条消息:
0xB0 0x07 0x0
和 0xB0 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 Monitor 来验证我的外发消息是否全部正确。所以我的代码没问题。我将我的电缆连接到不同的 MIDI 设备(Roland Juno 合成器)并向它发送了一堆重复的消息:没问题。我的结论是 Mac、我的代码、UM-ONE 接口和电缆都没有问题。剩下的就是搅拌机。我尝试了第二台 Behringer Powerplay P16-M,得到了完全相同的结果。因此,这似乎是混音器的 design/manufacture 问题。
我得出的结论:
Behringer Powerplay P16-M 混音器无法正确处理按顺序发送到同一通道的传入 MIDI 消息,除非它们都是相同的控制更改类型。
解决方法:
经过大量实验后,我得出以下结论:
向同一频道发送连续(但不同)的控制更改消息时,在消息之间插入至少 500 毫秒的延迟。例如,如果您向某个频道发送一堆音量消息,然后向该频道发送一堆声像消息,则没有问题。如果你想发送一堆交错的音量和平移消息到同一个频道,那么你需要在每个消息之间插入一个延迟。
重置调音台时,通道 0 ("main") 需要特殊处理。通过插入至少 500 毫秒的延迟,发送到它的消息需要从发送到其他通道的消息中 "insulated"。
[编辑]
更好的解决方案:
不是插入延迟,而是向设备发送 MIDI 弯音消息(例如,发送 0xE0 0x0 0x0
)。尽管 P16-M 没有任何关于弯音消息的使用记录,但它显然足以改变一切并让设备与消息流同步。无论如何对我有用。
我正在编写一个简单的 C++ 命令行实用程序,用于将 MIDI 消息从我的 Mac (MacOS 10.12) 通过罗兰 UM-One Mk 2 USB 接口。我正在使用 rtmidi
realtime MIDI API.
当我向同一个通道发送两个连续的 MIDI 消息时,这两个消息似乎被调音台弄乱了。当我向备用频道发送两个连续的消息时,它工作正常。
例如,为了将通道 1 电平设置为 midi 0 (-51dB) 并将通道 1 声像设置为 midi 64(居中),我知道我应该发送这两条消息:
0xB0 0x07 0x0
和 0xB0 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 Monitor 来验证我的外发消息是否全部正确。所以我的代码没问题。我将我的电缆连接到不同的 MIDI 设备(Roland Juno 合成器)并向它发送了一堆重复的消息:没问题。我的结论是 Mac、我的代码、UM-ONE 接口和电缆都没有问题。剩下的就是搅拌机。我尝试了第二台 Behringer Powerplay P16-M,得到了完全相同的结果。因此,这似乎是混音器的 design/manufacture 问题。
我得出的结论: Behringer Powerplay P16-M 混音器无法正确处理按顺序发送到同一通道的传入 MIDI 消息,除非它们都是相同的控制更改类型。
解决方法: 经过大量实验后,我得出以下结论:
向同一频道发送连续(但不同)的控制更改消息时,在消息之间插入至少 500 毫秒的延迟。例如,如果您向某个频道发送一堆音量消息,然后向该频道发送一堆声像消息,则没有问题。如果你想发送一堆交错的音量和平移消息到同一个频道,那么你需要在每个消息之间插入一个延迟。
重置调音台时,通道 0 ("main") 需要特殊处理。通过插入至少 500 毫秒的延迟,发送到它的消息需要从发送到其他通道的消息中 "insulated"。
[编辑]
更好的解决方案:
不是插入延迟,而是向设备发送 MIDI 弯音消息(例如,发送 0xE0 0x0 0x0
)。尽管 P16-M 没有任何关于弯音消息的使用记录,但它显然足以改变一切并让设备与消息流同步。无论如何对我有用。