diff --git a/ArduinoMidiDrums.ino b/ArduinoMidiDrums.ino index 8eade8f..4867591 100644 --- a/ArduinoMidiDrums.ino +++ b/ArduinoMidiDrums.ino @@ -1,226 +1,244 @@ -/* - * 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 . - */ - -//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 31250 - -//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 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); -} +/* + * 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 . + */ + +//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 + +//Piezo scaling defines +#define SNARE_SCALE 20 // 100 is 100% of raw value - that is no scaling +#define LTOM_SCALE 50 // < 100 scales the velocity down so that you have to hit harder to get maximum velocity +#define RTOM_SCALE 50 // > 100 scales the velocity up so you get maximum velocity with softer hits +#define LCYM_SCALE 50 +#define RCYM_SCALE 50 +#define KICK_SCALE 100 + +//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 31250 + +//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 scales for each pad +unsigned short velScale[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 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 * velScale[slot] / 100; + } + 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); +}