Flight of the Bumble Bee: sample based for Sonic Pi

freebumblebeepic400Picture from www.public-domain-image.com

Having worked on a sample based Piano voice for Sonic Pi, I determined to tackle a much more ambitious project to add a Xylophone based sample voice and then to transcode Rimsky Korsakov’s Flight of the Bumble Bee for Sonic Pi 2. I know that there is at least one version out there already for Sonic Pi 1, written by Alex Eames of Raspi-TV, which  used a single synth, and numerical coding for the notes with just the solo instrument, but I wanted to reproduce as realistic an instrument sound as possible, with a piano accompaniment as well as the solo Xylophone.

In the event this turned out to be a bigger challenge than I had envisaged. On the way I discovered that currently, there is a limit of 16216 bytes on a workspace in Sonic Pi 2, and if you exceed this limit it won’t play. In the event, by ditching nearly all the comments, and rewriting some code more efficiently in space terms, I managed to complete the project with 37 bytes to spare! I would still like to add a few further notes right at the end, but it is quite performable “as is”. It also was quite a challenge to work out how to play the parts, with long running sections of semiquavers for both Xylophone and Piano, as well as sections with chords on the piano. I had to write all the functions to deal with playing both of these, written ultimately to call a sample command for every note played. Five different parts were played using threads, One of these parts played all the chords. It was quite a long job debugging all the notes and timings, as inevitably mistakes were made initially in writing these. All the threads played together, but the contents of some were delayed, sometimes for 80 bars, and others had big gaps in the middle between different played sections. I had to keep the number of notes in the chords to 3, so that Sonic Pi could cope. The piece also plays with a long set_sched_ahead_time! of 12 seconds, so each change took some time to check. I also included the ability to transpose the whole piece (I couldn’t use the normal with_transpose command but had to write my own sample based one). This was not just for fun, but because the top notes in the Xylophone register didn’t sound so realistic, so I transposed 7 semitones down. (It is am amazing feeling to change one number and have all those notes shift!)
I will not release the program at this time, because I would wish to tidy it up and add some comments in again first, once Sam Aaron has overcome the space limitation (the same in the Mac version but a lower limit of about 9000 characters applies) which he hopes to do for version 2.1 of Sonic Pi. However until then you can enjoy the audio, recorded from Sonic Pi 2.0.1 playing on a Raspberry Pi, and then converted from a .wav to a .mp3 file using the open source editor Audacity.

Enjoy!                       The Flight of the Bumble Bee for Sonic Pi 2

Advertisements

Grand Piano for Sonic Pi: improved performance

If you have looked at my articles on adding a sample based Grand Piano voice to Sonic Pi, then adding the following three lines to the programs immediately after the sam array is defined will improve performance. The lines use a loop to preload all the samples before the pieces start to play, and so they become immediately available when required to play a note.

sam.each do |s|
load_sample s[1]
end

basically the loop iterates around the entries in the sam array, and takes the SECOND entry (offset 1) which is the name of the sample and uses the load_sample command to preload it into memory.

You can add this to all the programs in the article.

Grand Piano voices (two of them!) finalised for Sonic Pi 2

I have now managed to complete the work on setting up a sample-based Grand Piano voice for Sonic Pi 2. The sample base (Sonatina Symphonic Orchestra – SSO) contained two sets of samples, one for a piano being played forte, and one for it being played softly piano, so I have catered for both (currently you select one or the other in the program).
Previously I was having difficulty with the tuning of the samples, but I have now used a different method which gives perfect tuning across the entire 7 octave range. An associated article here explains how things are set up, and includes a base program with which you can play with the sample voices and try out your own programs, and four completed works utilising the voices which I have reworked from previous pieces set up to use the built in synths in Sonic Pi 2.

To whet your appetite, recordings of a Raspberry Pi playing these with SonicPi 2 are below.

Bach Minuet in G

Bach Prelude in C

Scott Joplin Maple Leaf Rag (without the trio section)

That’s the lot for just now, as other commitments will prevent me working on Sonic Pi during the coming week.

Work in progress: Grand Piano sample based voice for Sonic Pi

Piano

