Controlling Sonic Pi with a Flotilla Hub

IMG_1450

Click here for a video of the project in operation (using the final version of the program)

Recently I received my Kickstarter reward of a Flotilla Treasure Chest. As this is fairly new there are not yet very many published projects to enable you to use the kit. I got the Rockpool code running fairly quickly to use the hub on both my Pi2 and with a firmware upgrade on my Mac, and although this works quite well, I wanted to see what else might be achievable with the hardware. There is a python interface published, but at present it only runs (easily) on the Pi2 and on the earlier version of firmware.

My eye was caught by an article by Ian Stephenson, who had one of the smaller kits with fewer modules, but had developed his Sniff language to communicate with the Flotilla Hub. Sniff is a high level language which is a bit like Scratch but in a textual rather than graphical format. He had already got this language working with a wide range of other environments like controlling the Pi GPIO pins, or an arduino or various other boards, and had added support for Flotilla to it.
With one or two questions speedily answered by him I was able to get this going on my Mac, initially, and later on my Pi2. I was also able to do some testing for him to add one or two further modules, and at the time of writing most are included. Each module has three small files which define its capabilities and gives an interface which can be used in the Sniff language.
Having played around with test files for the various modules I first wrote a little program to control the motors on the robot buggy chassis that Pimoroni supply, which controlled them via the Joystick module. (video here). However I then noticed that  Sniff could fairly easily initiate other programs via a command line system call. I resolved to see if I could set something up to control Sonic Pi (my favourite Pi program!) by utilising the sonic pi command line interface gem written by Nick Johnstone. After some playing around I got an initial program going (see the video here), and over the last couple of days I have refined this to add further features, utilising the Noisemaker chassis that Pimoroni supply which can hold 5 modules: The rainbow led display, the slider and dial input controls, the touch keypad and the motion detector display. In this project I make use of all of them except the motion detector.

First I thought it useful to discuss my experiences in installing Sniff which may be helpful to others. Sniff is downloaded from http://www.sniff.org.uk/p/downloads.html
The latest version currently is release24. This can be downloaded as a zip file onto Pi2 or Mac and unzipped. There is also a Windows version but I haven’t tried this (yet)

You will also need a c compiler. On a Mac this means you need to install Xcode, which you can do freely from the App store. Be warned though that it is a large installation.

On a Pi2 a c compiler is in the Jesse distribution, but I did find that you needed to install one further set of libraries using:

apt-get update
apt-get install libx11-dev

On my Mac, I first of all updated the firmware on the Flotilla hub, using the code in

git clone https://github.com/pimoroni/flotilla-pre


(Note you can revert to the earlier firmware easily)

plug in your dock
enter the flotilla-pre folder wherever you have downloaded it then do

cd macosx/dock-firmware
./update

It will be the same process if you do this on a Pi. There you can use the location.

cd raspi/dock-firmware
./update

In fact it is the same code that is loaded, so it doesn’t matter which you choose.
I altered the Sniff code to accommodate the increase in serial access speed from 9600 to 115200 by editing the file $SNIFFDIR/lib/XHosted/Xflotilla.c and replace every occurrence of 9600 with 115200 where $SNIFFDIR is the install directory for Sniff.
Secondly, I found that running the setup program using
./setup did NOT retain the environmental variables set. Instead I had to use
source setup I used this also when running Sniff setup on my Pi2.
Note that these environmental variables only exist for the duration of your terminal window. If you close it you have to redo this command when you start again in order to recreate them. You can test if they are set by typing
echo $SNIFFDIR
and
echo $ARDUINODEV
In my case these returned
/Users/rbn/Sniff24Release
and
cu.usbmodem1411 (on my mac)  
One other problem I had with the setup program on the Pi2 was that on the latest Jesse distribution the gpio program is installed. The Sniff setup program detects this, and thinks that you want to interface to the GPIO pins via Wiring Pi. It then requires you to run programs using sudo. To prevent this I used the following rather dirty solution. I renamed /usr/bin/gpio to something else eg /usr/bin/gpiomoved. I did this using

