A Sonic Pi controlled Glockenspiel

Mark II version now available here

The release of Sonic Pi version 3 last summer opened up a whole new range of possibilities for the use of this free versatile music program, available for the Raspberry Pi, Apple Mac, and Windows PC computers, and, if you compile it yourself, for various Linux Computers. The key to this was the ability of Sonic Pi to send and receive signals using MIDI, OSC messages, and audio input and output. Over the year I have written about several projects making use of these facilities, but this current project is to me one of the most satisfying, and fulfils an ambition to produce a Sonic Pi controlled musical instrument. This article assumes you are using a Pi 3.

About 30 or so years ago, we bought a Glockenspiel for our Son, who was musical, but for many years when he had grown up it languished in our attic. It had a range of two octaves from :G5 to :G7 Although it is possible to utilise all these notes, I decided to limit the range, using only the notes :C6 to :E7 plus a “roving note” which could be allocated to any of the other notes. Initially I used this on :Fs6, although I have also used it on :G5 in some pieces. The reason for limiting the range was largely on cost grounds, as I intended to use small 5v solenoids to activate the notes, and these cost £5 each. Also, I wanted to mount the driver circuitry on a RasPiO HAT and this could only easily accommodate 11 circuits. Finally, I intended to use lego parts for the hammer actuators, and I had a limited quantity of lego available.

Similar projects have been done in the past, but this is the first I have seen which utilises Sonic Pi to drive the instrument, most of the others having a midi interface, often achieved using an arduino in the interface circuit, for example this one by Mike Cook. There is even a hackaday project from 2010  to build one from scratch, making the bars and the solenoids. My constructional skills are not great, so I resolved to try and use Lego as my main building material, and in fact the only other “construction” involved was to saw two pieces of wood to act as supports for the Glockenspiel, with which I could cope!

The Glockenspiel I used was manufactured in the (then) GDR by a firm called Stimmrein under the name GOLDON “Chromatic Elite”.

It is still possible to pick up one of these on Ebay. Other Glockenspiels can be used, and there are several cheaper childrens’ toys, often just with 8 notes, but also sometimes with dubious tuning. I wanted to use gravity to return the hammers to their resting position, so this entailed hitting the notes from the underneath rather than from on top in normal player mode. Bear this in mind if you want to use an alternative Glockenspiel. Also, the spacing of the keys has to be appropriate for use with the lego construction, the pitch of my hammers being 3 lego holes or about 24mm. If you really want to do-it-yourself I think that the pipe glockenspile featured in this instructable might be a good starting point to consider.

I played around with one or two setups for the hammer, but the end result which worked well turned out to be remarkably simple, as shown below.

The solenoids I used were bought from ThePiHut.com They were produced by Adafruit PRODUCT ID: 2776.

The construction of the hammer mechanism. A Lego 15 beam  with a 5×3 angle on the end is pivoted on a axle (length 3). The hammer used for “sharp” notes is shorter but with a longer hammer.

The blue tack is to hold the assembly in place. This single actuator can be moved to different notes as desired. The main assembly with 10 hammers is fixed.

some more detail of the frame construction is shown below. The base plate is not essential, and doesn’t fit exactly, but I found it useful to prevent movement of the array.

The solenoids I used were bought from ThePiHut.com at £5 each They were made by Adafruit product ID 2776. They wre mounted onto the T shaped lego part using rubber bands.

Two different circuits were used to drive the solenoids. Eight of them were driven from a ULN2803 open collector darlington array chip, sold by Adafruit but again purchased from ThePiHut These are extremely easy to use. They have a ground connection, a connection to the 5v supply used to drive the solenoids (NB NOT the same supply as used to drive the Raspberry Pi), 8 input pins connected directly to GPIO pins on the Raspberry Pi and 8 output pins which are each connected to a different solenoid, the other wire from the solenoids being connected to the 5V solenoid supply. The chip contains protection diodes to handle the inductive pulse effects from the solenoids. If there had been rooom I would have used two of these chips, but to fit the circuitry onto the RasPiO ProHat I used three TP102 darlington power transistors again sourced from Adafruit but sold by ThePiHut These required separate protection diodes and a series resistor ( I used 1K  ohms) connected to the Base of the transistor. The circuit is shown here.