Following the successful development of a sample based flute over three octaves for Sonic Pi 2, I have today embarked on the more ambitious project of producing a 7 octave sample based Grand Piano voice for Sonic Pi 2. The technique described in my previous article is the same as for the Flute voicing as is the source of the samples from the Sonatina Symphonic Orchestra (SSO). However the scale is much bigger with 7 octaves to cover. I made use of the automator app on my Mac to do global renaming of the samples (whose names contained – and # characters), the TextWrangler editor to set up tab separated list of the parameters from the .sfz file from the SSO containing the sample names and associated notes, and an Excel Spreadsheet to calculate the entries for the sam array using the same format as in the Flute program featured here. There were various tweaks and changes necessary in the remainder of the program. The samples worked better with a zero attack time, and there was no need for the “bodge” to deal with “breathy” notes as occurred with the flute samples.
There were no fine tuning “cent” numbers for these samples in the .sfz file, but in practice I found it necessary to use some as the tuning was not very good. In fact, although the present state of affairs is not too bad, the values require further tweaking before I would want to release the program. However results are encouraging, and I have converted one or two of my previous programs which played with synths to utilise the sample based piano voice.
One unfortunate problem is that the files can become quite large, and on the Mac version of Sonic Pi 2 I have with one or two of the programs hit the limit of just over 9000 characters above which it will not play the files. Sam Aaron is working on the required fix, but it will not be available until a later version. So at present some of the files, including the one I am featuring today, will only work on a Raspberry Pi.

Below are three recordings I made tonight of a Bach Prelude in C, which uses the new voice. It also illustrates the use of crescendos an diminuendos and a rit in the music. The code I have written for the voice can handle transposition, and so to illustrate the range of the piano I have a recording at the normal setting, one transposed an octave down and one transposed two octaves up. For comparison I also include a recording of Sonic Pi playing the previous version of the piece using built in Sonic Pi synths.

Prelude at normal pitch with sample Grand Piano

Prelude one octave down with sample Grand Piano

Prelude two octaves up with sample Grand Piano

Original Prelude played with two synths

I hope to add further details of this sample voice, including the program at a later date

sample based flute voice for Sonic Pi 2: update

The eagle eyed amongst you who have read my article on adding a sample based flute voice to Sonic Pi 2 may have noticed that I have missed out a few notes in the range covered by the “instrument”. Although I added aliases for the flat notes :db3,: gb4, :gb4 etc I omitted to cater for all the e sharp and and b sharp notes, which will of course render the instrument difficult to use for those composing in key signatures with six or seven sharps!!

The solution is easy. You simply have to add a further six entries to the flat and sharp arrays which are used to generate the additional entries  the array named “extra”. So we want :es3 to be played by :f3, :es4 by :f4, :es5 by :f5 and :bs3 by :c4 (note the octave number change), :bs4 by :c5 and :bs5 by :c6

This is done by adding three extra lines into the program which are shown in context after the existing definitions for the flat and sharp arrays below:

#note aliases
flat=[:db3,:eb3,:fb3,:gb3,:ab3,:bb3,:cb3,:db4,:eb4,:fb4,:gb4,:ab4,:bb4,:cb4,:db5,:eb5,:fb5,:gb5,:ab5,:bb5,:cb5]
sharp=[:cs3,:ds3,:e3,:fs3,:gs3,:as3,:b3,:cs4,:ds4,:e4,:fs4,:gs4,:as4,:b4,:cs5,:ds5,:e5,:fs5,:gs5,:as5,:b5]
#add es and bs with aliases
flat.concat [:es3,:es4,:es5,:bs3,:bs4,:bs5]
sharp.concat [:f3,:f4,:f5,:c4,:c5,:c6]

There is one further correction resulting from this. :bs5 uses the “breathy” sample :c6, so we have to add it to the list of notes which are corrected in the pl definition, so that they sample starts playing at the 20% mark. The change to the pl definition is shown below.

#definition to play a sample "note" specify note, duration,volume as parameters
define :pl do |n,d=0.2,pan=0,v=0.8|
 st = 0
 if [:b5,:bs5,:c6,].include? n then #bodge for :b5,:bs5 and :c6. Too breathy so cut the start
 st = 0.2
 end
 sample (sam.assoc(n)[1]),rate: (sam.assoc(n)[2]),attack: d/50,sustain: d*0.88,release: d*0.1,amp: v,pan: pan,start: st
end

Finally I added a comment #version 2 at the top of the program, so that you can tell if you are using the original, or this amended version.

#version 2

It should be added that the original works perfectly well with the example piece featured. This change will only have an effect if you use the setup for the new flute voice to play other music which features these extra note definitions.

The complete version 2 file is shown below:

#defining a flute sample based instrument for Sonic Pi, and playing a Canon for two flutes by Telemann
#version 2
#use_sample_pack '/Users/rbn/Desktop/samples/flute/'
use_sample_pack '/home/pi/samples/flute/'

define :rateoffset do |s,c=0|#first parameter semitone shift, second correction in cents (1/1200 of an octave)
 case s
 when 0
 val=1
 when -1
 val=(1-0.5/12)
 when 1
 val=(1+1.0/12)
 end
 return val*(1+c.to_f/1200) #make sure cent value is a float to allow division
end

sam=[[:c3,:flute_c3,rateoffset(0)],[:cs3,:flute_c3,rateoffset(1)]]
sam.concat [[:d3,:flute_ds3,rateoffset(-1)],[:ds3,:flute_ds3,rateoffset(0)],[:e3,:flute_ds3,rateoffset(1)]]
sam.concat [[:f3,:flute_fs3,rateoffset(-1)],[:fs3,:flute_fs3,rateoffset(0)],[:g3,:flute_fs3,rateoffset(1,-15)]]
sam.concat [[:gs3,:flute_a3,rateoffset(-1)],[:a3,:flute_a3,rateoffset(0)],[:as3,:flute_a3,rateoffset(1)]]
sam.concat [[:b3,:flute_c4,rateoffset(-1,-16)],[:c4,:flute_c4,rateoffset(0)],[:cs4,:flute_c4,rateoffset(1,-16)]]
sam.concat [[:d4,:flute_ds4,rateoffset(-1,-7)],[:ds4,:flute_ds4,rateoffset(0)],[:e4,:flute_ds4,rateoffset(1,-7)]]
sam.concat [[:f4,:flute_fs4,rateoffset(-1,-4)],[:fs4,:flute_fs4,rateoffset(0)],[:g4,:flute_fs4,rateoffset(1,-4)]]
sam.concat [[:gs4,:flute_a4,rateoffset(-1,-17)],[:a4,:flute_a4,rateoffset(0)],[:as4,:flute_a4,rateoffset(1,-17)]]
sam.concat [[:b4,:flute_c5,rateoffset(-1,-4)],[:c5,:flute_c5,rateoffset(0)],[:cs5,:flute_c5,rateoffset(1,-4)]]
sam.concat [[:d5,:flute_ds5,rateoffset(-1,-8)],[:ds5,:flute_ds5,rateoffset(0)],[:e5,:flute_ds5,rateoffset(1,-8)]]
sam.concat [[:f5,:flute_fs5,rateoffset(-1,-16)],[:fs5,:flute_fs5,rateoffset(0)],[:g5,:flute_fs5,rateoffset(1,-16)]]
sam.concat [[:gs5,:flute_a5,rateoffset(-1,-17)],[:a5,:flute_a5,rateoffset(0)],[:as5,:flute_a5,rateoffset(1,-17)]]
sam.concat [[:b5,:flute_c6,rateoffset(-1,-18)],[:c6,:flute_c6,rateoffset(0)]]

#note aliases
flat=[:db3,:eb3,:fb3,:gb3,:ab3,:bb3,:cb3,:db4,:eb4,:fb4,:gb4,:ab4,:bb4,:cb4,:db5,:eb5,:fb5,:gb5,:ab5,:bb5,:cb5]
sharp=[:cs3,:ds3,:e3,:fs3,:gs3,:as3,:b3,:cs4,:ds4,:e4,:fs4,:gs4,:as4,:b4,:cs5,:ds5,:e5,:fs5,:gs5,:as5,:b5]
#add es and bs with aliases
flat.concat [:es3,:es4,:es5,:bs3,:bs4,:bs5]
sharp.concat [:f3,:f4,:f5,:c4,:c5,:c6]
extra=[]
flat.zip(sharp).each do |f,s|
 extra.concat [[f,(sam.assoc(s)[1]),(sam.assoc(s)[2])]]
end
sam = sam + extra #add in flat definitions

#definition to play a sample "note" specify note, duration,volume as parameters
define :pl do |n,d=0.2,pan=0,v=0.8|
 st = 0
 if [:b5,:bs5,:c6,].include? n then #bodge for :b5,:bs5 and :c6. Too breathy so cut the start
 st = 0.2
 end
 sample (sam.assoc(n)[1]),rate: (sam.assoc(n)[2]),attack: d/50,sustain: d*0.88,release: d*0.1,amp: v,pan: pan,start: st
end

define :ntosym do |n| #this returns the equivalent note symbol to an input integer e.g. 59 => :b4
 #nb no error checking on integer range included
 #only returns notes as n or n sharps.But will sound ok for flats
 @note=n % 12
 @octave = n / 12 - 1
 #puts @octave #for debugging
 #puts @note
 lookup_notes = {
 0 => :c,
 1 => :cs,
 2 => :d,
 3 => :ds,
 4 => :e,
 5 => :f,
 6 => :fs,
 7 => :g,
 8 => :gs,
 9 => :a,
 10 => :as,
 11 => :b}
 return (lookup_notes[@note].to_s + @octave.to_s).to_sym #return the required note symbol
end

define :tr do |nv,shift| #this enables transposition of the note. Shift is number of semitones to move
 if shift ==0 then
 return nv
 else
 return ntosym(note(nv)+shift)
 end
end

s = 1.0 / 20 #s is speed multiplier
#note use of 1.0
dsq = 1 * s
sq = 2 * s
sqd = 3 * s
q = 4 * s
qt = 2.0/3*q
qd = 6 * s
qdd = 7 * s
c = 8 * s
cd = 12 * s
cdd = 14 * s
m = 16 * s
md = 24 * s
mdd = 28 * s
b = 32 * s
bd = 48 * s

define :plarray do |nt,dur,shift=0,pan=0| #This plays associated arrays of notes and durations, transposing if set, and handling rests
 nt.zip(dur).each do |n,d|
 if n != :r then
 puts n
 pl(tr(n,shift),d,pan)
 end
 sleep d
 end
end

#set up the note and duration arrays here
tn = [:r,:d5,:g5,:fs5,:e5,:d5,:c5,:b4,:c5,:b4,:a4,:b4,:g4,:a4,:d4,:r]
td = [c,c,m,q,q,q,q,q,q,q,q,q,q,c,c,c]
#bar 3
tn.concat [:r,:d5,:g5,:fs5,:g5,:a5,:b5,:c6,:b5,:a5,:b5,:c6,:a5,:g5,:fs5,:g5,:a5,:g5,:fs5,:g5,:a5,:fs5,:d5,:e5,:fs5,:g5,:a5,:b5,:c6,:b5,:a5,:b5,:c6,:a5,:r]
td.concat [c,c,m,q,q,q,q,c,q,q,q,q,cd,q,q,q,c,q,q,q,q,q,sq,sq,q,q,q,q,c,q,q,q,q,m,c]
#bar 7
tn.concat [:r,:d5,:d5,:d5,:d5,:d5,:d5,:d4,:d5,:d4,:d5,:c5,:b4,:a4,:b4,:c5,:b4,:a4,:b4,:g4,:c5,:a4,:b4,:c5,:b4,:a4,:b4,:g4,:a4,:fs4]
td.concat [q,q,q,q,q,q,q,q,q,q,sq,sq,sq,sq,sq,sq,sq,sq,q,q,q,q,sq,sq,sq,sq,q,q,q,q]
#bar 9
tn.concat [:g4,:g5,:a5,:fs5,:g5,:b4,:c5,:a4,:b4,:c5,:b4,:a4,:b4,:e5,:fs5,:ds5,:e5,:fs5,:e5,:ds5,:e5,:g5,:fs5,:e5,:ds5,:fs5,:b4,:r,:b4,:g5,:b4,:r]
td.concat [cd,q,q,q,cd,q,q,q,sq,sq,sq,sq,q,q,q,q,sq,sq,sq,sq,q,q,q,q,q,q,c,c,q,q,c,c]
#bar 12
tn.concat [:b4,:a5,:b5,:a5,:g5,:fs5,:g5,:b5,:a5,:g5,:fs5,:e5,:fs5,:b4,:ds5,:e5,:e4,:r,:r,:r,:a5,:gs5,:a5,:b5]
td.concat [q,c,sq,sq,q,q,q,c,sq,sq,q,q,c,c,c,c,c,c,c,c,m,q,q,c]
#bar 15
tn.concat [:e5,:c6,:b5,:c6,:a5,:b5,:e5,:d5,:c5,:b4,:a4,:r,:r,:r,:gs5,:a5,:e5,:cs5,:a4,:e5,:d5,:cs5,:d5,:a4,:fs4,:d4,:d5,:cs5,:b4]
td.concat [c,q,q,q,q,c,c,c,q,q,c,c,c,c,c,q,q,q,q,q,sq,sq,q,q,q,q,q,sq,sq]
#bar 18
tn.concat [:cs5,:e5,:g5,:fs5,:e5,:d5,:r,:r,:a4,:a4,:a4,:a4,:b4,:cs5,:a4,:d5,:d4,:d5,:d5,:d5,:e5,:fs5,:d5,:g5,:r,:g5,:fs5,:e5,:d5,:c5]
td.concat [c,c,c,q,q,c,c,q,q,q,q,sq,sq,sq,sq,q,q,q,q,sq,sq,sq,sq,c,c,m,q,q,q,q]
#bar 21
tn.concat [:b4,:c5,:b4,:a4,:b4,:g4,:a4,:d4,:r,:r,:d5,:g5,:fs5,:g5,:a5,:b5,:c6,:b5,:a5,:b5,:c5,:a5,:g5,:fs5,:g5]
td.concat [q,q,q,q,q,q,c,c,c,c,c,m,q,q,q,q,c,q,q,q,q,cd,q,q,q]
#bar 24
tn.concat [:a5,:g5,:fs5,:g5,:a5,:fs5,:d5,:e5,:fs5,:g5,:a5,:b5,:c6,:b5,:a5,:b5,:c6,:a5,:r,:r,:d5,:d5,:d5,:d5,:d5,:d5,:d4,:d5,:d4,:d5,:c5,:b4,:a4]
td.concat [c,q,q,q,q,q,sq,sq,q,q,q,q,c,q,q,q,q,m,c,q,q,q,q,q,q,q,q,q,q,sq,sq,sq,sq]
#bar 27
tn.concat [:b4,:c5,:b4,:a4,:b4,:g4,:c5,:a4,:b4,:c5,:b4,:a4,:b4,:g4,:a4,:fs4,:g4,:g5,:a5,:fs5,:g5,:b4,:c5,:a4,:as4,:c5,:a4,:as4,:r,:b4,:c5,:a4,:b4,:r]
td.concat [sq,sq,sq,sq,q,q,q,q,sq,sq,sq,sq,q,q,q,q,cd,q,q,q,cd,q,q,q,qt,qt,qt,c,c,qt,qt,qt,c,c]
#bar 30
tn.concat [:g5,:a5,:fs5,:g5,:a5,:fs5,:g5,:a5,:fs5,:g5,:fs5,:e5,:d5,:c6,:b5,:c6,:a5,:b5,:c6,:a5,:b5,:c6,:a5,:b5,:a5,:g5,:a5,:fs5]
td.concat [qt,qt,qt,qt,qt,qt,qt,qt,qt,cd,sq,sq,q,q,qt,qt,qt,qt,qt,qt,qt,qt,qt,q,sq,sq,c,c]
#bar 32
tn.concat [:g5,:d5,:g5,:d5,:g5,:d5,:g5,:b4,:c5,:a4,:g4,:b4,:a4,:g4,:b4,:a4,:g4,:b4,:a4,:g4,:a4,:fs4,:g4,:r,:r]
td.concat [q,q,q,q,q,q,q,q,c,c,q,sq,sq,q,sq,sq,q,sq,sq,c,c,c,m,c,m]
puts tn.length #debugging check notes and duration arrays have same lengths
puts td.length

#play the canon here ==================================
in_thread do #start the second part of the canon after 6 beat rest in thread transposed 2 semitones down
 sleep 6*c
 plarray(tn,td,-2,-0.8) #transpose -2 pan -0.8
end
plarray(tn,td,-2,0.8) #play the main canon part transposed 2 semitones down pan +0.8
#end==============================

The new version, together with the samples can be downloaded from here.

A new sample based flute voice for Sonic Pi 2 plays Telemann

Following the discovery of the Sonatina Symphonic Orchestra sample library, I have used some of the samples to set up a new sample based “voice” for Sonic Pi 2, and written code so that it can be played just like one of the built in synths. I have written a full article explaining how this has been done, and included as an example the first movement of one of Telemann’s Canons for two flutes.

Telemann_4

You can hear the piece here, and read the article, which includes download details so that you can try it out on your own Sonic Pi 2 program here.

HDMIPi video of the construction of a lego support for the monitor

hdmipi stand

Yesterday was a bit of a red letter day for me. The long awaited Kickstarter pledged HDMIPi monitor kit arrived, as also, earlier than expected, did an iPhone 6 pre-ordered from Apple. As you can imagine the day was quite busy. Setting up the iPhone, configuring my old iPhone 4 for my Wife to use, working out how to transfer her contacts from an even older pre-“smart” Nokia phone to the iPhone 4, changing all the numbers over, and….finding time to assemble the HDMIPi. I had already watched Alex Eames’ splendid Assembly Guide video, and although I didn’t have his white gloves, I did have a nice lint free cloth supplied with the screen film guard that I had got for the iPhone!
I decided not to include a Raspberry Pi inside the monitor structure, as I have more than one, and would like to use the monitor with different ones at different times. I had not made one of the top-end pledges for the HDMIPi and so did not have a monitor stand supplied in the kit, so having quite a lot of technical Lego (Mindstorms is another of my interests) I decided to design and build a Lego stand for it. I am quite pleased with the result, which is robust, very easy to fit, and does the job nicely. I think that it may of interest to others wanting to get a suitable stand, so I decided to film its construction using the new iPhone 6 which I have done. I hope that the result gives sufficient information for others to follow suit.
You can see the video here