sudo mv /usr/bin/gpio /usr/bin/gpiomoved

You can move it back later, by renaming again.

In the Sniff directory structure you can navigate to
examples/Hosted/Flotilla/test
There you will see simple sniff files showing the use of each of the modules.
To try one out you first have to compile it using sniff. For example:
sniff dial.sniff
when the compile has finished run
./dial
to try it out, having of course plugged in your dock, and a rotary dial first! Rotate the dial and observe the output. Use ctrl+C to terminate.

The compile process is the same on Mac or Pi2, but obviously different compilers are invoked and the resulting binary files will be different for each platform, even though the source .sniff files are the same.

The first program I wrote to test whether I could talk to sonic pi is shown below

make system device
make command string
make flotilla device
make keypad flotillaTouch device
make key1 boolean
make key2 boolean
make key3 boolean
make key4 boolean
when start
.forever
..tell keypad to "update"
..if key1
...set command to "sonic_pi 'sample :loop_amen_full'"
...tell system to "run"
..if key2
...set command to"sonic_pi 'play [:c4,:e4,:g4,:c5],release: 4'"
...tell system to "run"
..if key3
...set command to "sonic_pi 'play_pattern_timed scale(:c4,:major),0.3'"
...tell system to "run"
..if key4
...set command to "sonic_pi stop"
...tell system to "run"
..wait 0.1 secs

Note one important point. There MUST be a blank line at the end of a Sniff program, or it will give an error when compiling.
(I’ve modified the first sonic pi command from the original to use a sample which is built in to Sonic Pi, rather than an external file of my own as was shown in the video).

The file is saved as PlaySP.sniff and then compiled using
sniff PlaySP.sniff
producing the executable file
Before you can run this you need to install the Sonic Pi cli gem.
Assuming you are using the system ruby which on OSX 10.11.3 is Ruby 2.0 you will have to install this as a system user using:

sudo gem install sonic-pi-cli

and supplying the administrative password when asked.
If you have rvm installed then you will be able to install it without using sudo.
I have tried both ways on different macs with success.
You should be using the latest version of Sonic-Pi 2.9 to ensure compatibility with the gem file. Some early versions of Sonic Pi require old versions of the gem file.
You can test the correct operation of the gem by starting sonic pi and then from a terminal typing

sonic_pi 'play 72'

This should play a note in sonic pi. Note the _ rather than a –

If you are using a Pi then the process for installing the gem is shown in one of my previous posts here in the section entitled Adding sonic-pi-cli gem

Assuming this works you can proceed to trying out the compiled sniff file.
Make sure you have the hub connected and a touch keypad connected to it.
Then type:

./PlaySP

If you press Key 1 it plays the loop_amen_full sample. You can truncate it by pressing Key 4 which issues a stop command. Key 2 plays a chord and Key 3 plays a scale.
Encouraged by the way this worked, I then developed the program, utilising the slider, rotary dial and rainbow LED array.

The second program I came up with is shown below

