Originally created as an interactive and fun instructional tool for teaching both programming and music, Sonic Pi is also a very powerful tool for creating sophisticated live-coding online performances as well as algorithmic compositions, all by writing code.
This tutorial was written by Chris Bush and Daria Tsaregorodtseva. It first appeared in The MagPi #61.
Sonic Pi is perfect for people who love to code and love to create music, even if they can’t play an instrument. Best of all, Sonic Pi is included in Raspbian, making it easy to get started with coding your own musical creations on your Raspberry Pi. Wouldn’t it be great if you could share those creations over the internet? In this project, we’ll show you how using live-coding.
Live-coding online: Sharing Sonic Pi compositions
For this project, we will assume that you are at least somewhat familiar with the Sonic Pi GUI and its coding language, and you have at least tried some simple code or played with the many examples included with Sonic Pi. If not, check out the Essentials book, Code Music with Sonic Pi, for some excellent beginners tutorials.
Our goal for this project is to give you a way to go beyond playing your Sonic Pi compositions for whoever happens to be in the room with you. We’ll show how you can share your Sonic Pi coding creations with your friends, family, and fans over the internet, so they can listen to your musical masterpieces virtually anywhere in the world.
To do that, we have created a couple of simple Python programs that can interface with Sonic Pi, and communicate with each other over the internet, using a lightweight IoT messaging protocol called MQTT.
MQTT is a publish/subscribe messaging transport protocol (in case you are wondering, MQTT originally stood for MQ Telemetry Transport, but is no longer officially an acronym). It was designed to be very lightweight, simple and easy to implement and is most commonly used for communication in machine-to-machine and Internet of Things applications.
The main aspect in the publish/subscribe pattern is the decoupling of publisher and receiver, where the publisher and subscriber don’t know about the existence of one another, don’t have to run at the same time, and are not halted during publishing or receiving. A third component, a broker, is known by both parties: this filters and distributes all incoming messages by subject/topic.
For this project, we have created two Python programs that use the Eclipse Paho MQTT Python client library to implement an MQTT publisher and subscriber. The programs use the public Eclipse IoT MQTT broker at iot.eclipse.org to exchange messages.
The publisher program, sp-mqtt-publisher.py, is used to read a file that contains Sonic Pi code, and publish it to the Eclipse IoT MQTT broker. The subscriber program, sp-mqtt-subscriber.py, is then used to retrieve the Sonic Pi code from the broker and send it to Sonic Pi for playback.
Getting started with live-coding online
To get started, first download the two included Python programs, sp-mqtt-publisher.py and sp-mqtt-subscriber.py, and save them on your Raspberry Pi.
Next, you will need to install the Python MQTT client library from the Eclipse Paho project. This is easy with the Python package manager, called pip, which is included with Raspbian Jessie. Open a Terminal window on your Raspberry Pi and enter the following command:
sudo pip install paho-mqtt
Finally, you will need a way to send Sonic Pi code directly to the Sonic Pi synth server, rather than typing it into the Sonic Pi GUI. Currently, there is no built-in capability to play your compositions on Sonic Pi from the command line – although that is apparently planned for Sonic Pi 3. Consequently, we are using sonic-pi-cli, written by Nick Johnstone. This provides a very simple command-line interface, written in Ruby, for interacting with Sonic Pi. Luckily, Ruby also comes as part of the Raspbian Jessie distribution, and sonic-pi-cli should work just fine with it. From the Terminal window, enter the following command:
sudo gem install sonic-pi-cli
This will install a Ruby script, called sonic_pi, in the /usr/local/bin directory on your Raspberry Pi, where it can easily be called from the Python code.
Try out live-coding
Once you have these components installed, it’s time to try everything out. First, let’s make sure that the sonic_pi Ruby program is working. Start by opening Sonic Pi. Once the Sonic Pi GUI is running, open a Terminal window and enter the following command:
sonicpi "sample :loopbreakbeat"
You should hear the breakbeat sample loop that is included with Sonic Pi.
With that working, now it’s time to try a simple live-coding performance over the internet, using the two Python programs provided. With Sonic Pi open, copy one of the code examples included with Sonic Pi into a buffer. Then, using the Save button, save the buffer to a file. In the screenshot, we have chosen the tron-bikes example because, well – who doesn't like the sound of Tron light cycles?
Once the buffer is saved to a file, run the sp-mqtt-publisher.py program from a Terminal window. You will need to include two command-line arguments when you run the program. The first specifies the MQTT topic name that you will publish to. Choose something that is simple and likely to be unique. The second argument is the name of the code file in which you saved the buffer from Sonic Pi. For example, you might enter the following command:
python sp-mqtt-publisher.py saulpimon tron-bike.txt
The publisher program will read the contents of the code file named on the command line, and publish it to the Eclipse IoT MQTT broker under the provided topic name. The publisher program runs an infinite loop, and every ten seconds will re-publish the code to the MQTT broker. With each iteration of the loop, it will check to see if the code file has been updated and, if so, it will re-read the file before re-publishing, so that the most recent version of the live-coding composition is published to the MQTT broker.
Now, of course, this isn’t much of a live-coding performance without listeners, right? If you have a couple of friends who want to help, you can have them install the required components as described in the ‘Getting started’ section, and run the Python subscriber program, sp-mqtt-subscriber.py, to retrieve the published code from the MQTT broker and play it using Sonic Pi on their own Raspberry Pi. Or, if you like, you can run the subscriber program yourself on the same Raspberry Pi that you used to publish the code. To run the subscriber program, first be sure you have installed the Paho MQTT client and the sonic_pi Ruby script as described above. Next, open Sonic Pi and run the Python subscriber program, being sure to specify the same topic name that was used with the publisher above as a command-line argument. For example, enter the following command:
python sp-mqtt-subscriber.py saulpimon
The subscriber program will connect to the Eclipse IoT MQTT broker and retrieve messages that are published to the topic named on the command line. When a message is received, the subscriber program then calls the sonicpi Ruby script, passing the retrieved message as data. The sonicpi Ruby script connects to Sonic Pi and sends it the code, which will then be played. Don’t worry when you don’t see the retrieved code in the Sonic Pi GUI on the Raspberry Pi on which you’re running the subscriber program. The sonic_pi Ruby script doesn’t interact with the Sonic Pi GUI at all. Instead, it connects directly to the Sonic Pi synth server to send the code.
You are now ready to start sharing your live-coding masterpieces over the internet. Simply make changes to the Sonic Pi code as you would with any other live-coding performance, and save the buffer to a file as described above. The simple Python programs and MQTT will handle the rest, for you and your faithful listeners.
import paho.mqtt.client as paho import time, os, sys topic = sys.argv filename = sys.argv host = "iot.eclipse.org" client = paho.Client() client.connect(host, 1883) timestamp1 = -10 while True: timestamp2 = os.path.getmtime(filename) if timestamp1 != timestamp2: with open(filename, 'r') as myfile: filedata=myfile.read() timestamp1 = timestamp2 client.publish(topic, filedata) time.sleep(10)
import paho.mqtt.client as paho import sys, subprocess def on_message(client, userdata, message): pipe = subprocess.Popen("sonic_pi", stdin=subprocess.PIPE, shell=True) pipe.communicate(input=message.payload) print(str(message.payload.decode("UTF-8"))) def on_disconnect(client, userdata, rc): subprocess.call("sonic_pi stop", shell=True) topic = sys.argv host = "iot.eclipse.org" client = paho.Client() client.connect(host, 1883) client.subscribe(topic, 0) client.on_message = on_message client.on_disconnect = on_disconnect print("Press Ctrl-C to stop...") try: client.loop_forever() except KeyboardInterrupt: client.loop_stop() client.disconnect()