arduino
MIDI-коммуникация
Поиск…
Вступление
Цель этой темы - продемонстрировать некоторые основные MIDI-программы, которые показывают, как работать с протоколом, и постепенно добавлять полезные функции, требуемые более сложными приложениями.
Пример MIDI THRU
MIDI Thru прост и удобен в тестировании. При правильной работе вы сможете установить проект Arduino между двумя MIDI-устройствами, MIDI IN и MIDI OUT, и вы сможете убедиться, что оба устройства работают вместе. Если у вас есть возможность измерять задержку, вы увидите увеличение из-за команд последовательного буфера и повторной передачи.
// This is a simple MIDI THRU. Everything in, goes right out.
// This has been validate on an Arduino UNO and a Olimex MIDI Shield
boolean byteReady;
unsigned char midiByte;
void setup() {
// put your setup code here, to run once:
// Set MIDI baud rate:
Serial.begin(31250);
byteReady = false;
midiByte = 0;
}
// The Loop that always gets called...
void loop() {
if (byteReady) {
byteReady = false;
Serial.write(midiByte);
}
}
// The little function that gets called each time loop is called.
// This is automated somwhere in the Arduino code.
void serialEvent() {
if (Serial.available()) {
// get the new byte:
midiByte = (unsigned char)Serial.read();
byteReady = true;
}
}
MIDI Thru with Queue
// This is a more complex MIDI THRU. This version uses a queue. Queues are important because some
// MIDI messages can be interrupted for real time events. If you are generating your own messages,
// you may need to stop your message to let a "real time" message through and then resume your message.
#define QUEUE_DEPTH 128
// Queue Logic for storing messages
int headQ = 0;
int tailQ = 0;
unsigned char tx_queue[QUEUE_DEPTH];
void setup() {
// put your setup code here, to run once:
// Set MIDI baud rate:
Serial.begin(31250);
}
// getQDepth checks for roll over. Folks have told me this
// is not required. Feel free to experiment.
int getQDepth() {
int depth = 0;
if (headQ < tailQ) {
depth = QUEUE_DEPTH - (tailQ - headQ);
} else {
depth = headQ - tailQ;
}
return depth;
}
void addQueue (unsigned char myByte) {
int depth = 0;
depth = getQDepth();
if (depth < (QUEUE_DEPTH-2)) {
tx_queue[headQ] = myByte;
headQ++;
headQ = headQ % QUEUE_DEPTH; // Always keep the headQ limited between 0 and 127
}
}
unsigned char deQueue() {
unsigned char myByte;
myByte = tx_queue[tailQ];
tailQ++;
tailQ = tailQ % QUEUE_DEPTH; // Keep this tailQ contained within a limit
// Now that we dequeed the byte, it must be sent.
return myByte;
}
void loop() {
if (getQDepth>0) {
Serial.write(deQueue());
}
}
// The little function that gets called each time loop is called.
// This is automated somwhere in the Arduino code.
void serialEvent() {
if (Serial.available()) {
// get the new byte:
addQueue((unsigned char)Serial.read());;
}
}
Генерация MIDI-часов
// This is a MiDI clk generator. This takes a #defined BPM and
// makes the appropriate clk rate. The queue is used to let other messages
// through, but allows a clock to go immediately to reduce clock jitter
#define QUEUE_DEPTH 128
#define BPM 121
#define MIDI_SYSRT_CLK 0xF8
// clock tracking and calculation
unsigned long lastClock;
unsigned long captClock;
unsigned long clk_period_us;
// Queue Logic for storing messages
int headQ = 0;
int tailQ = 0;
unsigned char tx_queue[QUEUE_DEPTH];
void setup() {
// Set MIDI baud rate:
Serial.begin(31250);
clk_period_us = 60000000 / (24 * BPM);
lastClock = micros();
}
// getQDepth checks for roll over. Folks have told me this
// is not required. Feel free to experiment.
int getQDepth() {
int depth = 0;
if (headQ < tailQ) {
depth = QUEUE_DEPTH - (tailQ - headQ);
} else {
depth = headQ - tailQ;
}
return depth;
}
void addQueue (unsigned char myByte) {
int depth = 0;
depth = getQDepth();
if (depth < (QUEUE_DEPTH-2)) {
tx_queue[headQ] = myByte;
headQ++;
headQ = headQ % QUEUE_DEPTH; // Always keep the headQ limited between 0 and 127
}
}
unsigned char deQueue() {
unsigned char myByte;
myByte = tx_queue[tailQ];
tailQ++;
tailQ = tailQ % QUEUE_DEPTH; // Keep this tailQ contained within a limit
// Now that we dequeed the byte, it must be sent.
return myByte;
}
void loop() {
captClock = micros();
if (lastClock > captClock) {
// we have a roll over condition - Again, maybe we don't need to do this.
if (clk_period_us <= (4294967295 - (lastClock - captClock))) {
// Add a the ideal clock period for this BPM to the last measurement value
lastClock = lastClock + clk_period_us;
// Send a clock, bypasing the transmit queue
Serial.write(MIDI_SYSRT_CLK);
}
} else if (clk_period_us <= captClock-lastClock) {
// Basically the same two commands above, but not within a roll over check
lastClock = lastClock + clk_period_us;
// Send a clock, bypasing the transmit queue
Serial.write(MIDI_SYSRT_CLK);
}
if (getQDepth>0) {
Serial.write(deQueue());
}
}
// The little function that gets called each time loop is called.
// This is automated somwhere in the Arduino code.
void serialEvent() {
if (Serial.available()) {
// get the new byte:
addQueue((unsigned char)Serial.read());;
}
}
Определенные MIDI-сообщения
В общем, протокол MIDI разбит на «сообщения». Существует 4 общих класса сообщений:
- Голос канала
- Режим канала
- Общая система
- Системные сообщения в реальном времени
Сообщения начинаются с байтового значения выше 0x80. Любое значение ниже 0x7F считается данным. Фактически это означает, что 127 - это максимальное значение, которое может быть закодировано в один байт данных MIDI. Для кодирования больших значений требуется два или более байта данных MIDI.
Следует отметить, что сообщения должны быть отправлены, чтобы начать завершение без перерыва ... ЗА ИСКЛЮЧЕНИЕМ ... Системные сообщения в режиме реального времени, которые представляют собой один байт, который может быть введен в середине любого сообщения.
Канальные голосовые сообщения
Статус D7..D0 | Байт данных | Описание |
---|---|---|
1000nnnn | 0kkkkkkk 0vvvvvvv | Примечание. Событие выключения. Это сообщение отправляется при отпускании заметки (завершено). (kkkkkkk) - ключевой (примечательный) номер. (vvvvvvv) - скорость. |
1001nnnn | 0kkkkkkk 0vvvvvvv | Примечание. Событие. Это сообщение отправляется при подавлении заметки (старт). (kkkkkkk) - ключевой (примечательный) номер. (vvvvvvv) - скорость. |
1010nnnn | 0kkkkkkk 0vvvvvvv | Полифоническое ключевое давление (Aftertouch). Это сообщение чаще всего отправляется нажатием на клавишу после того, как оно «выйдет из строя». (kkkkkkk) - ключевой (примечательный) номер. (vvvvvvv) - значение давления. |
1011nnnn | 0ccccccc 0vvvvvvv | Изменение управления. Это сообщение отправляется при изменении значения контроллера. Контроллеры включают такие устройства, как педали и рычаги. Номера контроллеров 120-127 зарезервированы как «Сообщения режима канала» (см. Ниже). (ccccccc) - номер контроллера (0-119). (vvvvvvv) - значение контроллера (0-127). |
1100nnnn | 0ppppppp | Изменение программы. Это сообщение отправляется при изменении номера патча. (ppppppp) - новый номер программы. |
1101nnnn | 0vvvvvvv | Давление в канале (после касания). Это сообщение чаще всего отправляется нажатием на клавишу после того, как оно «выйдет из строя». Это сообщение отличается от полифонического после касания. Используйте это сообщение для отправки единственного максимального значения давления (всех текущих нажатых клавиш). (vvvvvvv) - значение давления. |
1110nnnn | 0lllllll 0mmmmmmm | Изменение высоты тона. Это сообщение отправляется, чтобы указать изменение высоты тона (колесо или рычаг, как правило). Бендер высоты измеряется четырнадцатиразрядным значением. Центр (без изменения высоты) - 2000H. Чувствительность является функцией приемника, но может быть установлена с использованием RPN 0. (lllllll) являются наименее значимыми 7 бит. (mmmmmmm) являются наиболее значительными 7 бит. |
Сообщения режима канала
Статус D7..D0 | Байт данных | Описание |
---|---|---|
1011nnnn | 0ccccccc 0vvvvvvv | Сообщения режима канала. Это тот же код, что и Control Change (выше), но реализует управление режимом и специальное сообщение с использованием зарезервированных номеров контроллеров 120-127. Команды: |
Звук выключен. При приеме All Sound Off все генераторы выключаются, а их объемные конверты устанавливаются на ноль как можно скорее. c = 120, v = 0: весь звук выключен | ||
Сбросьте все контроллеры. Когда принимается Сброс всех контроллеров, все значения контроллера сбрасываются до значений по умолчанию. (См. Конкретную Рекомендуемую практику для дефолтов). | ||
c = 121, v = x: значение должно быть равным нулю, если иное не разрешено в конкретной Рекомендуемой практике. | ||
Локальный контроль. Когда Local Control выключен, все устройства на данном канале будут реагировать только на данные, полученные по MIDI. Воспроизводимые данные и т. Д. Будут проигнорированы. Local Control On восстанавливает функции обычных контроллеров. | ||
c = 122, v = 0: локальное управление выключено | ||
c = 122, v = 127: локальное управление включено | ||
Все заметки выключены. Когда будет выведено All Notes Off, все генераторы отключится. | ||
c = 123, v = 0: все примечания выключены (см. текст для описания команд реального режима). | ||
c = 124, v = 0: Omni Mode Off | ||
c = 125, v = 0: Omni Mode On | ||
c = 126, v = M: включен режим Mono (Poly Off), где M - количество каналов (Omni Off) или 0 (Omni On) | ||
c = 127, v = 0: Poly Mode On (Mono Off) (Примечание: эти четыре сообщения также вызывают All Notes Off) |
Общие сообщения системы
Статус D7..D0 | Байт данных | Описание |
---|---|---|
11110000 | 0iiiiiii [0iiiiiii 0iiiiiii] 0ddddddd --- --- 0ddddddd 11110111 | Исключительная система. Этот тип сообщения позволяет производителям создавать свои собственные сообщения (такие как объемные дампы, параметры патча и другие данные, не относящиеся к спецификации) и обеспечивает механизм для создания дополнительных сообщений MIDI Specification. Идентификационный код производителя (присвоенный MMA или AMEI) составляет либо 1 байт (0iiiiiii), либо 3 байта (0iiiiiii 0iiiiiii 0iiiiiiii). Два из 1 байтовых идентификаторов зарезервированы для расширений, называемых универсальными эксклюзивными сообщениями, которые не зависят от производителя. Если устройство распознает идентификационный код как свой собственный (или как поддерживаемое универсальное сообщение), он будет прослушивать остальную часть сообщения (0ddddddd). В противном случае сообщение будет проигнорировано. (Примечание. Только сообщения в режиме реального времени могут чередоваться с помощью System Exclusive.) |
11110001 | 0nnndddd | Временная диаграмма временного кода MIDI. nnn = Тип сообщения dddd = Значения |
11110010 | 0lllllll 0mmmmmmm | Указатель позиции песни. Это внутренний 14-битный регистр, который содержит количество битов MIDI (1 бит = шесть MIDI-часов) с начала песни. l - LSB, m - MSB. |
11110011 | 0sssssss | Выбор песни. Выбор песни определяет, какую последовательность или песню необходимо воспроизвести. |
11110100 | Не определено. (Зарезервированный) | |
11110101 | Не определено. (Зарезервированный) | |
11110110 | Tune Request. Получив Tune Request, все аналоговые синтезаторы должны настраивать свои осцилляторы. | |
11110111 | Конец Эксклюзив. Используется для завершения сброса System Exclusive (см. Выше). |
Системные сообщения в реальном времени
Статус D7..D0 | Байт данных | Описание |
---|---|---|
11111000 | Часы синхронизации. Отправляется 24 раза в квартал, когда требуется синхронизация (см. Текст). | |
11111001 | Не определено. (Зарезервированный) | |
11111010 | Начните. Начните воспроизведение текущей последовательности. (Это сообщение будет сопровождаться синхронизирующими часами). | |
11111011 | Продолжить. Продолжите в точке, где последовательность была остановлена. | |
11111100 | Стоп. Остановите текущую последовательность. | |
11111101 | Не определено. (Зарезервированный) | |
11111110 | Активное зондирование. Это сообщение предназначено для отправки повторно, чтобы сообщить получателю, что соединение живое. Использование этого сообщения является необязательным. При первоначальном приеме приемник будет ожидать получить еще одно сообщение Active Sensing каждые 300 мс (макс.), А если нет, то предполагается, что соединение было прекращено. При завершении приемник отключит все голоса и вернется в нормальное (неактивное зондирование). | |
11111111 | Сброс. Сбросьте все приемники в системе для включения питания. Это следует использовать экономно, предпочтительно под ручным управлением. В частности, его нельзя отправлять при включении питания. |