Pull to refresh

Приставка MIDI для синтезатора (taptempo)

Reading time 3 min
Views 7.2K


Коллега музыкант попросил сделать ему приставку к синтезатору, чтобы темп задавать не переменным сопротивлением, а кнопкой.

Для реализации был выбран Arduino, как наиболее доступный.



Покурив основы MIDI выяснил, что за задание темпа отвечает специальный байт-0хF8. В стандарте принято 24 таких сообщения в секунду при темпе 1 удар в секунду, то есть при 60 bpm (ударов в минуту).

Значит надо снимать нажатие кнопки, брать интервал между ними и выдавать соответствующий сигнал одновременно отобразив его на маленьком экране.

Звучит хорошо, но на практике выяснилось что выдаваемое на экран с реальным сигналом не сочетается, оказалось что одна из переменных была INT, а не FLOAT, поэтому сигнал на синтезатор выводился округленный до целого, хотя на экране выдавался до 0.001.

Поправив типы переменных получил отличный результат, но подвел человеческий фактор- коллега не мог точно выстучать нужный темп. Поэтому было добавлено уточнение-каждый следующий интервал складывался с предыдущим и делился на 2 (усреднение).

Результат стал лучше, но получалась вилка вокруг нужного значения, например нужно 120bpm, а выходило 117 и 124.

В итоге был рожден оптимальный алгоритм:

Записываются от 1 до 20 интервалов(произвольно), значения усредняются с каждым нажатие(чтобы была видна динамика изменения темпа на экране) и если оператор не кликает 1.5 секунды, то массив значений интервалов обнуляется и он может начинать заново. Полторы секунды были взяты как определение минимального ритма-40 ударов в минуту.

Код
//6 вход кнопки

//Tx1-выход миди сигнала

#include <LiquidCrystal.h>/////подключаем библиотеку экрана

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);///задаем для экрана ножки данных

int statButton=0;///статус кнопки
int laststatButton=LOW;
unsigned long int lastinHigh=0;///счетчики
unsigned long int inHigh=0;
unsigned long int clock=0;
unsigned long int lastclock=0;

int durations[19];///массив для хранения периодов нажатия
int count=0;
int duration=0;
float sum=3000;////переменная суммы массива периодов
float bps=2;//удары в секунду
float mps;///тики TapTempo в секунду
float tic;
int count2=0;///счетчики
int count3=0;
int count4=0;

void setup()
{

Serial.begin(31250);///////////////ОБЯЗАТЕЛЬНО ТАКАЯ СКОРОСТЬ
//Serial.begin(9600);
pinMode(6,INPUT);

lcd.begin(16, 2);

}

void loop()
{
mps=bps*24 ;//удары в секунду*сообщений в секунду
tic=1000000/mps;// секунду/сообщений в секунду

clock=micros();///засекаем время для тиков

if(clock-lastclock>tic)///Выдача тиков по таймеру
{

Serial.write(byte(0xF8));///собственно сам тик
lastclock=clock;

}
if(count2==400)///чтение кнопки
{
readbut();
count2=0;
}
count2++;

if(count3==2000)//отображение на дисплей
{
lcd.clear();
//lcd.setCursor(0, 0);

lcd.print(bps*60);
count3=0;
}
count3++;

}

void readbut()///отдельно чтение кнопки
{
statButton=digitalRead(6);
if(statButton==HIGH&&laststatButton==LOW)///если кнопка перешла из LOW в HIGH
{
inHigh=millis();///засекаем время нажатия

duration=inHigh-lastinHigh;//вычисляем разницу между последними нажатиями
// Serial.println(duration);

if(duration<1500&&duration>100)//и если она вписывается
{
if(count<19)///то записываем ее в массив
{
durations[count]=duration;
// Serial.println(durations[count]);
count++;

}

}else{
for(int i=0;i<19;i++)
{
durations[i]=0;
}
count=0;

}

}
lastinHigh=inHigh;
laststatButton=statButton;
for(int i=0;i<19;i++)///перебираем массив
{
if(durations[i]!=0)//ищем не нулевые элементы
{

sum=(sum+durations[i]);
// println(durations[i]);
count4++ ;//и считаем их

bps=1000.0/(sum/count4);///сумма не нулевых/число не нулевых
}
}
count4=0;
sum=0;
}


В итоге все работает и участвует в выступлениях.

Если есть пожелания или предложения — пишите.
Tags:
Hubs:
0
Comments 8
Comments Comments 8

Articles