MIDI Барабаны на 2560

ex3mgamer
Offline
Зарегистрирован: 12.06.2016

Здравствуйте.

Я, ничего не смылся в программировании, пытаюсь собрать барабанный контроллер, преобразующий сигнал с пьезо-звукоснимателей в MIDI-данные)

Использую Arduino MEGA 2560.

В интернете куча скетчей подобных поделок. Я выбрал самый подходящий, немного переделал(изменил количество используемых входов, переназначил MIDI ноты и использовал библиотеку Arduino MIDI LIbrary).

В таком виде все работает отлично:

#include <MIDI.h>

//Piezo defines
#define NUM_PIEZOS 12
#define KICK_THRESHOLD 30     //anything < TRIGGER_THRESHOLD is treated as 0
#define TOM1_THRESHOLD 30
#define TOM2_THRESHOLD 30
#define TOM3_THRESHOLD 100
#define SNARE_THRESHOLD 100
#define RIM_THRESHOLD 50
#define HAT_THRESHOLD 100
#define CRASH_THRESHOLD 100
#define RIDE_THRESHOLD 100
#define BELL_THRESHOLD 100
#define CHINA_THRESHOLD 100
#define COWBELL_THRESHOLD 100
#define START_SLOT 0     //first analog slot of piezos

//MIDI note defines for each trigger
#define KICK_NOTE 36
#define TOM1_NOTE 71
#define TOM2_NOTE 69
#define TOM3_NOTE 67
#define SNARE_NOTE 38
#define RIM_NOTE 37
#define HAT_NOTE 7
#define CRASH_NOTE 77
#define RIDE_NOTE 62
#define BELL_NOTE 61
#define CHINA_NOTE 79
#define COWBELL_NOTE 47

//MIDI defines
#define MAX_MIDI_VELOCITY 127

//Program defines
//ALL TIME MEASURED IN MILLISECONDS
#define SIGNAL_BUFFER_SIZE 100
#define PEAK_BUFFER_SIZE 30
#define MAX_TIME_BETWEEN_PEAKS 20
#define MIN_TIME_BETWEEN_NOTES 50

//map that holds the mux slots of the piezos
unsigned short slotMap[NUM_PIEZOS];

//map that holds the respective note to each piezo
unsigned short noteMap[NUM_PIEZOS];

//map that holds the respective threshold to each piezo
unsigned short thresholdMap[NUM_PIEZOS];

//Ring buffers to store analog signal and peaks
short currentSignalIndex[NUM_PIEZOS];
short currentPeakIndex[NUM_PIEZOS];
unsigned short signalBuffer[NUM_PIEZOS][SIGNAL_BUFFER_SIZE];
unsigned short peakBuffer[NUM_PIEZOS][PEAK_BUFFER_SIZE];

boolean noteReady[NUM_PIEZOS];
unsigned short noteReadyVelocity[NUM_PIEZOS];
boolean isLastPeakZeroed[NUM_PIEZOS];

unsigned long lastPeakTime[NUM_PIEZOS];
unsigned long lastNoteTime[NUM_PIEZOS];

void setup()
{
  MIDI.begin();
  
  //initialize globals
  for(short i=0; i<NUM_PIEZOS; ++i)
  {
    currentSignalIndex[i] = 0;
    currentPeakIndex[i] = 0;
    memset(signalBuffer[i],0,sizeof(signalBuffer[i]));
    memset(peakBuffer[i],0,sizeof(peakBuffer[i]));
    noteReady[i] = false;
    noteReadyVelocity[i] = 0;
    isLastPeakZeroed[i] = true;
    lastPeakTime[i] = 0;
    lastNoteTime[i] = 0;    
    slotMap[i] = START_SLOT + i;
  }
  
  thresholdMap[0] = KICK_THRESHOLD;
  thresholdMap[1] = TOM1_THRESHOLD;
  thresholdMap[2] = TOM2_THRESHOLD;
  thresholdMap[3] = TOM3_THRESHOLD;
  thresholdMap[4] = SNARE_THRESHOLD;
  thresholdMap[5] = RIM_THRESHOLD;
  thresholdMap[6] = HAT_THRESHOLD;
  thresholdMap[7] = CRASH_THRESHOLD;
  thresholdMap[8] = RIDE_THRESHOLD;
  thresholdMap[9] = BELL_THRESHOLD;
  thresholdMap[10] = CHINA_THRESHOLD;
  thresholdMap[11] = COWBELL_THRESHOLD;
  

  noteMap[0] = KICK_NOTE;
  noteMap[1] = TOM1_NOTE;
  noteMap[2] = TOM2_NOTE;
  noteMap[3] = TOM3_NOTE;
  noteMap[4] = SNARE_NOTE;
  noteMap[5] = RIM_NOTE;
  noteMap[6] = HAT_NOTE;
  noteMap[7] = CRASH_NOTE;
  noteMap[8] = RIDE_NOTE;
  noteMap[9] = BELL_NOTE;
  noteMap[10] = CHINA_NOTE;
  noteMap[11] = COWBELL_NOTE;
}

void loop()

