Boot to Sonic Pi from a USB stick (no SD card) on Pi3

Recently I ran up an image on a USB stick which can be used to boot a Pi3 (NOT Pi2 or earlier) straight into Sonic Pi 2.11, without needing an SD card. This is based on excellent documentation on the Raspberry Pi website at https://www.raspberrypi.org/documentation/hardware/raspberrypi/bootmodes/msd.md and I claim no credit for this whatsoever other than having tried it out successfully.

Note this is still an experimental procedure to a certain extent, although with the USB drive I specify below I had no problem in getting to to work with two different Pi3. Your Pi3 will still be bootable as normal from a standard SD card after you have carried out this procedure.

In order to set this up you will need the following:
1 A Pi3 (it will not work on a Pi2 as it uses bootcode built into the Pi3)****
2 A freshSD card on which to build the image which will be transferred to your USB stick. I used a 16Gb Sandisk Ultra miscroSDHC
3 A usb stick. I used a Sandisk Cruzer SDCZ50 16GB from Amazon for £4.99 (not every usb stick will work. The article give a list of some which have been tested)
4 An internet connection for your Pi3

*** It is possible to change this to boot from a Pi2 or earlier, using just one boot file on an SD card. Thereafter the USB stick takes over. See https://www.raspberrypi.org/documentation/hardware/raspberrypi/bootmodes/ for details.

Follow the details of the first part of the article to build up your SD card image Use the Raspbian image dated 2016-09-23 from raspberrypi.org/downloads  Before the second stage, where you transfer the image to your USB stick, install Sonic Pi 2.11 (just released) onto the SD card. You can get it from
http://sonic-pi.net/files/releases/v2.11.0/
download the file sonic-pi_1-2.11.0-2_armhf.deb
I downloaded this via the Chromium browser on the Pi3 to the Downloads folder
Then I installed it with:-

cd ~/Downloads
sudo dpkg -i sonic-pi_1-2.11.0-2_armhf.deb
sudo apt-get install -f

You may find it then lists some redundant packages which you can remove as suggested with

sudo apt-get autoremove

I also added dtoverlays for my IQaudio amp to config.txt using sudo nano /boot/config.txt

dtoverlay=iqaudio-dacplus,auto_mute_amp
dtoverlay=i2s-mmap

which you can do also if you have one, or else any overlays required for an alternative eg HiFiBerry card.

In order to add an auto boot into Sonic Pi you do the following.

mkdir -p ~/.config/autostart
cp /usr/share/applications/sonic-pi.desktop ~/.config/autostart

If you have an IQaudio (or alternative audio card) installed on your Pi3, you should select it as the default card in the Audio Device Settings on the Menu -> Preferences tab

Once you are happy with the SD card, you should complete the second section of the article setting up the USB stick with parted as discussed in the raspberry pi boot documentation article.

All being well, once you have finished you should be able to boot your Pi3 from the usb stick you have set up. Note it does take some time to boot, as it first checks to see if you have an SD card installed, and then switches to search for a USB card after a 5 second timeout. You will see a large rainbow coloured square on your monitor during this process.

I advise you to remove the line added to config.txt

program_usb_boot_mode=1

which was added to program the One Time Programmable memory (OTP) in your Pi3 to enable USB booting. This is an irreversible process, and you may not wish to inflict it on any other Pi3 that the card is subsequently used to boot, although the Pi3 can still boot normally with an SD card even if this has been programmed.

The original github page for the procedure is at
https://github.com/raspberrypi/documentation/blob/master/hardware/raspberrypi/bootmodes/msd.md

and there is more useful information in the parent folder (bootmodes)

This project requires some care to follow the procedures accurately, and it takes a little time to transfer the card contents to the USB stick. Although the boot time is quite long, the performance thereafter is not noticeably different from that of the SD card. Apparently you can add a resistor to one of the GPIO pins to speed up the booting by getting it to ignore the SD card, but I haven’t managed to find out documentation as to which pin is involved. You can also leave a blank SD card in the Pi3 which will also speed up the boot time, by eliminating the timeout wait.

Sonic-Pi controlled conversation between two McRoboFaces

IMG_4843

Following on from my previous project with a single McRoboFace, 4Tronix have kindly supplied me with a second face to enable me to develop the idea to control two McRoboFaces with Sonic Pi. I have amended the previous project to feed the outputs of the left and right audio channels to two separate adc inputs on the piconzero board, and daisy chained the two McFaces (you merely connect the Dout pin of the first to the Din pin of the second) and then address the leds on the second McRoboFace with an offset of 17. I have developed routines in the python driver program to control each face separately. Each mouth can be set to a fixed position: closed, open, smile or sad, or can be fed from the audio input via the adc, so that it is triggered to open when the signal exceeds a preset threshold.

In order to provide greater control, and to synchronise it to the audio feed from Sonic Pi, I have added Ruby routines to the Sonic Pi program which can send text strings to the python program via a text file. These strings can set the mouth state for each face, and also alter the colours of the leds. because there is only a common brightness setting for both faces (using pwm) If only one face is receiving audio I use that output to control the brightness of both faces. If both faces are set to receive audio then I set the brightness at a fixed value.

The conversation is entirely controlled from Sonic Pi. It plays the audio for each face via a series of pre-recorded samples, and plays each face with a separate audio channel by setting the pan: value either to -1 or to 1. Before each sample is played, control signals are sent via the text file to set up the required state for each face. At the end of the presentation both faces receive audio input together as they “sing” along to a round of Frere Jaques. Finally a control signal is sent to reduce the brightness to zero, effectively switching off all the leds.

