Каталог

Arduino MIDI-drums - барабаны из Ардуино

Барабаны из Ардуино и пьезоизлучателей

Попалось мне как-то в интернете видео про барабаны из пьезоизлучателей и Ардуино. Идея мне очень понравилась и я решил собрать что-то подобное. Поэтому начал изучать информацию по данной теме.

Пьезоэлектрический излучатель состоит из металлической пластины, на которую нанесён слой пьезоэлектрика, имеющий на внешней стороне токопроводящее напыление. Пластина и напыление являются двумя контактами. Пьезоизлучатель способен как генерировать звуковой сигнал из электрической энергии, так и конвертировать приходящие механические колебания в электрические. Именно на этом эффекте и основан принцип работы MIDI-барабанов, описанных в данной статье.

С подключением к Ардуино все просто, кроме собственно пьезоизлучателя нужен резистор на 1МОм. Ниже приведена схема подключения одного барабана.

Подключение пьезоизлучателя к Ардуино

Сигнал с пьезоизлучателя поступает на аналоговый вход Ардуино, а значит, используя плату UNO, можно подключить до 6 барабанов (если нужно больше, то можно воспользоваться аналоговым коммутатором, например, CD5041). Разрешение аналоговых входов 10 бит, т.е. считанное значение будет в диапазоне 0-1023. При этом необходимо отсеять шум, вызванный посторонними звуками и вибрациями. Для этого достаточно игнорировать все значения, меньшие некоторого порогового значения. Превышение выбранного порога будем принимать за удар по барабану.

Теперь, когда мы можем отследить момент удара по барабану, необходимо воспроизвести соответствующий звук. Эту задачу можно возложить на компьютер. Для этого будем передавать ему сообщения в MIDI-формате. Мы не будем вдаваться в формат этих сообщений, при желании можно найти эту информацию в интернете, скажу лишь, что нам необходимо передавать на компьютер (через последовательный порт) два MIDI-сообщения: NOTE ON и NOTE OFF. Данные сообщения состоят из 3 байт:

Формат midi-сообщений NOTE ON и NOTE OFFКомпьютер, а точнее установленная на нем музыкальная программа, при получении с MIDI-входа сообщения NOTE ON воспроизводит ноту с заданной частотой и громкостью (частота ноты определяется ее номером). Соответственно, NOTE OFF используется для снятия ноты. Здесь есть момент, имеющий отношение к нашим MIDI-барабанам: большинство звуков ударных имеют фиксированное время звучания, и управлять их длительностью с помощью команды NOTE OFF мы не можем. Тем не менее данная команда должна рано или поздно поступить, как того требует спецификация. Также отмечу, что для перкуссии в стандарте MIDI зарезервирован отдельный 10-й канал, поэтому Ардуино будет передавать в сообщениях номер канала 9 (нумерация с 0). Ниже приведён скетч, который опрашивает 4 пьезоизлучателя и передаёт необходимые MIDI-сообщения на компьютер.

struct drum       // Структура для хранения информации об инструменте
{
  bool IsActive;  // Признак звучания инструмента в данный момент
  byte Note;      // Нота
  int Threshold;  // Порог чувствительности
  int PlayTime;   // Максимальное время звучания (мс)
  unsigned long NoteOffTime; // Время снятия ноты
};

drum Drums[] = // Массив инструментов (не больше 6). Номера инструментов можно подсмотреть в Википедии: https://en.wikipedia.org/wiki/Percussion_instrument
{
  {0, 35, 400, 50, 0},
  {0, 38, 400, 50, 0},
  {0, 40, 400, 50, 0},
  {0, 49, 400, 50, 0}
};

int DrumCount = sizeof(Drums) / sizeof(Drums[0]);
int hit = 0;
int i;

void setup()
{
  Serial.begin(115200);
}

void loop()
{
  for (i = 0; i < DrumCount; i++)
  {
    hit = analogRead(i); //  Считываем значение с аналогового входа
    analogWrite(i, 0);
    if ((hit >= Drums[i].Threshold))  // Если значение не ниже порога срабатывания
    {
      if (!Drums[i].IsActive)    // И если нота еще не звучит
      {
        // то включаем ноту
        Drums[i].IsActive = true;
        MIDI_TX(144, Drums[i].Note, 127); // Посылаем сообщение взятия ноты (144 = NOTE ON)
        Drums[i].NoteOffTime = millis() + Drums[i].PlayTime;
      }
    }
    else if (Drums[i].IsActive) // Иначе (если нет сигнала с аналогового входа), если нота звучит
    {
      // то проверяем не пора ли ее снять
      if (millis() >= Drums[i].NoteOffTime)
      {
        Drums[i].IsActive = false;
        MIDI_TX(128, Drums[i].Note, 127); // Снимаем (128 = NOTE OFF)
      }
    }
    
  }
}

