A MIDI to RS232 adapter (electronic circuit)

Skip to main content (skip navigation menu)
Letterhead logo






A MIDI to RS232 adapter

 

The article contains two parts: a description of a circuit that translates the MIDI signal level to RS232 levels (and vice versa), and programming notes to send and receive MIDI packets. The first part, the circuit, is general purpose —although using this converter to connect a MIDI keyboard to the serial port of your PC may require a special RS232 card. The second part, though, is specific to the H0420 programmable MP3 player. In fact, the article was written specifically to connect this MP3 player to a MIDI chain.

An MP3 player in a MIDI chain? Why?

MIDI is an acronym for Musical Instruments Digital Interface. The standard describes the hardware interface and the protocols through which (music-producing) instruments communicate amongst each other, or with computer and other devices. The key elements for MIDI are performance and reliability in hostile environments.

The MIDI protocol carries commands and timings. It does not carry digitized audio, although a command may indicate what kind of instrument should sound on a particular channel. When you move a MIDI file from one PC or instrument set-up to another, it it is actually likely to sound entirely differently.

So MIDI is only loosely coupled to audio. As a communication norm, the MIDI standard is about performance and timing. The "instruments" that it links in a network are not necessarily musical instruments. Lighting colour organs and stage effects (e.g. confetti cannons) have also been linked into a MIDI network, in order to synchronize visual effects with music.

In conclusion, MIDI is a general purpose communication standard that focuses on music applications. The H0420 programmable MP3 player is a general purpose controller with a high-quality MP3 decoder & audio circuit on board. Adding this controller to a MIDI network allows you to synchronize special audio effects to other events in the music performance. In such a set-up, the H0420 receives and handles MIDI packets, but does not send them. In an alternative set-up, the H0420 monitors all kinds of other sensors that can be attached to it and sends MIDI packets to notify the entire MIDI network of these events. The H0420 can also both send and receive MIDI packets.

The circuit

The protocol is not unlike a typical RS232 protocol: one start bit (must be 0), eight data bits, one stop bit (must be 1), no parity and no hardware handshaking. The Baud rate is 31250 bps (±1%). In order to get a fair level of immunity against electrical interference (irradiation), MIDI uses a current loop signal line (5mA, usually at approximately 5V), rather than the standard voltage level signals. A logic 0 is defined as current flowing, and a logic 1 as no current. This counter-intuitive definition was chosen so that an unconnected input wire does not "see" a series of start bits —a start bit is logic 0.

MIDI uses two 5-pin DIN connectors (180°) for input and output, plus sometimes a third for an unprocessed pass-through from the input. Pins 4 and 5 carry the signal, with pin 4 being the positive voltage; pin 2 is connected to the shielding of the cable on the output connector, but it is left unconnected on the input connector; pins 1 and 3 are not used. To avoid ground loops, there is an opto-isolator at the input connector.

All capacitors are 1µF. The resistors in the signal lines (R1 and R2) are 220 ohm, together with the voltage drop of the opto-coupler (6N138), these regulate the current at approximately 5 mA. The value of R3 is not critical; 1k is fine.

The circuit assumes that it will be connected to a "DCE" device. When you wish to connect it to a "DTE" device, such as a PC, you should replace the male DB9 connector (K1) by a female one and swap the pins 2 and 3 on that connector.

The circuit requires an external power source. Some alternative designs for an RS232-to-MIDI converter that I have seen extract power from (unused) handshaking or modem-control lines on the RS-232. I chose an explicit 5V connector because the H0420 MP3 player provides a 5V power source and because I feel that using signal lines as a power source is more like a hack than a design.

The closest that most serial cards and/or drivers inside PCs can come to the MIDI Baud rate is 28800 or 38400 Baud —exceeding the 1% margin that MIDI defines. Therefore PCs will typically need either a special RS232 card or an interface with a memory buffer and a Baud rate converter (typically based in a micro-controller). As an aside, the USB-to-RS232 adapters that are based on the FT232R chips from FTDI support non-standard Baud rates, but those based on the Prolific chips do not. So if you use a USB-to-RS232 converter, choose one with an FTDI chip (see the references below).

The H0420 programmable MP3 player, on the other hand, has a very flexible RS232 interface, and it supports the MIDI Baud rate directly (it also supports any other Baud rate, up to at least 115200 bps).

Sending and receiving packets

MIDI commands have a variable size with a fixed structure. Every byte carries 7 bits of information. The highest bit of a byte is set for the first byte in a command and clear for any follower bytes. The start of a command can therefore be detected easily. The end of a command is not marked in a specific manner; to determine weather a you have received the last byte in a stream, you can use a set of heuristics:

The last two heuristics are easy to implement on the H0420 programmable MP3 player, because the function packetfilter() allows you to specify the format of an acceptable packet and a time-out for waiting for (more) follower bytes. The packet specification is in a string notation that looks like a regular expression. Since MIDI defines a leader byte as one with the highest bit set, the byte value for a leader byte will be in the range 128-255. Follower bytes are, hence, in the range 0-127. A specification for the packet format is then: a byte in the range 128-255 followed by zero or more bytes in the range 0-127.