Writing and reading the data via a text file is perhaps not the most elegant way to do things, but does seem to work OK. I used a technique I developed previously when reading in large numbers of sample files to “hold up” the Sonic Pi program utilising a cue and sync while the writing completes. Otherwise you can run into “too far behind errors”. On the reception side, at the start of the main program loop the python program polls for the existence of the text file, and if it finds one, reads the data, then deletes the file. It then alters parameters according to the received data. It took quite a lot of experimentation to get the timings and consistent operation of the two programs correct, but having done so, the final system is quite stable. I boost the audio levels to amp: 4 in Sonic Pi, which gives a good signal for the adc inputs to latch on to.

Setup is fairly straight forward. The calibrate button used in the single face project us utilised again, and sets separate offsets for each channel, and the code used to modulate the mouths is very similar to that used in the previous project. Once set, the Sonic Pi program can be run several times, leaving the python program running continuously..

I have enjoyed this project, which had brought together Sonic Pi, Ruby and Python in an interesting way, not to mention recording and processing the samples with Audacity., and I hope you enjoy the video of the final system. I hope it may be possible to write up teh system more fully in the future, but it will be quite a big job to do so.

You can see the video here

 

McRoboFace project to singalong with Sonic Pi

IMG_4773

Recently I was sent a pre-production version of 4Tronix McRoboFace, which is a small face whose features light up with 17 Neopixel leds. It is ideally suited as a companion to their Picon Zero controller board, which already has software in its library to accommodate it. I experimented with the item, and the result was a project to produce a talking/singing face, which could respond to an audio input either fed from a microphone, or internally from a raspberry pi running Sonic Pi.

An article here gives full constructional details and links to the software used.

4Tronix hope to launch a kickstarter project for the McRoboFace soon. If you support it, this will give you a nice project with which to use it.

video link to the project in action, explaining itself!

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

 

Sonic Pi: beware where it can lead you!

binary

I have added a video of this program driving iTunes visualiser to my youtube channel. See it at https://youtu.be/6V3wqwxYimA

Recently I wrote some code in Sonic Pi which investigated the production of a rhythmic pattern based on the binary numbers 0 to 15. This was inspired by an article I found at http://bernhardwagner.net/musings/RPABN.html where the writer use hex names to represent rhythmic patterns: thus E3 was 11100011 and A7 was 10100111. He also explored generating new patterns by shifting the bits in the original pattern 1 bit left or right. Thus E3 becomes 11000111 or C7 when you rotate it. In the program I developed I started with 4 strings, representing the number 0 to 15

0000000100100011 0100010101100111 1000100110101011 1100110111101111
These were assigned to 4 ring variables r1 to r4, and then 4 live_loops were set up, each one using a different sequence of these 4 rings r1r2r3r4 r2r3r4r1 r3r4r1r2 and r4r1r2r3

Each was used to play either a sequence of notes (chosen at random) from a pentatonic scale, with the tonic note changing using the sequence :c3,:g4,:g2,:c3. The mechanism used was to use the “look” function to test in turn each digit in the sequence and to play the note (two ocataves higher) if a 1 was detected. In each live loop a second note was played (at the base pitch) using the inverse rhythm, ie when a 0 was detected.
The base note altered every 64 ticks, and every 16 ticks the individual rings in each sequence were rotated either clockwise or anti-clockwise). As a rhythmic pattern, the resulting sounds continued ad infinitum, and to give a more presentable end result I decided to play the pattern for a given number of cycles, and to sandwich it between a winding up and winding down start and finish note generated by the fm synth with varying note pitch. I also added a level control so that the sound could gradually fade away before stopping.

The end result was quite pleasing, and I placed a recording on soundcloud.com and published to code on my gist site. By now I had become quite hooked on experimenting with the system, and lots of questions along the line of “I wonder what would happen if?…” sprang to mind. The result of a further couple of days of playing and experimenting resulted in version 2 being born, which I present here. I do this partly for my own benefit, as there are so many bits added that it is quite difficult to follow all the ins and outs of the final program. But here goes, and I hope that you will find it interesting and informative.

The first thing I decided to do was to emphasise the rotating nature of the patterns, by adding an accent to the first pulse of each 16. Here I must say that the one thing that makes the whole program possible is the use of tick and look functions. These can be very powerful in enabling to make selections and changes depending on the current values returned by these functions. In its simplest form tick is a counter that increments by 1 every time you invoke it, whereas look returns the current value that tick holds without altering it. I find it best to use one isolated tick at the start of a loop and then to use multiple look functions to extract its value at other position in the loop. You can use this value to iterate around the values in a ring variable. Thus if a=(ring 2,4,6,8) then a.look will return 2,4,6,8,2,4,6,8… as look changes from 0,1,2,3,4,5,6,7….
However, as well as a basic tick, you can create other tick counters with statements like
tick_set :foo,look/4  In this case as look increases you will get this:
look value:            0,1,2,3,4,5,6,7,8,9,10,11,12,13,14…
look(:foo) value    0,0,0,0,1,1,1,1,2,2,2,2,3,3….
That is look(:foo) increases at quarter of the rate.
Another useful trick I use to give a trigger say every 16 increments is:
puts”triggered” if look%16==0
which will print triggered every 16 times round the loop.

