Unopad: Бесшовный Arduino MIDI-контроллер для Ableton Live
Компоненты и материалы
| Компонент | Количество | Примечание |
|---|---|---|
| Arduino UNO | 1 | |
| Кнопки APEM A01 серии (с подсветкой) | 9 | |
| Перемычки | 1 комплект |
Необходимые инструменты
- Паяльник (опционально)
Приложения и онлайн-сервисы
- loopMIDI — виртуальный MIDI-порт
- Hairless MIDI<->Serial Bridge — мост между Serial и MIDI
- Ableton Live 10 Trial
О проекте
Краткое описание
Большинство уроков по MIDI-контроллерам охватывают только сторону Arduino, но не работу в DAW. Я хотел собрать всё в одном месте. Этот проект поможет новичкам понять процесс создания и использования контроллера.
Давно хотел сделать MIDI-контроллер и наконец сделал. Arduino Uno не умеет слать MIDI по USB, поэтому нужны дополнительные программы (объясню ниже).
Далее: электрическая схема, код Arduino, подключение к компьютеру и DAW (Ableton).
Схема
Очень простая. 9 кнопок, не в матрице, а по отдельному пину — чтобы можно было нажимать несколько одновременно (проще и свободных пинов хватает: 9 вместо 6 в матрице).
Кнопка 3 → D2
Кнопка 2 → D3
Кнопка 1 → D4
Кнопка 6 → D5
Кнопка 5 → D6
Кнопка 4 → D7
Кнопка 9 → D8
Кнопка 8 → D9
Кнопка 7 → D10
MIDI-маппинг
Определены 9 MIDI-нот (36–44) и команды NOTE_ON / NOTE_OFF.
#define NOTE_ONE 36
#define NOTE_TWO 37
// ... до NOTE_NINE 44
#define NOTE_ON_CMD 0x90
#define NOTE_OFF_CMD 0x80
#define MAX_MIDI_VELOCITY 127
Этот MIDI-учебник поможет понять, как использовать MIDI для управления любым устройством.
MIDI через USB
Arduino Uno не отправляет MIDI в DAW напрямую. Нужны две программы: LoopMIDI (создаёт виртуальный MIDI-порт) и Hairless MIDI<->Serial Bridge (соединяет Serial-порт Arduino с виртуальным MIDI-портом).
Разбор кода
Создал класс Button и использую так:
int NUMBER_BUTTONS = 9;
Button Button1(4, NOTE_ONE);
Button Button2(3, NOTE_TWO);
Button Button3(2, NOTE_THREE);
Button Button4(7, NOTE_FOUR);
Button Button5(6, NOTE_FIVE);
Button Button6(5, NOTE_SIX);
Button Button7(10, NOTE_SEVEN);
Button Button8(9, NOTE_EIGHT);
Button Button9(8, NOTE_NINE);
Button *Buttons[] {&Button1,&Button2,&Button3,&Button4,&Button5,&Button6,&Button7,&Button8,&Button9};
Подробности реализации кнопки см. в Buttons.cpp.
Никаких внешних резисторов — кнопка между пином и землей. Используем INPUT_PULLUP вместо INPUT.
// Buttons.cpp
pinMode(_pin, INPUT_PULLUP);
Трюк: при нажатии кнопка даёт LOW (0), а не HIGH. Начальное состояние — 1.
// Button is pressed
if (state == 0) { midiNoteOn(Buttons[i]->Note,127); }
// Button is not pressed
if (state == 1) { midiNoteOff(Buttons[i]->Note,0); }
// Buttons.cpp
_state = 1;
Каждому NOTE_ON должен соответствовать NOTE_OFF. При нажатии — NOTE_ON, при отпускании — NOTE_OFF. Храним последнее состояние.
// Buttons.cpp
_lastState = 1;
MIDI-сообщения шлём только при смене состояния.
// Buttons.cpp
if(state != _lastState) { // ... }
Функции отправки MIDI (параметры: нота, velocity — использую максимум):
void midiNoteOn(byte note, byte midiVelocity) {
Serial.write(NOTE_ON_CMD);
Serial.write(note);
Serial.write(midiVelocity);
}
void midiNoteOff(byte note, byte midiVelocity) {
Serial.write(NOTE_OFF_CMD);
Serial.write(note);
Serial.write(midiVelocity);
}
Логика в функции updateButtons:
void updateButtons() {
for (int i = 0; i < NUMBER_BUTTONS; i++) {
int state = Buttons[i]->getButtonState();
if (state == 0) midiNoteOn(Buttons[i]->Note,127);
if (state == 1) midiNoteOff(Buttons[i]->Note,0);
}
}
Вызывается в loop():
void loop() {
if (NUMBER_BUTTONS != 0) updateButtons();
}
Использование контроллера в DAW (Ableton Live)
Как новичок в DAW, не знал, как подключить самодельный контроллер. Korg Nanokey Ableton определил сам, а Arduino — нет, ПК видит в нём просто Arduino.
Пошагово для Ableton Live 10:
Подключение к Ableton
- Открыть loopMIDI, создать виртуальный MIDI-порт (скриншоты в оригинале).
- Открыть Hairless MIDISerial, назначить: Serial port — порт Arduino, MIDI In/Out — созданный виртуальный порт.
- Установить baudrate такой же, как в скетче Arduino (Файл → Preferences).
- Если всё верно, в Hairless будут видны MIDI-сообщения при нажатии кнопок.
- Не закрывать Hairless!
- В Ableton: Options → Preferences → Link MIDI. Настроить как на примере (скриншот).
- Проверить: в правом верхнем углу Ableton верхний квадратик должен мигать при нажатии кнопок (GIF).
Контроллер готов!
Использование как Drum Rack или клавиатура
Выбрать MIDI-трек. В секции Monitor выбрать In вместо Auto, в MIDI to — выход на колонки. Должен звучать. То же для Drum Rack (GIF).
Мой готовый проект
Корпус — старый динамик, пластик дома. Вырезал пластины под корпус, просверлил отверстия под кнопки и один светодиод (добавлю позже). Ещё один такой же динамик оставлю для следующего контроллера с энкодерами и джойстиками.
Планы (To Do)
- Длинное нажатие кнопки для переключения в CC-режим.
Код
- MidiDrumPad.ino
- Buttons.cpp
- Buttons.h
MidiDrumPad.ino (Arduino)
#include "Buttons.h"
//------Notes-------------
#define NOTE_ONE 36
#define NOTE_TWO 37
#define NOTE_THREE 38
#define NOTE_FOUR 39
#define NOTE_FIVE 40
#define NOTE_SIX 41
#define NOTE_SEVEN 42
#define NOTE_EIGHT 43
#define NOTE_NINE 44
#define NOTE_ON_CMD 0x90
#define NOTE_OFF_CMD 0x80
#define MAX_MIDI_VELOCITY 127
//------------------------
int NUMBER_BUTTONS = 9;
Button Button1(4, NOTE_ONE);
Button Button2(3, NOTE_TWO);
Button Button3(2, NOTE_THREE);
Button Button4(7, NOTE_FOUR);
Button Button5(6, NOTE_FIVE);
Button Button6(5, NOTE_SIX);
Button Button7(10, NOTE_SEVEN);
Button Button8(9, NOTE_EIGHT);
Button Button9(8, NOTE_NINE);
Button *Buttons[] {&Button1,&Button2,&Button3,&Button4,&Button5,&Button6,&Button7,&Button8,&Button9};
int red_light_pin= 11;
int green_light_pin = 12;
int blue_light_pin = 13;
void setup() {
Serial.begin(9600);
}
void loop() {
if (NUMBER_BUTTONS != 0) updateButtons();
}
void updateButtons() {
for (int i = 0; i < NUMBER_BUTTONS; i++) {
int state = Buttons[i]->getButtonState();
if (state == 0) {
midiNoteOn(Buttons[i]->Note,127);
}
if (state == 1) {
midiNoteOff(Buttons[i]->Note,0);
}
}
}
void RGB_color(int red_light_value, int green_light_value, int blue_light_value) {
analogWrite(red_light_pin, red_light_value);
analogWrite(green_light_pin, green_light_value);
analogWrite(blue_light_pin, blue_light_value);
}
void midiNoteOn(byte note, byte midiVelocity) {
Serial.write(NOTE_ON_CMD);
Serial.write(note);
Serial.write(midiVelocity);
}
void midiNoteOff(byte note, byte midiVelocity) {
Serial.write(NOTE_OFF_CMD);
Serial.write(note);
Serial.write(midiVelocity);
}
Buttons.cpp (Arduino)
#include "Buttons.h"
Button::Button(int pin, short note) {
_pin = pin;
pinMode(_pin, INPUT_PULLUP);
_state = 1;
_lastState = 1;
Note = note;
}
int Button::getButtonState() {
int returnValue = -1;
int state = digitalRead(_pin);
if(state == _lastState) return 5;
if(state != _lastState) {
if(state == 0) {
returnValue = 0;
} else {
returnValue = 1;
}
}
_lastState = state;
return returnValue;
}
Buttons.h (Arduino)
#ifndef Buttons_h
#define Buttons_h
#include <Arduino.h>
//Button (Pin Number, Note Number)
class Button {
public:
Button(int pin, short note);
int getButtonState();
int Note;
private:
int _pin;
short _note;
int _state;
int _lastState;
};
#endif
Код также на GitHub: https://github.com/sadreactonly/MidiDrumpad
Схемы
Производственный процесс
- Управление яркостью LED с ШИМ через кнопку на Arduino Mega
- Создание игры в реальном времени на Arduino Nano с гироскопом MPU-6050 и OLED-дисплеем
- Проект с датчиком DHT11: индикация температуры светодиодами и пьезо-динамиком на Arduino
- Строим Iron Man на Arduino: компоненты, сенсоры и пошаговый гайд
- Простой ИК-датчик препятствий на Arduino: ИК-светодиод и фотодиод
- Find Me: Умный поисковик вещей на Arduino и Bluetooth
- DIY-контроллер увлажнителя на Arduino с реле: безопасная работа с высоким напряжением
- Создание интерактивного 4x4x4 светодиодного куба на Arduino Uno и 1Sheeld
- Своё рулевое управление (джойстик) для игр на Arduino
- Arduino 101: Пedometer с датчиком DHT11 и LCD-дисплеем