void MIDI_TX(byte MESSAGE, byte PITCH, byte VELOCITY)
{
  Serial.write(MESSAGE + 9); // 9 - номер MIDI канала для перкуссии
  Serial.write(PITCH);       // Нота
  Serial.write(VELOCITY);    // Громкость
}

Как было сказано ранее, для воспроизведения звука требуется установка соответствующей программы. И такой подход применялся во всех публикациях на данную тему, которые мне попадались. При этом входящие с Ардуино сообщения должны перенаправляться на MIDI-вход, например, при помощи программы Hairless MIDI <-> Serial. Я решил пойти по другому пути: написал свою программу, которая читает сообщения из указанного COM порта и воспроизводит соответствующие звуки. Для её написания я выбрал бесплатную среду разработки Lazarus. По этой ссылке вы можете скачать исходники и скомпилированную программу. А вот и видео работы получившихся барабанов:

В принципе это уже готовый проект и мне для моих целей его хватило. Но есть еще один момент, о котором стоит упомянуть: громкость барабанов. Чем сильнее удар, тем громче должен быть звук. Я решил покопать в этом направлении. И здесь есть два варианта для определения силы удара: делать несколько считываний с пьезоизлучателя, вычислять по ним среднее значение и на его основе определять значение громкости либо замерять время угасания сигнала пьезоизлучателя до некоторого порогого уровня (чем сильнее удар, тем дольше угасает сигнал). Я остановился на первом варианте и немного доработал приведенный ранее скетч:

#define SamplesRequired 8 // Количество считываний для определения громкости

struct drum       // Структура для хранения информации об инструменте
{
  byte SamplesCounter; // Счетчик считанных значений для определения громкости
  unsigned long int Velocity; // Громкость
  byte Note;      // Нота
  int Threshold;  // Порог чувствительности
  int PlayTime;   // Максимальное время звучания (мс)
  unsigned long NoteOffTime; // Время снятия ноты
};

drum Drums[] = // Массив инструментов (не больше 6). Номера мнструментов можно подсмотреть 
               // в Википедии: https://en.wikipedia.org/wiki/Percussion_instrument
{
  {0, 0, 35, 400, 50, 0},
  {0, 0, 38, 400, 50, 0},
  {0, 0, 40, 400, 50, 0},
  {0, 0, 49, 400, 50, 0}
};

int DrumCount = sizeof(Drums) / sizeof(Drums[0]);
int hit = 0;
int i;

void setup()
{
  Serial.begin(115200);
}

void loop()
{
  for (i = 0; i < DrumCount; i++)
  {
    if (Drums[i].SamplesCounter == SamplesRequired)
    { // Проверяем не пора ли снять ноту
      if (millis() >= Drums[i].NoteOffTime)
      {
        Drums[i].SamplesCounter = 0; // Нулевое значение - признак что нота не звучит
        Drums[i].Velocity = 0;
        MIDI_TX(128, Drums[i].Note, Drums[i].Velocity); // Снимаем ноту (128 = NOTE OFF)
      }
    }
    else
    {
      hit = analogRead(i); //  Считываем значение с аналогового входа
      if  ((Drums[i].SamplesCounter > 0) and (Drums[i].SamplesCounter < SamplesRequired))
      { // Нужно произвести несколько считываний для определения уровня громкости
        Drums[i].Velocity += hit; // Суммируем значения
        Drums[i].SamplesCounter++;
        if (Drums[i].SamplesCounter == SamplesRequired)
        { //Получено необходимое количество выборок. Найдем среднее значение
          Drums[i].Velocity = Drums[i].Velocity / SamplesRequired;
          if (Drums[i].Velocity < Drums[i].Threshold) Drums[i].Velocity = Drums[i].Threshold;
          Drums[i].Velocity = map(Drums[i].Velocity, Drums[i].Threshold, 1023, 40, 127);
          MIDI_TX(144, Drums[i].Note, Drums[i].Velocity); // Посылаем сообщение взятия ноты (144 = NOTE ON)
          Drums[i].NoteOffTime = millis() + Drums[i].PlayTime;
        }
      }
      else if (hit >= Drums[i].Threshold)
      {
        Drums[i].Velocity = hit;
        Drums[i].SamplesCounter++;
      }

    }
  }
}

void MIDI_TX(byte MESSAGE, byte PITCH, byte VELOCITY)
{
  Serial.write(MESSAGE + 9); // 9 - номер MIDI канала для перкуссии
  Serial.write(PITCH);       // Нота
  Serial.write(VELOCITY);    // Громкость
}

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

footer shadow
Контакты

г. Москва, Пятницкое ш. д. 18, пав. 566

zakaz@compacttool.ru

8-495-752-55-22

compacttool logoadaptive site

accepted payment systems

Информация представленная на данном информационном ресурсе преследует исключительно рекламные цели и не является договором-офертой !

© Все права защищены 2015 - 2024г https://compacttool.ru