arduino Leanordo Midi Drum Kit

26. November 2016 at 16:45
Print Friendly, PDF & Email

Inspired by the great tutorial from on instructables, I have adopted the project using a Leonardo.

The main objective was to get rid of the USB-Midi cable and just use a Leonardo instead of an Arduino Uno or Nano.
The constraint is, that the Leonardo won’t provide the MIDI functionally out of the box.
You would need to use a special core called TeeOnArdu.
The one on github did not work out of the box, so I have used the one from Adafruit.

The setup in eclipse is described in another post here:

leo_drum_1

 

 

leo_drum_2

The original code has been slightly updated to use the build-in MIDI functions.

/* This code is placed in the public domain by its author, Ian Harvey
 * October 2016.
 * http://www.instructables.com/id/Pringle-Can-MIDI-Drums/
 *
 * update by Thomas Hoeser to Leonardo based on TeensyCore
 * December 2016
*/
 
#include <Wire.h>
 
#define LED_Status 2 // Pin for status LED (shows piezo got hit)
#define LED_HeartBeat 3 // PWM Pin for heartbeat LED (shows code is working)
int brightness = 0; // how bright the LED is
int fadeAmount = 5; // how many points to fade the LED by
 
uint8_t heart = 0; // Heartbeat LED counter
unsigned long prevReadTime = 0L; // Keypad polling timer
uint8_t note = 0;
 
// On MIDI Channel 10, each MIDI Note number ("Key#") corresponds to a different drum sound, as shown below
const int MIDI_CHANNEL=10;
 
const int NCHANNELS = 4;
const int inPins[NCHANNELS] = { A0, A1, A2, A3 };
const int midiNotes[NCHANNELS] =
{
 // Follows General MIDI specs at https://www.midi.org/specifications/item/gm-level-1-sound-set
 36, // C3, Kick
 38, // D3, Snare
 42, // F#3, Closed hi-hat
 46, // A#3. Open hi-hat
};
 
/*
35 Acoustic Bass Drum 36 Bass Drum 1 37 Side Stick 38 Acoustic Snare
39 Hand Clap 40 Electric Snare 41 Low Floor Tom 42 Closed Hi Hat
43 High Floor Tom 44 Pedal Hi-Hat 45 Low Tom 46 Open Hi-Hat
47 Low-Mid Tom 48 Hi-Mid Tom 49 Crash Cymbal 1 50 High Tom
51 Ride Cymbal 1 52 Chinese Cymbal
 */
 
const int thresholdLevel[NCHANNELS] = { 30, 30, 30, 30 }; // ADC reading to trigger; lower => more sensitive
const long int maxLevel[NCHANNELS] = { 400, 400, 400, 400 }; // ADC reading for full velocity; lower => more sensitive
 
static unsigned int vmax[NCHANNELS] = { 0 };
static unsigned int trigLevel[NCHANNELS];
static unsigned int counter[NCHANNELS] = { 0 };
 
static unsigned int CTR_NOTEON = 10; // Roughly 5ms sampling peak voltage
static unsigned int CTR_NOTEOFF = CTR_NOTEON + 30; // Duration roughly 15ms
// 0 -> not triggered
// 1..CTR_NOTEON -> sampling note on
// CTR_NOTEON+1 .. CTR_NOTEOFF -> note off
 
//--------------------------------------------------------------------------------------------------
void setup() {
 
 pinMode(LED_HeartBeat, OUTPUT);
 digitalWrite(LED_HeartBeat, LOW);
 pinMode(LED_Status, OUTPUT);
 digitalWrite(LED_Status, LOW);
 
 for (int i = 0; i < NCHANNELS; i++)
 {
 pinMode(inPins[i], INPUT);
 analogRead(inPins[i]);
 trigLevel[i] = thresholdLevel[i];
 }
 
 /* show module is ready for use */
 digitalWrite(LED_Status, HIGH);
 analogWrite (LED_HeartBeat, 80);
 delay(500);
 digitalWrite(LED_Status, LOW);
 analogWrite (LED_HeartBeat, 160);
 delay(500);
 digitalWrite(LED_Status, HIGH);
 analogWrite (LED_HeartBeat, 254);
 delay(500);
 digitalWrite(LED_Status, LOW);
 analogWrite (LED_HeartBeat, 0);
 
} // void setup()
 
//--------------------------------------------------------------------------------------------------
void loop() {
 unsigned long t = millis();
 int ch;
 
 // if((t - prevReadTime) >= 20L) { // 20ms = min Trellis poll time
 
 
 for (ch=0; ch < NCHANNELS; ch++) // Drums: NCHANNELS=4
 {
 unsigned int aVal = analogRead(inPins[ch]);
 if ( counter[ch] == 0 )
 {
 if ( aVal >= trigLevel[ch] )
 {
 vmax[ch] = aVal;
 counter[ch] = 1;
 digitalWrite(LED_Status, HIGH);
 }
 } // if counter[ch] == 0
 else
 {
 if ( aVal > vmax[ch] )
 vmax[ch] = aVal;
 counter[ch]++;
 
 if ( counter[ch] == CTR_NOTEON )
 {
 long int vel = ((long int)vmax[ch]*127)/maxLevel[ch];
 //Serial.println(vel);
 if (vel < 5) vel = 5;
 if (vel > 127) vel = 127;
 // usbMIDI.sendNoteOn(note, velocity, channel)
 usbMIDI.sendNoteOn(midiNotes[ch], vel, MIDI_CHANNEL);
 trigLevel[ch] = vmax[ch];
 }
 else if ( counter[ch] >= CTR_NOTEOFF )
 {
 usbMIDI.sendNoteOn(midiNotes[ch], 0, MIDI_CHANNEL);
 counter[ch] = 0;
 digitalWrite(LED_Status, LOW);
 }
 } // else counter[ch] == 0
 
 // The signal from the piezo is a damped oscillation decaying with
 // time constant 8-10ms. Prevent false retriggering by raising
 // trigger level when first triggered, then decaying it to the
 // threshold over several future samples.
 trigLevel[ch] = ((trigLevel[ch] * 19) + (thresholdLevel[ch] * 1)) / 20;
 }
 
 prevReadTime = t;
 analogWrite(LED_HeartBeat, brightness); // set the brightness
 brightness = brightness + fadeAmount; // change the brightness for next time
 if (brightness == 0 || brightness == 255) { // reverse the direction of the fading at the ends of the fade
 fadeAmount = -fadeAmount ;
 }
 
 //} // if t - prevReadTime) >= 20L
 
 while(usbMIDI.read()); // Discard incoming MIDI messages
 
} // void loop()
 
//--------------------------------------------------------------------------------------------------
/* Here's the set of MIDI functions for making your own projects:
 
 usbMIDI.sendNoteOn(note, velocity, channel)
 usbMIDI.sendNoteOff(note, velocity, channel)
 usbMIDI.sendPolyPressure(note, pressure, channel)
 usbMIDI.sendControlChange(control, value, channel)
 usbMIDI.sendProgramChange(program, channel)
 usbMIDI.sendAfterTouch(pressure, channel)
 usbMIDI.sendPitchBend(value, channel)
 usbMIDI.sendSysEx(length, array)
 usbMIDI.send_now()
 
 Some info on MIDI note numbers can be found here:
 http://www.phys.unsw.edu.au/jw/notes.html
 
 Rather than MIDI, one could theoretically try using Serial to
 create a sketch compatible with serialosc or monomeserial, but
 those tools have proven notoriously unreliable thus far. */