In this part we’ll look at how to finally make a sound and how the MD_MIDIFile library supports this in software.
Making a Sound
Making a MIDI sound using the Arduino comes to a choice of
- Making your own musical instrument and actuating the right note based on the MIDI instructions, or
- Sending the MIDI message to a MIDI synthesiser to make the sound for you.
The key to both these options is that the MIDI note-on and note-off messages are mapped to specific digital or analog actuations in the real world. The first option is very specific to the project you are working on and is beyond the scope of this article.
The second, and probably easier, option is to ‘outsource’ the sound generation to another device. This can be an instrument like a MIDI keyboard, computer sound generation software or other specialised hardware. A good summary of the computer based options for processing MIDI messages can be found here.
Sending MIDI data into the real world
One of the simplest ways to interface to a MIDI instrument is to acquire or build a MIDI interface for the Arduino. A MIDI hardware interface is simply an opto-isolated byte-based serial interface configured at 31,250 baud (bps).
The standard MIDI connector is a 5 pin DIN, however other types of connections (like MIDI over USB) have become increasingly common as alternative interfaces serial, joystick, etc. have disappeared as standard features on personal computers.
Standard MIDI cables terminate in a 180 degree five-pin DIN connector with applications using only three of the five conductors – a ground wire and a balanced pair of conductors that carry a +5 volt signal. This connector configuration can only carry messages in one direction, so a second cable is necessary for two-way communication. These are usually labeled IN and OUT designating the messages going into, and coming out of, the device respectively.
Most devices do not copy messages from their IN to their OUT port. A third type of port, the THRU port, emits a copy of everything received at the input port, allowing data to be forwarded to another instrument in a daisy-chain arrangement. Not all devices contain THRU ports, and devices that lack the ability to generate MIDI data, such as effects units and sound modules, may only have IN ports.
Opto-isolators keep MIDI devices electrically separated from their connectors, which prevents the occurrence of ground loops and protects equipment from voltage spikes. There is no error detection capability in MIDI, so the maximum cable length is set at 15 meters (50 feet) in order to limit interference.
A MIDI communications circuit for use with the Arduino Serial interface is shown below. This can be built as a stand-alone interface or incorporated into an Arduino shield and is based on the device described in this article. Note the addition of a switch to isolate the MIDI circuit as pins 0 and 1 do double duty as the standard Arduino and MIDI serial ports.
My own version of this circuit is built as an Arduino shield and is shown below. In order to keep a stackable shield height, the DIN sockets are mounted on a daughter board and positioned over the end of the shield. This shield has worked well to interface to a MIDI synthesizer keyboard.
As an alternative MIDI synthesizer device, I also mounted this Sparkfun VS1053 breakout (unfortunately now discontinued, but Adafruit has a similar product) on a prototyping shield, configured to boot in special MIDI mode so that it reads MIDI data on a UART pin from its serial interface.
This breakout also has audio output to directly drive earphones or an amplifier. With this shield, I connected the VS1053 Serial Receive through a jumper block to a number of Arduino pins to allow a separation between Arduino and MIDI serial streams.
Bringing it all together with software – the MD_MIDIFile Library
Designing a library to play Standard MIDI Files (SMF) on an Arduino microcontroller requires a few design decisions.
The first is how to store the files. The MD_MIDIFile library uses SD cards to store SMF for processing, only reading a few bytes at a time for each MIDI track. The advantage is that SMF can be much larger than would fit in the MCU memory (basically unlimited in size). With the SD library buffering the data from the SD card, the read latency is insignificant compared to the time between processing MIDI events for each track.
The second design decision is how to output the MIDI stream. The MD_MIDIFile library passes the MIDI and SYSEX events to the calling program through callback functions. This allows the calling application to manage sending to a MIDI synthesizer through a serial interface or to another output device, such as a MIDI shield or a musical instrument implementation.
To simplify the management of SMF playback, the MIDI stream is controlled through library methods to start(), pause() and restart() playback. SMF may also be looped automatically play continuously.
Additionally, while time ticks are normally generated by the library during playback, this function can be taken over by the user program if different time signal or synchronization with external MIDI clock is required.