Tangible Interactions - Midi Controller

by Simon M. Jensen - February 21, 2019

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).

circles

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:

circles

Inside:

circles

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;
  }

}