#program to control Sonic Pi from Flotilla written by Robin Newman, Feb 2016
#requires rainbow, touch keypad, slider and rotary dial modules
#to be connected to the flotilla hub
#also requires Sonic Pi to be running and sonic-pi-cli gem
#to be installed
#tested on Pi2 and Mac OSX
make system device
make command string
make flotilla device
make keypad flotillaTouch device
make key1 boolean
make key2 boolean
make key3 boolean
make key4 boolean
make slider flotillaSlider device
make position number
make base number
make dial flotillaDial device
make synths list of strings
make cursynth string
make index number
make rainbow flotillaRainbow device
make rainbowShade list of number
make rainbowColor list of number
make count number
make ledCount number
when start
.add ":tri" to synths
.add ":saw" to synths
.add ":tb303" to synths
.add ":fm" to synths
.add ":mod_tri" to synths
.set ledCount to 5
.repeat ledCount
..add 0 to rainbowColor
.
.forever
..tell dial to "update"
..set index to round(position *4) + 1
..set cursynth to item index of synths
..#say cursynth
..#say [index]
..
..set count to 1
..delete all of rainbowShade
..repeat ledCount
...if index = count
....add 10 to rainbowShade
...else
....add 0 to rainbowShade
...change count by 1
..tell rainbow to "update"
..
..tell slider to "update"
..set base to round(position *5)
..#say join"base: "[base]
..
..tell keypad to "update"
..if key1
...set command to join "sonic_pi 'use_synth " cursynth
...set command to join command "; play note(:c4)+"
...set command to join command [base]
...set command to join command ",sustain: 20'"
...tell system to "run"
...repeat until not key1
....tell keypad to "update"
....wait 0.05 secs
...set command to "sonic_pi stop"
...tell system to "run"
..
..if key2
...set command to join "sonic_pi 'use_synth " cursynth
...set command to join command "; play note(:e4)+"
...set command to join command [base]
...set command to join command ",sustain: 20'"
...tell system to "run"
...repeat until not key2
....tell keypad to "update"
....wait 0.05 secs
...set command to "sonic_pi stop"
...tell system to "run"
..
..if key3
...set command to join "sonic_pi 'use_synth " cursynth
...set command to join command "; play note(:g4)+"
...set command to join command [base]
...set command to join command ",sustain: 20'"
...tell system to "run"
...repeat until not key3
....tell keypad to "update"
....wait 0.02 secs
...set command to "sonic_pi stop"
...tell system to "run"
..
..if key4
...set command to join "sonic_pi 'use_synth " cursynth
...set command to join command "; play note(:c5)+"
...set command to join command [base]
...set command to join command ",sustain: 20'"
...tell system to "run"
...repeat until not key4
....tell keypad to "update"
....wait 0.02 secs
...set command to "sonic_pi stop"
...tell system to "run"
..wait 0.02 secs

The program starts by defining some flotilla module instances and setting up some variables which are used in the program. The system device is used to send the commands to sonic_pi cli. command is a string holding the command. keypad is set up as the touch keypad, with variables key1 to key4 holding the button states.
slider is set up using the variable position to hold the slider setting (range 0 to 1)
base is a number variable which will hold the number of semitones to be added to the programmed sonic pi note.
similarly dial is set up with position holding the rotary dial setting (range 0 to 1)
synths is a string list which holds the names of the five synths to be used.
index is a number variable which will hold the number of the synth to be chosen (1-5) and is set by the dial position.
rainbow is the instance of the rainbow led array
rainbowShade is a number list which will contain 5 numbers representing the brightness of each led.
rainbowColor is a number list which will hold numbers representing the colour displayed on each led. I set them all to 0 which is red.
count is a number variable  used within the loop which sets the rainbowShade values
ledCount is a number variable set to 5, the number of leds in the Rainbow array

First the five synth names are added to the synths string list
Then rainbowColor is set to contain 5 zeros in a repeat loop. Note that as in Python, Sniff uses indentation to signify the scope of loops. To make things clear it uses . to indicate the indentation rather than spaces.

A forever loop then commences.
The way the interface with the flotilla modules work is that everytime an update command is sent to the relevant instance the associated variable(s) is/are updated. So when tell dial to “update” is issued, the position variable is set to the current value of the rotary dial. If later tell slider to “update” is issued then position is set to the current value of the slider.
The variable names chosen are specified for each module, and can most easily be ascertained from the examples for each module in the test folder, or else by looking at the module header files in the lib/Xhosted folder. Thus for example dial.h contains

#ifndef XFLOTILLA_DIAL_H
#define XFLOTILLA_DIAL_H

struct XflotillaDial_struct {
uint8_t config;
};
void XflotillaDial_init(struct XflotillaDial_struct *instance);
void XflotillaDial_update(struct XflotillaDial_struct *instance);

extern float SN_position;
#endif

and the variable which returns the data is SN_position which matches the position variable we use.
The example dial.sniff file shows it in use:

make flotilla device
make dial flotillaDial device
make position number

when start
.forever
..tell dial to "update"
..say join "Dial:"[position]
..say ""
..wait 1 secs

