Making MIDI Music 

in Processing


Ben Centra

@TheBenCentra

blcentra@gmail.com

bencentra.com


BarCamp Rochester - Spring 2014

Why?


  • "New Media Team Project," Senior Capstone

  • "EEGJ," Interactive audio/visual experience

  • DJ with your MIND (and other biosensors)

  • Still a work in progress


Why Processing?


  • Plenty of contributed libraries

  • Easy to learn, easy to use


Why MIDI?


  • Focus on the music, worry about sound later

  • Swap out instruments easily in software

  • Plenty of DAW options (i.e. Ableton Live)

  • There's a library for that: TheMidiBus

MIDI Messages


  • Up to 4 bytes, usually: status, channel, and 2 data bytes

  • Example: Note On/Off
    • Status = 128/144 + channel (0 - 15)
    • Channel = N/A
    • Data1 = pitch (0-127)
    • Data2 = velocity (0-127)

  • Example: Channel Volume
    • Status = 176 (any "channel message" status)
    • Channel = channel (0-15)
    • Data1 = 7
    • Data2 = volume (0-127)

Using TheMidiBus


 import themidibus.* 
 MidiBus mb;

 void setup() {
   // See available MIDI ins/outs
   MidiBus.list();
   // MidiBus(PApplet sketch, int input, int output)
   mb = new MidiBus(this, -1, 1);
 }

 void draw() {
   // Play a note on channel 0 of pitch 60 (C) and velocity 80
   mb.sendNoteOn(0, 60, 127);
   delay(500);
   // Stop the note
   mb.sendNoteOff(0, 60, 127);
   delay(500);
 }

What I Need


  • Notes: A single tone plays for some duration

  • Chords: Multiple notes play simultaneously

  • Melody: Multiple notes play in sequence

  • Need to manipulate Notes, Chords, and Melodies
    programmatically, in real time, as MIDI messages

RiriFramework


  • Processing library (almost) for making MIDI music

  • Several types of objects:
    • RiriNote - single note
    • RiriChord - simultaneous notes
    • RiriSequence - sequential notes
    • RiriMessage - general MIDI message

  • Uses an instance of TheMidiBus for sending MIDI messages

  • Code: https://github.com/codenameriri/riri-framework

RiriObject


  • Everything extends RiriObject, contains common properties
    • Duration - length of the note/chord
    • Repeats - how many times to play the note/chord
    • Infinite - repeat forever
    • Playing - currently playing

  • All RiriObjects are Threads
    • Used for timing and duration
    • Needs tweaking, but it works well enough for now

RiriNote


  • Has a channel, pitch, and velocity

 public void run() {
   while (running && counter < repeats) {
      // Send a noteOn message
      noteOn();
      // Sleep for the note's duration
      try {
         sleep((long) duration);
      } catch (Exception e) {
         println("Problem sleeping thread...");
         println(e.getMessage());
      }
      // Send a note off event and quit executing
      noteOff();
      if (!infinite) {
        counter++;
      }
   }
   running = false;
   counter = 0;}

RiriChord


  • Has an ArrayList of RiriNotes, to be played simultaneously
  • Duration set to that of the chord's longest RiriNote 
public void run() {
   while (running && counter < repeats) {
      // Play the notes in the chord
      for (int i = 0; i < notes.size(); i++) {
         notes.get(i).start();
      }
      // Sleep for the chord's duration
      try {
         sleep((long) duration);
      } catch (Exception e) {
         println("Problem sleeping thread...");
         println(e.getMessage());
      }
      // Stop the notes in the chord
      counter++;
   }
   running = false;
   counter = 0;
}

RiriSequence


  • Has an ArrayList of RiriObjects (notes or chords), played in order

 public void run() {
    // Loop through each note in the sequence
    while(running && counter < repeats) {
      int c = 0;
      while (c < notes.size()) {
        // Grab and play the current note in the sequence
        RiriObject currentNote = notes.get(c).clone();
        int wait = currentNote.duration() * currentNote.repeats();
        currentNote.start();
        // Sleep for the duration of the note
        try {
          //sleep((long) currentNote.duration());
          sleep((long) wait);
        } catch (Exception e) {
          println("Problem sleeping thread...");
          println(e.getMessage());
        }
        c++;
      }
      if (!infinite) {
        counter++;
      }
    }
    // If we're out of notes, stop executing
    running = false;
    counter = 0;
    println("Done");
  }

Now What?


  • Apply some rules to make it sound more "musical"

  • Add structure for performing "songs"

  • Use sensor data to determine what  sounds to play

Music Theory


  • Keep things in time, maintain the rhythm
    • Helper function: beatsToMils(1);

  • Use scales that sound pleasant
    • Ex) Pentatonic scale: I, II, III, V, VI

  • Use common chord progressions
    • Ex) Basic Song: I - IV - V - I

  • Don't forget dynamics!
    • Adjust the velocity of notes in a natural way

Song Structure


  • Determine the time signature
    • Beats per measure = 4

  • Determine the length of each section
    • Measures per "phase" = 8

  • Determine the number of sections per song
    • Phases = 4

  • Adjust chords and scales based on position within the song

Data => Music


  • Average data from 8 brain waves, map values
    • -100 = Max "Relax"
    • 0 = "Neutral"
    • 100 = Max "Focus"

  • Store last X values, use average to set global level

  • Use absolute value of level (0 - 100) to determine "grain"
    • Higher grain = more musical activity

  • Other sensors:
    • Pulse sensor = Tempo of the song
    • Pressure sensors = Musical flourishes, Live effects

Brain Data!



Sample 1



Sample 2


 

Going Forward


  • Officially package RiriFramework as a Processing Library

  • Finish EEGJ for Imagine RIT in 2 weeks


Thanks for Coming!





Ben Centra

@TheBenCentra

blcentra@gmail.com

bencentra.com

Making MIDI Music with Processing

By Ben Centra

Making MIDI Music with Processing

An overview of my "RiriFramework" for creating MIDI music in Processing. Maybe some details about EEGJ, using Arduino-powered sensors to get our data, etc.

  • 2,621