In the first article I shared how I came to acquire the record player and some of how I wired the mechanical switches and installed new controls. New controls consist of five shiny gold potentiometers, a rotary encoder and two LCDs, all hooked up to an Arduino. The switches and knob positions are sent to the Pi, while the rotary encoder is used to interact with the LCD Menu.
Design Evolution
Since this is a real-time music player, I wanted to offload reading the controls from the Pi. The Arduino Mega handles that and sends the switch and knob states via USB to the Pi. It also drives the LCD screen and handles menu navigation and selection backend stuff. I bit-packed the switch states for extra efficiency which might never be needed, but seemed like the right thing to do in principle.
My first plan was to use SonicPi as the backend but I got terrible performance out of the Pi. I tried using a sound card, running headless, and everything else I could think of given my resources. But even with the best I could get it, I wasn’t confident it would keep up with my demands. Although I resisted using MIDI, I went with it in the end because of course I did. But another benefit is that I didn’t have to write my own music generating algorithm
Arduino
Starting out, I programmed the Arduino via USB, and using the Arduino IDE. But this soon became cumbersome once the Pi was installed. To update the Arduino, I had to unplug it from the Pi and into my laptop, then put it back… but then the Pi thought it was a new device which caused a whole bunch of other problems. But no worries, because the solution was pretty clever (said with utmost humility)!
Since I was already logged into the Pi, and the Pi was connected to the Arduino, I just used the Pi to program the Arduino. Without a GUI I could obviously not run the Arduino IDE on the Pi, but I could edit .ino files with vim, use make to cross-compile and upload to the Arduino. Luckily I was not the first person who wanted to do this and I found this Arduino Makefile that some awesome person decided to share with the Internet. There is something gratifying about writing Arduino code with vim and using make upload. And not only that, but all the source code fits nicely in the same git repo, which is cleaner to deal with.
I wrote my own Arduino sketch for reading and sending the control inputs, but another approach would be to run firmata on the Arduino and use pyfirmata for programming the Pi. I only discovered firmata after I wrote my own Arduino sketch for this project but I did use it later in a different project with good results. It’s a really easy way to expand your Pi’s IO. However, I also used the Arduino to drive the LCDs, so firmata wouldn’t have worked for me in this case anyway.
Rotary Encoder
The rotary encoder took a bit more work than the other controls. I had fun working on this and learned some things in the process. There are plenty of good tutorials for using rotary encoders with Arduinos, but this one from Instructables was closest to what I wanted to achieve. Low-latency control response is a requirement for obvious reasons and using the external interrupt on the Arduino is the way to get it with a rotary encoder.
One thing I had to do differently from the Instructable is to read from a different register address. The tutorial uses an Arduino Uno, but the Mega uses an ATMega 2560. The digital inputs are read in to a different register so this had to be accounted for. I used this Arduino pin mapping to figure out which one it was. Below is the snippet from the instructables code where the changes were needed. PINE is the register address on the Mega that corresponds to PIND on the UNO, which is what this code is for. I also had to change the bitmask.
uses an Arduino Uno but I am using a Mega. It is different for the Mega than the board used in that Instructable. I used this Arduino pin mapping to figure out which one it was. Below is the snippet from the instructables code where the changes were needed. PINE is the register address on the Mega that corresponds to PIND on the UNO, which is what this code is for. I also had to change the bitmask.
Before:
void PinB(){
reading = PIND & 0xC;
if (reading == B00001100 && bFlag)
After:
void PinB(){
reading = PINE & 0x30;
if (reading == B00010000 && bFlag)
Bit Banging Serial Communication
The Pi needs to know when the controls are being manipulated, but I don’t want to waste processor time or send unnecessary data. The Arduino reads in all of the control values each frame and if any of the controls have changed it packs the values into a series of bytes and sends them to the Pi via the USB cable. Each knob is one byte, there is one byte for the switches, and then there is also a “message byte,” that is set through interacting with the Arduino-driven LCD screen menus. The switches are each saved as a single 1 or 0 and packed into a single byte. This is almost never necessary anymore with modern computing equipment and software libraries, but in this case it seemed worthwhile to send one byte representing all the switches instead of eight bytes.
Raspberry Pi
I used Python for the main Pi application. The main program downloads a computer-generated MIDI file from Wolfram Tones. Then, during playback I can manipulate, add or delete the MIDI messages before sending them to the fluidsynth backend.
The main loop spawns three threads: a curses GUI, hardware monitor, and music player. I programmed the Pi with vim over ssh on my home WiFi, so it made sense to write a curses GUI for debugging and testing. The hardware monitor gets control input updates from the Arduino Mega, and the music player reads the MIDI messages from a specified file, manipulates them if desired, and sends them over a Unix socket to fluidsynth running in the background. After spawning these three threads, the main loop mixes the inputs from the hardware monitor and GUI. This lets me use the record player controls but easily override them if I want to.
Wolfram Tones is an interesting site that is worth checking out. If you play around with that site for even a few minutes it becomes apparent that a lot of what it generates is terrible. Setting the parameters is important. I decoded the URL so I can have the Pi make requests that are more likely to sound good or at least be fun to play around with. Using the LCD Menu, the user can download a new MIDI file or use one of the saved files.
At this point, real-time manipulation is limited to creating a drum roll and fading in and out individual instrument tracks. Eventually I will get back to it, and I plan to add alternate melodies and let the user adjust note density, etc. There is a lot of potential for creativity and when it’s more refined it will make a great conversation piece or party toy.