{  unsigned long currentTime = millis();
  
  for(short i=0; i<NUM_PIEZOS; ++i)
  {
    //get a new signal from analog read
    unsigned short newSignal = analogRead(slotMap[i]);
    signalBuffer[i][currentSignalIndex[i]] = newSignal;
    
    //if new signal is 0
    if(newSignal < thresholdMap[i])
    {
      if(!isLastPeakZeroed[i] && (currentTime - lastPeakTime[i]) > MAX_TIME_BETWEEN_PEAKS)
      {
        recordNewPeak(i,0);
      }
      else
      {
        //get previous signal
        short prevSignalIndex = currentSignalIndex[i]-1;
        if(prevSignalIndex < 0) prevSignalIndex = SIGNAL_BUFFER_SIZE-1;        
        unsigned short prevSignal = signalBuffer[i][prevSignalIndex];
        
        unsigned short newPeak = 0;
        
        //find the wave peak if previous signal was not 0 by going
        //through previous signal values until another 0 is reached
        while(prevSignal >= thresholdMap[i])
        {
          if(signalBuffer[i][prevSignalIndex] > newPeak)
          {
            newPeak = signalBuffer[i][prevSignalIndex];        
          }
          
          //decrement previous signal index, and get previous signal
          prevSignalIndex--;
          if(prevSignalIndex < 0) prevSignalIndex = SIGNAL_BUFFER_SIZE-1;
          prevSignal = signalBuffer[i][prevSignalIndex];
        }
        
        if(newPeak > 0)
        {
          recordNewPeak(i, newPeak);
        }
      }
  
    }
        
    currentSignalIndex[i]++;
    if(currentSignalIndex[i] == SIGNAL_BUFFER_SIZE) currentSignalIndex[i] = 0;
  }
}

void recordNewPeak(short slot, short newPeak)
{
  isLastPeakZeroed[slot] = (newPeak == 0);
  
  unsigned long currentTime = millis();
  lastPeakTime[slot] = currentTime;
  
  //new peak recorded (newPeak)
  peakBuffer[slot][currentPeakIndex[slot]] = newPeak;
  
  //1 of 3 cases can happen:
  // 1) note ready - if new peak >= previous peak
  // 2) note fire - if new peak < previous peak and previous peak was a note ready
  // 3) no note - if new peak < previous peak and previous peak was NOT note ready
  
  //get previous peak
  short prevPeakIndex = currentPeakIndex[slot]-1;
  if(prevPeakIndex < 0) prevPeakIndex = PEAK_BUFFER_SIZE-1;        
  unsigned short prevPeak = peakBuffer[slot][prevPeakIndex];
   
  if(newPeak > prevPeak && (currentTime - lastNoteTime[slot])>MIN_TIME_BETWEEN_NOTES)
  {
    noteReady[slot] = true;
    if(newPeak > noteReadyVelocity[slot])
      noteReadyVelocity[slot] = newPeak;
  }
  else if(newPeak < prevPeak && noteReady[slot])
  {
    noteFire(noteMap[slot], noteReadyVelocity[slot]);
    noteReady[slot] = false;
    noteReadyVelocity[slot] = 0;
    lastNoteTime[slot] = currentTime;
  }
  
  currentPeakIndex[slot]++;
  if(currentPeakIndex[slot] == PEAK_BUFFER_SIZE) currentPeakIndex[slot] = 0;  
}

void noteFire(unsigned short note, unsigned short velocity)
{
  if(velocity > MAX_MIDI_VELOCITY)
    velocity = MAX_MIDI_VELOCITY;
  



  MIDI.send(NoteOn, note, velocity, 1);
  MIDI.send(NoteOff, note, velocity, 1);
}

Хочу добавить потенциометр, который будет управлять положением педали хай хета(midi control change)

Вот код для потенциометра, который по отдельности тоже работает исправно:

#include <MIDI.h>


// Variables:
int cc = 1;
int AnalogValue = 0; // define variables for the controller data
int lastAnalogValue = 0; // define the "lastValue" variables

void setup() {
// launch MIDI
MIDI.begin();

}

void loop() {

AnalogValue = analogRead(15); //knob 1 cutoff
// convert to a range from 0 to 127:
cc = AnalogValue / 8;
// check if analog input has changed
if (abs(lastAnalogValue - cc) > 1) {
MIDI.sendControlChange(1, cc, 1);
// update lastAnalogValue zero variable
lastAnalogValue = cc;
}

}

 

Помогите объединить эти две задачи.

Я попробовал просто скопировать второй код в первый, предварительно объявив переменные, но в таком случает работает только первая часть, а значения с потенциометра не отправляются.

Куда мне следует поместить второй код, чтоб всё вместе отправлялось на выход?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

ex3mgamer пишет:

Здравствуйте.

Я, ничего не смылся в программировании, пытаюсь собрать барабанный контроллер, преобразующий сигнал с пьезо-звукоснимателей в MIDI-данные)

Вы, судя по всему, музыкант? Давайте я Вам задам вопрос, подобный Вашему.

"Я, ничего не смылся в" музыке и никогда ни на чём не играл, пытаюсь выступить на концерте с симфоний №3 Бетховена. Погуглил немного, скачал таблицу соответсвия клавиш пианино этим ... как их ... кружочкам, которыми музыка записывается. Нажимаю клавиши. Каждая в отдельности звучит отлично! Осталось только объединить их, чтобы "Героическая" симфония зазвучала, но тут засада. Пытался и подряд их нажимать и в обратном порядке и случайным образом - "не выходит каменный цветок". Подскажите, куда копать-то!

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