Having updated the position variable, the value of index is set to round(position *4) + 1
This will give a number between 1 and 5 as the knob is rotated to different positions.
This is used to set cursynth to the index position within the synths string list, choosing the synth to use. Note that this is indexed from 1 not from 0. You can uncomment the two lines say [index] and say currsynth if you want, and recompile to see their values as the program runs. The [ ] around index , a number, let it be printed by converting it to a string output.
To turn on the associated led, first the list rainbowShade is emptied, then a loop is used to set all the values to 0 except for the one corresponding to the index value. This is set to 10, giving a lowish light level (range is 0 to 50) for the selected led. When the subsequent tell rainbow to “update” command is issued the selected led is illuminated red.
The next three lines grouped together, tell the slider to update and set the variable base to contain a number between 0 and 5 round(position * 5) determined by the slider position. (Note here that the position variable previously used the the dial is now used by the slider, since it is updated AFTER the dial)
Now we move on to consider what happens when a key is pressed. The code is identical for each of the four keys, apart from the fundamental note specified, these being :c4, :e4, :g4 and :c5 for each of the keys 1-4 respectively
First we tell the keypad to update.
if key1 is pressed then a variable command is built up by joining together four statements,
the final string being
“sonic_pi ‘use_synth ” currsynth “; play note(:c4)+” base “,sustain: 20′”
here currsynth and base are replaced by the values giving for example
“sonic_pi ‘use_synth :tri; play note(:c4)+2,sustain: 20′”
This string is sent to the command line by the statement
tell system “run” 
and is equivalent to typing sonic_pi ‘use_synth :tri; play note(:c4)+2,sustain: 20’
in a terminal. This invokes the sonic pi CLI and executes the section in single quotes in Sonic Pi. :c4 is the midi note 60 so this plays 62 using the :tri synth and sustaining it for 20 seconds. However, having sent this command the sniff program waits until the key is released, polling every 0.05 seconds, and when it detects this sends the command stop to the sonic_pi cli, effectively stopping the note from sounding. In the same way the other three keys are each polled in turn,enabling monophonic sounds to be played by sonic pi as determined by the key, slider and dial settings.
A final loop wait of 0.02 secs completes the forever loop which thus cycles round polling for changes to the various controls.

I have tested the program with sniff and the flotilla module and Sonic Pi 2.9 on both a Pi2 and a mac running OSX 10.11.3, although it should work with some earlier versions of MacOSX as well.

Version 2. Things get better!

Having played around with the first version of the program, I developed it further in the following ways.
First the program is inefficient in that the same code almost exactly is employed in each of the four key press handling sections of the code.
Secondly, it turned out that it was fairly tricky to set the slider position accurately when selecting an offset number of semitones to be added to the basic note, and I wanted to make this easier.
Finally the choice of variable name “base” was perhaps a bit misleading, and perhaps a better name for this would be noteOffset.

