DJ Pi 2: receiving serial input
October 4, 2017
In the previous post we looked at how to use an Arduino to read an analogue control and transmit it to a Raspberry Pi over serial. Now it’s time to look at how to handle those values on the Pi. We’ll also look briefly at how the C++ application uses the JUCE framework and its multi-threaded approach.
You can look at the complete C++ code here (MainComponent.cpp
will be of most interest). The structure of this code is roughly taken from the multi-threading example here.
Please be aware that the purpose of this project is partly for me to educate myself about C++ and JUCE, so you might see me do things a little oddly in the C++ code. I’ll keep checking over it as I learn to try and fix the most egregious errors, but don’t take any of the C++ code as an example of the ‘right’ way to do things. If you spot any errors please let me know.
JUCE
JUCE is a multi-platform C++ framework designed to make it easier to develop audio and GUI applications. Really I’m sort of wasting its capabiities by using it to just create a command-line application runing solely on the Raspberry Pi. But even so it provides a nice abstraction over the nitty-gritty of sending and receiving audio samples, allowing us to focus on the actual audio processing.
JUCE is based around the concept of components that communicate with each other by broadcasting messages. Tasks that need to be run very quickly, such as processing audio or updating the UI, run in high priority threads. Less urgent tasks, such as broadcasting messages, run in lower priority threads. What this means for our effects box is that we will have a thread responsible for checking for new control parameters and broadcasting them to the effects components as necessary. This leaves our effects components free to busily process their blocks of audio samples as fast as they can.
Code walkthrough
You’ll see two C++ components in the directory linked to above. The first one, Main.cpp
, is mostly boilerplate generated by JUCE’s project management tool, the Projucer. I think of this class as being a JUCE-controlled ‘frame’ within which my actual application sits.
In this case, my content all sits in MainContentComponent
. For now this is acceptable but as the project grows in complexity this component will itself become mostly a holder for the components (effects, etc) that will do the actual work. Following the multi-threading example linked to above, I subclass the MainContentComponent
in order to add a thread (I’ll discuss this further below).
Here’s the most interesting part of the code:
Serial on Pi
One of the benefits of using the Arduino is that it has built-in support for serial over USB. This allows us to communicate with (and power) the Arduino just by plugging in a USB cable. On the host device (the Pi, in this case), a new tty
(short for ‘teletype writer’) device will appear.
I’m not going to go into the details of how exactly the serial protocol works, but suffice to say that it transmits data one byte at a time, least significant bit first. You can see that I am using the helpful wiringPi library. This is a C library that provides a simple interface to the Pi’s various hardware interfaces, including GPIO and serial. If you want to know more about the serial protocol itself, have a look at the library’s source and this Sparkfun article.
With all that complexity neatly hidden away by the library, the task becomes quite simple. We simply open the tty
device created by the operating system, check if a value is ready to be read and pass it to the receiving method receivePotVal
. This method then updates the audio and UI with the new control parameter.
Handling threads
The main point of interest is where we create a MessageManagerLock
before calling receivePotVal
. By doing so we attempt to gain a lock on the message loop so that we can safely communicate with the rest of the application. The only thread-safe way of communicating with Component
classes is by using the message loop in this way.
Dangerous sub-classing
I said above I’d return to the threaded sub-class. Basically, despite it being in the JUCE example code, this approach feels pretty weird to me. If we assume that inheritance should be used when we can say that the child component ‘is an’ instance of the parent then it clearly doesn’t fit. Composition (the parent ‘has an’ instance of the child) seems more appropriate. A further limitation is that we are tightly coupled to the parent class. This will become an issue when we want the control parameters thread to communicate with multiple effects components.
Of course, the JUCE framework provides a more idiomatic way of handling such situations. We should have a class that polls for new serial values and broadcasts its change to registered listening classes (which will be our effects components). This will greatly reduces the coupling between the broadcaster and the listeners but at the cost of greater complexity.
Each component will be notified when the state of its corresponding control changes so that its internal state can update to match the external controls. No single component will know more than it has to. I’m envisaging that the core functionality of MainContentComponent
will become little more creating the serial poller and effects components, setting up the listeners and routing the audio through the effects components.
Before that excitement, however, we need to get our Arduino house in order by rethinking how it transmits its information.