Arduino and MIDI out

Level: 
Tools: 

Arduino.

Note! We have received some concern about connecting "directly" from the Arduino to the MIDI in port of a HW synth. Although we have done this successfully with several different HW setups, note that LMP does not take any responsibility for any catastrophic results of this tutorial. In the next part of this tutorial, "Arduino and MIDI in", we will introduce the optocoupler.

In this tutorial we will learn to use the Arduino micro-controller as a tool to control musical instruments through MIDI. We will assume you have some basic understanding of what a program is, as well as functions and variables. If not:

  • A program is a collection of statements and functions that tells the computer (the Arduino) what to do.
  • A function is a way of grouping and naming a couple of lines of code so that you can reuse them. Arduino has two special functions that must be present in all projects. The first is called setup and is called once when the program starts. The seconds is called loop. The Arduino will run the loop function continously until it is powered off.
  • A variable is a box that can hold different values. It is given a name so that you can change and read the value.

This tutorial also assumes you have a computer running the Arduino IDE, as well as an Arduino connected with an USB cable. Instructions on how to install the Arduino IDE.

You must also have a GM-compatible synth (as most are, and if it is not, you will still be able to do most of the tutorial) and a MIDI cable. For this tutorial I have used an old (and small!) Yamaha QY70 MIDI workstation.

1. MIDI out

MIDI is a serial protocol for talking to synths. As the Arduino is already very capable of talking serially, making a MIDI out interface is very easy.

You will need:

  • a MIDI DIN contact
  • a 220 Ohm resistor

Both of these can be bought at your local electronics store or on Ebay.

You solder the DIN contact like this:

MIDI DIN contact.

and connect the parts like this:

Arduino connected to MIDI interface.

You can test the interface using this simple code which outputs a short sequence of notes:

Download midi_out.ino.

Make sure to put the source code in a folder named "midi_out".

Note

As you can see in the code

// note lengths
const int bpm = 128; // tempo, BPM (beats per minute), a value that makes t16 an integer
const int t1 = 1024 * (bpm/64); // 1 whole beat = 512
const int t2 = t1/2; // 256
const int t4 = t2/2; // 128
const int t8 = t4/2; // 64
const int t16 = t8/2; // 32
 

we are using a BPM value of 128. For our purposes (and for the code to work properly later on in the tutorial) it is a good idea to keep the BPM as a value that will divide nicely, so that the calculations of the following constants will not create any rounding errors.

Exercise

Try changing the note values and the note lengths. Can you make it play a melody?

2. Program control

If you test and change the code and re-upload new sketches (as programs for the Arduino are called), notes will probably be sounding as the program is interrupted between a NOTE ON and a NOTE OFF message.

To get rid of this, we will add a short loop in the setup function that sends the ALL NOTES OFF and ALL SOUNDS OFF on all 16 MIDI channels.

// all sounds off on all 16 channels (can be synth-specific)
for (int i = 0; i < 16; i++) {
    midiData2((0xB0 | i), 0x78, 0);
    delay(midiSendDelay);
    midiData1((0xB0 | i), 0x7B);
    delay(midiSendDelay);
}
 

Now we will introduce dynamic control of the MIDI commands. Let’s start by panning the note sequence. We will use  a variable to control the pan. In MIDI, the pan value can go from 0 (extreme left) to 127 (extreme right), so we will let the variable step through these values and send out a MIDI pan message for each value.

And why not dynamically control the pitch too? We use a variable for the MIDI note value and let it increase by 3 for each iteration.

 // pan
pan = pan + 5;
if (pan > 127)
{
  pan = 0;
}
midiData2((0xB0 | voiceMidiChannel), 10, pan);
 
note = note + 3;
if (note > 90)
{
  note = 24;
}
midiNote(midiNoteOn + voiceMidiChannel, note, midiVelocity);
delay(t8);
midiNote(midiNoteOff + voiceMidiChannel, note, midiVelocity);
 

Download midi_out_pan.ino and try it out.

Exercise

Introduce other controllers and step through them, for example MIDI cutoff and resonance.