Scratch, and hence Sniff employs a broadcast mechanism, whereby one section of running code can send a signal to start a different section of code running in parallel. We can employ this to effectively create a subroutine which can be called whenever a keypress is detected. We can also separate out the code which chooses a particular led to be lit on the rainbow array, and then utilise this both in indicating the dial selection of a synth, as in the first version of the program, but also to indicate the position of the slider and give a more accurate indication of the noteOffset to be employed.
The revised program shown below solves all the three points mentioned above. I will not describe it in detail, but I have added quite a few comments (signified by #), and at various places in the program there are commented say commands which will print out various values on the terminal screen if they are uncommented (by removing the preceding # sign) and recompiling the program. This can be useful for debugging purposes and for understanding how the program is operating.

#program to control Sonic Pi from Flotilla written by Robin Newman, Feb 2016
#requires rainbow, touch keypad, slider and rotary dial modules
#to be connected to the flotilla hub
#also requires Sonic Pi to be running and sonic-pi-cli gem
#to be installed
#tested on Pi2 and Mac OSX
#version 2 uses broadcast to reduce program size
#also allows dual use of teh leds to indicate both slider AND dial selections
make system device
make command string
make flotilla device
make keypad flotillaTouch device 
make key1 boolean
make key2 boolean
make key3 boolean
make key4 boolean
make slider flotillaSlider device
make position number
make noteOffset number
make dial flotillaDial device
make synths list of strings
make currSynth string
make lastSynth string
make index number
make rainbow flotillaRainbow device
make rainbowShade list of number
make rainbowColor list of number
make count number
make ledCount number
make noteVal number
make ledChoice number


when start
.#set up list of synths
.add ":tri" to synths
.add ":saw" to synths
.add ":tb303" to synths
.add ":fm" to synths
.add ":mod_tri" to synths
.set lastSynth to ":tri" #used to check if the current synth has changed
.set ledCount to 5
.repeat ledCount
..add 0 to rainbowColor #initialise the led colours to use (red)
.
.forever #start main loop
.. #select the synth to use
..tell dial to "update"
..set index to round(position *4) + 1 #gives range 1 to 5
..set currSynth to item index of synths #get the synth name
..#say currSynth
..#say [index]
..if not currSynth = lastSynth #check if it has changed
...set lastSynth to currSynth #is so update lastSynth
...set ledChoice to index #set choice for led to turn on
...broadcast ledOn #turn it on (it will flash for 0.3 seconds)
...wait 0.3 secs
..
..#now check slider position
..tell slider to "update"
..set noteOffset to round(position *5) #set noteOffset 0..5 according to slider position
..#say join"noteOffset: "[noteOffset]
..#set ledChoice to noteOffset and display on rainbow
..set ledChoice to noteOffset
..broadcast ledOn
..#now poll keypad, set noteVal according to key depressed and broadcast keyDown
..#wait for completion of any keyDown even before trying another one.
..tell keypad to "update"
..# if a keypress is detected process the relevant note
..if key1 
...set noteVal to 60 + noteOffset # 60 is note :c4
...broadcast keyDown and wait #broadcast to the routine processing keyDown
..
..if key2
...set noteVal to 64 + noteOffset # 64 is note :e4
...broadcast keyDown and wait
..
..if key3
...set noteVal to 67 + noteOffset # 67 is note :g4
...broadcast keyDown and wait
..
..if key4
...set noteVal to 72 + noteOffset # 72 is note :c5
...broadcast keyDown and wait
..
..wait 0.005 secs #small gap so loop is not solidly continuous


when keyDown #handle key pressed event
.#say "keydown"
.set command to join "sonic_pi 'use_synth " currSynth # build up command string
.set command to join command "; play "
.set command to join command [noteVal]
.set command to join command ",sustain: 20'"
.# typical command string will be "sonic_pi 'use synth :tri, play 64+2,sustain: 20'"
.#say command
.tell system to "run" #send the command to sonic_cli to play the next note
.repeat until not key1 and not key2 and not key3 and not key4 #wait till none pressed.
..tell keypad to "update"
..wait 0.005 secs
.#say "keysUp"
.set command to "sonic_pi stop" #set up command to stop
.tell system to "run" #send stop to the sonic_cli


when ledOn #handles displaying the selected led
.#say [ledChoice]
.set count to 1
.delete all of rainbowShade #empty out previous values
.repeat ledCount #now insert the new value in this loop
..if ledChoice = count
...add 10 to rainbowShade
..else
...add 0 to rainbowShade
..change count by 1
.tell rainbow to "update" #update rainbow with the new ledChoice

I hope that this article gives an insight into what can be achieved with Sniff and Flotilla and encourages you to have a go, I have also written a simple buggy control program which drives the Flotilla buggy around controlled by the joystick module. There is also Ian Stephenson’s version of the game Simon, which utilises the Rainbow led array, which is included in the Sniff24 distribution for you to try.
Finally just to give my thanks to Ian Stephenson for Sniff, and for his encouragement and help in getting me going in using it.

Code for the three Sonic Pi control programs can be downloaded from my gist site at https://gist.github.com/rbnpi/248bef1fb462c94ae141

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s