Наверняка вы слышали про такой старинный музыкальный инструмент, как шарманка. Когда, вращая ручку, исполняется записанная заранее по нотам мелодия. В этой статье мы решили воссоздать забытый инструмент, используя новые технологии.
Мы решили написать для нашей шарманки два варианта работы:
5V
соедините через резистор 200 Ом к пину 4
MIDI-разъёма, землю GND
к пину 2
MIDI-разъёма и цифровой пин 1
к пину 5
MIDI-разъёма. 4
по 11
пин.
В этом проекте мы используем Arduino Leonardo, потому что она распознается компьютером как HID-устройство и может притворяться компьютерной клавиатурой. Если у вас другая платформа, например Arduino UNO, вы можете использовать второй вариант с подключением к MIDI-синтезатору, поменяв в скетче Serial1
на Serial
.
Каждые 10 миллисекунд опрашиваем по очереди каждый датчик на предмет изменения, если датчик изменил своё состояние и увидел чёрное, отправляем подсоединенному компьютеру сигнал о нажатии клавиши, которой соответствует данный датчик.
В случае с MIDI-синтезатором ситуация будет похожа на предыдущий случай. Также, каждые 10 миллисекунд опрашиваем по очереди каждый датчик на предмет изменения. Если датчик изменил своё состояние и увидел чёрное, отправляем на синтезатор MIDI-сообщение, в котором будет содержаться информация о нажатии ноты, которая закреплена за этим датчиком. Как только значения датчика опять вернётся к нулю, отправляем MIDI-сообщения, в котором будет содержаться информация о снятии ноты, которая закреплена за этим датчиком.
// датчики подключены по порядку в числовой последовательности // даём разумное имя для пина к которому подключен первый датчик #define START_PIN 4 // Массив состояний каждого из датчиков bool state[8]; // Массив кнопок клавиатуры char keys[] = {'q', 'w', 'e', 'r', 't', 'y', 'u', 'i'}; void setup() { // Начало работы эмуляции клавиатуры (только на платах Leanardo или Due) Keyboard.begin(); } void loop() { // проверяем по очереди каждый датчик for (int i = 0; i < 8; ++i) { // считываем состояние каждого из датчикок, где // i — номер датчика с которого идёт считывание bool curState = digitalRead(START_PIN + i); if (curState != state[i]) { // если датчик изменил своё состояние // присваиваем ему новое состояние state[i] = curState; if (curState) // если новое состояние истино // отправляем подсоединенному компьютеру // сигнал о нажатии клавиши, где // i — номер кнопки Keyboard.write(keys[i]); } } delay(10); }
// даём разумное имя для каждой ноты из MIDI таблицы (0–127) #define NOTE_1 29 // Фа первой октавы #define NOTE_2 31 // Соль первой октавы #define NOTE_3 33 // Ля первой октавы #define NOTE_4 35 // Си первой октавы #define NOTE_5 36 // До первой октавы #define NOTE_6 38 // Ре второй октавы #define NOTE_7 40 // Ми второй октавы #define NOTE_8 41 // Фа второй октавы // сила нажатия(громкость) #define VELOCITY 100 // тип MIDI сообщения — статус байт // 1001 взять ноту (Note On) // 0000 номер канала #define NOTE_ON 0b10010000 // датчики подключены по порядку в числовой последовательности // даём разумное имя для пина к которому подключен первый датчик #define START_PIN 4 // массив нот int note[8] = { NOTE_1, NOTE_2, NOTE_3, NOTE_4, NOTE_5, NOTE_6, NOTE_7, NOTE_8 }; // массив состояний каждого из датчиков bool state[8]; void setup() { // открываем последовательный порт // задаем скорость передачи данных 31250 кбит/с для MIDI Serial1.begin(31250); } void loop() { // проверяем по очереди каждый датчик for (int i = 0; i < 8; ++i) { // считываем состояние каждого из датчикок, где // i — номер датчика с которого идёт считывание bool curState = digitalRead(START_PIN + i); if (curState != state[i]) { // если датчик изменил своё состояние // присваиваем ему новое состояние state[i] = curState; if (curState) // если новое состояние истино // воспроизводим ноту, которая закреплена за датчиком // для воспроизведения ноты и других функций протокола MIDI, // необходимо вызвать функцию и передать ей 3 параметра: // тип MIDI-сообщения, ноту и силу нажатия. sendMidiMessage(NOTE_ON, note[i], VELOCITY); else // воспроизведение ноты нужно остановить, // иначе она так и будет звучать // в нашем случае высталяем громкость равной 0 sendMidiMessage(NOTE_ON, note[i], 0); } } delay(10); } // функция отправки MIDI сообщений через Serial1 порт void sendMidiMessage(int cmd, int data_1, int data_2) { Serial1.write(cmd); Serial1.write(data_1); Serial1.write(data_2); }
Вы можете увеличить/уменьшить количество воспроизводимых нот путём увеличения/уменьшение количество датчиков. Длительность мелодии зависит от диаметра барабана. Так же можно автоматизировать процесс путём подключение вместо ручки вращение шаговый двигатель или сервопривод.