Secondly, in order to hear the rhythm “make up” of the four parts more clearly I decided to utilise the use_bpm_mul function to slow down the tempo during the first half of the “performance” and to speed it back up to its starting value during the second half. The user specifies the percentage drop in tempo required. (figures 0 to 30 are probably appropriate) and a factor is calculated to progressively change the tempo every 64 pulses after the first 32. Halfway through the factor is inverted e.g. 0.8 becomes 1.0/0.8 = 1.25 and the tempo speeds up again. By setting the percentage change to 0 the tempo will remain constant throughout.

The third change was to allow different scale structures rather than the original minor_pentatonic. To do this, I choose a scale type at random, and generated a list of the note offsets for the scale using scale(0,scale_name)
{for octave 0} and I then shuffled this to put the offsets in random order and generated the note by adding in the note value for the base note sequence :c3,:g3,:g2,:c3. To give further variation I also shuffled this sequence. I used the same shuffled sequence for each of the four live_loops x1 to x4, but for two of them I reversed the order of the shuffled sequence. Further flexibility came from having an alternative base note sequence :c3,:f3,:g3,:g2 which could be selected.

With quite a complex range of notes being played, it was sometimes difficult to get the full sense of the rhythms, so I added an alternative note selection, where instead of playing a sequence based on the notes in a given scale type, I played just the base note for each thread, with an offset for threads 2,3 and 4 to give the notes in a major chord: +4,+7 and +12. I also simplified things a bit for both the scale and chord options by setting the main rhythm part an octave higher that the inverted rhythm part.

The next change to the sound output was to set the pan values for the main rhythm parts to 0.8 and for the inverted parts to -0.8 and then to flip them to the opposite side every 32 ticks.

In the original program as discussed I played both the main rhythm and the inverse rhythm together. This gave a very full texture, but made it difficult to hear the rhythms generated.  In this version I have added options to play just the normal or inverted rhythms or both together as previously. I also added options to select which ones of the four generated parts should play. All of the above may sound very confusing but you can see the end result in the two play commands inside each of the four live_loops x1 to x4

play n+12,amp: rvol+krv,attack: y*t,release: (1-y)*t,pan: 0.8*flip if (r.look == 1) and rhythm.include?"n"#main rhythm
play n,amp: lvol+klv,attack: y*t,release: (1-y)*t,pan: -0.8*flip if (r.look != 1) and rhythm.include?"i" #inverse rhythm

r holds the current rhythm eg (ring 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0)
The first note plays when a 1 is encoutered, the second when a 0 is found as look iterates through the ring. A second condition for the note to play is given by rhythm.include?”n” and rhythm.include?”i” If the variable rhythm includes an “n” then the main rhythm note is played and if it contains an “i” then the inverse rhythm note is played. Either or both can be selected.
The accents are given by krv and klv which are set non-zero on each 16th tick
flip is either 1 or -1 and is switched every 32 ticks reversing the pan values.
t {between 0 and 1} is set by by user and adjusts the timbre of the note by altering the attack/release proportions. rvol and lvol enable the user to adjust the relative amp settings for the right and left channels, bearing in mind that they cover different frequency ranges.
n is set to the current base note (plus chord offset) which alters every 64 when using the chord play option, otherwise, if using the scale based option, it is set to  note(base+bs.look) where bs holds the shuffled list of scale note offsets, which is added to the base note, or to note(base+bs.reverse.look) if it is to play the sequence backwards.

I now present sections of the program each with some comments. Unfortunately it is not possible to incorporate full comments in the program itself, or it exceeds the length limit to be able to play on a Mac. (It is a few character short of this limit!)

#Rotating binary rhythms by Robin Newman (v2), January 2016
#see rbnrpi.wordpress.com for details

use_debug false

define :bpm do
  return 60.0/Thread.current.thread_variable_get(:sonic_pi_spider_sleep_mul)
end

#function rotates ring entries
define :rv do |r,dir=1| #if dir = -1 then the rotation is backwards
  return ring( r[0].rotate(dir),r[1].rotate(dir),r[2].rotate(dir),r[3].rotate(dir))
end

loop do
  rs=Random.new_seed
  puts "random seed "+rs.to_s+"\n"
  use_random_seed(rs)

  sname=scale_names.choose
  ######### user settings below ###########
  rvol=0.4 #vol sets for left and right
  lvol=0.6
  kr=0.4 #accent inc right
  kl=0.4 #accent inc left
  numpasses=2 #should be even
  t=0.1
  tempochange=[0,10,16,20].choose
  plscale=[TRUE,TRUE,FALSE,TRUE].choose #TRUE scale based: FALSE chord notes
  entryexit=[TRUE,FALSE].choose #control entry/exit notes
  y=0.25 #adjust attack/release ratio
  startlevel=0.7
  loops=["1234","13","24","12","34"].choose#set loops to play x1...x4
  rhythm=["ni","ni","n","i"].choose  #n normal i=inverse
  ##############################################
  puts "USER DATA SETTINGS"
  puts "Number of 256 'tick' passes set to "+numpasses.to_s
  puts "playing major chords" if plscale==FALSE
  puts"playing notes from "+sname.to_s+" scale" if plscale==TRUE
  puts"playing loop numbers "+loops
  puts "playing rhythms "+rhythm+" (n normal, i inverse)"
  puts "entryexit note selected " if entryexit
  use_bpm 60

In the first section, a function bpm is defined. This uses an internal function in Sonic Pi to return the current bpm setting for the context when the function is called. I use this in calculating the current reduction in tempo as the program progresses.

