По профессии я - программист, одно из моих хобби - музыка. Программирую с 14-ти лет, а первый раз взял гитару в руки в 20. С тех пор "оброс" множеством инструментов, как помню, через полгода первой работы купил себе электронные барабаны.

Много лет назад я написал цикл хабра-статей - как запрограммировать музыкальный синтезатор "с нуля", где разобрал программирование осцилляторов, простых эффектов, модуляций.

Барабаны для музыканта - серьезный выбор, за которым идут трудности и траты. Дома акустику не поставишь - соседи прибегут через много этажей, в гараже без надлежащей звукоизоляции (у меня пошла трещина по стене и отлетела вся штукатурка) и отопления (конец барабанам) тоже, а железо (тарелки) стоят ого-го. А уважающий себя барабанщик ещё и рабочий обычно свой по концертам возит)))

А играть хотелось. И конечно, на всём. Зарплату платили, вот я и заинтересовался электронными барабанами. Как программист-задрот-домосед, уже имея дома гитары и электронное пианино, я хотел, чтобы и барабанный станок тоже был рядом. Всегда отмечал эту несправедливость: гитарист может оттачивать мастерство ночами (на электрогитаре - без проблем), а барабанщик - увы, ходите на точку. А это время на дорогу и деньги. Зато барабанщики в цене!)

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

Изначально в комплекте шла педаль бас-бочки, с модулем, называлась она Alesis Stealth Kick, но, чёрт возьми, нифига там не stealth. Низкие глухие удары были слышны очень хорошо по всей квартире.

Поскольку я очень люблю панк-рок с элементами металкора, то быстро приобрёл себе настоящий кардан (двойную педаль) и модуль к ней - Roland KD-9 Kick Trigger. Низкого шума было дофига, любая практика превращалась для меня и соседей в ремонт со странными ломаными ритмами. С одной стороны - круто - ты играешь на настоящих педалях для бас-бочки, больше вероятность (или, надежда), что сразу заиграешь на настоящей акустике; с другой стороны - адский для меня и соседей шум.

Я никогда не любил играть громко, я автоматически сразу представляю себя на месте соседей, воспитался я по принципу "отношусь к людям так, как бы хотел, чтобы они относились ко мне".
Конечно, проблема шума остро встала на пути барабанных тренировок. Особенно низких ударов по пэду бас-бочки.
Сначала я колхозил всякие "умягчители ударов" - тряпки, полотенца, губки на пэде. Они плохо и не долго держались, да и не помогали.

Где-то в интернете появился тренд, что электронные барабаны ставят на платформу, чтобы гасить вибрации и низкие частоты - интересная такая конструкция, два листа толстой фанеры, а между ними - теннисные мячики. "Домашний колхоз".

В интернете куча роликов и статей по запросу "tennis ball electric drum platform". Позиционируется как, якобы, "антивибрационная шумогасительная платформа". Мне стало интересно, я её собрал. Её видно на первой фотографии установки в начале статьи. Не знаю насчёт шума, по-моему, ничего не поменялось особо, а вибраций на моих барабанов, будто и не было изначально. Зато у меня появилась стильная платформа-место для барабанов!

У педалей есть специальные винты-фиксаторы, и, если быть внимательным, то можно заменитить, что барабанные установки всегда ставят на ковры - именно для фиксации расположения всех деталей друг относительно друга. Очевидно, без ковра любимый домашний паркет и ламинат будет испорчен и исцарапан, а педали будут кататься туда-сюда в ногах.

Начал думать дальше. Вся суть была именно в сильном ударе колотушки басовой педали.
На электронных барабанах я не преследовал цель "точно воспроизвести акустику", скорее, хотелось базово разучивать песни, оттачивать парадидлы, кусочки, сбивки. Т.е. упор - на практику, ритм, дисциплину.

Значит, от реальных педалей нужно было отказываться. Я долго думал и фантазировал, чтобы убрать колотушку, и максимально смягчить удар, поставить какие-нибудь магнитные датчики Холла и по ним фиксировать удар, а потом как-нибудь его воспроизводить.

