Передача MIDI данных от Arduino в компьютер
Передача одиночного потока данных
От одного сенсора передать данные от Arduino в Max/MSP очень просто. Для примера возьмем потенциометр и подключим его к pin 0 контроллера Arduino. Контроллер будет считывать состояние потенциометра и передавать данные последовательным потоком в компьютер в ПО Max/MSP. Диапазон чисел от потенциометра будет лежать в пределах 0-127, что как раз подходит для MIDI.
Как только данные приняты Max/MSP, они сразу же будут перенаправлены на объект ctlout, что дает возможность контроля над любым параметром в любом приложении, которые принимают MIDI данные.
Скетч для Arduino очень прост:
byte val; void setup() { Serial.begin(57600); // Открываем последовательный вывод данных } void loop() { val = analogRead(0) / 8; // считываем значение потенциометра и преобразовываем к диапазону 0 - 127 Serial.print(val, BYTE); // отдаем значение в послед. порт delay(5); // задержка } }
Патч для Max/MSP:
Но что делать, если необходимо передавать данные от 2-х, 3-х, или сразу от 4-х потенциометров? Вот тут сразу начнутся проблемы. Дело в том, что в один промежуток времени может передаваться/приниматься только один байт. И будут возникать неизбежные задержки в передаче данных.
В данном варианте в первом байте будет содержаться информация от первого потенциометра, во втором байте - от второго. На стороне ПК, в MIDI-секвенсоре будет трудно разделять эти данные, да и неэффективно.
Передача многопоточных данных
Одним из решением данной проблемы является преобразование всех данных от потенциометров в MIDI-формат в контроллере Arduino, а затем уже передача MIDI-данных от Arduino в Max/MSP.
MIDI структура
Структура MIDI кажется сложной только на первый взгляд, на самом деле вы в ней быстро освоитесь. Каждое действие (взятие ноты, снятие ноты, питч-бенд и др.) передается в виде MIDI-событий. Мы будем работать с MIDI-событиями состоящими из трех байт. Соответственно в каждом байте хранится 8 бит и мы охватываем диапазон от 0 до 255.
Первый байт - статус, за ним идут 2 байта с данными. В статусном байте содержится информация о типе события, который мы передаем (взятие ноты, снятие ноты, пауза и т.п.) и номер канала от 1 до 16. Байты с данными содержат информацию о действии, к примеру если бы нажали клавишу какой-либо ноты, то данные будут содержать тон ноты и как резко вы ее нажали (velocity): Status Byte; Data Byte #1; Data Byte #2;
Байт статуса
Статусный байт разделен на два полубайта. Первый полубайт содержит биты 4-7 байта, а второй полубайт биты 0-3. Для удобства, принято использовать шестнадцатеричную систему счисления, т.о. первый полубайт может содержать значение от 0 до F, то же самое и второй 0-F.
Первый полубайт указывает на тип события:
8 = Note Off
9 = Note On
A = After Touch
B = Control Change
C = Patch Change
D = Channel Pressure
E = Pitch Bend
F = System Message
Второй полубайт указывает на номер канала:
0 = Канал 1
1 = Канал 2
2 = Канал 3
3 = Канал 4
4 = Канал 5
5 = Канал 6
6 = Канал 7
7 = Канал 8
8 = Канал 9
9 = Канал 10
A = Канал 11
B = Канал 12
C = Канал 13
D = Канал 14
E = Канал 15
F = Канал 16
Если совместить два полубайта, то мы получим всю необходимую информацию статусного байта о MIDI-действии. К примеру, если необходимо передать информацию о взятии ноты на 1-м канале, то значение статусного байта будет 0x90. Префикс 0x указывает на то, что используется шестнадцатеричная система счисления, 9 - взятие ноты, 0 - 1 канал. Или, если необходимо передать питч-бенд на 3-канале, то значение статус-байта будет 0xE2.
Байты данных
После того, как передан статусный байт, нужно передать два байта с данными. Для того, чтобы стало более понятно, приведу два примера:в первом будет взятие ноты (note-on), во втором сообщение контроллера (control change).
При взятии ноты в байте #1 содержится значение тона (pitch) в диапазоне 0-127. В байте #2 содержится значение velocity также в диапазоне 0-127. К примеру, если необходимо передать взятие ноты на 3 канале с pitch 60 и velocity 123, то последовательность байтов для передачи будет следующая:
0x92
60
123
В статусном байте 9 указывает на взятие ноты, 2 - третий канал. В следующем байте #1
цифра 60 указывает на тон, в байте #2 цифра 123 - значение velocity. Если передается взятие ноты (note-on), но с значением velocity - 0, то это равносильно снятию ноты (note-off).
Если статусный байт передает сообщение контроллера, то в 1-ом байте содержится номер контроллера 0-127, а во втором его значение 0-127. К примеру:
0xB4
1
82
Здесь значение B в статусном байте указывает, что это сообщение контроллера (control change), 4 - пятный канал. Первый байт данных содержит цифру 1, это контроллер Modulation. Второй байт содержит величину 82
контроллера.
Основные контроллеры MIDI
Modulation (CC #1)
Breath (CC #2)
Foot Pedal (CC #4)
Portamento Time (CC #5)
Volume (CC #7)
Pan (CC #10)
Expression (CC #11)
Soft Pedal (CC #68)
Передача данных с Arduino
Итак, после всего вышеописанного мы сможем легко посылать MIDI-данные с Arduino. Затем эти данные поступают в Max/MSP и дальше передаются в virtual MIDI path (IAC Driver Bus 1 под OS X или LooBe1 под Windows), а затем уже в музыкальное ПО или DAW (Live, Logic, ProTools, Reason).
Приведу примеры с взятием ноты и с передачей сообщения контроллера.
Пример 1. Отсылаем событие взятия ноты, пауза 1 сек, снятие ноты, пауза 1 сек.
Скетч Arduino:
void setup() { Serial.begin(57600); } void loop() { // посылаем note-on Serial.print(0x90, BYTE); // MIDI Note-on; канал 1 Serial.print(60, BYTE); // MIDI note pitch 60 Serial.print(127, BYTE); // MIDI note velocity 127 delay(1000); // пауза 1 сек // посылаем note-off Serial.print(0x90, BYTE); // MIDI Note-on; канал 1 Serial.print(60, BYTE); // MIDI note pitch 60 Serial.print(0, BYTE); // MIDI note velocity 0 (т.е. note off) delay(1000); // пауза 1 сек }
Патч для Max/MSP:
Пример 2. Отсылаем событие контроллера каждую секунду
Скетч Arduino:
void setup() { Serial.begin(57600); } void loop() { // посылаем сообщение контроллера Serial.print(0xB2, BYTE); // MIDI control change; канал 3 Serial.print(1, BYTE); // MIDI controller #1 Serial.print(127, BYTE); // MIDI controller value of 127 delay(1000); // пауза 1 сек }
Патч для Max/MSP:
Соединяем все воедино
Теперь, мы легко можем считает состояние нескольких потенциометров, сформировать и послать MIDI-данные в Max/MSP. Рассмотрим пару примеров.
Пример 1. Считываем значение потенциометров, подключенных к аналоговым входам 0 и 1. Эти значения будут посылаться в MIDI канал 1, контроллер 1 и канал 2, контроллер 1.
Скетч Arduino:
byte val = 0; void setup() { Serial.begin(57600); } void loop() { val = analogRead(0) / 8; // считываем значение потенциометра 1 // let's send a control change message Serial.print(0xB0, BYTE); // MIDI control change; канал 1 Serial.print(1, BYTE); // MIDI controller #1 Serial.print(val, BYTE); // MIDI controller значение потенциометра 1 val = analogRead(1) / 8; // считываем значение потенциометра 2 // let's send a control change message Serial.print(0xB1, BYTE); // MIDI control change; канал 2 Serial.print(1, BYTE); // MIDI controller #1 Serial.print(val, BYTE); // MIDI controller значение потенциометра 2 delay(5); }
Патч для Max/MSP:
Пример 2. Считываем значение с 6 потенциометров и передаем данные контроллера 1 в канал 1, 2, 3, 4, 5 и 6.
Скетч Arduino:
byte val = 0; void setup() { Serial.begin(57600); } void loop() { for(int i = 0; i < 6; i ++) { val = analogRead(i) / 8; // считываем значения потенциометров // (на аналоговых входах 0 - 5) // посылаем сообщение контроллера Serial.print(0xB0 + i, BYTE); // MIDI control change; номер канала Serial.print(1, BYTE); // MIDI controller #1 Serial.print(val, BYTE); // MIDI controller значение от потенциометра delay(1); // пауза } }
Патч для Max/MSP:
Оригинал статьи на английском языке (перевод Колтыков А.В.)