Friday, January 18, 2013

Breath and Note On/Off Transitions

If we're going to build a MIDI wind instrument controller, it's going to need to send MIDI note on and off messages - it's just part of the MIDI spec. But the tricky part is knowing when your breath is producing enough pressure to justify sending the MIDI note on message.

An approach that makes sense for our project is to choose some value a little above the "idle" value that the pressure sensor reads (that is, when you're not blowing into it). My sensor produces values of about 65 when I'm not blowing into it, and a threshold value of 100 seems to work well. In the Arduino sketches in this post, you may need to change the threshold value to something appropriate for the way your air tubing is set up, as well as your playing style. To adjust that threshold, look for the line:

#define NOTE_ON_THRESHOLD 100

And experiment with different values until you find something that works. Don't forget to recompile and upload the sketch after you make changes.

One Note


The following sketch reads the pressure sensor and sends a MIDI Note On event for middle C (the C about in the middle of the piano keyboard) when it detects that the pressure is above the threshold value. It sends a MIDI Note Off event when the pressure is below the threshold value. It also keeps track of whether a note is sounding or not, so it knows if a Note On/Off message needs to be sent (if the note is already on, we don't need, or want, to send another Note On message).

#define MIDI_CHANNEL 1
// For this sketch, we only can play one note
#define MIDI_NOTE 60 // Middle C (C4)
// The threshold level for sending a note on event. If the
// sensor is producing a level above this, we are in note on
// state, otherwise note off
#define NOTE_ON_THRESHOLD 100

// We keep track of whether a note is sounding or not,
// so we know whether to send a note on or off event.
boolean noteSounding = false;
// The value read from the sensor
int sensorValue;

void setup() {
  // Nothing to initialize for this sketch
}

void loop() {
  // read the input on analog pin 0
  sensorValue = analogRead(A0);
  // Send the appropriate MIDI note on or off message
  if (sensorValue > NOTE_ON_THRESHOLD) {
    if (noteSounding) {
      // Nothing to do - note is already on
    } else {
      // Value has risen above threshold - turn the note on
      usbMIDI.sendNoteOn(MIDI_NOTE, 100, MIDI_CHANNEL);
      noteSounding = true;
    }
  } else {
    if (noteSounding) {
      // Value has fallen below threshold - turn the note off
      usbMIDI.sendNoteOff(MIDI_NOTE, 100, MIDI_CHANNEL);
      noteSounding = false;
    } else {
      // Nothing to do - note is already off
    }
  }
  // Delay a bit to avoid glitches (very short notes that
  // sound because the breath falls below the threshold
  // value, then goes back above it). Adding 20
  // milleseconds of delay reduces the responsiveness
  // of the instrument, and we'll discuss better ways
  // of handling this in later posts. For now, this is
  // good enough.
  delay(20);
}

Here's how the sketch sounds when playing the Grand Piano patch from GarageBand.

Four long notes:

A synth pad, long notes:

And to test the responsiveness, here I'm playing faster and faster notes (using a technique called double-tonguing and then flutter-tonguing). It keeps up pretty well:


Ok, great. We can send MIDI Note On/Off messages. But this is pretty boring, only playing one note. We'll be talking about how to select notes based on fingerings in later posts, but let's at least make our sketch choose a couple of different notes at random.

Here's a modification to the previous sketch. Instead of always sending a middle C for the note, whenever a Note On event is to be sent, it randomly chooses one of 5 notes from a pentatonic scale by using the random() method from the Arduino library. Look at the get_note() method - that's where we pick the random note to play. The Arduino library's random() method picks a number from 0 through 4 for us, and we use that to select one of the MIDI notes 60 (middle C), 62 (D), 65 (F), 67 (G), or 69 (A).

#define MIDI_CHANNEL 1
// The threshold level for sending a note on event. If the
// sensor is producing a level above this, we are in note on
// state, otherwise note off
#define NOTE_ON_THRESHOLD 80  

unsigned int notes[5] = {60, 62, 65, 67, 69};

// We keep track of which note is sounding. The value
// -1 means no note is sounding.
int noteSounding = -1;
// The value read from the sensor
int sensorValue;

void setup() {
  // Nothing to initialize for this sketch
}

int get_note() {
   return notes[random(0,4)];
}

void loop() {
  // read the input on analog pin 0
  sensorValue = analogRead(A0);
  // Send the appropriate MIDI note on or off message
  if (sensorValue > NOTE_ON_THRESHOLD) {
    if (noteSounding != -1) {
      // Nothing to do - note is already on
    } else {
      // Value has risen above threshold - turn the note on
      noteSounding = get_note();
      usbMIDI.sendNoteOn(noteSounding, 100, MIDI_CHANNEL);
    }
  } else {
    if (noteSounding != -1) {
      // Value has fallen below threshold - turn the note off
      usbMIDI.sendNoteOff(noteSounding, 100, MIDI_CHANNEL);
      noteSounding = -1;
    } else {
      // Nothing to do - note is already off
    }
  }
  // Delay a bit to avoid glitches (very short notes that
  // sound because the breath falls below the threshold
  // value, then goes back above it). Adding 20
  // milleseconds of delay reduces the responsiveness
  // of the instrument, and we'll discuss better ways
  // of handling this in later posts. For now, this is
  // good enough.
  delay(20);
}


Here's how it sounds. I'm playing a syncopated rhythm, using the GarageBand Grand Piano patch. Notice how the note pitches change randomly:

Here I'm playing the new sketch and using the "Kotu Chords" patch from GarageBand's Synth Textures collection, but playing the same rhythm. This patch harmonizes the input notes, so even though the get_note() method only produces one randomly selected note, once I feed that note into the synthesizer, I get about 4 notes out.


A cool aspect of this sketch is that, since we're using the random() method from the Arduino library to choose which note to play, two performances of the same piece on this instrument are likely to be very different. And by combining our pretty simple breath based controller with some interesting patches from GarageBand, we're able to make some interesting sounds. Try playing around with different patches in GarageBand, or if you're using a different audio environment and/or OS, experiment with what you have. Upload your creations and share links to them here.

Next

While these sketches can produce some interesting sounds, they are pretty much devoid of expression. All the notes are exactly the same volume, and we have no control over how the note evolves over time. In my next post, I'll talk about how to create an instrument that is much more responsive to the nuances of breath control.

2 comments:

  1. wow, i just integrated this into my midi-bass setup and it works VERY well, thanks for the idea with the mpx sensor, i was before experimenting with a bosch180, but the latency is way higher. i wrote my own code for it, but it turned out quite similar to yours. i omitted the 20ms delay and i cannot really make it glitch, it reacts just as it should. where did you get those glitches? starting of new notes or in between notes? or during a note? cheers

    ReplyDelete
    Replies
    1. Hi lokki, glad the freescale sensor worked out for you. If you aren't seeing any glitching without the delay, then great, leave it that way. If I recall correctly, my problem was that when the pressure fell below the note-on threshold value, the sound would stop abruptly and turn back on. That may have been due to the particular patch I was using and how the patch reacted to the continuous controller input. So, if you're not seeing that problem, great!

      Delete