И как-то раз, у кого-то в гостях, я играл на электронном пианине, а на нём была педаль сустейна Casio SP3. Так мне она понравилась! Такая плавная.
Обычно, педали сустейна ("квадратные") издают характерный щелчок, при нажатии, и быстро нажимать их несколько раз не предоставляется возможным. Или это у меня такие были?... А тут - плавная педаль, без характерного щелчка, и можно быстро нажимать её ногой... сразу вспомнились электронные барабаны!

А что, если вместо бас-педалей поставить педаль(и) сустейна, именно вот эти - Casio SP3?
Почему бы и нет, внешне - даже привлекает. Поискал в интернете - не я один беспокоюсь за проблему шума, и тоже есть какие-то самоделкины, пытающиеся решить схожие задачи.
Окей, но нужно решить несколько технических моментов.

Как работают пэды (те штуки, эмулирующие настоящие барабаны, куда надо бить) электронных барабанов? Например, есть эмуляция "тарелки" - круглая такая штука из пластика и резины. Когда по ней ударяет палочка, возникают механические колебания всей этой штуковины. Внутри стоит пьезодатчик - он преобразует механические колебания в электрические. У него есть мембрана, которая колеблется, и к нему подведены два провода для передачи уже электрических колебаний.

Далее, электрические колебания по проводу передаются на барабанный процессор - маленький компьютер, главное устройство электронных барабанов, которые уже и воспроизводят нужный звук. Обычно в нём есть всякие настройки, дисплей, метроном и всякая всячина.

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

Да, есть реально бесшумные педали бас-бочки для электронных барабанов. Без удара, а просто регистрирующие нажатия. Но стоят они.... ляяяяяя.
Пример такой педали - Yamaha KU100. Цена на январь 26-го - 8490р.

Собственно, я выбираю среднее - коль уж от "реалистичного удара" я ухожу, покупать коммерческую педаль - тоже не хочу, буду пробовать идею с пианинной педалью сустейна.

Про технические моменты. Стандартная педаль сустейна на пианино работает как примитивная кнопка - нажата/не нажата. Банально, все педали имеют стандартный моно штекер JACK 6.3. Нажатие педали либо соединяет цепь, либо разъединяет её (бывают педали с возможностью инвертирования).

Если через педаль подавать напряжение, то грубо график нажатия-отжатия будет выглядеть так:

Из-за сложности/неидеальности физического мира, в моменты нажатия и отжатия будет наблюдаться эффект "дребезг контактов" (contact bounce).

Окей. А какой сигнал получает барабанный процессор от реальных пэдов с пьезодатчиками? Конечно, по-трушному - подключить пэд к осциллографу и посмотреть график.
Я быстро загуглил это дело в интернете - какой то чувак собирает сам электронную установку - https://arduinoplusplus.wordpress.com/2020/05/06/diy-midi-percussion-kit-part-1/.

Здесь показан удар по "тарелке" - в пике напряжение 5 вольт, и утихание колебаний примерно за 30 миллисекунд.

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

Сначала попробовал просто подключать разные педали сустейна (и ту самую, Casio SP3) напрямую вместо бас-пэда. Результат на моих барабанах - нулевой. Разве что при подключении/отключении педали от процессора регистрировался удар. Значит, просто замкнуть/разомкнуть цепь - недостаточно.
Нужно именно иметь какой-то сигнал во времени.

Всё, значит, без Arduino точно не обойтись. Педаль сустейна будет подключаться к Arduino и работать, как кнопка, при регистрировании нажатия нужно либо

  • генерировать похожий затухающий сигнал

  • воспроизводить записанный реальный сигнал с пьезодатчика

В арсенале у меня имелась самая простая Arduino Uno для разработки/тестирования и Arduino Nano для самой конструкции.

Да, у такой версии Arduino имеются аналоговые входы и выходы, но, они работают путем генерирования ШИМ-импульсов, а мне нужен "нормальный" аналоговый сигнал.
Чтобы генерировать такой аудиосигнал, нужен цифро-аналоговый преобразователь, который доступен отдельным чипом.

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