For the diode I had some 1N4007 diodes in my spares box, but ThePiHut sell some 1N4001 didoes which should do the job as well. They are used for reverse voltage protection to clamp the spikes produced by the inductive load of the solenoids. If you are happy to mount the components on a separate board, then you could use two ULN2803 instead which would be a little cheaper, and would allow for the expansion of a further 5 solenoids.

The solenoids came with a small 2 pin connector. You can (and I did to start with) push standard patch lead male plugs into these to make the connections. I purchased some from Amazon Once I had got the system working I decided to make leads with plugs on them to fit the solenoids to make it easier to assemble and disassemble the system when not in use. I used another pack from Amazon from WOSKY which contained a bag of sockets plus some heat shrink tube and I cut some of the patch leads and soldered them onto the plugs to make custom leads. You might also try using these alternative WOSKY leads but in this case you would have to source suitable patch plugs for the other end or join it to a cut patch lead. Here is a photo of my leads being prepared.

Note the heat shrink tube which is slid over the joint once it is made, and then heated with a hot hair dryer to shrink it. The lead is made from a patch lead cut in two, and trimmed to the required length.

Two completed leads connected.

A breadboard is used for distributing the signals to the solenoids. On the right the leads to the solenoids are connected, using the common +ve power rail. and on the left the orange leads go to the RasPiO ProHAT on the Raspberry Pi. The moveable eleventh solenoid (on several extensions leads) is connected at the bottom, and a single red lead provides the 5v solenoid supply to the board. The green lead links the two +ve power busses together.

And so to the wiring on the RasPiO ProHat. The reason I used this hat is first that it provides some protection to the GPIO pins, and secondly, the pins are accessible in numeric order which makes connections very easy to do. The phtos below show the wiring and components fitted to the board, first without the three power transistors, and then with them in place.

The photo above shows the input leads from the 5v solenoid power supply. This was a 5v 3A supply similar to this Pimoroni one You will also need a barrel connector adapter like this one from ThePiHut

The final connections for the project are shown in the photo below.

The orange wires from the first eight solenoids are connected to the eight output pins of the ULM2803 chip. The remaining three orange wires are connected spaced apart by alternate connection holes to the collectors of the three power transistors. The Red power lead from the distribution board is connected to the end hole of the ldftmost connection strip on the ProHAT board, which is also connected to the Red 5v power input lead. The black power input lead is connected to one of the GND connection points on the ProHAT. You should make sure everything is up and running BEFORE switching on the solenoid 5v power. Check the circuit thoroughly as 5v applied to the wrong place could cause expensive damage. It is best to leave the solenoid power disconnected until the software is all set up ready to go.

The software required to make the project work is in two parts. First (obviously) you need Sonic Pi version 3.0.1 which is included in the latest version of Raspbian (currently dated 2018-04-18). I will look at the software you run on this a bit later. Also, you need a python script which drives the gpio pins and hence the solenoids. It utlises the GPIOZERO python library included in the Raspbian image, but you also need to install a python library to handle the OSC calls which will be sent from Sonic Pi. To do this, make sure that your Pi is connected to the internet, open a terminal window on your raspberry pi 3 and type in

sudo apt-get update
sudo pip3 install python-osc

This will install the library. The code for the python script is shown below.

#!/usr/bin/env python3
#OSCglockenspiel.py written by Robin Newman, May 2018 
#program sets up an OSC server and receives OSC messages sent to "/note"
#Sonic Pi can be running either on the Raspberry Pi (default),
#or on an external networked computer

#All of the libraries required should be standard on the latest Raspbian apart from pythonosc
#To install this use sudo pip3 install python-osc


from gpiozero import LEDBoard
from pythonosc import udp_client
from pythonosc import dispatcher
from pythonosc import osc_server
from time import sleep
import argparse
import sys

