Hello, I am new to this forum and generally to Arduino and C++ altogether. I took up an electronic drum project thinking that C++ would be easy to figure out given that I know some Python, but that is not the case.
This project uses a piezo sensor that picks up drum strikes, with the intention of coding a MIDI controller to send note and velocity values to a MIDI library on the PC (using Hairless MIDI and loopMIDI to route into Reaper for sampling).
I tried using other code from elsewhere and while it worked, there was a noticeable delay between a drum strike and the note fire that is not ideal in practice. There is a lot of signal processing going on in the code, and the note being fired is calculated as the maximum of signals stored in an array of specified size.
I made my own coding scheme in Python that gets me what I need and is short and simple. I took some ideas from the original code and gutted what I thought wasn't necessary, but I clearly missed something important.
Here is the code, pretending that only the snare is to be used (in reality there are up to 10 slots):
#define NUM_PIEZOS 1 //active piezo pins
#define START_SLOT 0 //first analog slot of piezos
#define BAUD_RATE 115200 //MIDI baud rate
//This should work for all triggers, and if not, change resistor used for that trigger
#define THRESHOLD 10
//MIDI note definitions for each trigger
#define SNARE_NOTE 38
//MIDI definitions
#define NOTE_ON_CMD 0x90
#define NOTE_OFF_CMD 0x80
#define MAX_MIDI_VELOCITY 127
//Minimum masking time
//TIME MEASURED IN MILLISECONDS
#define MIN_MASK 8
//float that scales max hit value for dynamic mask time between notes
//will be used as follows:
//MASKTIME = MIN_MASK + (analogRead - THRESHOLD) * SIGNAL_SCALER
float SIGNAL_SCALER = 1.07 ;
//array of piezo names
unsigned short slotMap[NUM_PIEZOS];
//array of MIDI notes
unsigned short noteMap[NUM_PIEZOS];
//Ring buffers to store analog signal
unsigned short signalBuffer[NUM_PIEZOS][2];
boolean noteSuppress[NUM_PIEZOS];
unsigned long reinitNoteTime[NUM_PIEZOS];
void setup()
{
Serial.begin(BAUD_RATE);
//initialize globals
for(short i=0; i<NUM_PIEZOS; ++i)
{
memset(signalBuffer[i],0,sizeof(signalBuffer[i]));
signalBuffer[i][0] = 0;
noteSuppress[i] = false;
reinitNoteTime[i] = 0;
slotMap[i] = START_SLOT + i;
}
noteMap[0] = SNARE_NOTE;
}
void loop()
{
unsigned long currTime = millis();
for(short i=0; i<NUM_PIEZOS; ++i)
{
unsigned short currSignal = analogRead(slotMap[i]);
signalBuffer[i][1] = currSignal;
unsigned short prevSignal = signalBuffer[i][0];
//Serial.write(currSignal);
//reinitialize
if(currTime > reinitNoteTime[i]) noteSuppress[i] = false;
if(prevSignal >= THRESHOLD){
if(
prevSignal - currSignal > 1 &&
noteSuppress[i] == false
) {
noteFire(noteMap[i], signalBuffer[i][0]);
noteSuppress[i] = true;
//calculate time to reinitialization with dynamic masking time based on velocity
reinitNoteTime[i] = currTime + MIN_MASK + (prevSignal - THRESHOLD) * SIGNAL_SCALER;
}
}
signalBuffer[i][0] = currSignal;
}
}
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);
}
The code is supposed to do the following:
- Read analog value from the piezo and store it in
signalBuffer[1]
, wheresignalBuffer[0]
is the previous signal (initialized to 0 insetup()
). - Disable note suppression if the current time is outside the suppression window.
- If the previous signal is greater than the current signal, and note suppression is not on (initialized to false), and the previous signal is above the threshold, then:
a. Fire the note.
b. Enable note suppression.
c. Calculate timestamp for when note suppression will be disabled. - Assign the current signal as the previous signal.
As is, the code does not work. If I allow the read signal to be output during the loop, it seems that after a single drum strike the code stops executing. I have no idea why.
Any help is appreciated. And any coding tips or know-hows are welcome, I would like to continue with Arduino after this project is finished. Thanks!