Hello guys! I hope you are doing good!
So, after a long while, I have started messing with arduino again.
But this time I have accomplished good things!
I've written myself a midi controller code from scratch, and have learned the basics and even some advanced stuff!
There's nuch to learn yet, and that's why I am kinda lost in this new task.
I want to read a piezo value so I can build myself a midi drum.
I've started researching and discovered that this code does all that I want!
#include <MIDI.h>
MIDI_CREATE_DEFAULT_INSTANCE();
byte triggerThreshold[] = {30, 30}; // If this is set too low, hits on other pads will trigger a "hit" on this pad
byte initialHitReadDuration[] = {500, 500}; // In microseconds. Shorter times will mean less latency, but less accuracy. 500 microseconds is nothing, anyway
byte midiVelocityScaleDownAmount[] = {4, 3}; // Number of halvings that will be applied to MIDI velocity
byte inputPin[] = {A0, A1};
byte midiNote[] = {38, 35};
// Getting the ideal balance of these two constants will ensure that fast subsequent hits are perceived accurately, but false hits are not generated
byte subsequentHitThreshold[] = {1.7, 1.7};
byte subsequentHitThresholdDecaySpeed[] = {14, 14};
uint16_t highestYet[2];
uint32_t startReadingTime[2];
uint32_t highestvalueTime[2];
boolean hitOccurredRecently[2] = {false, false};
void setup() {
Serial.begin(9600);
}
void loop() {
for (byte i = 0; i < 2; i ++) {
// Assume the normal hit-threshold
uint16_t thresholdNow = triggerThreshold[i];
// But, if a hit occurred very recently, we need to set a higher threshold for triggering another hit, otherwise the dissipating vibrations
// of the previous hit would trigger another one now
if (hitOccurredRecently[i]) {
// Work out how high a reading we'd need to see right now in order to conclude that another hit has occurred
uint16_t currentDynamicThreshold = (highestYet[i] >> ((micros() - highestvalueTime[i]) >> subsequentHitThresholdDecaySpeed[i])) * subsequentHitThreshold[i];
// If that calculated threshold is now as low as the regular threshold, we can go back to just waiting for a regular, isolated hit
if (currentDynamicThreshold <= triggerThreshold[i]) hitOccurredRecently[i] = false;
// Otherwise, do use this higher threshold
else thresholdNow = currentDynamicThreshold;
}
// Read the piezo
uint16_t value[2] = {analogRead(inputPin[i]),analogRead(inputPin[i])};
// If we've breached the threshold, it means we've got a hit!
if (value[i] >= thresholdNow) {
startReadingTime[i] = micros();
highestYet[i] = 0;
// For the next few milliseconds, look out for the highest "spike" in the reading from the piezo. Its height is representative of the hit's velocity
do {
if (value[i] > highestYet[i]) {
highestYet[i] = value[i];
highestvalueTime[i] = micros();
}
value[i] = analogRead(inputPin[i]);
} while (timeGreaterOrEqual(startReadingTime[i] + initialHitReadDuration[i], micros()));
// Send the MIDI note
MIDI.sendNoteOn(midiNote[i], (highestYet[i] >> midiVelocityScaleDownAmount[i]) + 1, 1); // We add 1 onto the velocity so that the result is never 0, which would mean the same as a note-off
//Serial.println(highestYet); // Send the unscaled velocity value[i] to the serial monitor too, for debugging / fine-tuning
hitOccurredRecently[i] = true;
}
}
}
// Compares times without being prone to problems when the micros() counter overflows, every ~70 mins
boolean timeGreaterOrEqual(uint32_t lhs, uint32_t rhs) {
return (((lhs - rhs) & 2147483648) == 0);
}
The problem is that it sends at least 3 midi messages almost at the same time , and the same note...
So let us say that I am hitting the snare Pad... It will sound almost as it was hit just once, but it will send 3 or more midi messages for the same note... (you can see the attachment for a clearer explanation).
While this is ok for playing along, it can be quite annoying for a mix, where you will have to delete every double(or triple) note...
I have tried several modifications, but none of them improved.
I suspect that this is the problematc line:
while (timeGreaterOrEqual(startReadingTime[i] + initialHitReadDuration[i], micros()));
// Send the MIDI note
MIDI.sendNoteOn(midiNote[i], (highestYet[i] >> midiVelocityScaleDownAmount[i]) + 1, 1); // We add 1 onto the velocity so that the result is never 0, which would mean the same as a note-off
//Serial.println(highestYet); // Send the unscaled velocity value[i] to the serial monitor too, for debugging / fine-tuning
hitOccurredRecently[i] = true;
I have tried creating an if else statement and making the boolean false, but it makes things worse...
I don't know what else to do!
The original code is here:
Drum Code
I have changed just a couple of things: As this was mainly written for a Teensy, and I am using an Arduino Mega...
Also, I have created some arrays to store more than one pad and etc, nothing fancy. But this will happen with only one pad as well, so it's not crosstalk problem.
I have tried using higher resistance resistors, but the result is the same.
I have used a diode, and nothing changes.
I have tried comparing it to other drum codes, but as the were really different, I did not get a clue.
I'd like to use this code rather than any other, because this one has a really good calculation of the hit vs velocity!
Any help is highly appreciated!
Thanks in advance!
Cheers ![]()

