Коллега музыкант попросил сделать ему приставку к синтезатору, чтобы темп задавать не переменным сопротивлением, а кнопкой.
Для реализации был выбран 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;
}
В итоге все работает и участвует в выступлениях)
Если есть пожелания или предложения-пишите.
Автор: Dorrin
а как проще всего сделать мигающий согласно темпу светодиод?