3. User control

Now we can control our synth with a program. But how about some user interaction? For this part you will need:

  • 2 x momentary push buttons (buttons that don't "get stuck" in a position)
  • 2 x 10 kOhm resistors
  • wires for connections
  • a breadboard to make all the connections

Add the push buttons

Arduino push buttons.

and connect them

Arduino push button schematics.

The push buttons are read by the Arduino on two different pins (inputs).

First we have to tell the Arduino what pins we are using and that they are inputs. We do that in the setup function.

pinMode(pinDown, INPUT);
pinMode(pinUp, INPUT);
 

The two (constant) variables pinDown and pinUp are declared at the start of the program. The const word means that their value can't be changed.

We read the values in our loop function and play two different notes depending on which button is pressed.

 // if user presses 1st button play a short note 
if (digitalRead(pinDown) == HIGH)
{
  midiNote(midiNoteOn + voiceMidiChannel, 48, midiVelocity);
  delay(t8);
  midiNote(midiNoteOff + voiceMidiChannel, 48, midiVelocity);
}
 
// if user presses 2nd button play a short note 
if (digitalRead(pinUp) == HIGH)
{
  midiNote(midiNoteOn + voiceMidiChannel, 51, midiVelocity);
  delay(t8);
  midiNote(midiNoteOff + voiceMidiChannel, 51, midiVelocity);
}
 

Download midi_out_control.ino and try it out.

Exercise

Use a light sensitive resistor instead of the push buttons to make a simple theremin type controller. Use the map function to map the input to a proper range.

4. A simple MIDI sequencer

 

And now for the grand finale in this tutorial: A MIDI sequencer with a simple remix functionality! Although this might sound more fancy than it is, this code can be a framework for all kinds of multitimbral sequence based projects.

First we will add some hardware: the LEDS. They will show which sequence we are currently playing. Use the buttons from the previous step and add the LEDs (and resistors). So for this part you will also need:

  • 3 x LEDs (one yellow, one green and one red, one for each kind of sequence: Intro/Outro, Verse and Chorus)
  • 3 x 220 ohm resistors
  • some more wires

Arduino schematics.

The buttons are connected as in the previous step. They will allow the user to cycle through a predetermined list of sequences (stored in the song array).

We will now control several voices at once, letting each one play in sync with the others.

Each voice will have one or more sequences that you can compare to a block in a sequencer program.

Each sequence contains a list of notes to play.

Which sequence to play is decided by a state variable (sequenceIndex), so that we can move between the blocks just like a true software sequencer plays a song.

For that, we will do some object oriented programming (OOP). The idea is to create an object for every voice. An object is a  collection of functions and variables, neatly packaged. Once you have written the code for an object you can create an arbitrary number of the object, each with slightly different behaviour depending on how you create it.

This object will contain some important properties of the voice:

byte voiceId; // unique id
byte midiChannel; // MIDI channel
note notes[maxNotes]; // the notes in the currently playing sequence
int numberOfNotes; // number of notes in notes[]
int currentNote; // note currently playing - index into notes[]
unsigned long currentNoteStart; // time in ms when current note started playing
 

as well as functions (called methods in OOP programming) that play the sequences:

void noteFill();
void noteStart();
void noteStop();
void initSynthVoice(byte, int, int, int, int);
 

The notes are stored in an array of structs. What does this mean?

A struct is a collection of variables, in our case the MIDI note value, the length and the velocity.

An array is an ordered list of values, in our case a sequence (of notes).

We will initialize all the voices (objects) in the setup function.

In the loop function we will tell the objects to play the next note (if it is time to do so = if the previous note has finished) as well as check if the user has pressed any buttons. As the loop executes quite fast, we have to introduce some timing, so that it is only possible to press a button once every half second (500 ms).

 // let the user control which sequence should be played
if ((digitalRead(pinDown) == HIGH) && (currentTime - pushTime > 500))
{
  sequenceIndex--;
  pushTime = millis();
  if (sequenceIndex < 0) {
   sequenceIndex = 0;
  }
  lightLeds(sequenceIndex);
}
 
if ((digitalRead(pinUp) == HIGH) && (currentTime - pushTime > 500))
{
  sequenceIndex++;
  pushTime = millis();
  if (sequenceIndex > maxSequences) {
   sequenceIndex = maxSequences;
  }
  lightLeds(sequenceIndex);
}
 

Download midi_sequencer.ino and try it out by pressing the buttons so the sequencer moves through the different sequences. The LEDs will show which sequence is playing.

Exercises

  • Use other controllers for selecting sequences.
  • Implement sound/patch selection using banks for non-GM synths (for example XG or other standards)

Memory

As the Arduino Uno only has 2K memory, the length of sequences and the number of voices will be a problem. We solve this by keeping the sequences short (max 16 notes) and the number of voices low.

Other possible solutions that you can explore would be:

  • to use the Arduino Mega which has 8K RAM.
  • to get an SD card reader for the Arduino and keep the sequences on a card, and only read them into RAM when you need them.
  • to create the sequences not as static arrays, but as programmatically created note values.

By Staffan Melin

Comments

Hey, I got the following feedback when reposting a link to this article on Diaspora: "This extremly simplified schematic could be dangerous to both your synth gear and to a PC (or arduino). It lacks the most important thing — an opto-isolator. I've burned my Synth MIDI IN using something like this years ago. Please use a correct schematics with a 6N138 or something like that. Never use a MIDI without a separation of electrical levels! Don't be reckless, value of 6N138 is far lesser that value of your synths MIDI IN replacement." Figured it was worth passing on. You'll find the original comment here: https://joindiaspora.com/posts/5911484

Thanks for the heads up Harald! I have added a note about his in the beginning of the tutorial. If you search for "Arduino midi out" on the web you will find that this way of constructing a MIDI out interface to be kind of standard.

It's not just kind of standard, it is THE way to make midi out according to the Midi Association. I've been building midi circuit for years and NEVER used anything to isolate midi out. Only midi IN.

Don't worry about that. It's not extremely simplified but the way it is. What you always need to opto-isolate is MIDI IN. It's nonsense to isolate both. You won't burn anything. I've been using arduino for MIDI for a long time and my synths are still alive.

Hi Staffan, a really good example for midi in/out can be found here: http://www.thebox.myzen.co.uk/Hardware/MIDI_Shield.html Grumpy Mike has made also some other very impressive midi tools like the "Midi Foot Steps" http://www.thebox.myzen.co.uk/Hardware/MIDI_Footsteps.html

Thanks a lot for the links, Georg! I am planning a follow up tutorial on MIDI in this summer, and this will be very helpful. And of course the links are useful for any reader! :)