Чтож, я решил просто провести простейшие эксперименты: взять Arduino, подключить один из аналоговых выходов к процессору (как будто Arduino у меня теперь - пэд) и подавать ШИМ-импульс с максимальным напряжением определенное время, быть может, через какое-то количество миллисекунд процессор определит удар.

Для удобства отладки используем выходной пин 13, так как он соединен непосредственно со светодиодом (на плате отображается буквой L), и он будет мигать и гореть каждый раз, как только мы будем подавать на 13-й пин напряжение HIGH.

Итак, устанавливаем 13-й пин на вывод, и "мигаем" за N миллисекунд, потом ждём секунду и повторяем всё снова.
Нужно определить, будет ли процессор детектировать удар и сколько нужно для этого минимального количества миллисекунд N.

void setup() {
  pinMode(13, OUTPUT);
}

void loop() {
  const int N = 1; // будем увеличивать N, и проверять регистрирование удара процессором
  digitalWrite(13, HIGH);
  delay(N);
  digitalWrite(13, LOW);
  
  delay(1000);
}

О, чудо! Моя простая гипотеза подтвердилась! При значении N в 10 миллисекунд я слышу удар бочки каждую секунду.
Супер! Это значит, что процессор сначала считывает изменение (скачок) напряжения (видимо, как пик удара) а затем некоторое время N наблюдает за сигналом, и, в конечном итоге, детектирует удар, воспроизводя мне на динамики звук удара бочки.

Хорошо. Теперь хочется выяснить, а как быстро можно генерировать удары. Я же люблю кардан, металл, бласт биты, и всё такое.
Пусть, удар происходит за 10 миллисекунд, теперь хочется понять, как быстро можно генерировать удары, т.е. сколько минимум миллисекунд нужно ждать (отправив на пин LOW напряжение) до следующего удара?

Итак, теперь у нас время удара KICK_DURATION = 10, и мы будем искать минимальное время паузы P между ударами. Начну с "большой" паузы в 100 миллисекунд, и буду её уменьшать.

void setup() {
  pinMode(13, OUTPUT);
}

void loop() {
  const int KICK_DURATION = 10;
  digitalWrite(13, HIGH);
  delay(N);
  digitalWrite(13, LOW);
  
  const int P = 100; // пауза, которую мы будем экспериментально уменьшать
  delay(P);
}

Опытным путём получено значение тоже в 10 миллисекунд.
Это значит, в теории, что мы можем получить, если один удар+отдых занимает 20 миллисекунд, 1000/20 = 50 ударов в секунду, т.е. 3000 BPM, думаю, вполне достаточно. Теоретически!

Собственно, эксперименты проведены, и, оказалось, что для простого имитирования удара педалью сустейна с помощью Arduino не нужно никаких ЦАПов, всё достаточно примитивно.

Пишем технические требования, пишем код, собираем и паяем схему!

  • С одной стороны имеется педаль сустейна, а точнее две (одна как то несерьёзно) со штекером JACK 6.3. С другой стороны - вход в барабанный процессор, так же шнур со штекером JACK 6.3.

  • Педали сустейна работают как кнопки, на Arduino их будет подключать через подтягивающие резисторы, возьмем стандартный на 10 кОм.

  • Учесть эффект дребезга контактов от педалей

  • После нажатия на педаль сустейна, Arduino должна генерировать ШИМ-импульс напряжения HIGH в течении KICK_WAVE_DURATION_MS = 10 миллисекунд

  • После эмулирования удара до следующего удара должно пройти минимум KICK_WAVE_COOLDOWN_DURATION_MS = 10 миллисекунд.

  • Обе педали работают параллельно, и нажатие по одной педали не должно мешать нажатию по другой, учесть так же, если нажатия произошли одновременно (или почти одновременно, например, вторая педаль нажалась, пока генерировался ШИМ-импульс для первой).

С точки зрения схемы, она простая - 2 подтягивающих регистра для педалей, подключаем их в 2 входящих пина, и один выходящий пин для генерирования ШИМ-импульса OUTPUT_KICK_PIN = LED_BUILTIN (13), заодно будет мигать диодом, для показания удара.