ex3mgamer
Offline
Зарегистрирован: 12.06.2016

спасибо.

Baks
Baks аватар
Offline
Зарегистрирован: 11.01.2016

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

Тоесть определите в каком случае что должно произойти и после чего. кстати loop() выпорняется постоянно начиная с верху и до конца и потом снова на верхх...

ex3mgamer
Offline
Зарегистрирован: 12.06.2016

Мне нужно, чтоб код для потенциометра исполнялся независимо от основного. они друг на друга никак не влияют, просто нужно чтоб при прохождении каждого цикла на выход отправлялись еще и данные из второго куска кода. Это я к тому, что условие тут, судя по всему, неуместно.

Переменные в сетапе-то я объявил, а куда остальной код из loop вставлять, не знаю)

Про это и спрашиваю)

 

Baks
Baks аватар
Offline
Зарегистрирован: 11.01.2016

или после первой открывающейся скобкой {

или перед последней закрывающей }

имею в виду в функции loop() { //СЮДА МОЖНО ЭТОТ КУСОК


//----------------------
AnalogValue = analogRead(15); //knob 1 cutoff
// convert to a range from 0 to 127:
cc = AnalogValue / 8;
// check if analog input has changed
if (abs(lastAnalogValue - cc) > 1) {
MIDI.sendControlChange(1, cc, 1);
// update lastAnalogValue zero variable
lastAnalogValue = cc;
}
//----------------------

 

ex3mgamer
Offline
Зарегистрирован: 12.06.2016

сую перед последний, компилятор говорит

"exit status 1
'AnalogValue' was not declared in this scope"

вот так сейчас выглядит код:

//Piezo defines
#define NUM_PIEZOS 12
#define KICK_THRESHOLD 30     //anything < TRIGGER_THRESHOLD is treated as 0
#define TOM1_THRESHOLD 30
#define TOM2_THRESHOLD 30
#define TOM3_THRESHOLD 100
#define SNARE_THRESHOLD 100
#define RIM_THRESHOLD 50
#define HAT_THRESHOLD 100
#define CRASH_THRESHOLD 100
#define RIDE_THRESHOLD 100
#define BELL_THRESHOLD 100
#define CHINA_THRESHOLD 100
#define COWBELL_THRESHOLD 100
#define START_SLOT 0     //first analog slot of piezos

//MIDI note defines for each trigger
#define KICK_NOTE 36
#define TOM1_NOTE 71
#define TOM2_NOTE 69
#define TOM3_NOTE 67
#define SNARE_NOTE 38
#define RIM_NOTE 37
#define HAT_NOTE 7
#define CRASH_NOTE 77
#define RIDE_NOTE 62
#define BELL_NOTE 61
#define CHINA_NOTE 79
#define COWBELL_NOTE 47

//MIDI defines
#define MAX_MIDI_VELOCITY 127

//Program defines
//ALL TIME MEASURED IN MILLISECONDS
#define SIGNAL_BUFFER_SIZE 100
#define PEAK_BUFFER_SIZE 30
#define MAX_TIME_BETWEEN_PEAKS 20
#define MIN_TIME_BETWEEN_NOTES 50

//map that holds the mux slots of the piezos
unsigned short slotMap[NUM_PIEZOS];

//map that holds the respective note to each piezo
unsigned short noteMap[NUM_PIEZOS];

//map that holds the respective threshold to each piezo
unsigned short thresholdMap[NUM_PIEZOS];

//Ring buffers to store analog signal and peaks
short currentSignalIndex[NUM_PIEZOS];
short currentPeakIndex[NUM_PIEZOS];
unsigned short signalBuffer[NUM_PIEZOS][SIGNAL_BUFFER_SIZE];
unsigned short peakBuffer[NUM_PIEZOS][PEAK_BUFFER_SIZE];

boolean noteReady[NUM_PIEZOS];
unsigned short noteReadyVelocity[NUM_PIEZOS];
boolean isLastPeakZeroed[NUM_PIEZOS];

unsigned long lastPeakTime[NUM_PIEZOS];
unsigned long lastNoteTime[NUM_PIEZOS];

void setup()
{
  int cc = 1;
  int AnalogValue = 0; // define variables for the controller data
  int lastAnalogValue = 0; // define the "lastValue" variables
  MIDI.begin();
  
  //initialize globals
  for(short i=0; i<NUM_PIEZOS; ++i)
  {
    currentSignalIndex[i] = 0;
    currentPeakIndex[i] = 0;
    memset(signalBuffer[i],0,sizeof(signalBuffer[i]));
    memset(peakBuffer[i],0,sizeof(peakBuffer[i]));
    noteReady[i] = false;
    noteReadyVelocity[i] = 0;
    isLastPeakZeroed[i] = true;
    lastPeakTime[i] = 0;
    lastNoteTime[i] = 0;    
    slotMap[i] = START_SLOT + i;
  }
  
  thresholdMap[0] = KICK_THRESHOLD;
  thresholdMap[1] = TOM1_THRESHOLD;
  thresholdMap[2] = TOM2_THRESHOLD;
  thresholdMap[3] = TOM3_THRESHOLD;
  thresholdMap[4] = SNARE_THRESHOLD;
  thresholdMap[5] = RIM_THRESHOLD;
  thresholdMap[6] = HAT_THRESHOLD;
  thresholdMap[7] = CRASH_THRESHOLD;
  thresholdMap[8] = RIDE_THRESHOLD;
  thresholdMap[9] = BELL_THRESHOLD;
  thresholdMap[10] = CHINA_THRESHOLD;
  thresholdMap[11] = COWBELL_THRESHOLD;
  

  noteMap[0] = KICK_NOTE;
  noteMap[1] = TOM1_NOTE;
  noteMap[2] = TOM2_NOTE;
  noteMap[3] = TOM3_NOTE;
  noteMap[4] = SNARE_NOTE;
  noteMap[5] = RIM_NOTE;
  noteMap[6] = HAT_NOTE;
  noteMap[7] = CRASH_NOTE;
  noteMap[8] = RIDE_NOTE;
  noteMap[9] = BELL_NOTE;
  noteMap[10] = CHINA_NOTE;
  noteMap[11] = COWBELL_NOTE;
}

void loop()

{  unsigned long currentTime = millis();
  
  for(short i=0; i<NUM_PIEZOS; ++i)
  {
    //get a new signal from analog read
    unsigned short newSignal = analogRead(slotMap[i]);
    signalBuffer[i][currentSignalIndex[i]] = newSignal;
    
    //if new signal is 0
    if(newSignal < thresholdMap[i])
    {
      if(!isLastPeakZeroed[i] && (currentTime - lastPeakTime[i]) > MAX_TIME_BETWEEN_PEAKS)
      {
        recordNewPeak(i,0);
      }
      else
      {
        //get previous signal
        short prevSignalIndex = currentSignalIndex[i]-1;
        if(prevSignalIndex < 0) prevSignalIndex = SIGNAL_BUFFER_SIZE-1;        
        unsigned short prevSignal = signalBuffer[i][prevSignalIndex];
        
        unsigned short newPeak = 0;
        
        //find the wave peak if previous signal was not 0 by going
        //through previous signal values until another 0 is reached
        while(prevSignal >= thresholdMap[i])
        {
          if(signalBuffer[i][prevSignalIndex] > newPeak)
          {
            newPeak = signalBuffer[i][prevSignalIndex];        
          }
          
          //decrement previous signal index, and get previous signal
          prevSignalIndex--;
          if(prevSignalIndex < 0) prevSignalIndex = SIGNAL_BUFFER_SIZE-1;
          prevSignal = signalBuffer[i][prevSignalIndex];
        }
        
        if(newPeak > 0)
        {
          recordNewPeak(i, newPeak);
        }
      }
  
    }
        
    currentSignalIndex[i]++;
    if(currentSignalIndex[i] == SIGNAL_BUFFER_SIZE) currentSignalIndex[i] = 0;
  }
}

void recordNewPeak(short slot, short newPeak)
{
  isLastPeakZeroed[slot] = (newPeak == 0);
  
  unsigned long currentTime = millis();
  lastPeakTime[slot] = currentTime;
  
  //new peak recorded (newPeak)
  peakBuffer[slot][currentPeakIndex[slot]] = newPeak;
  
  //1 of 3 cases can happen:
  // 1) note ready - if new peak >= previous peak
  // 2) note fire - if new peak < previous peak and previous peak was a note ready
  // 3) no note - if new peak < previous peak and previous peak was NOT note ready
  
  //get previous peak
  short prevPeakIndex = currentPeakIndex[slot]-1;
  if(prevPeakIndex < 0) prevPeakIndex = PEAK_BUFFER_SIZE-1;        
  unsigned short prevPeak = peakBuffer[slot][prevPeakIndex];
   
  if(newPeak > prevPeak && (currentTime - lastNoteTime[slot])>MIN_TIME_BETWEEN_NOTES)
  {
    noteReady[slot] = true;
    if(newPeak > noteReadyVelocity[slot])
      noteReadyVelocity[slot] = newPeak;
  }
  else if(newPeak < prevPeak && noteReady[slot])
  {
    noteFire(noteMap[slot], noteReadyVelocity[slot]);
    noteReady[slot] = false;
    noteReadyVelocity[slot] = 0;
    lastNoteTime[slot] = currentTime;
  }
  
  currentPeakIndex[slot]++;
  if(currentPeakIndex[slot] == PEAK_BUFFER_SIZE) currentPeakIndex[slot] = 0;  
}

void noteFire(unsigned short note, unsigned short velocity)
{
  if(velocity > MAX_MIDI_VELOCITY)
    velocity = MAX_MIDI_VELOCITY;
  



  MIDI.send(NoteOn, note, velocity, 1);
  MIDI.send(NoteOff, note, velocity, 1);

  AnalogValue = analogRead(15); //knob 1 cutoff
  // convert to a range from 0 to 127:
  cc = AnalogValue / 8;
  // check if analog input has changed
  if (abs(lastAnalogValue - cc) > 1) {
  MIDI.sendControlChange(1, cc, 1);
  // update lastAnalogValue zero variable
  lastAnalogValue = cc;
  }
}

 

ex3mgamer
Offline
Зарегистрирован: 12.06.2016

Я не понял, что я сделал, но всё заработало) Большое спасибо за помощь.