The program utilises “selection rings” each of which contains 4 rings of 16 binary digits. A second function rv is defined which rotates the elements of each of these four rings either one place clockwise or anticlockwise depending on the dir variable.
Following this, the remainder of the program is placed inside a loop which repeats indefinitely (until you press the stop button), on each iteration playing a rotating binary rhythm set up with randomly selected parameters to control it.
Normally Sonic Pi generates a deterministic range of random numbers when it is running. This means that the same sequence of numbers is generated on each run, and that values returned by .choose or .shuffle will be repeatable. By changing the random number seed using the use_random_seed() function the sequence can be changed to a different one. To save manually changing this you can use the Ruby function Random.new_seed to generate an arbitrary value to place in the use_random_seed function. This means that a different setup will be generated EVERY time the loop starts. You could move the three lines after the loop do line outside this loop, and everything would work fine, but I have deliberately placed the lines inside the loop so that the (very long) seed number is printed for EVERY cycle played, and you can see the number required to recreate a particular rendition. But be warned, you will NOT be able to recreate a “nice” result without retaining the value of this very long integer. You can of course, just put in your own (known) value instead of using this, although in this case you should place it outside the loop, otherwise you will get exactly the same version on each iteration of the loop.
The scale_name is selected by sname=scale_names.choose and then there is a section where the user can preset various program settings. In this version choices are made for you for a list associated with each entry with a .choose command. Many of these settings have already been discussed: others are self evident. startlevel adjusts the initial level set in the with_fx :level command.
When the program is run, the values associated with many of these parameters are printed on the screen. If you want to make manual adjustments, you can remove the outside loop doend and set individual values for the parameters on each run.

bn=[ring(:c3,:f3,:g3,:g2 ),ring(:c3,:g3,:g2,:c3)].choose.shuffle #base notes
  puts"base notes "+bn.to_s
  bs=scale(0,sname).shuffle #get sequence of notes in scale shuffled
  #tempo changes
  fac1= (1-tempochange.to_f/100)**(1.0/(numpasses*4/2)) #calculate bmp_mul factor for slow down
  #puts fac1
  puts "tempo reduction set to "+((1-fac1**(numpasses*4/2))*100).round.to_s+"%" #check
  fac5=fac4=fac3=fac2=fac1 #set individual starting fac values for each loop

  #start with 4 16 bit rhythm patterns based on the binary numbers 0-15
  #set up binary rhythms r1 0-3,r2 4-7,r3 8-11,r4 12-15
  br=["0000000100100011","0100010101100111","0100010101100111","1100110111101111"].shuffle
  r1=[br[0],br[0].reverse].choose
  r2=[br[1],br[1].reverse].choose
  r3=[br[2],br[2].reverse].choose
  r4=[br[3],br[3].reverse].choose
  puts "Starting Rhythms "+r1+" "+r2+" "+r3+" "+r4 #currently selected rhythms

  #now convert to rings
  r1= r1.split('').map(&:to_i).ring
  r2= r2.split('').map(&:to_i).ring
  r3= r3.split('').map(&:to_i).ring
  r4= r4.split('').map(&:to_i).ring

  #make 4 selection rings
  rl1=ring(r1,r2,r3,r4)
  rl2=rl1.rotate
  rl3=rl2.rotate
  rl4=rl3.rotate

In the second section of the program, first the bass notes are setup in the ring bn and shuffled, and the shuffled note offsets for the chosen scale are setup in the ring bs.
fac1, the factor to use with each application of the use_bpm_mul command is calculated using the formula
fac1= (1-tempochange.to_f/100)**(1.0/(numpasses*4/2)) Basically with a tempo change every 64 ticks and a complete pass of the rhythm changes every 256 ticks there 4 changes each complete pass. If the program runs for numpasses there are (numpasses*4/2) steps to reduce the tempo by the required percentage, HALF the duration of the piece. (1-tempochange.to_f/100) is the complete tempo change required e.g. 0.8 for 20%  **(1.0/number of stages) works out the number of stages root of this number which gives the required factor. eg for 20% reduction with numpasses=4 we get 1/8th root of 0.8 = 0.9724924724660731 If we multiply this number by itself 8 times we get 0.8 which is the required factor. fac1 to fac5 are all set to this value to give one separate variable for each of the live_loops that need it.
br holds the initial 4 binary strings for number 0-3,4-7,8-11,12-15 which are initially shuffled in their order. The shuffled order is then split into four strings r1,r2,r3 and r4 and these are randomly reversed in their order. eg
r1=[br[0],br[0].reverse].choose
Then four selection rings rl1 to rl4 are created, using the four elements r1..r4 arranged in different orders r1r2r3r4, r2r3r4r1, r3r4r1r2 and r4r1r2r3
Later in the program they will be rotated using the rv function previously described.

  if entryexit then #optional fm "wind up" note to start
    use_synth :fm
    p=play 36,sustain: 5,divisor: 12,amp: 0.3
    control p,note_slide: 3,note: 56,amp_slide: 4,amp: 1
    sleep 3
    control p,amp_slide: 2,amp:0
  end