Подключать педали будем через входящие пины с номерами KICK_1_PIN = 7 и KICK_2_PIN = 10.

И так, настала пора программирования.

Эффект дребезга контактов можно нивелировать путём ожидания некоторого времени (пара миллисекунд, определяется снова, экспериментально) после детектирования изменения напряжения на входном пине.

Будем использовать паттерн стейт-машина для управления состояниями педалей и генерации ШИМ-импульса.

Определимся с константами:

enum {
  KICK_1_PIN = 7, // номер входного пина первой педали
  KICK_2_PIN = 10, // номер входного пина второй педали
  OUTPUT_KICK_PIN = LED_BUILTIN, // 13 номер выходного пина (для генерирования ШИМ-импульса) в барабанный процессор

  KICK_TRIGGER_BOUNCE_COOLDOWN_DURATION_MS = 3, // время дребезга контактов

  KICK_WAVE_DURATION_MS = 10, // время генерации ШИМ-импульса ("удар")
  KICK_WAVE_COOLDOWN_DURATION_MS = 10, // минимальное время между "ударами"
  
  // состояния педали 
  KICK_TRIGGER_STATE_RELEASED = 0, // отпущена
  KICK_TRIGGER_STATE_PRESSED = 1, // нажата

  // состояния генерирования ШИМ-импульса для эмуляции "удара"
  KICK_WAVE_STATE_IDLE = 0, // ничего не делаем
  KICK_WAVE_STATE_GENERATION = 1, // генерируем ШИМ-импульс
  KICK_WAVE_STATE_COOLDOWN = 2 // ожидаем минимальное время между "ударами"
};

Чтобы измерять пройденное время, будем использовать функцию millis(), запоминать время действий, а для рассчёта пройденного времени вычитать из текущего времеми millis() сохраненное время.

Например, когда педаль меняет своё физическое состояние (нажата/отпущена), при первом изменении напряжения на входном пине мы ставим время kickTriggerTimer = millis(), а проденное время с этой поры будет рассчитываться как millis() - kickTriggerTimer.

Чтобы педали друг другу не мешали и работали параллельно, я придумал ввести "счётчик ударов" - kicksAmount. Каждый раз, когда педаль меняет свое состояние с KICK_TRIGGER_STATE_RELEASED на KICK_TRIGGER_STATE_PRESSED, мы увеличиваем kicksAmount на 1. А уж потом, отдельная логика, если kicksAmount больше 1, генерирует ШИМ-импульсы в соответствии с требованием паузы KICK_WAVE_COOLDOWN_DURATION_MS между "ударами".

Получаем следующие переменные:

int kick1TriggerState = KICK_TRIGGER_STATE_RELEASED; // состояние педали 1
unsigned long kick1TriggerTimer = 0; // таймер педали 1
bool kick1Bounce = false; // флаг дребезга педали 1

int kick2TriggerState = KICK_TRIGGER_STATE_RELEASED; // состояние педали 2
unsigned long kick2TriggerTimer = 0; // таймер педали 2
bool kick2Bounce = false; // флаг дребезга педали 2

int kickWaveState = KICK_WAVE_STATE_IDLE; // состояние генерирования ШИМ-импульса
unsigned long kickWaveTimer = 0; // таймер генерирования ШИМ-импульса

unsigned long time = 0; // текущее время, обновляется в начале каждого цикла loop()
int kicksAmount = 0; // количество ударов, которые нужно сгенерировать

Напишем функции инициализации в setup(), определим первоначальное состояние педалей:

void initializeTrigger(int kickPin, int& kickTriggerState) {
  pinMode(kickPin, INPUT); // выставляем пин в режим ввода

  kickTriggerState = digitalRead(kickPin) == LOW ? KICK_TRIGGER_STATE_RELEASED : KICK_TRIGGER_STATE_PRESSED;
}

void initializeKick() {
  pinMode(OUTPUT_KICK_PIN, OUTPUT); // выставляем пин в режим вывода
}

