This thing has two parts, the manual components and the presets, and the sounds are generated through a VST software. For the manual side, the drum machine will play a bass drum if I hit either of my heels against the floor. If I hit the ball of my left foot, it will play a snare drum. On the console, there’s a silver ribbon that will play a suspended cymbal roll as I slide my finger from left to right. For the presets, I have 11 preset drum beats. The tempo can be tapped in before you start the preset, and it can be sped up by a knob. The volume for each of the drums is also adjustable via knobs. Then, there are 3 buttons that will generate random drum fills made of 8th notes, triplets, or 16th notes. The last button turns everything off. There is also a switch that will enable a visual count off for each of the presets.
On the technical side, the manual components work via types of variable resistors.
I put force sensitive resistors (FSRs) in my shoes inbetween the lining and the sole of the shoes on the heels and in the ball of my left shoe and taped them down to secure them. I punctured holes in the right side of the shoes so the wires for power and data could stick out. I soldered all of the power wires together and have it all connected to my breadboard, and I have each of the information cables connected to my Teensy through 10k resistors. The more force I exert on the FSRs, the less the resistance, and these specific FSRs can take up to 20 Newtons of force, which is about 5 lbs of force. In digital terms, the Teensy microcontroller will output numbers between 0 and 1023 – 0 being the least force/most resistance and 1023 being the most force/least resistance. Then, I callibrated a threshold number for when a drum should be activated. For example, one of my sensors, if the number that the FSR gives me is above 600, it will trigger a note, and when it’s below 500, it will reset the trigger. I also have the velocity scaled between the numbers that the FSR’s output and a good range of MIDI velocity values. That section of my code looks like this:
void ManualDrums() { SDOne = analogRead(0); BDOne = analogRead(1); BDTwo = analogRead(2); if (BDOne > 500 && statushitBDOne == "off") { scaledVelocity = map(BDOne, 600, 900, 70, 127); constrain(scaledVelocity, 0, 127); usbMIDI.sendNoteOn(BD, scaledVelocity, 1); statushitBDOne = "on"; } if (BDOne < 400 && statushitBDOne == "on") { usbMIDI.sendNoteOff(BD, 0, 1); statushitBDOne = "off"; } if (BDTwo > 600 && statushitBDTwo == "off") { scaledVelocity2 = map(BDTwo, 600, 900, 70, 127); constrain(scaledVelocity2, 0, 127); usbMIDI.sendNoteOn(BD, scaledVelocity2, 1); statushitBDTwo = "on"; } if (BDTwo < 450 && statushitBDTwo == "on") { usbMIDI.sendNoteOff(BD, 0, 1); statushitBDTwo = "off"; } if (SDOne > 500 && statushitSDOne == "off") { scaledVelocity3 = map(SDOne, 600, 900, 70, 127); constrain(scaledVelocity3, 0, 127); usbMIDI.sendNoteOn(SD, scaledVelocity3, 1); statushitSDOne = "on"; } if (SDOne < 200 && statushitSDOne == "on") { usbMIDI.sendNoteOff(SD, 0, 1); statushitSDOne = "off"; } }
The other resistor is a ribbon resistor that changes resistance based on where I touch the strip of ribbon. On the far left, it has the most resistance, and on the far right, it has the least resistance. There are three pins that are routed to ground, power, and information. The information wire is connected to an analog read pin on the Teensy with a 1k resistor. The 1k resistor makes sure that the amount of voltage sent to the teensy is 0 when I’m not pressing the ribbon. For this thing, I&amp;amp;amp;amp;amp;nbsp;want the VST to hit notes with increasing speed as the number increases. So, I divided the numbers that the resistor inputs into 4 chunks: 0 to 250, 250 to 500, 500 to 750, and 750 to 1010. For the first chunk, I have a 250 millisecond delay inbetween hits. For 250 to 500, I have a 125 ms delay. 500 to 750 has 62 ms and 750 to 1010 has a 50 ms delay. When the number goes above 1010, the cymbal crashes at full 127 velocity and has to be set back to 0 in order to start the process again. In code, that looks like this:
void suspendedCymbal() { number = analogRead(9); CymbVelocity = map(number, 0, 1000, 50, 124); if (number == 0) { CymbVelocity = 0; } if (number == 0 && number < 250 && CymbStatus == "start") { usbMIDI.sendNoteOn(Cymb, CymbVelocity, 1); delay(250); } if (number > 250 && number < 500 && CymbStatus == "start") { usbMIDI.sendNoteOn(Cymb, CymbVelocity, 1); delay(125); } if (number > 500 && number < 750 && CymbStatus == "start") { usbMIDI.sendNoteOn(Cymb, CymbVelocity, 1); delay(62); } if (number > 750 && number < 1010 && CymbStatus == "start") { usbMIDI.sendNoteOn(Cymb, CymbVelocity, 1); delay(50); } if (number > 1010 && CymbStatus == "start") { CymbVelocity = 127; usbMIDI.sendNoteOn(Cymb, CymbVelocity, 1); CymbStatus = "end"; } if (number == 0 && CymbStatus == "end") { CymbVelocity = 0; CymbStatus = "start"; usbMIDI.sendNoteOff(Cymb, CymbVelocity, 1); } }
As for the presets, I bought a 16 key capacitive touch keypad. It’s an SPI device that outputs an active low in correspondence with the number that is touched. That took some figuring out since it defaulted to using 8 keys, but connecting two pins together allowed the full 16 key usage. In order to establish communication with this keypad, you have to send in a number – an address – from 0 to 15 inclusively, and then the device will output a 1 or 0 answer. So if you pressed 5, the teensy would have to send the number 4 to the keypad, and it would output a 0 for every clock pulse, which is an extremely fast rate. So in order to read this, I have a for() loop that counts from 0 to 15 and concatenates the data values to a 16 bit word. I put an S in front originally to signify start, but also to establish a new string (a variable that is equal to something in quotation marks). In code, that looks like this:
void readButtons() { datavalue = "S"; for (int button = 0; button < 16; button++) { digitalWrite(clockPin, LOW); int databit = digitalRead(dataPin); digitalWrite(clockPin, HIGH); datavalue.concat(databit); } }
The clockPin needs to go low in order for the keypad to accept a number, and it can only accept one address at once, so it needs to be re-written high and back to low in order to get another address. In that time, a new address is sent in. Concat is a function that squashes all of the data values together, and it takes the string previously established, “datavalue” and places each data bit after what datavalue is equal to. So if none of the buttons are pressed, datavalue will become S1111111111111111. If 1 is pressed, datavalue will become S0111111111111111. If 2 is pressed, datavalue is S1011111111111111 all the way up to 16 when it becomes S1111111111111110. Since this happens extremely quickly, it reads these numbers hundreds of times per second.
Using that, I used the first button to create a tap tempo. I wanted to be able to press this button 4 times and the Teensy would record the amount of time inbetween all 4 taps and average it out to find the average amount of milliseconds per quarter note. As a visual aide, I have four different lights that light up each time the button is pressed, and the 4th time it’s pressed, all the lights turn off. For the code, I have a status for each time the button is pressed and released. When the 1 button is pressed and the status is at zero, I start a millisecond counter, make the status “zeroand”, and turn on an LED. When it’s released, the tempo status becomes “one”. When the button is pressed again, the status becomes “oneand”, I have a variable called TempoTimerTwo that is equal to the millis() – StartTempo, and another LED is lit up. When the button is released, the status becomes “two”. The code repeats this for the three taps. At the end, after it becomes “threeand”, the status is reset to “zero”, and I have a variable called Metron that is equal to TempoTimerTwo + TempoTimerThree + TempoTimerFour / 3. In code, it looks like this:4
void TempoTap() { if (datavalue == "S0111111111111111" tempohit == "zero") { tempohit = "zeroand"; StartTempo = millis(); TempoTimer = millis() - StartTempo; digitalWrite(LedPin, HIGH); } if (datavalue != "S0111111111111111" tempohit == "zeroand") { tempohit = "one"; } if (datavalue == "S0111111111111111" tempohit == "one") { tempohit = "oneand"; TempoTimerTwo = millis() - StartTempo; digitalWrite(LedPin2, HIGH); } if (datavalue != "S0111111111111111" tempohit == "oneand") { tempohit = "two"; } if (datavalue == "S0111111111111111" tempohit == "two") { tempohit = "twoand"; TempoTimerThree = millis() - StartTempo - TempoTimerTwo; digitalWrite(LedPin3, HIGH); } if (datavalue != "S0111111111111111" tempohit == "twoand") { tempohit = "three"; } if (datavalue == "S0111111111111111" tempohit == "three") { tempohit = "threeand"; TempoTimerFour = millis() - StartTempo - TempoTimerThree - TempoTimerTwo; digitalWrite(LedPin4, HIGH); } if (datavalue != "S0111111111111111" tempohit == "threeand") { tempohit = "zero"; Metron = (TempoTimerTwo + TempoTimerThree + TempoTimerFour) / 3 ; digitalWrite(LedPin, LOW); digitalWrite(LedPin2, LOW); digitalWrite(LedPin3, LOW); digitalWrite(LedPin4, LOW); } }
Now I can get into my actual presets. I have 11 of them with these drumbeats in music notation form:
I coded each of these presets using arrays. I assigned a number to each combination of drums and cymbals in each of the drumbeats. If the shortest note in the drumbeat is an 8th note, I have a total of 8 numbers in the array per measure – one for each note. If it’s a triplet, I have an array of 12 numbers. If there are 16th notes, there are 16 notes in the array per measure.
Essentially, it’s a preset looping sequencer. In order to make the thing play, I have a for loop that counts from 0 to whatever the last step is. Whatever that integer i is corresponds to the position in each array. Each number in the array corresponds to a combination of drums, and then I delay for a step value. That step value comes from the tempo tap. If the step is an 8th note, it’s Metron / 2. If the step is a triplet, it’s Metron / 3. If the step is a 16th note, it’s Metron / 4. All of this is triggered by a button getting pressed and released. Here’s what one of them looks like, with some added effects that I had that I’ll explain in more depth later:
//Preset One String DrumOneStatus = "off"; int noteArrayOne[] = {1, 2, 3, 1, 1, 2, 3, 2}; void PlayOne() { if (datavalue == "S1011111111111111" DrumOneStatus == "off") { DrumOneStatus = "turning on"; } if (datavalue != "S1011111111111111" DrumOneStatus == "turning on") { DrumOneStatus = "on"; countOff(); // this makes an LED blink 4 times to the beat } while (DrumOneStatus == "on") { if (datavalue == "S1011111111111111") { DrumOneStatus = "off"; } for (int i = 0; i < 8; i++) { tempoMod(); // this can make the tempo increase if (analogRead(8) > 0 ) { // this is related to tempo modifying Step = modMetro / 2; // and this } else { Step = Metron / 2; // and this } presetOne(noteArrayOne[i]); continuousControls(); // this contains the readButtons and manual drum functions delay(Step); STOP(); // this contains a function that will stop all presets if 16 is pressed } } } void presetOne(int note) { if (note == 1) { usbMIDI.sendNoteOn(BD, ReadVelocityBass, 1); usbMIDI.sendNoteOn(HH, ReadVelocityHH, 1); } if (note == 2) { usbMIDI.sendNoteOn(HH, ReadVelocityHH, 1); } if (note == 3) { usbMIDI.sendNoteOn(HH, ReadVelocityHH, 1); usbMIDI.sendNoteOn(SD, ReadVelocitySnare, 1); } }
I have this same layout 11 times for buttons 2 through 12. The next feature I’ll explain is the tempoMod. That function is so much simpler than the rest of what I did. It reads a potentiometer, the last potentiometer on my control panel, and scales the numbers from 1 to 1023 to Metron to 100. Metron is default at 500 for a 120 bpm, so as this potentiometer increases in value, a new variable, modMetro, starts at Metron and decreases to a minimum of 100. That effectively speeds up the tempo. You can also turn the potentiometer back the other way and slow it down to the original tempo. That function looks like this:
void tempoMod() { if ((analogRead(8)) > 0) { modMetro = map(analogRead(8), 1, 1023, Metron, 100); } }
And I place it inside each preset so it reads the modMetro value if the potentiometer moves at all. The count off function is relatively self explanatory. I turn on an LED for half the beat value, turn it off for half a beat value, and repeat that 4 times. Since I only want this to count off when I want it to, I have it adjustable by a switch.
void countOff() { if (digitalRead(switch1) == HIGH switchStatus == "off") { switchStatus = "on"; digitalWrite(LedPin, HIGH); delay(Metron / 2); digitalWrite(LedPin, LOW); delay(Metron / 2); digitalWrite(LedPin, HIGH); delay(Metron / 2); digitalWrite(LedPin, LOW); delay(Metron / 2); digitalWrite(LedPin, HIGH); delay(Metron / 2); digitalWrite(LedPin, LOW); delay(Metron / 2); digitalWrite(LedPin, HIGH); delay(Metron / 2); digitalWrite(LedPin, LOW); delay(Metron / 2); if (digitalRead(switch1) == LOW switchStatus == "on") { switchStatus = "off"; } }
I also have 5 potentiometers that control the velocities of the drums while the presets are playing. So I have 5 different analog read pins set to those variables like this:
void readVelocities() { // these control the velocities for the presets ReadVelocitySnare = map(analogRead(3), 0, 1023, 1, 127); ReadVelocityBass = map(analogRead(4), 0, 1023, 1, 127); ReadVelocityRide = map(analogRead(5), 0, 1023, 1, 127); ReadVelocityPedal = map(analogRead(6), 0, 1023, 1, 127); ReadVelocityHH = map(analogRead(7), 0, 1023, 1, 127); }
The last major thing that I have in my control panel are buttons for randomly generated drum fills. I wanted 3 different types of subdivision levels for the fills, 8th note, triplets, and 16ths. So I have a for() loop counting from 0 to 8, 12, and 16 respectively. Each of those numbers represent each step. Then, for each step, I wanted there to be up to 3 different drums/cymbals playing at once. In order to make it random, I have a random function. random(min, max) generates a random number starting from the given minimum value up to one below the maximum value. If there is only one number enclosed, it starts at 0. So I have random(20) which will generate a random number from 0 to 19, and I have that inside of an array. My array is made up of the different MIDI notes for the drumset with 6 zeros :
int randNoteArray[] = {36, 38, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 0, 0, 0, 0, 0}; void randomEighth() { if (datavalue == "S1111111111110111") { Step = Metron / 2; for (int i = 0; i < 8; i++) { usbMIDI.sendNoteOn(randNoteArray[random(20)], random(70, 128), 1); usbMIDI.sendNoteOn(randNoteArray[random(20)], random(70, 128), 1); usbMIDI.sendNoteOn(randNoteArray[random(20)], random(70, 128), 1); delay(Step); } } }
The random(20) picks a random address in the array, and the random(70, 128) picks a random velocity value. Then, just like the preset, I delay it by the step value. This one is enclosed in an if() loop instead of a while() loop, though, because I only want it to play once.
Here’s a rough schematic of the wiring:
Here’s a video of it working:
Now, in it’s full commented glory, this is my complete code:
// Buttons int clockPin = 13; int dataPin = 11; String datavalue = "S"; // Switches int switch1 = 12; // Lights int LedPin = 3; int LedPin2 = 2; int LedPin3 = 1; int LedPin4 = 0; //Manual Drum Controls int BDOne = 0; int scaledVelocity = 0; String statushitBDOne = "off"; int BDTwo = 0; int scaledVelocity2 = 0; String statushitBDTwo = "off"; int SDOne = 0; int scaledVelocity3 = 0; String statushitSDOne = "off"; //Metronome counter int Metron = 500; int Step = 250; String tempohit = "zero"; unsigned long StartTempo = 0; unsigned long TempoTimer = 0; unsigned long TempoTimerTwo = 0; unsigned long TempoTimerThree = 0; unsigned long TempoTimerFour = 0; unsigned long OneTwo; int modMetro = 500; //Universal Controls int ReadVelocitySnare = 100; int ReadVelocityBass = 100; int ReadVelocityRide = 100; int ReadVelocityPedal = 100; int ReadVelocityHH = 100; int BD = 36; int SD = 38; int HH = 42; int Ride = 51; int Pedal = 44; int OHH = 46; int Side = 37; //Cymbal stuff String CymbStatus = "start"; int CymbVelocity = 0; int Cymb = 57; int number = 0; //Preset One String DrumOneStatus = "off"; int noteArrayOne[] = {1, 2, 3, 1, 1, 2, 3, 2}; //Preset Two String DrumTwoStatus = "off"; int noteArrayTwo[] = {1, 0, 2, 0, 3, 0, 2, 4, 2, 0, 1, 0, 3, 0, 2, 0}; //Preset Three String DrumThreeStatus = "off"; int noteArrayThree[] = {1, 0, 0, 2, 0, 1, 1, 0, 0, 2, 0, 1}; //Preset Four String DrumFourStatus = "off"; int noteArrayFour[] = {1, 2, 2, 2, 3, 2, 2, 1, 1, 2, 1, 2, 3, 2, 2, 2}; //Preset Five String DrumFiveStatus = "off"; int noteArrayFive[] = {1, 2, 2, 1, 3, 2, 4, 3, 3, 2, 4, 3, 3, 4, 2, 3}; //Preset Six String DrumSixStatus = "off"; int noteArraySix[] = {1, 2, 3, 4, 4, 5, 6, 1, 4, 5, 6, 1, 1, 5, 3, 4}; //Preset Seven String DrumSevenStatus = "off"; int noteArraySeven[] = {1, 0, 2, 0, 3, 4, 2, 0, 1, 0, 2, 0, 3, 4, 2, 0, }; //Preset Eight String DrumEightStatus = "off"; int noteArrayEight[] = {1, 2, 3, 2, 4, 2, 3, 0}; //Preset Nine String DrumNineStatus = "off"; int noteArrayNine[] = {1, 2, 3, 2, 1, 2, 3, 2}; //Preset Ten String DrumTenStatus = "off"; int noteArrayTen[] = {0, 0, 1, 2, 0, 0, 3, 0}; //Preset Eleven String DrumElevenStatus = "off"; int noteArrayEleven[] = {1, 0, 2, 0, 3, 0, 4, 5, 1, 0, 1, 0, 4, 6, 3, 0, 4, 5, 1, 0, 3, 0, 4, 5, 1, 0, 1, 0, 3, 5, 4, 5}; //Random Fill Generator int randNoteArray[] = {36, 38, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 0, 0, 0, 0, 0}; void setup() { pinMode(clockPin, OUTPUT); // These three are used for the keypad pinMode(dataPin, INPUT); digitalWrite(clockPin, LOW); pinMode(LedPin, OUTPUT); // LEDs pinMode(LedPin2, OUTPUT); pinMode(LedPin3, OUTPUT); pinMode(LedPin4, OUTPUT); pinMode(switch1, INPUT); Serial.begin(9600); } void loop() { continuousControls(); // contains manual drum controls, read button controls, and fills suspendedCymbal(); // suspended cymbal roll; cannot be played at the same time as the presets TempoTap(); // tap tempo function PlayOne(); // all of the Play functions are the presets PlayTwo(); PlayThree(); PlayFour(); PlayFive(); PlaySix(); PlaySeven(); PlayEight(); PlayNine(); PlayTen(); PlayEleven(); } void PlayOne() { // basic rock beat if (datavalue == "S1011111111111111" && DrumOneStatus == "off") { //reads the keypad and checks the status of the trigger DrumOneStatus = "turning on"; } if (datavalue != "S1011111111111111" && DrumOneStatus == "turning on") { // this is so the preset plays after the button is released DrumOneStatus = "on"; countOff(); // counts off in blinking lights if the switch is on } while (DrumOneStatus == "on") { // "while" loops this entire thing exclusively if (datavalue == "S1011111111111111") { DrumOneStatus = "off"; } for (int i = 0; i < 8; i++) { tempoMod(); // function to change the tempo if (analogRead(8) > 0 ) { //analogRead(8) is exclusively for tempo modulation Step = modMetro / 2; //changes the tempo } else { Step = Metron / 2; // or keeps it according to the already stated tempo } presetOne(noteArrayOne[i]); // chooses the specific combination of drums in the sequence continuousControls(); // since it's a while loop, it still needs to be able to read the buttons and manual settings delay(Step); STOP(); // turns off the preset (and all presets) } } } void PlayTwo() { //same exact format as PlayOne, varied rock beat if (datavalue == "S1101111111111111" && DrumTwoStatus == "off") { DrumTwoStatus = "turning on"; } if (datavalue != "S1101111111111111" && DrumTwoStatus == "turning on") { DrumTwoStatus = "on"; Step = Metron / 2; countOff(); } while (DrumTwoStatus == "on") { for (int i = 0; i < 16; i++) { tempoMod(); if (analogRead(8) > 0 ) { Step = modMetro / 2; } else { Step = Metron / 2; } presetTwo(noteArrayTwo[i]); continuousControls(); delay(Step / 2); STOP(); if (datavalue == "S1101111111111111") { DrumTwoStatus = "off"; } } } } void PlayThree() { //jazz time if (datavalue == "S1110111111111111" && DrumThreeStatus == "off") { DrumThreeStatus = "turning on"; } if (datavalue != "S1110111111111111" && DrumThreeStatus == "turning on") { DrumThreeStatus = "on"; Step = Metron / 3; countOff(); } while (DrumThreeStatus == "on") { for (int i = 0; i < 12; i++) { tempoMod(); if (analogRead(8) > 0 ) { Step = modMetro / 3; } else { Step = Metron / 3; } presetThree(noteArrayThree[i]); continuousControls(); Serial.println(datavalue); delay(Step); STOP(); if (datavalue == "S1110111111111111") { DrumThreeStatus = "off"; } } } } void PlayFour() { // R & B type beat if (datavalue == "S1111011111111111" && DrumFourStatus == "off") { DrumFourStatus = "turning on"; } if (datavalue != "S1111011111111111" && DrumFourStatus == "turning on") { DrumFourStatus = "on"; Step = Metron / 2; countOff(); } while (DrumFourStatus == "on") { for (int i = 0; i < 16; i++) { tempoMod(); if (analogRead(8) > 0 ) { Step = modMetro / 2; } else { Step = Metron / 2; } presetFour(noteArrayFour[i]); continuousControls(); delay(Step / 2); STOP(); if (datavalue == "S1111011111111111") { DrumFourStatus = "off"; } } } } void PlayFive() { // bossa nova if (datavalue == "S1111101111111111" && DrumFiveStatus == "off") { DrumFiveStatus = "turning on"; } if (datavalue != "S11111011" && DrumFiveStatus == "turning on") { DrumFiveStatus = "on"; Step = Metron / 2; countOff(); } while (DrumFiveStatus == "on") { for (int i = 0; i < 16; i++) { tempoMod(); if (analogRead(8) > 0 ) { Step = modMetro / 2; } else { Step = Metron / 2; } presetFive(noteArrayFive[i]); continuousControls(); delay(Step); STOP(); if (datavalue == "S1111101111111111") { DrumFiveStatus = "off"; } } } } void PlaySix() { // samba if (datavalue == "S1111110111111111" && DrumSixStatus == "off") { DrumSixStatus = "turning on"; } if (datavalue != "S1111110111111111" && DrumSixStatus == "turning on") { DrumSixStatus = "on"; Step = Metron / 2; countOff(); } while (DrumSixStatus == "on") { for (int i = 0; i < 8; i++) { tempoMod(); if (analogRead(8) > 0 ) { Step = modMetro / 2; } else { Step = Metron / 2; } presetSix(noteArraySix[i]); continuousControls(); delay(Step); STOP(); if (datavalue == "S1111110111111111") { DrumSixStatus = "off"; } } } } void PlaySeven() { // drumbeat to Kryptonite by Three Doors Down if (datavalue == "S1111111011111111" && DrumSevenStatus == "off") { DrumSevenStatus = "turning on"; } if (datavalue != "S1111111011111111" && DrumSevenStatus == "turning on") { DrumSevenStatus = "on"; Step = Metron / 4; countOff(); } while (DrumSevenStatus == "on") { for (int i = 0; i < 16; i++) { tempoMod(); if (analogRead(8) > 0 ) { Step = modMetro / 4; } else { Step = Metron / 4; } presetSeven(noteArraySeven[i]); continuousControls(); delay(Step); STOP(); if (datavalue == "S1111111011111111") { DrumSevenStatus = "off"; } } } } void PlayEight() { // drumbeat to Sound of Madness by Shinedown if (datavalue == "S1111111101111111" && DrumEightStatus == "off") { DrumEightStatus = "turning on"; } if (datavalue != "S1111111101111111" && DrumEightStatus == "turning on") { DrumEightStatus = "on"; Step = Metron / 2; countOff(); } while (DrumEightStatus == "on") { for (int i = 0; i < 8; i++) { tempoMod(); if (analogRead(8) > 0 ) { Step = modMetro / 2; } else { Step = Metron / 2; } presetEight(noteArrayEight[i]); continuousControls(); delay(Step); STOP(); if (datavalue == "S1111111101111111") { DrumEightStatus = "off"; } } } } void PlayNine() { // hip hop / pop beat if (datavalue == "S1111111110111111" && DrumNineStatus == "off") { DrumNineStatus = "turning on"; } if (datavalue != "S1111111110111111" && DrumNineStatus == "turning on") { DrumNineStatus = "on"; Step = Metron / 2; countOff(); } while (DrumNineStatus == "on") { for (int i = 0; i < 8; i++) { tempoMod(); if (analogRead(8) > 0 ) { Step = modMetro / 2; } else { Step = Metron / 2; } presetNine(noteArrayNine[i]); continuousControls(); delay(Step); STOP(); if (datavalue == "S1111111110111111") { DrumNineStatus = "off"; } } } } void PlayTen() { // afro-cuban drumming tumabu and hi hat if (datavalue == "S1111111111011111" && DrumTenStatus == "off") { DrumTenStatus = "turning on"; } if (datavalue != "S1111111111011111" && DrumTenStatus == "turning on") { DrumTenStatus = "on"; Step = Metron / 2; countOff(); } while (DrumTenStatus == "on") { for (int i = 0; i < 8; i++) { tempoMod(); if (analogRead(8) > 0 ) { Step = modMetro / 2; } else { Step = Metron / 2; } presetTen(noteArrayTen[i]); continuousControls(); delay(Step); STOP(); if (datavalue == "S1111111111011111") { DrumTenStatus = "off"; } } } } void PlayEleven() { // funk if (datavalue == "S1111111111101111" && DrumElevenStatus == "off") { DrumElevenStatus = "turning on"; } if (datavalue != "S1111111111101111" && DrumElevenStatus == "turning on") { DrumElevenStatus = "on"; Step = Metron / 4; countOff(); } while (DrumElevenStatus == "on") { for (int i = 0; i < 32; i++) { tempoMod(); if (analogRead(8) > 0 ) { Step = modMetro / 4; } else { Step = Metron / 4; } presetEleven(noteArrayEleven[i]); continuousControls(); delay(Step); STOP(); if (datavalue == "S1111111111101111") { DrumElevenStatus = "off"; } } } } void randomEighth() { //this is the random fill generator on the 8th note subdivision level if (datavalue == "S1111111111110111") { Step = Metron / 2; for (int i = 0; i < 8; i++) { usbMIDI.sendNoteOn(randNoteArray[random(20)], random(70, 128), 1); //the 20 digit array is made up of drum midi notes usbMIDI.sendNoteOn(randNoteArray[random(20)], random(70, 128), 1); usbMIDI.sendNoteOn(randNoteArray[random(20)], random(70, 128), 1); delay(Step); } } } void randomTriplet() { // triplet subdivision level as noted by... if (datavalue == "S1111111111111011") { Step = Metron / 3; // this for (int i = 0; i < 12; i++) { usbMIDI.sendNoteOn(randNoteArray[random(20)], random(70, 128), 1); usbMIDI.sendNoteOn(randNoteArray[random(20)], random(70, 128), 1); usbMIDI.sendNoteOn(randNoteArray[random(20)], random(70, 128), 1); delay(Step); } } } void randomSixteenth() { // and the 16th note subdivision level if (datavalue == "S1111111111111101") { Step = Metron / 4; for (int i = 0; i < 16; i++) { usbMIDI.sendNoteOn(randNoteArray[random(20)], random(70, 128), 1); usbMIDI.sendNoteOn(randNoteArray[random(20)], random(70, 128), 1); usbMIDI.sendNoteOn(randNoteArray[random(20)], random(70, 128), 1); delay(Step); } } } void presetOne(int note) { //this defines what each note in the array does, presetOne is a standard rock beat if (note == 1) { usbMIDI.sendNoteOn(BD, ReadVelocityBass, 1); // BD = Bass drum usbMIDI.sendNoteOn(HH, ReadVelocityHH, 1); // HH = Hi hat (closed) } if (note == 2) { usbMIDI.sendNoteOn(HH, ReadVelocityHH, 1); } if (note == 3) { usbMIDI.sendNoteOn(HH, ReadVelocityHH, 1); usbMIDI.sendNoteOn(SD, ReadVelocitySnare, 1); // SD = Snare drum } } void presetTwo(int note) { // variation on a rock beat if (note == 1) { usbMIDI.sendNoteOn(BD, ReadVelocityBass, 1); usbMIDI.sendNoteOn(HH, ReadVelocityHH, 1); } if (note == 2) { usbMIDI.sendNoteOn(HH, ReadVelocityHH, 1); } if (note == 3) { usbMIDI.sendNoteOn(HH, ReadVelocityHH, 1); usbMIDI.sendNoteOn(SD, ReadVelocitySnare, 1); } if (note == 4) { usbMIDI.sendNoteOn(SD, ReadVelocitySnare, 1); } } void presetThree(int note) { //jazz time if (note == 1) { usbMIDI.sendNoteOn(Ride, ReadVelocityRide, 1); //Ride cymbal } if (note == 2) { usbMIDI.sendNoteOn(Pedal, ReadVelocityHH, 1); usbMIDI.sendNoteOn(Ride, ReadVelocityRide, 1); } } void presetFour(int note) { // R & B beat if (note == 1) { usbMIDI.sendNoteOn(BD, ReadVelocityBass, 1); usbMIDI.sendNoteOn(HH, ReadVelocityHH, 1); } if (note == 2) { usbMIDI.sendNoteOn(HH, ReadVelocityHH, 1); } if (note == 3) { usbMIDI.sendNoteOn(HH, ReadVelocityHH, 1); usbMIDI.sendNoteOn(SD, ReadVelocitySnare, 1); } } void presetFive(int note) { //bossa nova if (note == 1) { usbMIDI.sendNoteOn(HH, ReadVelocityHH, 1); usbMIDI.sendNoteOn(Side, ReadVelocitySnare, 1); //Rim clicks emulating claves for latin beats usbMIDI.sendNoteOn(BD, ReadVelocityBass, 1); } if (note == 2) { usbMIDI.sendNoteOn(HH, ReadVelocityHH, 1); } if (note == 3) { usbMIDI.sendNoteOn(HH, ReadVelocityHH, 1); usbMIDI.sendNoteOn(BD, ReadVelocityBass, 1); } if (note == 4) { usbMIDI.sendNoteOn(HH, ReadVelocityHH, 1); usbMIDI.sendNoteOn(Side, ReadVelocitySnare, 1); } } void presetSix(int note) { //samba if (note == 1) { usbMIDI.sendNoteOn(Ride, ReadVelocityRide, 1); usbMIDI.sendNoteOn(Side, ReadVelocitySnare, 1); usbMIDI.sendNoteOn(BD, ReadVelocityBass, 1); } if (note == 2) { usbMIDI.sendNoteOn(Ride, ReadVelocityRide, 1); } if (note == 3) { if (ReadVelocityRide < 107) { //sambas accent specific notes, and this creates the accent by adding 20 to the velocity unless the velocity is already 127 ReadVelocityRide = ReadVelocityRide + 20; } else { ReadVelocityRide = 127; } if (ReadVelocitySnare < 107) { ReadVelocitySnare = ReadVelocitySnare + 20; } else { ReadVelocitySnare = 127; } usbMIDI.sendNoteOn(Ride, ReadVelocityRide, 1); usbMIDI.sendNoteOn(Side, ReadVelocitySnare, 1); readVelocities(); // then it resets the velocity back to normal } if (note == 4) { usbMIDI.sendNoteOn(Ride, ReadVelocityRide, 1); usbMIDI.sendNoteOn(BD, ReadVelocityBass, 1); } if (note == 5) { usbMIDI.sendNoteOn(Ride, ReadVelocityRide, 1); usbMIDI.sendNoteOn(Side, ReadVelocitySnare, 1); } if (note == 6) { if (ReadVelocityRide < 107) { ReadVelocityRide = ReadVelocityRide + 20; } else { ReadVelocityRide = 127; } usbMIDI.sendNoteOn(Ride, ReadVelocityRide, 1); readVelocities(); } } void presetSeven(int note) { //rock drumbeat of Kryptonite by Three Doors Down if (note == 1) { usbMIDI.sendNoteOn(BD, ReadVelocityBass, 1); usbMIDI.sendNoteOn(HH, ReadVelocityHH, 1); } if (note == 2) { usbMIDI.sendNoteOn(SD, ReadVelocitySnare, 1); usbMIDI.sendNoteOn(HH, ReadVelocityHH, 1); } if (note == 3) { usbMIDI.sendNoteOn(HH, ReadVelocityHH, 1); } if (note == 4) { usbMIDI.sendNoteOn(BD, ReadVelocityBass, 1); } } void presetEight(int note) { //thick, heavy rock drumbeat of Sound of Madness by Shinedown if (note == 1) { usbMIDI.sendNoteOn(OHH, ReadVelocityHH, 1); usbMIDI.sendNoteOn(BD, ReadVelocityBass, 1); } if (note == 2) { usbMIDI.sendNoteOn(BD, ReadVelocityBass, 1); usbMIDI.sendNoteOn(HH, ReadVelocityHH, 1); } if (note == 3) { usbMIDI.sendNoteOn(OHH, ReadVelocityHH, 1); usbMIDI.sendNoteOn(SD, ReadVelocitySnare, 1); } if (note == 4) { usbMIDI.sendNoteOn(OHH, ReadVelocityHH, 1); } } void presetNine(int note) { // hip hop or pop beat if (note == 1) { usbMIDI.sendNoteOn(HH, ReadVelocityHH, 1); usbMIDI.sendNoteOn(BD, ReadVelocityBass, 1); } if (note == 2) { usbMIDI.sendNoteOn(OHH, ReadVelocityHH, 1); } if (note == 3) { usbMIDI.sendNoteOn(HH, ReadVelocityHH, 1); usbMIDI.sendNoteOn(SD, ReadVelocitySnare, 1); } } void presetTen(int note) { // tumabu and hi hat pattern of afro-cuban drumming if (note == 1) { usbMIDI.sendNoteOn(OHH, ReadVelocityHH, 1); } if (note == 2) { usbMIDI.sendNoteOn(BD, ReadVelocityBass, 1); } if (note == 3) { usbMIDI.sendNoteOn(BD, ReadVelocityBass, 1); usbMIDI.sendNoteOn(OHH, ReadVelocityHH, 1); } } void presetEleven(int note) { // funk beat if (note == 1) { usbMIDI.sendNoteOn(HH, ReadVelocityHH, 1); usbMIDI.sendNoteOn(BD, ReadVelocityBass, 1); } if (note == 2) { usbMIDI.sendNoteOn(OHH, ReadVelocityHH, 1); } if (note == 3) { usbMIDI.sendNoteOn(HH, ReadVelocityHH, 1); usbMIDI.sendNoteOn(SD, ReadVelocitySnare, 1); } if (note == 4) { usbMIDI.sendNoteOn(HH, ReadVelocityHH, 1); } if (note == 5) { usbMIDI.sendNoteOn(SD, ReadVelocitySnare, 1); } if (note == 6) { usbMIDI.sendNoteOn(BD, ReadVelocityBass, 1); } } void TempoTap() { // tap tempo function if (datavalue == "S0111111111111111" && tempohit == "zero") { // datavalue == "S0111111111111111" means keep pressing button 1! tempohit = "zeroand"; StartTempo = millis(); // start the timer digitalWrite(LedPin, HIGH); // turn on the first LED } if (datavalue != "S0111111111111111" && tempohit == "zeroand") { // this is when the button is released tempohit = "one"; } if (datavalue == "S0111111111111111" && tempohit == "one") { // when the button is pressed again tempohit = "oneand"; TempoTimerTwo = millis() - StartTempo; // this is the difference between 1st button press and 2nd digitalWrite(LedPin2, HIGH); // turn on the second LED } if (datavalue != "S0111111111111111" && tempohit == "oneand") { // when the button is released tempohit = "two"; } if (datavalue == "S0111111111111111" && tempohit == "two") { tempohit = "twoand"; TempoTimerThree = millis() - StartTempo - TempoTimerTwo; // difference between 2nd button press and 3rd digitalWrite(LedPin3, HIGH); // turn on third LED } if (datavalue != "S0111111111111111" && tempohit == "twoand") { // etc tempohit = "three"; } if (datavalue == "S0111111111111111" && tempohit == "three") { tempohit = "threeand"; TempoTimerFour = millis() - StartTempo - TempoTimerThree - TempoTimerTwo; digitalWrite(LedPin4, HIGH); } if (datavalue != "S0111111111111111" && tempohit == "threeand") { tempohit = "zero"; // restart the entire process Metron = (TempoTimerTwo + TempoTimerThree + TempoTimerFour) / 3 ; // find average of the three digitalWrite(LedPin, LOW); // turn off all the LEDs digitalWrite(LedPin2, LOW); digitalWrite(LedPin3, LOW); digitalWrite(LedPin4, LOW); } } void readVelocities() { // these control the velocities for the presets ReadVelocitySnare = map(analogRead(3), 0, 1023, 1, 127); ReadVelocityBass = map(analogRead(4), 0, 1023, 1, 127); ReadVelocityRide = map(analogRead(5), 0, 1023, 1, 127); ReadVelocityPedal = map(analogRead(6), 0, 1023, 1, 127); ReadVelocityHH = map(analogRead(7), 0, 1023, 1, 127); } void countOff() { // turns on the LED for an 8th note, turns it off for an 8th note, and repeat 4x if (digitalRead(switch1) == HIGH) { digitalWrite(LedPin, HIGH); delay(Metron / 2); digitalWrite(LedPin, LOW); delay(Metron / 2); digitalWrite(LedPin, HIGH); delay(Metron / 2); digitalWrite(LedPin, LOW); delay(Metron / 2); digitalWrite(LedPin, HIGH); delay(Metron / 2); digitalWrite(LedPin, LOW); delay(Metron / 2); digitalWrite(LedPin, HIGH); delay(Metron / 2); digitalWrite(LedPin, LOW); delay(Metron / 2); } } void continuousControls() { // functionception where I put functions inside of a function to make things shorter ManualDrums(); readButtons(); readVelocities(); randomEighth(); randomTriplet(); randomSixteenth(); } void ManualDrums() { SDOne = analogRead(0); BDOne = analogRead(1); BDTwo = analogRead(2); if (BDOne > 500 && statushitBDOne == "off") { // if the button is pressed hard enough, it will turn on a trigger scaledVelocity = map(BDOne, 600, 900, 70, 127); // maps the velocity so the harder I hit, the louder it'll be constrain(scaledVelocity, 0, 127); // makes sure the velocity doesn't go above 127 usbMIDI.sendNoteOn(BD, scaledVelocity, 1); // sends the note with the scaledVelocity statushitBDOne = "on"; // turns on the trigger and won't play a note again until it's turned off } if (BDOne < 400 && statushitBDOne == "on") { // waits until my heel is released from the pressure sensor so it usbMIDI.sendNoteOff(BD, 0, 1); // it turns the note off statushitBDOne = "off"; // and resets the trigger } if (BDTwo > 600 && statushitBDTwo == "off") { // does the same exact thing, but this is for my right foot so it's callibrated differently scaledVelocity2 = map(BDTwo, 600, 900, 70, 127); constrain(scaledVelocity2, 0, 127); usbMIDI.sendNoteOn(BD, scaledVelocity2, 1); statushitBDTwo = "on"; } if (BDTwo < 450 && statushitBDTwo == "on") { usbMIDI.sendNoteOff(BD, 0, 1); statushitBDTwo = "off"; } if (SDOne > 500 && statushitSDOne == "off") { //same process except with the ball of my left foot scaledVelocity3 = map(SDOne, 600, 900, 70, 127); constrain(scaledVelocity3, 0, 127); usbMIDI.sendNoteOn(SD, scaledVelocity3, 1); statushitSDOne = "on"; } if (SDOne < 200 && statushitSDOne == "on") { usbMIDI.sendNoteOff(SD, 0, 1); statushitSDOne = "off"; } } void readButtons() { //continually reads the values coming from the keypad datavalue = "S"; for (int button = 0; button < 16; button++) { digitalWrite(clockPin, LOW); int databit = digitalRead(dataPin); digitalWrite(clockPin, HIGH); datavalue.concat(databit); } } void tempoMod() { // modifies the tempo if ((analogRead(8)) > 0) { // if the pot is above 0, it will take effect modMetro = map(analogRead(8), 1, 1023, Metron, 100); } } void suspendedCymbal() { number = analogRead(9); CymbVelocity = map(number, 0, 1000, 50, 124); // maps the velocity to the potentiometer value if (number == 0) { CymbVelocity = 0; } if (number == 0 && number < 250 && CymbStatus == "start") { // each if loop divides the potentiometer range into 4 chunks usbMIDI.sendNoteOn(Cymb, CymbVelocity, 1); //sends a note-on every 250 milliseconds, or a an 8th note at 120 bpm to emulate a roll delay(250); } if (number > 250 && number < 500 && CymbStatus == "start") { usbMIDI.sendNoteOn(Cymb, CymbVelocity, 1); // makes the roll strokes go faster with each chunk delay(125); } if (number > 500 && number < 750 && CymbStatus == "start") { usbMIDI.sendNoteOn(Cymb, CymbVelocity, 1); delay(62); } if (number > 750 && number < 1010 && CymbStatus == "start") { usbMIDI.sendNoteOn(Cymb, CymbVelocity, 1); delay(50); } if (number > 1010 && CymbStatus == "start") { // when the number reaches above 1010 (not 1023 to makeup for human/analog inaccuracies) CymbVelocity = 127; // it sends a crash at 127 velocity usbMIDI.sendNoteOn(Cymb, CymbVelocity, 1); CymbStatus = "end"; //and stops the notes from getting triggered } if (number == 0 && CymbStatus == "end") { // until the potentiometer is reset to 0 CymbVelocity = 0; CymbStatus = "start"; // then the entire process starts again usbMIDI.sendNoteOff(Cymb, 0, 1); } } void STOP() { if (datavalue == "S1111111111111110") { // if the keypad reads 16, it turns all drum statuses off DrumOneStatus = "off"; DrumTwoStatus = "off"; DrumThreeStatus = "off"; DrumFourStatus = "off"; DrumFiveStatus = "off"; DrumSixStatus = "off"; DrumSevenStatus = "off"; DrumEightStatus = "off"; DrumNineStatus = "off"; DrumTenStatus = "off"; DrumElevenStatus = "off"; } }
Here’s also a word document of this code with the correct spacing formatting that might be easier to copy:
12 thoughts on “The Shoe Drum Machine – Digital Electronics Lab”