The fourth section generates the starting note (if selected by the value of entryexit.
It uses the fm synth, and starts a low note playing sustained for 5 beats with a starting pitch 36. As this note plays, its pitch is controlled and raised over 3 seconds up to 56 whilst at the same time the amp: is increased to 1, and then faded out to 0.

  with_fx :level do |v| #used to fade out rhythms at the end
    control v,amp: startlevel #set initial level
    with_fx :reverb do
      live_loop :audio do
        tick
        if look >= (numpasses-1)*256+128 #start fading during last pass
        then
          limit=1 #set level endpoint
          limit=0.7 if entryexit #leave 30% if exit note
          control v,amp: startlevel*( 1.0-limit*(look-128-(numpasses-1)*256).to_f/128) #fade to 30% or 0%
        end
        fac5=1.0/fac5 if look==numpasses*256/2 #change fac to speed up at half way point
        use_bpm_mul fac5 if (32-look%64)==0
        sleep t
        stop if look==numpasses*256 #stop after numpasses completed
      end

The main execution section starts with the fifth section. First with_fx wrappers for :level and :reverb are set up. The level setting control is v, which is used to set the starting value to startlevel. Thereafter it is controlled by the live_loop :audio
This live_loop uses its tick counter to wait until the program is halfway through the final complete loop pass when it triggers a reduction in the level setting either to zero or to 30% depending upon whether the exit note is to be played or not as selected in the user settings. The bpm setting for this loop is adjusted according to the tempochange selected by the user, so that the timings remain synchronised to the four main live_loops x1 to x4 which generate the notes played. The factor fac5 is applied every 64 ticks after the first 32 to accomplish this. Halfway through fac5 is changed to 1.0/fac5 so that the tempo starts increasing again. A stop command kills the loop when look equals the number of passes selected *256, 256 being the number of ticks in one rhythmic cycle.

      live_loop :x1 do
        use_synth :blade
        tick
        tick_set :rc1,look/256 #used to select current ring from selection ring
        tick_set :bs1,look/64 #used to select base note pitch
        tick_set :flipper1,look/32 #used to flip pan settings

        base=bn.look(:bs1) #get base note
        r=rl1.look(:rc1) #get current ring

        if look%16==0 then #set emphasis for first beat of 16 (added to amp: setting)
          krv=kr;klv=kl
        else
          krv=klv=0
        end

        if look(:flipper1)%2==0 then #set pan flip
          flip=1
        else
          flip=-1
        end

        fac1=1.0/fac1 if look==numpasses*256/2 #change fac to speed up at half way point

        use_bpm_mul fac1 if (32-look%64)==0#apply bpm_mul every 64 beats
        puts "initial tempo=100%" if look == 0
        puts "tempo now reduced to "+(((bpm.to_f/60*10000).round).to_f/100).to_s+"% will increase back to 100%" if look == numpasses/2*256 - 32 and (tempochange !=0)
        if plscale then #select note according to whether scale or chord selected
          n=note(base+bs.look)
        else
          n=base
        end
        if loops.include?"1" then
          play n+12,amp: rvol+krv,attack: y*t,release: (1-y)*t,pan: 0.8*flip if (r.look == 1) and rhythm.include?"n"#main rhythm
          play n,amp: lvol+klv,attack: y*t,release: (1-y)*t,pan: -0.8*flip if (r.look != 1) and rhythm.include?"i" #inverse rhythm
        end
        sleep t

        rl1=rv(rl1) if look%16==0 #rotate all the rings in the selection ring

        if look(:rc1)==numpasses then
          cue :go
          stop
        end
        puts "Current pass "+(look(:rc1)+1).to_s if look%256==0 #print current cycle on the screen
      end

The four live_loops x1 to x4 are essentially the same, with minor differences. Each one uses a different synth. I will describe live_loop :x1. To avoid accidental “double ticking” ONE tick call is used in the loop in the third line. Three other named ticks are started.
:rc1 is set to increment every 256 ticks and is used to select the current ring from the selection ring.
:bs1 is set to increment every 64 ticks and is used to select the bass note pitch.
:flipper is set to increment every 32 ticks and is used to flip the pan settings.
The comments in the live_loop together with the previous description of other parts of the program should make the remaining operation of the loop reasonably clear.
Unlike the other three x2..x4 loops this loop also has a couple of puts statements to put information about the current tempo setting and the current pass number on the screen.
The application of the rv rotate function alters the rhythmic pattern as the loop plays.
Live_loop :x4 has one important addition. It includes the line:

      cue :fin if look==(numpasses*256)-20 #trigger finish loop

This is used to trigger final “wind-down” fm note if it has been enabled according to the user flag entryexit. This trigger occurs 20 ticks or 2 seconds at full tempo before the the live_loops :x1 rto :x4 terminate.
live_loop :x1 also contains an extra command to send a cue :go to allow the loop doend to complete when live_loop :x1 terminates in the case that the entryexit  is NOT to be generated. Otherwise the loop exit follows when the wind-down note has completed.

   end #reverb

    #end piece.....fm "wind down" finishes.
    if entryexit then
      sync :fin
      use_synth :fm
      p=play 56,attack: 0.1,sustain_level: 0.9,sustain: 6,divisor: 12,amp: 1
      sleep 1 #sustain for 1 sec before changing
      control p,note_slide: 6,note: 36
      sleep 3
      puts "finished!"
      sleep 1
    end
    sync :go if !entryexit #wait for sync from loop x1
    sleep 1
  end#end level
end#loop

The final section first ends the :reverb fx after the end of live_loop :x4, then triggers the exit note assuming entryexit is TRUE when it receives its sync :fin Again this is produced using the fm synth. This starts with the pitch with which the entry note finished 56 and uses a control function to reduce the pitch to 36 over 6 beats, producing a winding down sound to finish the piece.

For me this turned into a large project, with much experimentation and things to try out. As I said at the beginning be warned. Beware where Sonic Pi can lead you. This is why I think it is such a great program. it lets you explore both musical, mathematical and programming ideas all at the same time. It gives -usually :-)  immediate aural feedback to your ideas, is very powerful and flexible and basically great fun to use. Sadly it does use up an inordinate amount of time…but it is all worth it!