leds=LEDBoard(9,10,11,12,13,14,15,16,17,18,19) #pins connected to ULM2803A driver or TIP120 power transistors
#via GPIO pins 9-19
  
 #This is activated when /note OSC message is received by the server.
 #the arguments n represents the note position in the LEDBoard array 0->9  
def pulse(unused_addr,args,m): #pulse pin corresponding to received number for 0.05 seconds
    print("note",m)
    leds[m].on()
    sleep(0.05)
    leds[m].off()      

#The main routine called when the program starts up follows
if __name__ == "__main__":
    try: #use try...except to handle possible errors
        #first set up and deal with input args when program starts
        parser = argparse.ArgumentParser()
        #This arg gets the server IP address to use. 127.0.0.1 or
        #The local IP address of the PI, required when using external Sonic Pi
        parser.add_argument("--ip",
        default="127.0.0.1", help="The ip to listen on")
        #This is the port on which the server listens. Usually 8000 is OK
        #but you can specify a different one
        parser.add_argument("--port",
              type=int, default=8000, help="The port to listen on")
        #This is the IP address of the machine running Sonic Pi if remote
        #or you can omit if using Sonic Pi on the local Pi.
        args=parser.parse_args()
        dispatcher = dispatcher.Dispatcher()
        #following dispatcher handles /note osc messages being received
        dispatcher.map("/note",pulse,"n")
        #Now set up and run the OSC server
        server = osc_server.ThreadingOSCUDPServer(
              (args.ip, args.port), dispatcher)
        print("Serving on {}".format(server.server_address))
        #run the server "forever" (till stopped by pressing ctrl-C)
        server.serve_forever()
    #deal with some error events
    except KeyboardInterrupt:
        print("\nServer stopped") #stop program with ctrl+C
    #Used the AttributeError to specify problems with the local ip address
    except AttributeError as err:
        print(err.args[0])
    #handle errors generated by the server
    except OSError as err:
       print("OSC server error",err.args)
    #anything else falls through

This script is run from a terminal by typing:

python3 OSCglockenspiel

or if you wish you can make it executable by typing

chmod +x OSCglockenspiel.py

in which case to run it you can type instead

./OSCglockenspiel.py

There are two optional parameter you can use which will enable you to communcate with the file from an instance of Sonic Pi running on a different computer on the same local network. In this case you need to start the program supplying the actual IP address of the Raspberry Pi on which it is running, which you can find by typing (on the Pi)

ip address

and looking for the entry associated with your active network connection wired or WiFi. In my case it was 192.168.1.234 so I could start the program using

./OSCglockenspiel --ip 192.168.234

However normally you don’t need to do this.When you start the program in the default mode you should see a message Serving on (‘127.0.0.1’,8000) This indicates the localhost address 127.0.0.1 and the default port 8000 on which the program is listening for OSC messages to arrive. The second optional paramter is –port nnnn which lets you cnage the port used, which might be useful if it clashed with other software, but this is very unlikely to need to be used. In use the program responds to an OSC messages sent to the address “/note” on port 8000 of the local machine. When such is received it triggers the function pulse. This is fed the parameter m which should be a number in the range 0 to 10 each number corresponding to one of the eleven solenoids. These are connected to GPIO pins 9,10…19. The GPIO LEDBoard object maps the input numbers 0 to 10 to activate one of these pins, turning it on for 0.05 seconds and then turning it off again. This short impulse causes the solenoid to engage, moving the armature into the coil causing it to hit the lego beam below. This pivots, causing the hammer to rise and strike the underside of one of the glockenspiel bars causing the note to sound. The hammer then falls back under gravity, and the solenoid armature returns to its normal position moved by the built in spring.

In order to test things, you can now put the glockenspiel in place, resting on suitable supports to raise it above the hammers. I used two pieces of wood to raise the glockenspiel by about 30mm. Adjust the postion so that the hammers on the solenoid array strike the notes from the bottom C up to the top E when you tap down on the solenoid. You have to adjust things quite carefully to make sure that the right notes are hit, and also adjust the position of the solenoids by sliding them up and down so that the hammers move freely. With a little practice this is quite quick to do. Adjust the moveable solenoid so that it strikes the note F# in the middle of the range.

