Friday 11 January 2013

MIDI Controller on the RPi

Here's the story of my attempts to squeeze MIDI out of my new RPi. The How To's and Gotcha's concerning configuration, the hardware interface details and the full code examples make it something of a tutorial, which will allow others to build their own Raspberry-flavoured MIDI controller (should they be foolish enough to try)…


It started with my newly-MIDIfied Tube Screamer, which was all ready waiting for a controller. Of course I could continue to generate control signals in MIDI-OX, but I wanted to make something a little more specific to the Tube Screamer. Sat there on the bench was my new RPi waiting for a new application - so why not kill two birds with one stone? 

Unfortunately, the RPi can't generate MIDI straight out of the box, for a few good reasons;
  • the UART is doing something else by default - it's configured to serve up Linux Kernel messages
  • the UART doesn't offer the correct baud rate for MIDI serial comms
  • MIDI needs a hardware interface layer to implement the current loop
Fortunately, all these problem can be overcome, more-or-less simply…

In what follows, I'm referring to the standard, "Mini UART" accessible on the GPIO pins. There are dark whispers about a second, more flexible UART buried within the Broadcom device, but that's not for newbies like us. Also, there is the possibility of plugging in a USB - MIDI device to one of the USB sockets but that's not in the hack/knack spirit, so we're staying with the simple UART…