void setup() {
  initializeTrigger(KICK_1_PIN, kick1TriggerState);
  initializeTrigger(KICK_1_PIN, kick1TriggerState);
  initializeKick();
}

Функция loop() по сути, будет состоять из двух частей:

  • обрабатывать состояние педалей функцией processTrigger. Эта функция должна полностью разобраться с текущим состоянием педали, и вернуть true, если зафиксировано нажатие (в таком случае увеличим kicksAmount)

  • обрабатывать генерирование "ударов" функцией processKick

void loop() {
  time = millis();

  bool wasKick1Triggered = processTrigger(KICK_1_PIN, kick1TriggerState, kick1TriggerTimer, kick1Bounce);
  bool wasKick2Triggered = processTrigger(KICK_2_PIN, kick2TriggerState, kick2TriggerTimer, kick2Bounce);

  bool wasKickTriggered = wasKick1Triggered || wasKick2Triggered;
  
  if (wasKickTriggered) kicksAmount++;

  processKick();
}

processTrigger принимает в аргументах

  • kickPin - номер пина педали

  • kickTriggerState - переменную по ссылке - текущее состояние педали

  • kickTriggerTimer - переменную по ссылке - сохраненное время крайнего изменения состояния педали

  • kickBounce - переменную по ссылке - флаг дребезга педали Возвращает флаг - было ли нажатие педали, или нет

bool processTrigger(int kickPin, int& kickTriggerState, unsigned long& kickTriggerTimer, bool& kickBounce) {
  bool wasKickTriggered = false;
  
  if (kickBounce) {
	// если педаль дребезжит - ждём KICK_TRIGGER_BOUNCE_COOLDOWN_DURATION_MS
    if (time - kickTriggerTimer > KICK_TRIGGER_BOUNCE_COOLDOWN_DURATION_MS) {
      kickBounce = false;
      
	  // читаем напряжение с пина
      int kick = digitalRead(kickPin);
	  // текущий стейт - отпущена, а напряжение высокое - значит, нажатие
      if (kickTriggerState == KICK_TRIGGER_STATE_RELEASED &&
        kick == HIGH) {
		// регистрируем нажатие!
        wasKickTriggered = true;

        kickTriggerState = KICK_TRIGGER_STATE_PRESSED;
	  // текущий стейт - нажата, а напряжение низкое - значит, отпустили
      } else if (kickTriggerState == KICK_TRIGGER_STATE_PRESSED &&
        kick == LOW) {
        kickTriggerState = KICK_TRIGGER_STATE_RELEASED;
      }
    }
  } else {
	// педаль не дребезжит

	// читаем напряжение с пина
    int kick = digitalRead(kickPin);
    
	// если текущий стейт не соответствует напряжению - значит начинается дребезг педали, её либо нажимают, либо отжимают
    if ((kickTriggerState == KICK_TRIGGER_STATE_RELEASED && kick == HIGH) ||
      (kickTriggerState == KICK_TRIGGER_STATE_PRESSED && kick == LOW)) {
      kickTriggerTimer = time; // запоминаем время начала дребезга
      kickBounce = true;
    }
  }
  
  return wasKickTriggered;
}

processKick выглядит как типичный код стейт-машины свитч по всем возможным стейтам:

void processKick() {
  switch (kickWaveState) {
	// стейт что ничего не происходит
    case KICK_WAVE_STATE_IDLE: 
	  // накопились нажатия - начинаем процесс генерации ШИМ-импульса
      if (kicksAmount > 0) {
        kicksAmount--;

        digitalWrite(OUTPUT_KICK_PIN, HIGH);
        
		// запоминаем время начала генерации ШИМ-импульса и меняем стейт
        kickWaveTimer = time;
        kickWaveState = KICK_WAVE_STATE_GENERATION;
      }
    break;
    
	// стейт генерации ШИМ-импульса
    case KICK_WAVE_STATE_GENERATION: 
	  // прошло KICK_WAVE_DURATION_MS? - тогда заканчиваем генерацию ШИМ-импульса и меняем стейт на ожидание паузы
      if (time - kickWaveTimer > KICK_WAVE_DURATION_MS) {
        digitalWrite(OUTPUT_KICK_PIN, LOW);

        // запоминаем время начала ожидания паузы и меняем стейт
        kickWaveTimer = time;
        kickWaveState = KICK_WAVE_STATE_COOLDOWN;
      }
    break;
    
	// стейт ожидания паузы с момента kickWaveTimer
    case KICK_WAVE_STATE_COOLDOWN: 
      if (time - kickWaveTimer > KICK_WAVE_COOLDOWN_DURATION_MS) {
        kickWaveState = KICK_WAVE_STATE_IDLE;
      }
    break;
  }
}

