Tangible Interactions - Midi Controller

Make a device to control playback of synthesized or pre-recorded music using MIDI. Your device will send MIDI messages to another device which will play the music.
For this assignment I first had to understand how MIDI works. I used some resources provided in class to get familiar with MIDI messsages and hexadecimal numbers to better understand the organization of the MIDI protocol (resources: 1, 2, 3).
I decided to go with something xylophone inspired. I wanted to have 6 bars - each triggering a message that would be interpreted and played in Sforzando.
Each bar would have a piezo (vibration-sensor) connected to it as the trigger. Aside from just playing a tone - I wanted to make a sustain button. When pressing this button and hitting a note - the note would continuously play allowing you to play notes on top of the sustained one.
I attached the piezo to one of my bars and tested how reliable it was. It worked pretty well. However, as the video demonstrates there was a problem with my code. I was using a delay-function to decide the length of the node. Because of this delay I would not be able to hit two notes simultaneously or back to back. Instead, I created a timer that would set the tone length.
When I had the timer working I needed to make sure that hitting a bar would only trigger a node once. For that to work I was using a debounce.
At this point I had my bars working - and attached an on/off switch and a light to indicate whether the instrument was on/off and a light to indicate when a bar was triggered.
I never made it to the point where I could have a sustained tone play in the background when holding down a button. My impression - when talking to other people in this class and people who use MIDI extensively - is that I spent too much time on the Arduino side of things. Instead of using Arduino code to determine the length of a node I should use the computer software to control it instead. Or I could have put a potentiometer into my controller to control the length of the node through the music software.
It seems I should have taken advantage of the software to a greater extent than I did. Using the Arduino to message noteOn/noteOff and the software to create a sustained tone, determine length of tone etc. These are valuable lessons for my next MIDI project.
This is what my project eventually looked like:
Outside:
Inside:
The code:
#include "MIDIUSB.h"
//functions for nodeOn and nodeOff
void noteOn(byte channel, byte pitch, byte velocity) {
midiEventPacket_t noteOn = {0x09, 0x90 | channel, pitch, velocity};
MidiUSB.sendMIDI(noteOn);
}
void noteOff(byte channel, byte pitch, byte velocity) {
midiEventPacket_t noteOff = {0x08, 0x80 | channel, pitch, velocity};
MidiUSB.sendMIDI(noteOff);
}
// these constants won't change:
const int ledPin = 9;
const int knockSensor5 = A5;
const int knockSensor4 = A4;
const int knockSensor3 = A3;
const int knockSensor2 = A2;
const int threshold = 100;
const int instrumentOn = 14;
// these variables will change:
int sensorReading1 = 0;
int sensorReading2 = 0;
int sensorReading3 = 0;
int sensorReading4 = 0;
int ledState = LOW;
int toggle = 5;
long interval = 100;
long previousMillis = 0;
void setup() {
pinMode(ledPin, OUTPUT);
pinMode(instrumentOn, OUTPUT);
pinMode(toggle, INPUT_PULLUP);
Serial.begin(115200);
}
// First parameter is the event type (0x0B = control change).
// Second parameter is the event type, combined with the channel.
// Third parameter is the control number number (0-119).
// Fourth parameter is the control value (0-127).
boolean once = false;
void loop() {
int power = digitalRead(toggle);
//power switch - if power is not on do nothing
if (power == LOW) {
digitalWrite(instrumentOn,LOW);
return;
}
//power on -> light on
digitalWrite(instrumentOn, HIGH);
//first bar hit detection + timer + debounce
sensorReading1 = analogRead(knockSensor5);
unsigned long currentMillis = millis();
if (once == false && sensorReading1 >= threshold) {
ledState = HIGH;
digitalWrite(ledPin, ledState);
Serial.println("Sending note on");
noteOn(0, 40, 64); // Channel 0, middle C, normal velocity
once = true;
previousMillis = currentMillis;
} else {
ledState = false;
digitalWrite(ledPin, ledState);
}
if (currentMillis - previousMillis > interval && once) {
Serial.println("stop");
Serial.println("Sending note off");
noteOff(0, 40, 64); // Channel 0, middle C, normal velocity
once = false;
}
//second bar
sensorReading2 = analogRead(knockSensor4);
if (once == false && sensorReading2 >= threshold) {
ledState = HIGH;
digitalWrite(ledPin, ledState);
Serial.println("Sending note on");
noteOn(0, 50, 64); // Channel 0, middle C, normal velocity
once = true;
previousMillis = currentMillis;
} else {
ledState = false;
digitalWrite(ledPin, ledState);
}
if (currentMillis - previousMillis > interval && once) {
Serial.println("stop");
Serial.println("Sending note off");
noteOff(0, 50, 64); // Channel 0, middle C, normal velocity
once = false;
}
//third bar
sensorReading3 = analogRead(knockSensor3);
if (once == false && sensorReading3 >= threshold) {
ledState = HIGH;
digitalWrite(ledPin, ledState);
Serial.println("Sending note on");
noteOn(0, 60, 64); // Channel 0, middle C, normal velocity
once = true;
previousMillis = currentMillis;
} else {
ledState = false;
digitalWrite(ledPin, ledState);
}
if (currentMillis - previousMillis > interval && once) {
Serial.println("stop");
Serial.println("Sending note off");
noteOff(0, 60, 64); // Channel 0, middle C, normal velocity
once = false;
}
//fourth bar
sensorReading4 = analogRead(knockSensor2);
if (once == false && sensorReading4 >= threshold) {
ledState = HIGH;
digitalWrite(ledPin, ledState);
Serial.println("Sending note on");
noteOn(0, 70, 64); // Channel 0, middle C, normal velocity
once = true;
previousMillis = currentMillis;
} else {
ledState = false;
digitalWrite(ledPin, ledState);
}
if (currentMillis - previousMillis > interval && once) {
Serial.println("stop");
Serial.println("Sending note off");
noteOff(0, 70, 64); // Channel 0, middle C, normal velocity
once = false;
}
}