Я пишу простую утилиту командной строки C ++ для отправки MIDI-сообщений с моего Mac (MacOS 10.12) на аудиомикшер Behringer Powerplay P16-M через интерфейс USB Roland UM-One Mk 2. Я использую rtmidi
MIDI API в реальном времени.
Когда я отправляю два последовательных MIDI-сообщения на один и тот же канал, микшер искажает эти два сообщения. Когда я посылаю два последовательных сообщения на альтернативные каналы, все работает нормально.
Например, чтобы установить уровень канала 1 на миди 0 (-51 дБ) и панорамирование канала 1 на миди 64 (по центру), я понимаю, что должен отправить эти два сообщения:
0xB0 0x07 0x0
а также 0xB0 0x0A 0x40
, Когда я отправляю эти сообщения одно за другим, светодиоды на микшере неожиданно показывают, что уровень канала 1 составляет около 64, а панорамирование не изменяется — как если бы я отправил одно сообщение 0xB0 0x07 0x40
, Если вместо этого я чередую два сообщения с сообщением для другого канала или со смехотворно долгим сном (что-то около 900 миллисекунд делает это), светодиоды показывают ожидаемые настройки: уровень в нуле, панорамирование.
Что я делаю неправильно? Почему я не могу отправить два последовательных сообщения на один и тот же канал? Я отправляю сообщения слишком быстро? Это 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-сообщения, которые отправляются последовательно на один и тот же канал, если только они не имеют одинаковый тип изменения управления.
Обходной путь:
После долгих экспериментов вот что я придумала:
При отправке последовательных (но разных) сообщений о смене управления на один и тот же канал вставьте задержку не менее 500 миллисекунд между сообщениями. Например, если вы отправляете кучу объемных сообщений на канал, а затем кучу панорамирующих сообщений на этот канал, проблем не возникает. Если вы хотите отправить несколько чередующихся томов и сообщений панорамирования на один и тот же канал, то вам нужно вставить задержку между каждым сообщением.
При сбросе микшера канал 0 («основной») требует особой обработки. Сообщения, отправленные на него, должны быть «изолированы» от сообщений, отправленных на другие каналы, с задержкой не менее 500 миллисекунд.
Других решений пока нет …