Today I've published a very simple method for making a touch sensitive MIDI device (keyboard) with 18-20 inputs. It's also possible to use it as a touchless input device. It's made of an Arduino UNO and simply paper-clips. Arduino sketch available from my website. https://blog.georgmill.de/2015/07/09/touchduino-touchless-midi-device/

Thanks Georg Mill! I've added it to our Arduino tool page: http://libremusicproduction.com/tools/arduino.

It's a pleasure for me ;-) Have changed some code and added some comments on the sources. Have fun.

Added some code to make the sketch more configurable and easier to understand.

Added a demo video on https://blog.georgmill.de/2015/07/09/touchduino-touchless-midi-device/ that shows this device in action with sooperlooper, hydrogen and ardour 4.1. Have fun.

Cool!

The opto suggestion is clueless. In a current loop it's the driver end, not the opto receiving end, that is most likely going to be damaged by static. Damage to the midi-in opto only happens when someone bypasses the current limiting resistor and puts 5 volts across the opto's LED. The interface you are using is also incorrect. There is supposed to be another 220 ohm resister in series with the driving device. That gives the output of the driver some isolation. If you want to be completely safe, wire in two 1N4148 / 1N914 diodes, one with its anode going to the output and cathode to +5, and the other with it's cathode going to the output and the anode to ground.