The program has been tested on Sonic Pi v2.9 on both MAcOSX, Win PC and Raspbian Jessie running on a Pi2. It is unlikely to perform very well on a Model B+ or earlier. I haven’t tested it on Ubuntu, but I see no reason why it wont; work on that platform too.

You can download the complete version 2 program from my gist here

There is a soundcloud item containing two audio specimens from this new version here

A touchscreen driven jukebox player for Sonic Pi

TouchScreen Sonic Pi Jukebox

TouchScreen Sonic Pi Jukebox

This is a project which grew out of a previous jukebox program I had written for Sonic Pi, which enabled it to be controlled either with a python program running on the Pi, or via my mobile phone using the Telegram App. With the arrival of the Raspberry Pi official touch screen display, I saw that this might be used as the front end to such a program. I produced a first working version a couple of months ago, but it had some shortcomings, and recently I revisited and the result is this article.

There is a video of the project here

Prerequisites
You need a Raspberry Pi offical touch screen, with a Raspberry Pi attached. Preferrably this should be a Pi2. I haven’t tested with a Pi Model B+ but it should work, although performance may not be as good.
Also required, is a suitable means of listening to the sound via the 3.5mm output jack on the Pi.
You should have an up to date copy of Raspian (Jessie) on the SD micro card in the Pi.

A touch screen is not a magic panacea. You have to do a little bit of work to be able to use it to control a program. One of the environments which lends itself to producing touch screen operated front ends is Kivy. I had not heard of this before, but came across it in the superb article on using the Raspberry Pi touch screen to control GPIO connected devices by Matt Richardson.

The project falls into Four parts.
1 Installing Kivy
2 Adding the sonic_pi_cli gem which enables text files to be sent to Sonic Pi for execution.
3. Creating the kivy main.py program which which is the basis of the Jukebox. This will also involve choosing the (up to 12) pieces that you wish to play. For convenience I will supply some.
4. Configuring the Pi to autostart Sonic Pi and the Kivy program on startup.

Installing Kivy
I cannot better the excellent description in Matt Richardson’s article. Follow this up to and including step 17.

Adding sonic-pi-cli gem
In order to do this, without installing and using a system like rvm, we have to do a bit of preliminary work so that the correct permissions will be in place during the install process. First, if you have switched to booting into a command line environment, start the graphical environment using startx. You may also like to switch back to this environment on boot by using Raspberry Pi Configuration utility in Menu -> Preferences. The graphics environment is needed for Sonic Pi to work.
Start a terminal window and type the following:

sudo mkdir /var/lib/gems
sudo chown pi /var/lib/gems
sudo chown pi /usr/local/bin
gem install sonic-pi-cli

When the install has completed, reset the ownership of /usr/local/bin

sudo chown root /usr/local/bin

Now test the cli by starting Sonic Pi, loading in any piece of reasonable duration, eg one of the example files, and running it. While it is running from the terminal window type:

sonic_pi stop

The piece should stop playing, showing that the cli command is working.

Creating the kivy main.py program
Kivy can be quite confusing to start with. Partly this is because there are two ways to use it. You can have a .kv file which contains details of the screen layout, and a separate python script which contains the logic of how these items are used, or everything can be contained just in a python script. After some playing around, and googling for kivy scripts I elected to use the former, and the program I came up with has elements of Matt Richardson’s script to control GPIO pins, together with bits from a snippet I found at http://stackoverflow.com/questions/18958520
The program listing is below. Also required are two small graphics files which can be downloaded in the resources file at the end of the post.

#!/usr/bin/python
#Sonic Pi touch screen kivy program by Robin Newman
#with acknowlegements: https://github.com/mrichardson23/rpi-kivy-screen
# and http://stackoverflow.com/questions/18958520
# written January 2016

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.slider import Slider
from kivy.uix.image import Image
import os

b={} #holds button ids for binding
s=[] #holds text for buttons
c=[] #holds linked commands for buttons

#build text lists
#path to Sonic Pi files to play (include trailing /)
sf='/home/pi/SP-files/'
#used in building command line for sonic_pi gem
pre='cat '+sf
post=' | sonic_pi' 

#description (d**) (2 lines max separated by \n), and filename  (n**) entries
#for the 12 files in the jukebox selection
d1='Ambient\nExperience'
n1='ambient_experiment.rb'
d2='Blimp\nZones'
n2='blimp_zones.rb'
d3='Blip\nRhythm'
n3='blip_rhythm.rb'
d4='Chord\nInversions'
n4='chord_inversions.rb'
d5='Filtered\ndnb'
n5='filtered_dnb.rb'
d6='FM\nNoise'
n6='fm_noise.rb'
d7='Jungle'
n7='jungle.rb'
d8='Ocean'
n8='ocean.rb'
d9='Reich\nPhase'
n9='reich_phase.rb'
d10='Shufflit'
n10='shufflit.rb'
d11='Tilburg\nv2'
n11='tilburg_2.rb'
d12='Time\nMachine'
n12='time_machine.rb'

