About rbn2013

Retired Head of IT, now having fun playing with Raspberry Pi and Lego Mindstorms

Dual Sensor Theremin for Sonic Pi

Previously I built a theremin based on a modified design from The Magpi magazine, which is now also the subject of a project on the raspberrypi.org site here


Alexandra Stepanoff playing a Theremin in 1930. (from Wikipedia Theremin article)

While this is a great little project, I was not very satisfied with the end result which didn’t sound very similar to a “real” theremin. The original theremin was an analogue instrument in which the audio frequency was produced by the interaction between two radio frequency oscillators at very nearly the same frequency which were controlled by the proximity of a musicians hand to a metal rod aerial. A third oscillator controlled by the proximity to a metal loop of the musicians other hand was used to control the volume. this gave a sound which was continuous when started by the proximity of a hand, whose frequency and volume could be varied smoothly. You can see an example in the picture above (from the Wikipedia article on Theremin)

The first thing I resolved to do was to use two ultrasonic sensors instead of one. I used the same circuit as the one in the raspberrypi.org article but built it twice on the same breadboard. The first circuit used pins 4 and 17 as in the original, and the second one, used pins 23 and 24 for the trigger and echo signals instead, otherwise being identical. I built the circuit on a RasPiO ProHat board which gives convenient access to all the GPIO pins, as shown in the photographs below.

To read the inputs from the two ultrasonic sensors a python script is used. The starting point for the python script is to extract data from the two ultrasonic sensors using the excellent gpiozero library, included by default on the latest Raspbian Stretch image. This makes it very easy to get a series of readings from the two sensors.
In order to send the data to Sonic Pi an OSC message is used, using the python-osc library. You can install this using:

sudo pip3 install python-osc

The complete code for my python3 script is shown below:

#!/usr/bin/env python3
#program sets up two distance sensors and exports readings using OSC to host port 4559
#using gpiozero and python-osc libraries
#written by Robin Newman, May 2018
#
from gpiozero import DistanceSensor
from time import sleep
from pythonosc import osc_message_builder
from pythonosc import udp_client
import argparse
import sys

def control(spip):
    sensor = DistanceSensor(echo=17, trigger=4,threshold_distance=0.5) 
    sensor2 = DistanceSensor(echo=23,  trigger=24,threshold_distance=0.5)
    sender = udp_client.SimpleUDPClient(spip,4559)

    while True:
        try:
            r1 = (sensor.distance* 100) #send numbers in range 0->100
            r2 = (sensor2.distance* 100)
            sender.send_message('/play_this',[r1,r2]) #sends list of 2 numbers
            print("r1:",round(r1,2),"r2:",round(r2,2))
            sleep(0.06)
        except KeyboardInterrupt:
            print("\nExiting")
            sys.exit()

if __name__=="__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--sp",
    default="127.0.0.1", help="The ip onic Pi listens on")
    args = parser.parse_args()
    spip=args.sp
    print("Sonic Pi on ip",spip)
    sleep(2)
    control(spip)

You can type this in using text editor from the Pi Gui Accessories Menu, or using the nano editor from a terminal window. Alternatively you can download it from my gist site (link at the end of of the post). To make it easy to start, when you have it installed on your Raspberry Pi set it to be executable by the pi user by typing (in a terminal window):

chmod u+x theremin.py

I have recently received my Kickstarter Pi_Juice board after sdveral years wait :-) and I decided to try it out with this project, as it would mean that the sensor setup: RasPiO board, Pi-Juice board and Raspberry Pi were completely self contained. That is why I added the optional input parameter, so that I could send the OSC messages to an external computer on the local network, instead of just using it on the local Raspberry Pi. Of course if you don;t have a second suitable computer on which to run Sonic Pi, you can do all of this using the built in Sonic Pi on Raspbian Stretch (version 3.0.1), although you will need to have a screen and keyboard and speaker attached to your Pi to do so. (It IS possible to run Sonic Pi on a headless Raspberry Pi using xdrp, which I have done on other projects, but I don’t want to complicate this post by giving details here). If you are going to use a separate computer for Sonic Pi, then first make sure that your Raspberry Pi is set up to connect automatically to your network via WiFi. (I used a Pi3 with built in Wireless). Then you can use the Raspiconfig utility on the gui preferences menu to set your Pi to boot to the command line (logged in). You should also enable SSH using the same utility. When you do so, you should change the Raspberry Pi password for user pi from the default raspberry to something else you can remember. Again the same utility will let you do that. Note the ip address of your Raspberry Pi on your wireless network, so that you can connect to it remotely. If you don’t want to do this, you can of course leave the Pi connected to screen and keyboard and just start the theremin.py script directly from a terminal window.

