TapDrum - home made piezo element two-pad drum trigger
I've noticed that I'm quite capable of generating complex rhythms with my fingers tapping on the desk - something I'm not able to do as easily with drum sticks and drum pads. Since I'm not a real piano player (or a competent keyboard player for that matter), my workflow usually consists of entering tracks manually on the DAW or playing them one by one - lead melody there, chords here and so on. This extends to drum tracks - I record the bass drum, the snare and the hi-hats one by one. So one day I thought - why not build a finger tapping drum trigger to make my life easier?
So I went out and bought a couple of piezo triggers and experimented. Took an Arduino and couple pieces of plexiglass, wood and some other components and tinkered a while. This is the end result.
I borrowed 90% of the code from a person who wanted to make a whole electronic drum kit out of Arduino. The code has been modified to suit my needs - some simplification has been done and some errors corrected. The comments should reflect the changes.
I wanted to have a buffered output on the MIDI out so that I could have a LED indicator for the MIDI messages, so I used a 74125 to drive the MIDI out and the corresponding LED each having their own buffers. Strictly speaking, this is not necessary, but I wanted also to somewhat isolate the Arduino from the MIDI itself (74125 is cheaper than Arduino).
The piezo inputs are rectified with small diode bridges. After that the signal is limited and drained (since the piezos work a bit like capacitors) with 47KOhm resistors, and capped for the Arduino input with a signal diode. Piezos can output close to 50 volts, so it pays to be careful. Depending on the materials and piezo types, the acceptable grounding resistor values can be almost anything from 10KOhm to 1MOhm. Feel free to experiment.
The code has also low-limiting values for the signal and a simple buffer to look back and find out when the signal has peaked.
The original code utilized Arduino Mega (since the whole drum kit needed a lot of analog inputs) - I'm using the lowly Arduino Uno.
The piezo elements have been glued to the plexiglass, and the plexiglass pieces rest on soft foam to insulate them from one another. Crosstalk doesn't seem to be a problem. I cut the middle of the foam away so that it does not dampen the tapping on the piezo element itself. And oh yes, the piezo elements are UNDER the plexiglass, if you were wondering.
I'm going to mask the plexi bits with some self-adhesive plastic or something. Next time I'm going to use opaque plexiglass...
Schematic
Code
// Most of the code is stolen from https://www.instructables.com/id/Homemade-Electronic-Drum-Kit-With-Arduino-Mega2560/ // Some notable differences: // - using softwareserial so we do not need to remove the cable pin 1 on Arduino every time we want to upload something // - threshold values changed to reflect my piezo elements behaviour // - original code sent the input a/d value as velocity. A/D values are from 0-1024, and MIDI uses 0-127. // There is a velocity divider with some low-end boost (minimum value 32) in the code. // - code only uses two pads and no hi-hat foot pedal detection // // PLZI 2020 #include <SoftwareSerial.h> #define NUM_PIEZOS 2 #define LEFT_THRESHOLD 28 //anything < TRIGGER_THRESHOLD is treated as 0 #define RGHT_THRESHOLD 30 #define PRIMER_ANALOG 0 // Starting input pin for analog inputs, pin 0 is the first trigger input and so on #define LEFT_NOTE 40 // note to send on left pad hit #define RGHT_NOTE 36 // note to send on right pad hit #define NOTE_ON_CMD 0x90 //NOTE ON byte in MIDI #define NOTE_OFF_CMD 0x80 //NOTE OFF byte in MIDI #define MAX_MIDI_VELOCITY 127 // Limit max velocity value in MIDI (127 is maximum value, midi single byte values are 7 bits) #define SPEED_SERIAL 9600 //debug serial port speed #define SPEED_MIDI 31250 //midi serial speed #define SIGNAL_BUFFER_SIZE 100 //temporary buffer for analog input #define PEAK_BUFFER_SIZE 50 // temporary buffer for peaks #define MAX_TIME_BETWEEN_PEAKS 10 #define MIN_TIME_BETWEEN_PEAKS 40 unsigned short MapAnalog[NUM_PIEZOS]; // array for inputs unsigned short MapNotes[NUM_PIEZOS]; // array for notes mapped to inputs unsigned short MapThreshold[NUM_PIEZOS]; // array for threshold map for each pad short indexActualSignal[NUM_PIEZOS]; short indexActualPeak[NUM_PIEZOS]; unsigned short BufferSignal[NUM_PIEZOS][SIGNAL_BUFFER_SIZE]; // array for unsigned short BufferPeak[NUM_PIEZOS][PEAK_BUFFER_SIZE]; boolean noteList[NUM_PIEZOS]; unsigned short VelocityNoteList[NUM_PIEZOS]; boolean ultPeakZero[NUM_PIEZOS]; unsigned long ultTimePeak[NUM_PIEZOS]; unsigned long ultTimeNote[NUM_PIEZOS]; SoftwareSerial mSerial(2, 3); // Setup serial port for MIDI use, pin3 = MIDI out (we do not receive any MIDI) void setup() { Serial.begin(SPEED_SERIAL); mSerial.begin(SPEED_MIDI); //initialize globals for(short i=0; i<NUM_PIEZOS; ++i) { indexActualSignal[i] = 0; indexActualPeak[i] = 0; memset(BufferSignal[i],0,sizeof(BufferSignal[i])); memset(BufferPeak[i],0,sizeof(BufferPeak[i])); noteList[i] = false; VelocityNoteList[i] = 0; ultPeakZero[i] = true; ultTimePeak[i] = 0; ultTimeNote[i] = 0; MapAnalog[i] = PRIMER_ANALOG + i; } MapThreshold[0] = LEFT_THRESHOLD; MapThreshold[1] = RGHT_THRESHOLD; MapNotes[0] = LEFT_NOTE; MapNotes[1] = RGHT_NOTE; } void loop() { unsigned long TimeActual = millis(); for(short i=0; i<NUM_PIEZOS; ++i) { //get a new signal from analog read unsigned short newSignal = analogRead(MapAnalog[i]); BufferSignal[i][indexActualSignal[i]] = newSignal; //if new signal is 0 if(newSignal < MapThreshold[i]) { if(!ultPeakZero[i] && (TimeActual - ultTimePeak[i]) > MAX_TIME_BETWEEN_PEAKS) { grabNewPeak(i,0); } else { //get previous signal short indexSignalPrevious = indexActualSignal[i]-1; if(indexSignalPrevious < 0) indexSignalPrevious = SIGNAL_BUFFER_SIZE-1; unsigned short SignalPrevious = BufferSignal[i][indexSignalPrevious]; 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(SignalPrevious >= MapThreshold[i]) { if(BufferSignal[i][indexSignalPrevious] > newPeak) { newPeak = BufferSignal[i][indexSignalPrevious]; } //decrement previous signal index, and get previous signal indexSignalPrevious--; if(indexSignalPrevious < 0) indexSignalPrevious = SIGNAL_BUFFER_SIZE-1; SignalPrevious = BufferSignal[i][indexSignalPrevious]; } if(newPeak > 0) { grabNewPeak(i, newPeak); } } } indexActualSignal[i]++; if(indexActualSignal[i] == SIGNAL_BUFFER_SIZE) indexActualSignal[i] = 0; } } void grabNewPeak(short slot, short newPeak) { ultPeakZero[slot] = (newPeak == 0); unsigned long TimeActual = millis(); ultTimePeak[slot] = TimeActual; //new peak recorded (newPeak) BufferPeak[slot][indexActualPeak[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 IndexPeakPrevious = indexActualPeak[slot]-1; if(IndexPeakPrevious < 0) IndexPeakPrevious = PEAK_BUFFER_SIZE-1; unsigned short peakPrevious = BufferPeak[slot][IndexPeakPrevious]; if(newPeak > peakPrevious && (TimeActual - ultTimeNote[slot]) > MIN_TIME_BETWEEN_PEAKS) { noteList[slot] = true; if(newPeak > VelocityNoteList[slot]) VelocityNoteList[slot] = newPeak; } else if(newPeak < peakPrevious && noteList[slot]) { SendNote(MapNotes[slot], VelocityNoteList[slot]); noteList[slot] = false; VelocityNoteList[slot] = 0; ultTimeNote[slot] = TimeActual; } indexActualPeak[slot]++; if(indexActualPeak[slot] == PEAK_BUFFER_SIZE) indexActualPeak[slot] = 0; } void SendNote(unsigned short note, unsigned short velocity) { velocity = (velocity / 3) + 30; // velocity values are from threshold value to 1024, and MIDI only accepts 127, so we need to tone it down if(velocity > MAX_MIDI_VELOCITY) // if remaining velocity is larger than max, cap it velocity = MAX_MIDI_VELOCITY; midiNoteOn(note, velocity); midiNoteOff(note, velocity); } void midiNoteOn(byte note, byte velocityMidi) { mSerial.write(NOTE_ON_CMD); mSerial.write(note); mSerial.write(velocityMidi); // Serial.println(velocityMidi); Send debug values to serial } void midiNoteOff(byte note, byte velocityMidi) { mSerial.write(NOTE_OFF_CMD); mSerial.write(note); mSerial.write(velocityMidi); }