First job is to kill off the messages squirting out of the serial port by default. You need to edit the cmdline.txt file to remove (that's to say delete) the parameter:

console=ttyAMA0,115200 kgdboc=ttyAMA0,115200

Easy.

Next, you'll also want to disable the serial login ("getty") in /etc/inittab, by commenting out the line similar to this one:

T0:23:respawn:/sbin/getty -L ttyAMA0 115200 vt100

just by prefixing it with a hash.

After a reboot, you shouldn't have any more unwanted stuff squirting out of your Mini-UART Tx pin. There's any number of detailed tutorials explaining how to achieve the steps above, including this one .

Next, you need some software to allow you to interact with the UART - I was intending to program in Python, so I installed the pySerial module, which is available for download here as a tar.

Once you have pyserial installed, you can try to use the UART to send MIDI data - only you'll hit a problem…

The Mini-UART only supports a few baud rates and the MIDI rate of 31250 is not one of the available set! If you set the baud rate to 31250, it will send serial data happily - but at a higher baud rate (of 38400 - the nearest available "standard" value).

Fortunately, this is a mountain that has been climbed by others before us, so there's a trick that we can use…

We can fool the UART into sending serial data at the correct speed by changing its master clock. If we saw our intended 31250 baud data actually being output at 38400, that is an error ratio of 38400/31250 - it is sending 23% fast. So , if we reduce the master clock speed by the same ratio, everything should be fine. 

There's opportunity to edit the config.txt file by adding a line of form…

init_uart_clock = 3000000

This line is shown above in the default clock rate of 3 MHz - we can change it (by taking 3MHz * 31250/38400 = 2441406) to:

init_uart_clock = 2441406

I did it and - guess what - it did ABSOLUTELY NOTHING! The serial data was still pumping out at 38400.

It turns out that you also need to add another parameter to cmdline.txt, as taught by Dom in this important post. Dom, who's a moderator at RaspberryPi.org and obviously knows what he's doing, suggests

"Can you try adding :

bcm2708.uart_clock=3000000

to cmdline.txt"

I tried and - guess what - is still did ABSOLUTELY NOTHING! The serial data was STILL pumping out resolutely at 38400 baud.

I thought about it (for quite a long time, involving caffeine, alcohol etc) and eventually started to wonder if there was a clue lurking in the filename 'cmdline.txt' …

I wondered if the addition had to be part of ONE LINE, rather than just part of a file. Sure enough, it does - adding Dom's suggestion to the ONE LINE in cmdline.txt suddenly made everything work as intended - serial was being produced at 31250 baud.

I can hear all the Linux experts laughing at me. But, in truth, the joke is on them - this is a piece of legacy code from the days when Linux/Unix expected me to be a bearded, cardigan-wearing geek, typing lines on a serial console. OK - so I have a beard. OK - so I often wear a cardigan. Linux still includes some dumb, laughable, pre-historic features.

Now we've got the UART running, we need some code to generate MIDI. I'll show you three species of increasing complexity.

First, a simple command-line Python program …


This program prompts the user for a MIDI channel and a controller number, identifying a unique controller. It will pass a sequence of values to this controller (values are like function arguments for the controller), until you break the program with a "ctrl-c" input from the keyboard.

Here's a screenshot of my console when running it…


In the case above, I chose the MIDI channel 9 and the controller 12, which corresponds to the "Level" control of my Tube Screamer (I arbitrarily assigned these values in writing the PIC code for the Tube Screamer).

The Control Change Message format contains three bytes; the first has 0xB as its high nibble and the channel number-1 as its low nibble. 0xBn is the code for "Control Change" on channel n+1 and the channel can run from 1 to 16, so channel-1 (=n)  will fit into the lower nibble's four bits. The second byte is the controller number, which can run from 1 to 127, encrypted as the seven least significant bits - in my case this is 12 (0x0C). The final byte is the value selected. In the screenshot above, I entered two values; 20 and 127.

 Here's the signal that appears on the UART Tx pin (GPIO 8) for the first Control Change Message (value = d'20' = 0x14)…


You can see that MIDI message bytes have a "0" start bit and a '1' stop bit. Also, the data is transmitted LSB first, so 0xB8 (=b'10111000') appears reversed as b'00011101 (=0x1D) etc. You can also see that the signal is switching between 0 and 3.3 volts - we need some hardware…

MIDI is a serial protocol, much like RS232. It uses a current loop to transmit and receive data and we need to ensure that we are capable of sending the correct currents, without damage to the RPi. Luckily, it's easy - the current required is just a few milliAmps, having only to light an LED in the receiving device's opto-isolator - we all know that the GPIO pins of a RPi are well able to turn LEDs on and off! This implies that we just need a current-limiting resistor and an appropriate connector (which, for MIDI, is a 180 degree 5-pin DIN socket). However, we'll be a little more "proper" than that and transpose to the correct working voltages using a level converter to switch from the RPi's native 3.3 volt signals to the more common 5V.

In my case a 74HCT14 was staring at me from the "junk box", so I pressed it into service, just as others have done before me


You can see the realisation of this interface in the photo at the top of this post.

At this point, having confirmed correct operation of the command-line interface program above, I decided to try to build a GUI interface, so I installed 'tkinter' (other fine GUI programming toolkits for Python are [no doubt] available) and started to learn something about GUI programming in Python. It is easy!

Here's a little program that generates a GUI to send data to a single MIDI control in response to movement of a slider…

 
The comments in the program listing should be sufficient for you to figure out how it works - here's how it looks when it runs (you open the program from the terminal and the GUI window appears)…


This is all very well - but most MIDI devices have more than one control (why, even a simple device like the MIDI Tube Screamer has four!). This ought to be reflected in the controller - so we need to think about how to work with number of controls. Hang on a minute - that sounds like an opportunity to get involved in some "Object-Oriented Programming", which is just the sort of thing Python ought to be good at. Let's see if we can make an object-oriented controller…

 The Tube Screamer has three controls which take input argument 0:127. These "continuous" controls (which are called "Drive", "Tone" and "Level") correspond to the three potentiometers on the original device (and on Dolly the clone). I assigned control numbers 13,14 and 12 to these controls in my PIC code.

There's another control - associated with the footswitch - which switches the effect On or Off (actually, its on all the time - it just bypasses the effect for "off"). This "two-state" or "Boolean" control really only needs a single bit input - but the MIDI Control Change Message doesn't support this, so we still take the wasteful approach of sending a whole byte. Some controllers interpret 127 as on and 0 as off (or vice-versa). Heaven knows what they do if they receive anything other than 127 or 0!

I took the approach of interpreting anything greater than 63 as "On" and anything less than 63 as "Off" (in other words I just look at the highest bit of the seven bit "value"). These "continuous" and "Boolean" controllers sound rather different - but they can both be handled as instances of one class of MIDI controller...

Here's the resulting GUI, offering remote access to all the MIDI Tube Screamer's controls...

With the object-oriented structure it is easy to set up more instances of controllers - and GUI widgets to operate them, whether sliders or buttons. In fact, the greatest problem is arranging the widgets in the window!

Let me know if you construct any MIDI applications using these building blocks.

Today I ordered a DDS module hosting an AD 9850 chip - perhaps the next RPi application will be at RF rather than AF !

 ...-.- de m0xpd

5 comments:

  1. Just to confirm! That this has now got me midi playing total nonsense into an old Boss DR-330 synth module... Thanks for sharing the Knowledge de mOxpd!!!

    Fun times ahead for me, me thinks!

    www.soundcloud.com/virtualan

    :)

    ReplyDelete
  2. Thanks again M0XPD...

    https://www.youtube.com/watch?v=TfyDnGmUYyg&feature=youtu.be

    ReplyDelete
  3. Cool tutorial although:-
    I couldn't get it working for ages as I have a Rpi2 B

    Look here for solution.
    https://www.raspberrypi.org/forums/viewtopic.php?f=29&t=113753&start=125

    Don't use
    bcm2708.uart_clock=3000000 in cmdline.txt

    Do use:

    init_uart_clock=2441000
    init_uart_baud=38400
    dtparam=uart0_clkrate=3000000

    in config.txt

    Force baud rate in command line with:
    stty -F /dev/ttyAMA0 38400

    ReplyDelete
    Replies
    1. Sim, many thanks for this last killer command line trick; I've been pulling my hair out for days on this one ...

      Delete