All being well if you reboot your Raspberry Pi (you can do it with the screen and keyboard attached first time if you like to check that it is working) you should be able to connect to it from your second computer running Sonic Pi. I used a Macbook Pro, with ip address 192.168.1.134 and my Raspberry PI was on 192.168.1.182, so I typed:

ssh pi@192.168.1.182

on my Mac terminal window, followed by the password for user pi on my Raspberry Pi. This connected me, and since I had saved the theremin.py script in my pi home directory I then just had to type this to get the script running (remember it was set to be executable):

./theremin.py --sp 192.168.1.134

This started the script running, and it reported in the terminal window.

Sonic Pi on 192.168.1.134

Then if I started moving my hands back and foward near the ultrasound sensors I could see streams of numbers beneath, shown that the readings were geing generated. e.g.

r1: 6.69 r2: 6.45
r1: 6.69 r2: 6.45
r1: 9.28 r2: 6.45
r1: 9.28 r2: 6.45
r1: 9.26 r2: 6.49
r1: 9.26 r2: 6.53

That completes the setup for the Raspberry Pi. Moving on to Sonic Pi, this should be started on your second computer (in my case my Macbook Pro). I was using version 3.1 the latest release on this platform. In an empty buffer screen (selected using the tabs at the bottom of the main screen, you should add the following program, either by typing it out or by downloading it from the gist link at the end of this post.

#Theremin plus, controlling pitch and ixi_techno phase by Robin Newman May 2018
#I control a continously playing note (length 2000)
use_debug false

define :putsPretty do |n,p|
  num=(n*10**p).round/(10**p).to_f
  return num
end

define :scalev do |v,l,h|
  return (l+v).to_f*(h-l)/100
end


with_fx :reverb,room: 0.8,mix: 0.8 do
  with_fx :ixi_techno,phase: 4,phase_offset: 1,mix: 0.8 do |p|
    set :p, p
    use_synth :subpulse
    k=play octs(0,2),sustain: 10000,amp: 0
    set :k,k
    live_loop :theremin do
      use_real_time
      b = sync "/osc/play_this"
      r1=scalev(b[0],30,100)
      r2=scalev(b[1],0.1,1)
      puts putsPretty(r1,2),putsPretty(r2,2)
      if r1  <  60 then #adjust note pitch, and restore volume to 0.7
        control get(:k),note: octs(r1+12,2),note_slide: 0.06 ,amp: 0.7,amp_slide: 0.2
      else #set output vol to 0
        control get(:k),amp: 0,amp_slide: 0.2
      end
      if r2 < 0.8 then #adjust phase modulation rate, and restore mix to 0.8
        control get(:p),phase: r2,phase_slide: 0.06,mix: 0.8,mix_slide: 0.2
      else #switch off phase modulation by setting mix to zero
        control get(:p),mix: 0,mix_slide: 0.2
      end
    end
  end
end

Looking at this you will notice that I use the effect :ixi_techno, and further down the program you will see that the :phase value, or the rate at which this effect modulates sounding notes inside the effect are controlled by the value of r2, the second output sent from the two ultrasound sensors. In my initial description of the “real” theremin I said that the two available controls adjusted the pitch or frequency of the notes played and the volume of those notes. Initially I tried adjusting the volume of the notes, and it can be done, but I didn’t find it very effective. Instead I opted to add this optional modulation to the playing note, but the nice thing about Sonic Pi is that it is fairly easy to adjust one of a variety of characteristics using the second number, and I hope to try out some others, and may even find a more satisfying result for volume adjustment in the end.

The main technique I use in the program is to start a very long note playing with zero volume, and then to use Sonic Pi’s ability to control the parameters of the note while it is playing. By adding sliding values to the parameters which change I can get a smoothly varying pitch for the note unlike the more simple theremin in the raspberrypi.org example. There are two functions defined at the start of the program. The first one putsPretty is used to print the numbers to just 2 decimal places. The second scalev  is used to scale the number (which arrive in the range 0->100 to whatever I need in the program. r1 is adjusted to range 30->100 and r2 to the range 0.1->1 Some reverb is applied to all of the output, and then the :ixi_techno effect is applied The |p| is a variable which gets a reference to the running effect. I store this in the time_state as :p so that it can be referenced and used to control the parameters of the effect further down the program. Similarly the long note is started and a reference to it is stored in the variable k in the line:

k=play octs(0,2),sustain: 10000,amp: 0

This is then subsequently stored in the time-state as :k so that it can be retrieved and used to alter the pitch of the note according to the value of r1. Notice the use of the octs function to play two notes an octave apart. Initially the pitch is 0 and the volume 0 as well, but this is altered by the control later on.

The meat of the program takes place in the live_loop :theremin This waits for an incoming OSC messages addressed to /play/this in the sending program (Sonic PI adds the initial /osc to show the source. This contains the data of the two numbers r1, and r2 in a list. They are retrieved, printed on the screen using the putsPretty function and then processed to control the note. a cutoff value of 60 is taken for the scaled  r1 (equivalent to 60cm). For values above this the volume of the note is set to 0 (with a slide time from any previous value of 0.02), and no note sounds. For values below 60 the pitch of the note is adjusted, and the volume restored to 0.7. Again slide times are used, 0.06 for the note adjustment (the time between successive numbers arriving) and a more sedate 0.2 for the volume adjustment. For(scaled)  r2 values < 0.8 the phase rate of the modulation is adjusted, and the mix value is set to 0.8. For values > 0.8 the mix value is set to 0, effectively disabling the effect. Again suitable slide times are chosen.

The one final “gotcha” to be aware of is that for Sonic Pi to receive OSC messages from an external source you need to enable this in the preferences IO tab. (see below)

When you do this, if the theremin.py script is still running, you should see lots of osc messages in the cues section of the Sonic Pi screen like this:

I you now run the Sonic Pi program, then you should be able to control what you hear by moving your hands in front of the two sensors.

I have made a film of the project in action which you can see here
Details of the circuit can be seen on the raspberrypi.org site here
You can download the software here

Advertisements

Touch sensitive input for Sonic Pi

overallview

Development of a touch sensitive keyboard for use with Sonic Pi, using Adafruit’s MPR121 12 toiuch sensitive input board with OSC support added to the software.

Full article, with link to software is here.

Video of project in action is here.

Sonic Pi 3 Player /Recorder version 1.2

Now released version 1.2 of my Player ?recorder for Sonic Pi 3 utilising a TouchOSC interface. This has expanded considerably since version 1, but as a result is now is not suitable for use on a Pi3. Also, as the program has increased in length it now needs to be run using the run_file command from a text file.(.rb)

Full details of the interface and usage are contained in a page here including a link to the code.

Setting up Sonic-Pi to work with a pisound midi/audio interface board.

I thought it would be useful to gather my experience of using the excellent pisound midi/audio interface board by blokas.io with Sonic Pi.

The samplerate, frames/period and number of periods/buffer I use in this article give about the lowest latency I have been able to achieve with Sonic Pi on a Raspberry Pi 3. If you are not worried about latency, and mainly using Sonic Pi for playback, you may want to use values which are less demanding, but at the expense of worse latency.
Such figures might be sample rate 48000, frames/period 4096, periods/buffer 3.  I use these when playing pieces that are very demanding of sonic pi, with several parts playing together. Latency for these settings is 256ms as opposed to 21.3ms for the settings used below

Although the pisound board works “out of the box” with Sonic Pi, that is only as far as sound output is concerned. Sonic Pi does not automatically connect the stereo input ports on the pisound board. This is because, by default the Raspberry Pi does not have any audio input ports, and it cannot predict which external cards/boards a user is likely to install.
There are two ways around this problem. The first in non-invasive and makes use of an external program `qjackct` (installed on the Pi) to launch and configure the jackd connector which enables the SuperCollider scsynth which is at the heart of the sounds Sonic Pi produces to talk to the outside world. The second invasive method which involves altering one of the files in Sonic Pi is to configure this file (scsynthexternal.rb) to accommodate the actual board setup (in this case PiSound) that you are using.

In both instances the first thing to do is to set PiSound as the default audio board. This is achieved by using the Audio Device Settings in Preferences on the main Sonic Pi menu. From the Sound Card popup menu select pisound (Alsa mixer) and set it as default, using the Make Default button.

For the non-invasive approach, first open a terminal window and type
“`
killall jackd
qjackctl &
“`
The qjackctl interace window will open up

CLick the Setup Button, then select the Settings Tab and within that the Parameters tab
adjust the parameters as follows:

Driver alsa
Realtime (ticked)
Sample Rate: 96000
Frames/Period: 1024
Periods/Buffer: 2
MIDI Driver: seq

Switch to the Advanced tab and adjust as follows:

Preset Name: (default)
Server Prefix: jackd
Port Maximum: 1024
Time (msec): 500
Audio: Duplex
Dither: None
Output Device: hw:pisound
Input Device: hw:pisound,0
Channels I/0 (both) 2
Latency I/O (default)
Startup Delay: 2 secs
“`
Now click `OK`
Click the `Start` button on the Jackd screen. All being well jackd will start running and the Stop button will show a bright red rectangle beside it.

Click the `Messages` button and select the `Status` tab
You should see values reflecting the Sample Rate 96000 Hz and the Buffer Size 1024 that we set earlier.
Leave qjackctl running and start Sonic Pi
When it has started, you should be able to hear sound from Sonic Pi as usual. Check using for example `play 72`
However you still have to connect the inputs to SuperCollider to use the PiSound input. To do this go back to the qjackctl interface and click on the Connect button.
You should see two entries on the left pane, `SuperCollider` and `system` and two entries on the right pane `SuperCollider` and `system`

These represent the output and input ports. You will see that the Supercollider output is connected to the system input, enabling aduio to go from Sonic Pi to your audio sysetm where you can hear it.
However the input to SuperCollider on the right hand side is disconnected. Use your mouse to drag a connection line diagonally from the system output (your microphone for example) to the SuperCollider input
Note you can expand each of these which makes it a bit clearer. You will see the two system capture ports connected to the two “in” ports in SuperCollider.

If you now connect an audio source to the PiSound board eg a guitar or microphone, you can run the program below to test it.

with_fx :compressor,amp: 4 do
live_audio :min # for a mono input
end

For a stereo input change the live_audio line to `live_audio :min,stereo: true`

The invasive method.

Using qjackctl is easy and it allows you to experiment with different settings for the sample rate, buffer size (frames/period) and number of periods/buffer. By default Sonic Pi uses 41000Hz for the sample rate, 2048 frames/period and 3 periods/buffer which is more conservative to suit the inbuilt audio card, but which gives a much larger latency. Blokas.io recommend that you only use 48000, 96000 and 192000 with pisound, otherwise it has to do resampling. 192000 is a bit too high for Sonic Pi so try the other two.
If you want to dispense with qjackctl you can alter one of the files in Sonic Pi, but you should only attempt this if you know what you are doing and are comfortable with hacking apps.

The file in question is named scsynthexternal.rb and it is a Ruby text file situated inside the app. The following changes apply to the current release version on Raspberry Pi which is 3.0.1
If you have installed Sonic Pi using apt-get install or are using an up to date raspbian stretch you will find the file at:

/opt/sonic-pi/app/server/sonicpi/lib/sonicpi/scsynthexternal.rb

It is saved as user root, so you have to access it using sudo. In a terminal type:

sudo leafpad

When the text editor opens navigate to the file listed above and open it.
First save a backup copy of the file. eg save as scsynthexternal.rb.backup
Then reopen the original and scroll to line 363 and 364 which show

# `jack_connect SuperCollider:in_1 system_capture_1`
# `jack_connect SuperCollider:in_2 system_capture_2`

alter these to read as follows:

`jack_connect SuperCollider:in_1 system:capture_1`
`jack_connect SuperCollider:in_2 system:capture_2`

Note there are TWO changes on each line
Then scroll to line 350 which reads:

jack_pid = spawn "jackd -R -p 32 -d alsa -d hw:#{audio_card} -n 3 -p 2048 -o2 -r 44100& "

put a # before the line to comment it out, and add an extra line below it.

#jack_pid = spawn "jackd -R -p 32 -d alsa -d hw:#{audio_card} -n 3 -p 2048 -o2 -r 44100& "
jack_pid = spawn "jackd -R -p 32 -d alsa -d hw:#(audio_card) -n 2 -p 1024 -o2 -i2 -r 96000& "

(note this will alter subsequent line numbers by 1 if you scroll back to the first change)

Save the file, replacing the original.
Exit the text editor, and try running Sonic Pi (without using qjackctl first)
All being well it will startup and you should be able to run the live_audio example.

One final point. You can’t use the volume slider on the Raspberry Pi Menu Bar with the PiSound board. Instead use the volume knob on the PiSound itself.

Problems. If you find Sonic Pi won’t start and that it times out with an error message, this can sometimes be because parts of the app are left running and interfere with subsequent attempts to start it. One drastic cure for this is to restart the Raspberry Pi. Alternatively you can open a terminal window and issue a series of commands to kill these sub programs.

You can try:

ls -ae

and look for any entries that contain sonic-pi, ruby, scsynth, jackd, m20 or o2m
if so try

killall sonic-pi
killall ruby
killall scysnth
killall jackd
killall m2o
killall o2m

You may get no process found responses, which is fine, but for any that you don’t get this then you have successfully stopped the offending app.
Now try restarting Sonic Pi.

I hope that this article will be useful to you if you have a pisound. You can also find help in the pisound community section at https://community.blokas.io/

Fanfare for St Cecilia’s Hall played by Sonic Pi

Last week my Wife and I had the privilege of attending the official opening ceremony of St. Cecilia’s Hall in Edinburgh. This music hall, the oldest in Scotland, was purchased by Edinburgh University on 1959, and my Father, who was then Professor of Music at the Univerisity was instrumental (forgive the pun) i negotiating the housing of a collection of early musical keyboard instruments made by Raymond Russell. The Hall was opened in 1968 following this refurbishment.Since then the number of instruments in the Collection has increased, most notably by the acquisition of the Rodger Mirrey Collection of Early Keyboard Instruments. In January 2004 the decision was taken to add the Reid Concert Hall Museum of Instruments (the John Donaldson Collection of Musical Instruments).belonging to the University, and  major refurbishment program was started to give a fitting new home for all of the instruments. This took three years to complete, and the Hall was closed for over two years whilst the changes took place. It was opened again for use earlier this year, and last week was officially opened in a ceremony by H.R.H. The Princess Royal.

As part of that ceremony a musical Fanfare composed by Andrew Blair, Masters Student in Composition at the Reid School of Music in the University. He graciously gave me permission to take the score and transcribe the music for Sonic Pi to play. I typed the notes in by hand, as printed for two Bb trumpets, and applied a transposition of -2 semitones to give the correct concert pitch, and first produced a version using the built in :tri synth on Sonic Pi. With the addition of some reverb this produced a very acceptable performance. However in an effort to make it sound more authentic, I then tried using the trumpet samples from the Sonatina Symphonic Orchestra, for which I had previously written code which I have used extensively to produce sample based instruments for Sonic Pi. However in this case, I found that the samples didn’t work very well, particularly on some of the lower notes. I did some experimentation using the music program MuseScore 2. First I modified the Sonic Pi code to use midi commands instead of play commands, and I fed the output into MuseScore 2 setting up two instrument tracks with trumpet instruments. This gave a much better rendition, but was rather unwieldy.

I decided to generate my own trumpet samples from MuseScore 2. To do this, I chose the same notes as for the samples in the Sonatina Symphonic Library, namely
a#3,a#4,a#5,c#3,c#4,c#5,c#6,e3,e4,e5,e6,g3,g4,g5 (all concert pitch)
From these all other notes are produced by playing samples more slowly or faster. Using these pitches, I could use the samples with my existing code to handle the Sonatina Symphonic Samples. I set up a “score” to play single long notes for each of these pitches, with rests in between, and then played them in MuseScore 2, recording the output in Audacity. I then diced them up in audacity, producing individual .wav files for each of the pitches, and normalising them before saving the files. I tried the resulting samples with my Sonatina code and it worked perfectly.
So I produced the second version, using these samples which sounds very realistic.

You can download the code for both versions from my gist site here

You can hear what they sound like on soundcloud here

The opening of the Hall was a very moving experience for me, and ti was enhanced with Andrew’s brass fanfare, and with other music played on a Virginal from the collection, made by Stephen Keene in London in 1668

Two of the early keyboard instruments in the collection (plus my Wife in the distance!)

If you like musical instruments, and are visiting Edinburgh, I would encourage you to visit the Hall and the Museum. Their website is http://www.stcecilias.ed.ac.uk/ There is also an app which lets you explore the collection of instruments Apple version      Android version

You can read about the history of the hall on this site (It also contains pictures from the first refurbishment of the Hall in the 1960s).