Важный момент - педали маленькие, очень мало весят, и просят, я бы даже сказал, требуют жёсткой фиксации. Как раз платформа-подставка из фанеры очень пригодилась - поставил на саморезы.

Полный код для Arduino
// https://gist.github.com/lis355/3ac75815c34c02a3077756086316d698

// Эмулятор бас-бочки для электронных барабанов от 2х сустейн педалей

// Сустейн педали выполняют роль кнопок, замыкая цепь, подключаются к KICK_1_PIN и KICK_2_PIN через какой-нибудь резистор
// Как симуляция удара по бочке (триггеру) генерируется ШИМ импульс на пине OUTPUT_KICK_PIN в течении KICK_WAVE_DURATION_MS (подается HIGH на цифровой пин)

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

// Джек для выхода сигнала на электронные барабаны подключается к OUTPUT_KICK_PIN и GND

// При регистрировании нажатия на педали на Ардуине так же будет мигать светодиод L (т.к. он соединен с пином 13)

// KICK_TRIGGER_COOLDOWN_MS, KICK_WAVE_DURATION_MS настраиваются эмпирически

// На плате пины KICK_1_PIN и KICK_2_PIN подключены через подтягивающие резисторы

enum {
  KICK_1_PIN = 7,
  KICK_2_PIN = 10,
  OUTPUT_KICK_PIN = LED_BUILTIN, // 13,

  KICK_TRIGGER_BOUNCE_COOLDOWN_DURATION_MS = 3,

  KICK_WAVE_DURATION_MS = 10,
  KICK_WAVE_COOLDOWN_DURATION_MS = 10,
  
  KICK_TRIGGER_STATE_RELEASED = 0,
  KICK_TRIGGER_STATE_PRESSED = 1,

  KICK_WAVE_STATE_IDLE = 0,
  KICK_WAVE_STATE_GENERATION = 1,
  KICK_WAVE_STATE_COOLDOWN = 2
};

int kick1TriggerState = KICK_TRIGGER_STATE_RELEASED;
unsigned long kick1TriggerTimer = 0;
bool kick1Bounce = false;

int kick2TriggerState = KICK_TRIGGER_STATE_RELEASED;
unsigned long kick2TriggerTimer = 0;
bool kick2Bounce = false;

int kickWaveState = KICK_WAVE_STATE_IDLE;
unsigned long kickWaveTimer = 0;

unsigned long time = 0;
int kicksAmount = 0;

void setup() {
  // Мигаем LED_BUILTIN 3 раза на старке как признак хорошего запуска
  startBlinking();

  initializeTrigger(KICK_1_PIN, kick1TriggerState);
  initializeTrigger(KICK_1_PIN, kick1TriggerState);
  initializeKick();
}

void startBlinking() {
  pinMode(LED_BUILTIN, OUTPUT);
  for (int i = 0; i < 3; i++) {
    digitalWrite(LED_BUILTIN, HIGH);  
    delay(150);                      
    digitalWrite(LED_BUILTIN, LOW);   
    delay(150);                      
  }
}

void initializeTrigger(int kickPin, int& kickTriggerState) {
  pinMode(kickPin, INPUT);

  kickTriggerState = digitalRead(kickPin) == LOW ? KICK_TRIGGER_STATE_RELEASED : KICK_TRIGGER_STATE_PRESSED;
}

void initializeKick() {
  pinMode(OUTPUT_KICK_PIN, OUTPUT);
}