####### You shouldn't need to alter anything after this line
#setup strings for description and filenames
s.append('Tune 1\n'+d1)
c.append(pre+n1+post)
s.append('Tune 2\n'+d2)
c.append(pre+n2+post)
s.append('Tune 3\n'+d3)
c.append(pre+n3+post)
s.append('Tune 4\n'+d4)
c.append(pre+n4+post)
s.append('Tune 5\n'+d5)
c.append(pre+n5+post)
s.append('Tune 6\n'+d6)
c.append(pre+n6+post)
s.append('Tune 7\n'+d7)
c.append(pre+n7+post)
s.append('Tune 8\n'+d8)
c.append(pre+n8+post)
s.append('Tune 9\n'+d9)
c.append(pre+n9+post)
s.append('Tune 10\n'+d10)
c.append(pre+n10+post)
s.append('Tune 11\n'+d11)
c.append(pre+n11+post)
s.append('Tune 12\n'+d12)
c.append(pre+n12+post)
s.append('Stop\nPlaying')
c.append('sonic_pi stop')
s.append('Quit\nProgram')
c.append('')

def press_callback(obj):
    global s,c #make lists global
    if obj.text == str(s[13]):
        os.system('sonic_pi stop')
        os.system('killall sonic-pi')
        os.system('killall ruby')
        App.get_running_app().stop()
    for i in range(0,13):
        if obj.text == s[i]:
            os.system('sonic_pi stop') #double stop sent for stop command but doesn't matter
            os.system(str(c[i]))

def update_vol(obj, value):  #upadte volume via alsamixer
    global vol
    #print ("Updating volume to:" + str(obj.value))
    vol=obj.value
    os.system("amixer sset 'Master' "+str(vol)+"% >/dev/null")

    

Builder.load_string("""
:	
    boxes: _boxes 
    AnchorLayout:
        anchor_x: 'center'
        anchor_y: 'top'
        ScreenManager:  #maybe can simplify as only 1 screen used
            size_hint: 1, 1
            id: _screen_manager
            Screen:
                name: 'screen1'
                BoxLayout: 
                    orientation: 'vertical'
                    padding: 50
                    id: _boxes
    AnchorLayout:
        anchor_x: 'center'
        anchor_y: 'bottom'
        BoxLayout:
            orientation: 'horizontal'
            size_hint: 1,.12
            Label:
                #valign: 'top'
                font_size: '20sp'
                text: 'Sonic Pi 12-Tunes Touch Screen Jukebox'""")


class Boxes(FloatLayout):
    global s,c
    def __init__(self, **kwargs):
        super(Boxes, self).__init__(**kwargs)
        bx1 = BoxLayout(orientation='horizontal')
        bx2 = BoxLayout(orientation='horizontal')
        bx3 = BoxLayout(orientation='horizontal')
        bx4 = BoxLayout(orientation='horizontal')
        bx5 = BoxLayout(orientation='horizontal')
        bx6 = BoxLayout(orientation='horizontal')

        bx1.add_widget(Image(source='sonic-pi-web-logo.png'))
        bstop=Button(text=s[12])
        bstop.bind(on_press=press_callback)
        bx1.add_widget(bstop)
        bquit=Button(text=s[13])
        bquit.bind(on_press=press_callback)
        bx1.add_widget(bquit)
        bx1.add_widget(Image(source='logo.png'))

        for i in range(0,4):
            b[i]=Button(text=str(s[i]))
            b[i].bind(on_press=press_callback) 
            bx2.add_widget(b[i])
        for i in range(4,8):
            b[i]=Button(text=str(s[i]))
            b[i].bind(on_press=press_callback)
            bx3.add_widget(b[i])
        for i in range(8,12):
            b[i]=Button(text=str(s[i]))
            b[i].bind(on_press=press_callback)
            bx4.add_widget(b[i])       
        bx5.add_widget(Label(text='Adjust volume -->',font_size='20sp'))
        vol=80 #initial value
        b[12]= Slider(orientation='horizontal', min=0,max=100,value=vol)
        b[12].bind(on_touch_down=update_vol,on_touch_move=update_vol)
        bx5.add_widget(b[12])

        self.boxes.add_widget(bx1)
        self.boxes.add_widget(bx2)
        self.boxes.add_widget(bx3)
        self.boxes.add_widget(bx4)
        self.boxes.add_widget(bx5)

class TestApp(App):
    def build(self):
        return Boxes()

if __name__ == '__main__':
    TestApp().run()

I do not intend to give a detailed explanation of the program, but a few pointers may help. There are one or two user configurable bits near the start of the program. The variable sf holds the absolute path to the location of the sonic pi files that will be played by the jukebox program.

n1 to n12 are variables which hold the filenames of the 12 sonic pi files to be played. I have them ending in .rb but they could equally well be .txt files. In this example version I have used 12 of the example files included in Sonic Pi. These were accessed from /opt/sonic-pi/etc/examples and I used entries in the wizard and iillusionist folders stored there, copying them to the folder specified by sf /home/pi/SP-files.
In my own version I have 12 of my own sonic pi files used instead.

d1 to d12 are corresponding variables which hold a 1 or two line description of these files, which appear as the text on the selector buttons on the screen. If you want a second line it is separated from the first by a \n eg d4=’Chord\nInversions’.

From the variables n1-12 and d1-12 two string arrays s and c are generated, the first containing the complete text for each button incluidng the Tune 1…Tune 12 prefix, and teh second the complete command needed to be sent to the cli gem to tell Sonic Pi what to play.