vinichenko.dl
Offline
Зарегистрирован: 18.09.2016

А можно схему и рабочую прошивку увидеть?

И еще вопрос к програмистам как реализовать глушилку на краш?

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

vinichenko.dl пишет:

И еще вопрос к програмистам как реализовать глушилку на краш?

В программировании не используются эти термины: "глушилка" и "краш".

Jeka_M
Jeka_M аватар
Offline
Зарегистрирован: 06.07.2014

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

vinichenko.dl
Offline
Зарегистрирован: 18.09.2016

Jeka_M все верно, задача в том чтобы скетч

/*
 * Copyright (c) 2015 Evan Kale
 * Email: EvanKale91@gmail.com
 * Website: www.ISeeDeadPixel.com
 *          www.evankale.blogspot.ca
 *
 * This file is part of ArduinoMidiDrums.
 *
 * ArduinoMidiDrums is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

//Piezo defines
#define NUM_PIEZOS 6
#define SNARE_THRESHOLD 30     //anything < TRIGGER_THRESHOLD is treated as 0
#define LTOM_THRESHOLD 30
#define RTOM_THRESHOLD 30
#define LCYM_THRESHOLD 100
#define RCYM_THRESHOLD 100
#define KICK_THRESHOLD 50
#define START_SLOT 0     //first analog slot of piezos

//MIDI note defines for each trigger
#define SNARE_NOTE 70
#define LTOM_NOTE 71
#define RTOM_NOTE 72
#define LCYM_NOTE 73
#define RCYM_NOTE 74
#define KICK_NOTE 75

//MIDI defines
#define NOTE_ON_CMD 0x90
#define NOTE_OFF_CMD 0x80
#define MAX_MIDI_VELOCITY 127

//MIDI baud rate
#define SERIAL_RATE 115200

//Program defines
//ALL TIME MEASURED IN MILLISECONDS
#define SIGNAL_BUFFER_SIZE 100
#define PEAK_BUFFER_SIZE 30
#define MAX_TIME_BETWEEN_PEAKS 20
#define MIN_TIME_BETWEEN_NOTES 50

//map that holds the mux slots of the piezos
unsigned short slotMap[NUM_PIEZOS];

//map that holds the respective note to each piezo
unsigned short noteMap[NUM_PIEZOS];

//map that holds the respective threshold to each piezo
unsigned short thresholdMap[NUM_PIEZOS];

//Ring buffers to store analog signal and peaks
short currentSignalIndex[NUM_PIEZOS];
short currentPeakIndex[NUM_PIEZOS];
unsigned short signalBuffer[NUM_PIEZOS][SIGNAL_BUFFER_SIZE];
unsigned short peakBuffer[NUM_PIEZOS][PEAK_BUFFER_SIZE];

boolean noteReady[NUM_PIEZOS];
unsigned short noteReadyVelocity[NUM_PIEZOS];
boolean isLastPeakZeroed[NUM_PIEZOS];

unsigned long lastPeakTime[NUM_PIEZOS];
unsigned long lastNoteTime[NUM_PIEZOS];

void setup()
{
  Serial.begin(SERIAL_RATE);
  
  //initialize globals
  for(short i=0; i<NUM_PIEZOS; ++i)
  {
    currentSignalIndex[i] = 0;
    currentPeakIndex[i] = 0;
    memset(signalBuffer[i],0,sizeof(signalBuffer[i]));
    memset(peakBuffer[i],0,sizeof(peakBuffer[i]));
    noteReady[i] = false;
    noteReadyVelocity[i] = 0;
    isLastPeakZeroed[i] = true;
    lastPeakTime[i] = 0;
    lastNoteTime[i] = 0;    
    slotMap[i] = START_SLOT + i;
  }
  
  thresholdMap[0] = KICK_THRESHOLD;
  thresholdMap[1] = RTOM_THRESHOLD;
  thresholdMap[2] = RCYM_THRESHOLD;
  thresholdMap[3] = LCYM_THRESHOLD;
  thresholdMap[4] = SNARE_THRESHOLD;
  thresholdMap[5] = LTOM_THRESHOLD;  
  
  noteMap[0] = KICK_NOTE;
  noteMap[1] = RTOM_NOTE;
  noteMap[2] = RCYM_NOTE;
  noteMap[3] = LCYM_NOTE;
  noteMap[4] = SNARE_NOTE;
  noteMap[5] = LTOM_NOTE;  
}

void loop()
{
  unsigned long currentTime = millis();
  
  for(short i=0; i<NUM_PIEZOS; ++i)
  {
    //get a new signal from analog read
    unsigned short newSignal = analogRead(slotMap[i]);
    signalBuffer[i][currentSignalIndex[i]] = newSignal;
    
    //if new signal is 0
    if(newSignal < thresholdMap[i])
    {
      if(!isLastPeakZeroed[i] && (currentTime - lastPeakTime[i]) > MAX_TIME_BETWEEN_PEAKS)
      {
        recordNewPeak(i,0);
      }
      else
      {
        //get previous signal
        short prevSignalIndex = currentSignalIndex[i]-1;
        if(prevSignalIndex < 0) prevSignalIndex = SIGNAL_BUFFER_SIZE-1;        
        unsigned short prevSignal = signalBuffer[i][prevSignalIndex];
        
        unsigned short newPeak = 0;
        
        //find the wave peak if previous signal was not 0 by going
        //through previous signal values until another 0 is reached
        while(prevSignal >= thresholdMap[i])
        {
          if(signalBuffer[i][prevSignalIndex] > newPeak)
          {
            newPeak = signalBuffer[i][prevSignalIndex];        
          }
          
          //decrement previous signal index, and get previous signal
          prevSignalIndex--;
          if(prevSignalIndex < 0) prevSignalIndex = SIGNAL_BUFFER_SIZE-1;
          prevSignal = signalBuffer[i][prevSignalIndex];
        }
        
        if(newPeak > 0)
        {
          recordNewPeak(i, newPeak);
        }
      }
  
    }
        
    currentSignalIndex[i]++;
    if(currentSignalIndex[i] == SIGNAL_BUFFER_SIZE) currentSignalIndex[i] = 0;
  }
}

void recordNewPeak(short slot, short newPeak)
{
  isLastPeakZeroed[slot] = (newPeak == 0);
  
  unsigned long currentTime = millis();
  lastPeakTime[slot] = currentTime;
  
  //new peak recorded (newPeak)
  peakBuffer[slot][currentPeakIndex[slot]] = newPeak;
  
  //1 of 3 cases can happen:
  // 1) note ready - if new peak >= previous peak
  // 2) note fire - if new peak < previous peak and previous peak was a note ready
  // 3) no note - if new peak < previous peak and previous peak was NOT note ready
  
  //get previous peak
  short prevPeakIndex = currentPeakIndex[slot]-1;
  if(prevPeakIndex < 0) prevPeakIndex = PEAK_BUFFER_SIZE-1;        
  unsigned short prevPeak = peakBuffer[slot][prevPeakIndex];
   
  if(newPeak > prevPeak && (currentTime - lastNoteTime[slot])>MIN_TIME_BETWEEN_NOTES)
  {
    noteReady[slot] = true;
    if(newPeak > noteReadyVelocity[slot])
      noteReadyVelocity[slot] = newPeak;
  }
  else if(newPeak < prevPeak && noteReady[slot])
  {
    noteFire(noteMap[slot], noteReadyVelocity[slot]);
    noteReady[slot] = false;
    noteReadyVelocity[slot] = 0;
    lastNoteTime[slot] = currentTime;
  }
  
  currentPeakIndex[slot]++;
  if(currentPeakIndex[slot] == PEAK_BUFFER_SIZE) currentPeakIndex[slot] = 0;  
}

void noteFire(unsigned short note, unsigned short velocity)
{
  if(velocity > MAX_MIDI_VELOCITY)
    velocity = MAX_MIDI_VELOCITY;
  
  midiNoteOn(note, velocity);
  midiNoteOff(note, velocity);
}

void midiNoteOn(byte note, byte midiVelocity)
{
  Serial.write(NOTE_ON_CMD);
  Serial.write(note);
  Serial.write(midiVelocity);
}

void midiNoteOff(byte note, byte midiVelocity)
{
  Serial.write(NOTE_OFF_CMD);
  Serial.write(note);
  Serial.write(midiVelocity);
}

переработать так чтобы на цифровые входы повесить две кнопки: одну кнопку использовать как контролер хета, а вторую как глушилку краша, есле кто знает как это реализовать буду весьма признателен.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

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

Если это так, - Вам, вероятнее всего, в раздел "Ищу исполнителя".

vinichenko.dl
Offline
Зарегистрирован: 18.09.2016

andriano я не могу понять Вы хотите помочь или просто поглумится?

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

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

Клапауций 232
Offline
Зарегистрирован: 05.04.2016

andriano пишет:

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

Если это так, - Вам, вероятнее всего, в раздел "Ищу исполнителя".

так, а что там сложного?

if (кнопка БУМС) {функция БУМС}
if (кнопка ЦОК) {функция ЦОК}
if (кнопка КРАШ) {функция КРАШ}
vinichenko.dl
Offline
Зарегистрирован: 18.09.2016
С хетом вродебы разобрался, накидал вот такой скетч


int buttonState = 0;
int val = 0; 
void setup() {
  Serial.begin(115200);
 pinMode(2, INPUT); 
}

void loop() {
  
 buttonState = digitalRead(2);

 if (buttonState == HIGH)
{
val=127;
}

else
{
val=0;
}

  
  Serial.write(185);
  Serial.write(4);
  Serial.write(val);


  delay (5);
  
}

и все получилось, затем интегрировал его в скетч с пьезиками, получилось вот так

/*
 * Copyright (c) 2015 Evan Kale
 * Email: EvanKale91@gmail.com
 * Website: www.ISeeDeadPixel.com
 *          www.evankale.blogspot.ca
 *
 * This file is part of ArduinoMidiDrums.
 *
 * ArduinoMidiDrums is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
int buttonState = 0;
int val = 0;
//Piezo defines
#define NUM_PIEZOS 6
#define SNARE_THRESHOLD 30     //anything < TRIGGER_THRESHOLD is treated as 0
#define LTOM_THRESHOLD 30
#define RTOM_THRESHOLD 30
#define LCYM_THRESHOLD 100
#define RCYM_THRESHOLD 100
#define KICK_THRESHOLD 50
#define START_SLOT 0     //first analog slot of piezos

//MIDI note defines for each trigger
#define SNARE_NOTE 70
#define LTOM_NOTE 71
#define RTOM_NOTE 72
#define LCYM_NOTE 73
#define RCYM_NOTE 74
#define KICK_NOTE 75

//MIDI defines
#define NOTE_ON_CMD 0x90
#define NOTE_OFF_CMD 0x80
#define MAX_MIDI_VELOCITY 127

//MIDI baud rate
#define SERIAL_RATE 115200

//Program defines
//ALL TIME MEASURED IN MILLISECONDS
#define SIGNAL_BUFFER_SIZE 100
#define PEAK_BUFFER_SIZE 30
#define MAX_TIME_BETWEEN_PEAKS 20
#define MIN_TIME_BETWEEN_NOTES 50

//map that holds the mux slots of the piezos
unsigned short slotMap[NUM_PIEZOS];

//map that holds the respective note to each piezo
unsigned short noteMap[NUM_PIEZOS];

//map that holds the respective threshold to each piezo
unsigned short thresholdMap[NUM_PIEZOS];

//Ring buffers to store analog signal and peaks
short currentSignalIndex[NUM_PIEZOS];
short currentPeakIndex[NUM_PIEZOS];
unsigned short signalBuffer[NUM_PIEZOS][SIGNAL_BUFFER_SIZE];
unsigned short peakBuffer[NUM_PIEZOS][PEAK_BUFFER_SIZE];

boolean noteReady[NUM_PIEZOS];
unsigned short noteReadyVelocity[NUM_PIEZOS];
boolean isLastPeakZeroed[NUM_PIEZOS];

unsigned long lastPeakTime[NUM_PIEZOS];
unsigned long lastNoteTime[NUM_PIEZOS];

void setup()
{
  
  Serial.begin(SERIAL_RATE);
  pinMode(2, INPUT); 
  //initialize globals
  for(short i=0; i<NUM_PIEZOS; ++i)
  {
    currentSignalIndex[i] = 0;
    currentPeakIndex[i] = 0;
    memset(signalBuffer[i],0,sizeof(signalBuffer[i]));
    memset(peakBuffer[i],0,sizeof(peakBuffer[i]));
    noteReady[i] = false;
    noteReadyVelocity[i] = 0;
    isLastPeakZeroed[i] = true;
    lastPeakTime[i] = 0;
    lastNoteTime[i] = 0;    
    slotMap[i] = START_SLOT + i;
  }
  
  thresholdMap[0] = KICK_THRESHOLD;
  thresholdMap[1] = RTOM_THRESHOLD;
  thresholdMap[2] = RCYM_THRESHOLD;
  thresholdMap[3] = LCYM_THRESHOLD;
  thresholdMap[4] = SNARE_THRESHOLD;
  thresholdMap[5] = LTOM_THRESHOLD;  
  
  noteMap[0] = KICK_NOTE;
  noteMap[1] = RTOM_NOTE;
  noteMap[2] = RCYM_NOTE;
  noteMap[3] = LCYM_NOTE;
  noteMap[4] = SNARE_NOTE;
  noteMap[5] = LTOM_NOTE;  
}

void loop()
{
   buttonState = digitalRead(2);

 if (buttonState == HIGH)
{
val=127;
}

else
{
val=0;
}

  
  Serial.write(185);
  Serial.write(4);
  Serial.write(val);
   delay (5);
  unsigned long currentTime = millis();
  
  for(short i=0; i<NUM_PIEZOS; ++i)
  {
    //get a new signal from analog read
    unsigned short newSignal = analogRead(slotMap[i]);
    signalBuffer[i][currentSignalIndex[i]] = newSignal;
    
    //if new signal is 0
    if(newSignal < thresholdMap[i])
    {
      if(!isLastPeakZeroed[i] && (currentTime - lastPeakTime[i]) > MAX_TIME_BETWEEN_PEAKS)
      {
        recordNewPeak(i,0);
      }
      else
      {
        //get previous signal
        short prevSignalIndex = currentSignalIndex[i]-1;
        if(prevSignalIndex < 0) prevSignalIndex = SIGNAL_BUFFER_SIZE-1;        
        unsigned short prevSignal = signalBuffer[i][prevSignalIndex];
        
        unsigned short newPeak = 0;
        
        //find the wave peak if previous signal was not 0 by going
        //through previous signal values until another 0 is reached
        while(prevSignal >= thresholdMap[i])
        {
          if(signalBuffer[i][prevSignalIndex] > newPeak)
          {
            newPeak = signalBuffer[i][prevSignalIndex];        
          }
          
          //decrement previous signal index, and get previous signal
          prevSignalIndex--;
          if(prevSignalIndex < 0) prevSignalIndex = SIGNAL_BUFFER_SIZE-1;
          prevSignal = signalBuffer[i][prevSignalIndex];
        }
        
        if(newPeak > 0)
        {
          recordNewPeak(i, newPeak);
        }
      }
  
    }
        
    currentSignalIndex[i]++;
    if(currentSignalIndex[i] == SIGNAL_BUFFER_SIZE) currentSignalIndex[i] = 0;
  }
}

void recordNewPeak(short slot, short newPeak)
{
  isLastPeakZeroed[slot] = (newPeak == 0);
  
  unsigned long currentTime = millis();
  lastPeakTime[slot] = currentTime;
  
  //new peak recorded (newPeak)
  peakBuffer[slot][currentPeakIndex[slot]] = newPeak;
  
  //1 of 3 cases can happen:
  // 1) note ready - if new peak >= previous peak
  // 2) note fire - if new peak < previous peak and previous peak was a note ready
  // 3) no note - if new peak < previous peak and previous peak was NOT note ready
  
  //get previous peak
  short prevPeakIndex = currentPeakIndex[slot]-1;
  if(prevPeakIndex < 0) prevPeakIndex = PEAK_BUFFER_SIZE-1;        
  unsigned short prevPeak = peakBuffer[slot][prevPeakIndex];
   
  if(newPeak > prevPeak && (currentTime - lastNoteTime[slot])>MIN_TIME_BETWEEN_NOTES)
  {
    noteReady[slot] = true;
    if(newPeak > noteReadyVelocity[slot])
      noteReadyVelocity[slot] = newPeak;
  }
  else if(newPeak < prevPeak && noteReady[slot])
  {
    noteFire(noteMap[slot], noteReadyVelocity[slot]);
    noteReady[slot] = false;
    noteReadyVelocity[slot] = 0;
    lastNoteTime[slot] = currentTime;
  }
  
  currentPeakIndex[slot]++;
  if(currentPeakIndex[slot] == PEAK_BUFFER_SIZE) currentPeakIndex[slot] = 0;  
}

void noteFire(unsigned short note, unsigned short velocity)
{
  if(velocity > MAX_MIDI_VELOCITY)
    velocity = MAX_MIDI_VELOCITY;
  
  midiNoteOn(note, velocity);
  midiNoteOff(note, velocity);
}

void midiNoteOn(byte note, byte midiVelocity)
{
  Serial.write(NOTE_ON_CMD);
  Serial.write(note);
  Serial.write(midiVelocity);
}

void midiNoteOff(byte note, byte midiVelocity)
{
  Serial.write(NOTE_OFF_CMD);
  Serial.write(note);
  Serial.write(midiVelocity);
}

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

Jeka_M
Jeka_M аватар
Offline
Зарегистрирован: 06.07.2014

Цитата:

я просто не понимаю какую миди команду нужно послать чтобы замолчала тарелка

Так а что у Вас там на приёмной стороне стоит? Я вот вижу - ардуина отправляет MIDI-код по Serial, а принимает их кто? Вы же взяли где-то вот эти коды:

Serial.write(185);
Serial.write(4);
Serial.write(val); //127 или 0

 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Jeka_M пишет:

Так а что у Вас там на приёмной стороне стоит? Я вот вижу - ардуина отправляет MIDI-код по Serial, а принимает их кто? Вы же взяли где-то вот эти коды:

Serial.write(185);
Serial.write(4);
Serial.write(val); //127 или 0

Кстати, интересный вопрос.

Насколько я помню, скорость MIDI 31250, а у vinichenko.dl:

//MIDI baud rate

#define SERIAL_RATE 115200

...

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

void loop()
{
...
  Serial.write(185);
  Serial.write(4);
  Serial.write(val);
}

 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Цитата:

я просто не понимаю какую миди команду нужно послать чтобы замолчала тарелка

Из общих соображений либо Note Off, либо Note On с vel=0.

ex3mgamer
Offline
Зарегистрирован: 12.06.2016

Значит так:

Я всё это мастерил для использования с Addictive Drums. Там функция Choke организована следующим образом:

В миди-карте плагина у каждой тарелки есть нота, которая её глушит. Обычно - соседняя от основной. То есть, чтоб заглушить извлеченный звук тарелки, нужно отправить noteOn этой ноты, а не noteOff. Addictive не воспринимает длительности нот. Я не знаю, как работают другие VST, но в моём вот так. Велосити ноты-глушилки, по моему, может быть любым, но я ставил максимальное - 127.

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

А вообще, мусор это всё. Купи лучше дешевый роланд)

vinichenko.dl
Offline
Зарегистрирован: 18.09.2016

Jeka_M пишет:

Цитата:

я просто не понимаю какую миди команду нужно послать чтобы замолчала тарелка

Так а что у Вас там на приёмной стороне стоит? Я вот вижу - ардуина отправляет MIDI-код по Serial, а принимает их кто? Вы же взяли где-то вот эти коды:

Serial.write(185);
Serial.write(4);
Serial.write(val); //127 или 0

 

Я использую программы hairless-midiserial и loopmidi .

vinichenko.dl
Offline
Зарегистрирован: 18.09.2016

 

Кстати, интересный вопрос.

Насколько я помню, скорость MIDI 31250, а у vinichenko.dl:


 

так на скорости 115200 ардуинно "общается" с hairless-midiserial.

vinichenko.dl
Offline
Зарегистрирован: 18.09.2016

С глушением тарелок разобрался как и сказал ex3mgamer нужно послать команду взятия ноты глушения (CHOKE) этой тарелки.