void loop() {
  time = millis();

  bool wasKick1Triggered = processTrigger(KICK_1_PIN, kick1TriggerState, kick1TriggerTimer, kick1Bounce);
  bool wasKick2Triggered = processTrigger(KICK_2_PIN, kick2TriggerState, kick2TriggerTimer, kick2Bounce);

  bool wasKickTriggered = wasKick1Triggered || wasKick2Triggered;
  
  if (wasKickTriggered) kicksAmount++;

  processKick();
}

bool processTrigger(int kickPin, int& kickTriggerState, unsigned long& kickTriggerTimer, bool& kickBounce) {
  bool wasKickTriggered = false;
  
  if (kickBounce) {
    if (time - kickTriggerTimer > KICK_TRIGGER_BOUNCE_COOLDOWN_DURATION_MS) {
      kickBounce = false;
      
      int kick = digitalRead(kickPin);
      if (kickTriggerState == KICK_TRIGGER_STATE_RELEASED &&
        kick == HIGH) {
        wasKickTriggered = true;

        kickTriggerState = KICK_TRIGGER_STATE_PRESSED;
      } else if (kickTriggerState == KICK_TRIGGER_STATE_PRESSED &&
        kick == LOW) {
        kickTriggerState = KICK_TRIGGER_STATE_RELEASED;
      }
    }
  } else {
    int kick = digitalRead(kickPin);
    
    if ((kickTriggerState == KICK_TRIGGER_STATE_RELEASED && kick == HIGH) ||
      (kickTriggerState == KICK_TRIGGER_STATE_PRESSED && kick == LOW)) {
      kickTriggerTimer = time;
      kickBounce = true;
    }
  }
  
  return wasKickTriggered;
}

void processKick() {
  switch (kickWaveState) {
    case KICK_WAVE_STATE_IDLE: 
      if (kicksAmount > 0) {
        kicksAmount--;

        digitalWrite(OUTPUT_KICK_PIN, HIGH);
        
        kickWaveTimer = time;
        kickWaveState = KICK_WAVE_STATE_GENERATION;
      }
    break;
    
    case KICK_WAVE_STATE_GENERATION: 
      if (time - kickWaveTimer > KICK_WAVE_DURATION_MS) {
        digitalWrite(OUTPUT_KICK_PIN, LOW);

        kickWaveTimer = time;
        kickWaveState = KICK_WAVE_STATE_COOLDOWN;
      }
    break;
    
    case KICK_WAVE_STATE_COOLDOWN: 
      if (time - kickWaveTimer > KICK_WAVE_COOLDOWN_DURATION_MS) {
        kickWaveState = KICK_WAVE_STATE_IDLE;
      }
    break;
  }
}

Спаял схему на Arduino Nano, залил программу, и результат колхоза:

В итоге, получились педали, по шуму гораздо тише, чем все остальные удары палочками по пэдам. Играть на них получается, и очень даже сносно.
Практиковаться, разучивать рисунки - самое то.

По тратам - две педали Casio SP3 с Авито (~1500 * 2 р) и Arduino Nano (300 р). Ах да, ну и время - но, так как это экспериментирование и в удовольствие, то как будто бы его считать не стоит)

Минусы

  • да, силу удара тут никак не сделаешь, он будет всегда одинаковым. Чтож. Можно купить другие педали, возможно, там можно как то получить силу нажатия или что-то вроде того.

  • иногда нога слетает - если играть быстро, а педалька все-таки маленькая и не рассчитана по своей эргономике на целую ступню и частые нажатия. Тут как приноровиться.

Мне очень хочется показать этой статьёй, как можно использовать программирование в каких-то своих прикладных задачах, своих хобби.
Например, как то раз я хотел "оцифровать" гармонь - приделать к каждой кнопке на гармони датчик, и переводить нажатия в MIDI-партитуру. А, оказывается, таких умельцев уже - пруд пруди!
Но самому то сделать, всегда интересно) По-сути, это и есть творчество!

Спасибо за прочитанную статью и удачи в программировании!