The program defines two functions which are activated by the touch elements in the screen. First there is a callback routing which is called whenever one of the 14 touch buttons is touched. (The 12 program select buttons, plus buttons for Stop Playing and Quit Program.) This compares the text of the calling button with the values in the s array and when it finds a match  stops Sonic Pi playing the current tune (if any) and then sends the command for the selected tune. It deals separately with the commands to stop playing or to quit the program, in the latter case quitting Sonic Pi as well.
The second function deals with the graphical slider which is used as a volume control. This extracts the value (0-100) selected by the user, and sends it via an os.system command to utility amixer where it is used to adjust the ‘Master’ control. Here it is perhaps worth saying that I also installed pulseaudio on the system, which you should also do before using this script. Normally amixer only exposes a ‘PCM’ control which can be used to adjust the volume. However experiment showed that this was very non-linear, and did not give a very satisfactory performance. By installing pulseaudio the ‘Master’ volume control is exposed which gives a much more satisfactory adjustment to the volume. If you DONT want to install pulseaudio, you can amend the program by altering the line

os.system("amixer sset 'Master' "+str(vol)+"% >/dev/null")

to read

os.system("amixer sset 'PCM' "+str(vol)+"% >/dev/null")

To install pulseaudio use

sudo apt-get install pulseaudio

The program should be saved with name main.py and stored in a folder named SP-touch-screen created in the user pi home director together with the two logo files sonic-pi-web-logo.png and logo.png (download from the resources file at the end of the post).

Interim testing
At this stage you do some interim testing to see whether things are working ok so far. One word of warning. Once you start a kivy program ctrl-C will NOT work to stop the program. You can only use the built in Quit Program button to do this. It there are any problems you can end up with no means ot stopping the program other than removing the power lead.  As this is undesirable, you are strongly advised to have a remote ssh login available to the Raspberry Pi, so that you can log onto it from an external PC or Mac, and from there use killall python to stop the program running. If you are unfamiliar how to set this up, then the guide https://www.raspberrypi.org/documentation/remote-access/ssh/ should help.
To try out the program, make sure you have set up the folder SP-files to contain the 12 examples files used in the program, then start up Sonic Pi from the Menu->Programming link. When it has started, use a terminal window and type:

cd ~/SP-touch-screen
python main.py

which should startup the touch screen kivy front end. Try out some of the buttons to see if you can play a tune. If a tune is running pressing another tune selector button will stop the first tune and start the second. Stop Playing should sto the currently playing tune. You should also be able to change the volume by tapping on the horizontal selector at different points, or sliding the circle left or right. Finally pressing Quit Program should quit both the kivy screen AND Sonic Pi.

Configuring the Raspberry Pi to autostart into the touchscreen jukebox
The final stage is to add a small script and a .desktop file in order to get the Raspberry Pi to startup the program automatically on boot. To do this I utilised the fact that the Pi will autostart any application whose desktop file entry is copied to the folder /home/pi/.config/autostart
In this case we have to generate a small python script because we want TWO programs to start one after the other. First we must ensure that Sonic Pi is running, then we must start the kivy screen program. The startup script which I called startupkivy is shown below.

#!/usr/bin/python

import os
import time

os.system('/usr/bin/sonic-pi &')
time.sleep(8)
os.system("amixer sset 'Master' 90%") #starting volume setting
os.system('python /home/pi/SP-touch-screen/main.py &')

This needs to be placed in the folder /usr/local/bin
which you can do using

sudo nano /usr/local/bin/startupkivy

type in the text and then use ctrl+x followed by Y and return key to exit.
Set the file executable using

sudo chmod 755 /usr/local/bin/startupkivy

Now set up the desktop file:

cd ~/.config
mkdir autostart
cd autostart
nano kivy.desktop

type in the text:

[Desktop Entry]
Encoding=UTF-8
Version=1.0
Type=Application
Exec="startupkivy"
Icon=/usr/share/pixmaps/sonic-pi.png
Terminal=false
Name=startupkivy
Categories=Application;Development;

and quit with ctrl+x, followed by Y, and return key
If you wish, you can copy this file to the desktop as well using:

cp kivy.desktop ~/Desktop

That completes the setup. Now you can test it out.
First make sure that the Pi is set too boot to the GUI interface. You can check with the Raspberry Pi Configuration utility in Preferences. Make sure that the Boot: To Desktop button is marked.
Now reboot your Pi and it should startup in the GUI and immediately start Sonic Pi, followed 8 seconds later by the touchscreen jukebox program.
It should be possible to startup, play a selection of tunes, quit the program, select Shutdown from the Menu and shutdown the Pi, all with the use of your finger, and with no mouse or keyboard at all.
If you have copied the .desktop file to the Desktop you can double click it to restart Sonic Pi and the kivy main.py program if you wish.

At the time of writing Sonic Pi 2.7 is still the version of Sonic Pi bundled in Jessie. IF you want a later version you can download it from sonic-pi.net and install it on your desktop. For example version 2.9 installs in sonic-pi-v2.9.0 YOu can alter the startupkivy file to use this instead of the default version by changing the line:

os.system('/usr/bin/sonic-pi &')

to

os.system('/home/pi/sonic-pi-v2.9.0/bin/sonic-pi &')

Resources
You can download a file containing :
the SP-touch-screen folder containing the two logo files and the main.py script
the startupkivy program which should be copied (using sudo) to /usr/local/bin
the kivy.desktop file  which should be placed in ~/.config/autostart

To download open a terminal window and type

wget http://r.newman.ch/rpi/touchscreen/resources.tar.gz
tar zxvf resources.tar.gz
cd resources

All you have to do is to install the other requirements, kivy, sonic-pi-cli, pulseaudio as detailed above. Have fun!