In a blank Sonic Pi buffer, type in the following code.

use_osc "localhost",8000
osc "/note", 0

With the solenoid supply still disconnected, make sure the OSCglockenspiel script is running as described above, and then run the Sonic Pi program. You should see in the cues window bottom right on Sonic Pi OSC -> localhsot, 8000, /note, [0] If you now bring the terminal window to the front you should see note 0 printed showing that it has received the OSC message. All being well, connect the 5v solenoid supply, and run the Sonic Pi program again. This time the end solenoid, the top one in the picture above, should trigger and sound a C. You can now amend the program to try out all of the notes.

use_osc "localhost",8000
11.times do n
  osc "/note",|n|
  sleep 0.5
end

All being well, each of the notes should sound one after the other, with the f sharp added on the end. Adjust solenoid and/or hammer positions as necessary if any notes don’t sound properly. You can try reducing the time of the sleep to 0.1. If this all works, then congratulations, you have a working glockenspiel.

Now the fun starts, and we can start playing tunes with it, programmed into Sonic Pi. First we need to relate note names to the numbers 0->10 that we have been sending via OSC messages. The actual pitch of the glockenspiel I used was such that the bottom C was in fact :c6 and the top E was :e7. The f sharp was :fs6

I used a function defined in Sonic Pi so that I could use note names to play the glockenspiel. Modify the program to read:

use_osc "localhost",8000
define :notenum do |n|
  nl=(scale :c6, :major, num_octaves: 2).to_a[0..9]+[note(:fs6)]
  return nl.index note(n)
end

live_loop :testnotes do
  tune = (ring :c6,:e6,:g6,:c7,:e7,:c7,:b6,:a6,:g6,:f6,:e6,:d6,:c6,:fs6,:d6)
  osc "/note",notenum(tune.tick)
  sleep 0.2
end

The function defines a list of the playable notes which consists of the first 10 notes [0..9] of a two octave scale of C major starting from :c6 plus the note :fs6 added on the end. It uses the supplied note to find its numerical position (or index) in this list and returns that number. This will be then number required in the osc command to play the specified note. The live loop :test defines a tune and then uses the tick function to step through the tune repeatedly, sending each note to the notenum function and the resulting number in the “/note” osc message so that it can be played. A sleep delay of 0.2 seconds separates the notes.

I have written a range of pieces that can be played by the Sonic Pi glockenspiel and these can be downloaded from my gist site specified at the end of the article.

I have also published a video of the prototype version of the glockenspiel before I tidied up the wiring.

There is one further bonus for those who have a midi keyboard. Since Sonic Pi can receive midi input, it is possible to connect your keyboard to Sonic Pi, and with a very simple program to play the glockenspiel directly from the midi keyboard. The program for this is shown below.

# simple midi player for Sonic Pi glockenspiel by Robin Newman June 2018
use_osc "localhost",8000

define :notenum do |n|
  nl=(scale :c6,:major,num_octaves: 2).to_a[0..9]+[note(:fs6)]
  #puts nl
  return nl.index note(n)
end

live_loop :midiIn do #live loop receives midi input
  use_real_time
  b= sync "/midi/*/*/*/note_on"
  midiOffset=36
  currNote=b[0]+midiOffset
  #+36 offset scales my keyboard to required octave
  #or use octave shift on midi keyboard
  #note value for notenum() starts at :c6 -> 84 for bottom note
  puts "current midi note is #{b[0]+midiOffset} require range 84-100"
  puts "offset too low" if currNote<84 puts "offset too high" if currNote>100  
  osc "/note",notenum(b[0]+36) if b[1]>0 #ignore note_off (vel=0)
end

Note the comments, which describe how you can adjust the output of your keyboard to cover the right range.

You can download all the software, including several pieces I have produced to play tuned with the glockespeil from my gist here

A video of the first version prototype is here

Listen to a sample piece being played here

Advertisements