In code (with all values in hexadecimal):

"[`80-`ff]{`00-`7f}"

Sets or ranges in square brackets match a single byte; sets or ranges in braces (curly brackets) match zero or more bytes. To indicate a byte value (rather than a character), you use the back-quote or grave accent notation, with the ` character and two hexadecimal digits. You can also use the alternative pawn notations for "control characters": `80 is equivalent to \128 (decimal) and \x80. However, the pawn syntaxes do not let you insert a zero byte in a string. The back-quote notation is therefore preferred.

MIDI works with channels, and a device often monitors only a single channel (plus a handful of commands that apply to all channels). A second parameter in the packetfilter() function allows you to specify a filter to let only those commands through that pass the filter. If you do not have such a filter, your device will see the MIDI commands of all devices in the MIDI network. Setting an appropriate filter lets your device see only the subset of commands that are intended for it; this simplifies the code that you have to write to interpret incoming MIDI data. It also increases overall performance, because the reject filter runs in optimized firmware code, which is bound to be faster than the interpreted script code.

Wrapping up the reception of MIDI data, there are only two things left to do: initialize the serial port and write the function that interprets the incoming packets. This latter function is called @receivepacket(). Below is a script that plays a track on the "note on" command and implements a "seek to position" for the "program change" command (the standard "program change" command takes two follower bytes, my modified version accepts up to four follower bytes).

main()
    {
    /* Open the serial port and set the Baud rate and configuration for MIDI */
    setserial 31250, 8, 1, 0, 0

    /* set up a filter, accept only commands 9 and C and only on channel 1 */
    packetfilter "[`80-`ff]{`00-`7f}", "[`90`c0]*", 100
    }

@receivepacket(const packet[], numbytes)
    {
    if (packet[0] == 0x90)            /* 0x9 = note on */
        {
        if (packet[1] == 0)
            stop
        else
            {
            new name[20 char]
            strformat name, _, true, "track%d.mp3", packet[1]
            play name
            }
        }
    else if (packet[0] == 0xc0)       /* 0xc = program change */
        {
        new position = 0
        if (numbytes > 1)
            position += packet[1]
        if (numbytes > 2)
            position += packet[2] << 8
        if (numbytes > 3)
            position += packet[2] << 16
        if (numbytes > 4)
            position += packet[2] << 24
        seekto position
        }
    }

The leader byte of a command has the channel encoded in the lowest four bits. This is important for the filter expression. If you wish to receive the commands 0x9 and 0xc on the fourth channel, the filter expression becomes "[`93`c3]*" (instead of "[`90`c0]*"). Note that MIDI numbers channels from 1 to 16, but these get encoded in the commands as the values 0 to 15.

Sending commands is quite simple: construct the packet and send the bytes individually with the function sendbyte(). For example, the function below sends a "note on" command on channel 1 with a key number and a velocity. To send a "note off", call this function with velocity zero (this is a special case in the MIDI protocol).

note_on(key, velocity)
    {
    sendbyte 0x90
    sendbyte key
    sendbyte velocity
    }

note_off(key)
    note_on key, 0

Other notes (loosely related to the MIDI-RS232 converter)

MIDI uses 16 channels. An instrument may be selected for any of these channels, but the MIDI standard does not say what instruments exist and what these sound like. Channel 1 may be programmed to sound like a piano or like a clarinet. This, of course, makes it impossible to create a MIDI file that sounds okay wherever you would play it. To alleviate this problem, an auxiliary standard, "General MIDI", describes 127 instruments with unique "patch numbers". This enables a MIDI file to select the appropriate patch numbers before sending the notes.

Sound cards often also provide a MIDI connector. On a sound card, however, the MIDI connector is often combined with a "game adapter" (for joysticks), using a single 15-pin sub-D connector. Pins 12 and 15 are MIDI transmit and MIDI receive respectively. These pins carry TTL logic signals, however, not the MIDI current loop. The cable that connects a sound card to a MIDI instrument contains electronics that converts between TTL logic and current loop.

Further reading & references

Programmable MP3-player for kiosk applications
Our product: a programmable MP3 player/controller, which provides a flexible RS232 port.
MIDI Protocol Guide
A description of the MIDI protocol, that focuses on the standard MIDI commands.
Professional MIDI Guide
A page from the same site as the above one, but this one focusing on the hardware design.
Fastcom 232/4-PCI-335, an RS232 adapter for non-standard baud rates
This four port RS232 adapter (PCI bus) supports non-standard baud rates up to 1.0 Mbps.
UC232R-10: Economy USB-RS232 Serial Converter Cable
The UC232R-10 is a low-cost USB-to-RS232 adapter that supports non-standard Baud rates.