Fun with Sonic Pi 2 samples

One of the nice features of Sonic Pi is that it can play audio samples, and indeed it is supplied bundled with some 73 different samples. However it is also able to accept external samples supplied by the user as long as they are saved as .wav files.
Sonic Pi not only lets you play the samples, but it is able to manipulate them by changing the rate at which they are played back, or by using just a portion of the sample, or by modifying it with an envelope to change the amplitude as it plays.
I have been working on a program to demonstrate some of the possibilities…there are others that are not covered: like playing a sample backwards, or doing more advanced envelope shaping using the envelope curve parameter.
However I hope what I have produced will illustrate some of the possibilities and encourage you to have a go, using either the supplied samples, or external ones that you add yourself.
In this program I use 6 samples that I have recorded myself. Four of them involve saying (or singing) the word hello at four different pitches. Two of them just record messages that are played back verbatim. The other four are taken from a library of samples that I discovered recently on the internet. It is called the Sonatina Symphonic Orchestra and can be found here.
On other useful addition is to become familiar with an audio editor like the excellent open source Audacity editor. This is available free for most platforms including Windows, Mac and Raspberry Pi. You can install it on Raspberry Pi with

sudo apt-get update
sudo apt-get install audacity

It is very useful for selecting portions of a sample, for altering it, e.g. by adding compression or amplifying it, and for saving it in a variety of formats. (We need .wav for Sonic-Pi). So my voice samples were in fact recorded on an iPad as .mp3 files, transferred to Audacity and exported as .wav files. I in fact recorded all the 4 hello samples in one recording and then chopped up the four separate samples from that file.

Having got the samples you want, you make them available to Sonic Pi by putting them in a folder in a known location. On my Pi I put a folder named samples in the Pi user home directory. I then used the command

use_sample_pack '/home/pi/samples'

in the program to tell Sonic Pi where they were. In the Mac version I put the files in a folder called samples on my Desktop. My username on the Mac is rbn, so they were located using the command

use_sample_pack '/users/rbn/Desktop/samples'

The program has a series of defined functions in it which set up the samples in various ways. At the end of the program these are all called one by one to generate a samples performance. Before starting the definitions I set up one or two defined variables to help in the process.

#define intervals in a scale
sc =[ 0.0,2.0,4.0,5.0,7.0,9.0,11.0,12.0]
#define some note timings
s = 1.0 / 20#s is speed multiplier 1.0 / 8 makes crotchet 1s or 60 crotchets/minute the default bpm
q = 4 * s #quaver
c = 8 * s #crotchet
cd = 12 * s #crotchet dotted

The list (or array) sc holds the offsets to the notes of a major scale. For example, if the scale starts on :c5 (which is represented by the midi-note value 72), then the next note is at 72 + 2.0 which is 74, or :d4 The next one is at 72 + 4.0 or 76 which is :e4 and so on.
This is used in one of the definitions which generates a scale of hellos!
Three note duration values are defined for a quaver, crotchet and dotted crotchet using variables q,c and d, together with a temp “speed” multiplier s. This accommodates one of the definitions which produces a drum rhythm.
As an extra to the program I have first added a couple of definitions to play the raw samples which are used in it, so you can hear what the building blocks sound like.

#listen to 1 sample
define :lsample1 do |s|
 sample s
 #wait till the sample has finished plus 0.5 seconds
 sleep (sample_duration s) + 0.5
end
#listen to all samples
define :listenallsamples do
 lsample1(:d1c) #drum
 lsample1(:violg4) #sustained violin
 lsample1(:SP2plays) #speech
 lsample1(:hello1) #4 hellos at different pitches
 lsample1(:hello2)
 lsample1(:hello3)
 lsample1(:hello4)
 lsample1(:TryItIsEasy) #speech
 lsample1(:trumpetsSus) #sustained trumpet note
 lsample1(:trumpetsStac) #staccato trumpet note
end
#uncomment next two lines to listen to all the samples
#listenallsamples
#sleep 1000
#re-comment for normal use

These definitions are not used in the final” performance”, [in fact I DO use the first one] but if you temporarily uncomment the two lines indicated and run the program you will hear the 10 samples played in the order in which they are introduced in the program. The sleep 1000 is just a useful device I often use when testing a program to allow one part to run without progressing to the next: unless you wait 1000 seconds!
The first definition used in the “performance” plays a chord of four violins playing together.

#violins play a chord, using one sample but four rates
define :startchord do
 sample :violg4,attack: 0.1,sustain: 1.4,release: 0.5,pan: -0.8
 sample :violg4,attack: 0.1,sustain: 1.4,release: 0.5,rate: 1.25,pan: -0.2
 sample :violg4,attack: 0.1,sustain: 1.4,release: 0.5,rate: 1.5,pan: 0.2
 sample :violg4,attack: 0.1,sustain: 1.4,release: 0.5,rate: 2,pan: 0.8
 sleep 2
end

Here we start to manipulate the sample, both by adjusting the rate: parameter and by setting an overall envelope. Normally a sample plays for its entire duration once initiated. If you do NOT set a sustain: value, then any other parameters, eg release: are taken out of the overall duration, and the sustain: value is dynamically adjusted so that the entire sample plays. In this case the sample is quite long, and I want a shorter sound. By using the three attack, sustain and release values shown I get a total sample play time of 2 seconds. By default the first sample plays at normal pitch. The remaining three have rate values of 1.25,1.5 and 2 added. This causes the rate or speed at which the sample is played to increase by the factor given. Doubling the rate will double the pitch of the note, putting it up an octave. 1.25 puts it up a third and 1.5 up a fifth. The sample is long enough so that it doesn’t finish even though it is played quicker. You just effectively hear more if it inside the defined envelope. The result is that you hear a chord. I have also added a pan command to each sample, adjusting its position in the stereo spectrum from 80% left to 80% right.
The next two definitions deal with the trumpet parts. The first produces a short trumpet fanfare of five notes: A long note, three shorter notes and a long note to finish. The second (trumpetsgo) uses this as a building block and by altering the rate of the fanfare notes, plays an arpeggio and a chord of trumpets, the whole being placed in a fx loop to add a little reverberation. Two different trumpet samples are used, one for the sustained notes and another for the shorter stacatto ones.

#trumpet fanfare using separate samples for a stacatto and a sustained trumpet
define :fanfare do |d,p|
 sample :trumpetsSus,attack: 0,sustain: 0.8,release: 0.2,amp: 3,rate: d,pan: p
 sleep 1.0
 3.times do
 sample :trumpetsStac,attack: 0,sustain: 0.15,release: 0.05,amp: 1,rate: d,pan: p
 sleep 0.15
 end
 sample :trumpetsSus,attack: 0,sustain: 1,release: 0.5,amp: 3,rate: d,pan: p
 sleep 1.5
end

#complete fanfare with four trumpets. One sample used with four rates
#also distrubuted in stereo spectrum using the pan parameter
define :trumpetsgo do
 #add some reverberation room: sets the amount
 with_fx :reverb, room: 0.8 do
 #play seperately...
 fanfare(1,-1)
 fanfare(1.25,1)
 fanfare(1.5,-0.5)
 fanfare(2,0.5)
 #then play together using threads...
 in_thread do
 fanfare(1,-1)
 end
 in_thread do
 fanfare(1.25,1)
 end
 in_thread do
 fanfare(1.5,-0.5)
 end
 fanfare(2,0.5)
 end
end

Four definitions are implemented to use with the hello samples. The first uses the hello1 sample and adjusts its rate to play a scale of hellos. It uses the sc list previously defined to work out the rates required. There are 12 semitones in a scale, and dividing the offset by 12 and adding 1 gives the rate factor required for each note in the scale. the construct sc.each do |n|…..end traverses round this loop, each time reading the next value in the list sc and assigning it to the variable n. The pan: choose([01,-0.5,0.5,1]) chooses the stereo position of each note at random from the four positions listed.

#build a scale using one hello sample and adjusting the rate for each note, random pan position
define :helloscale do
 sc.each do |n|
 sample :hello1,rate: (1 + n/12),pan: choose([-1,-0.5,0.5,1])
 sleep 0.5
 end
end

The second hello procedure randhellos plays 15 hellos, choosing one of the four samples at random and adjusting its pitch and stereo position also at random. It makes use of a case statement to choose which sample to use. The value of p is generated at random in the range 1-4 and the appropriate section of the case statement is executed accordingly, the other sections being ignored. The rate is chosen by selecting one of the pitches in the scale using (1+sc.choose/12). The pan position is chosen as in the previous definition above. There are random gaps between the various hellos sleep rrand(0.4,1.2)

#play 15 hellos at random pitches and pan positions, selecting from four samples and different rates
define :randhellos do
 15.times do
 #use a case statement to select the sample at random
 p=rrand_i(1,4)
 case p
 when 1
 sample :hello1,rate: (1+sc.choose/12),pan: choose([-1,-0.5,0.5,1])
 when 2
 sample :hello2,rate: (1+sc.choose/12),pan: choose([-1,-0.5,0.5,1])
 when 3
 sample :hello3,rate: (1+sc.choose/12),pan: choose([-1,-0.5,0.5,1])
 when 4
 sample :hello4,rate: (1+sc.choose/12),pan: choose([-1,-0.5,0.5,1])
 end
 sleep rrand(0.4,1.2)
 end
end

There are four hello samples sung and recorded at different pitches. Sounded together they naturally produce a chord. The next two definitions play that chord, and also a sequence of chords by altering the rates of the samples. Again the pan position is adjusted. as with the other hello definitions no envelope is set so the whole sample is played.

#the samples hello1-4 were recorded at different pitches to give a chord
#this plays the chord, but can select the rate and pan position
define :schord do |r,p|
 sample :hello1,rate: r,pan: p
 sample :hello2,rate: r,pan: p
 sample :hello3,rate: r,pan: p
 sample :hello4,rate: r,pan: p
end
#this plays three hello chords going up an arpeggio, ranged round the stereo spectrum
define :chordsequence do
 schord(1,-1)
 sleep 1
 schord(1.5,0)
 sleep 1
 schord(2,1)
 sleep 1
end

The final two definitions deal with the drum sample. The first plays a single drum note, but has a parameter to adjust the volume. This is used in the second, which produces a sequence of drum strikes to a set rhythm. This uses note durations specified as crotchets, and quavers. I could have specified raw time values, but this function was “lifted’` from a previous program (Purcell’s Funeral Music) and it was convenient to leave this format.

#plays a drum sample
define :bass1 do |x,amp = 4|
 sample :d1c,amp: amp
 sleep x
end
#plays 4 drum notes in a rhythm. Varies the amplitude of each note
define :rbass do
 bass1(cd,4)
 bass1(q,2)
 bass1(c,3)
 bass1(c,4)
 bass1(c,4)
end

So having discussed the functions that are defined for this program, we can look at how they are all called to give the final “performace”

#start playing here =============
bass1(c)
startchord
lsample1(:SP2plays)
randhellos
sleep 0.5
helloscale
sleep 1
chordsequence
rbass
sleep 0.5
lsample1(:TryItIsEasy)
trumpetsgo
rbass
#===========the end =============

The entire program is listed below. Because of the samples required, if you want to try it out you need to download a zip file containing the program and a folder containing the samples. A README file is included describing how to install them. The download is here.

#fun with Sonic Pi 2 samples, coded by Robin Newman September 2014
#tested on Sonic Pi 2.0.1
#apart from the voice recordings of yours truly, the samples here
#all orginate in the Sonatina Symphonic Orchestra, a free to use CCL licence
#repository at http://sso.mattiaswestlund.net/
#some of the instrument samples are modified using the open source Audacity program
#available for Mac, Windows or Linux
#required here: d1c,hello1,hello2, hell3,hello4,Sp2plays,trumpetsStac,trumpetsSus,TryItIsEasy,violg4
#these should all be put in the samples folder specified below

set_sched_ahead_time! 1
#adjust the location below for the location of the samples
use_sample_pack '/home/pi/samples' #on a Raspberry Pi
#use_sample_pack '/users/rbn/Desktop/samples' #on a Mac (adjust for username)

#define intervals in a scale
sc =[ 0.0,2.0,4.0,5.0,7.0,9.0,11.0,12.0]
#define some note timings
s = 1.0 / 20#s is speed multiplier 1.0 / 8 makes crotchet 1s or 60 crotchets/minute the default bpm
q = 4 * s #quaver
c = 8 * s #crotchet
cd = 12 * s #crotchet dotted

#define functions used
#listen to 1 sample
define :lsample1 do |s|
 sample s
 #wait till the sample has finished plus 0.5 seconds
 sleep (sample_duration s) + 0.5
end
#listen to all samples
define :listenallsamples do
 lsample1(:d1c) #drum
 lsample1(:violg4) #sustained violin
 lsample1(:SP2plays) #speech
 lsample1(:hello1) #4 hellos at different pitches
 lsample1(:hello2)
 lsample1(:hello3)
 lsample1(:hello4)
 lsample1(:TryItIsEasy) #speech
 lsample1(:trumpetsSus) #sustained trumpet note
 lsample1(:trumpetsStac) #staccato trumpet note
end
#uncomment next two lines to listen to all the samples
#listenallsamples
#sleep 1000
#re-comment for the main program

#violins play a chord, using one sample but four rates
define :startchord do
 sample :violg4,attack: 0.1,sustain: 1.4,release: 0.5,pan: -0.8
 sample :violg4,attack: 0.1,sustain: 1.4,release: 0.5,rate: 1.25,pan: -0.2
 sample :violg4,attack: 0.1,sustain: 1.4,release: 0.5,rate: 1.5,pan: 0.2
 sample :violg4,attack: 0.1,sustain: 1.4,release: 0.5,rate: 2,pan: 0.8
 sleep 2
end

#trumpet fanfare using separate samples for a stacatto and a sustained trumpet
define :fanfare do |d,p|
 sample :trumpetsSus,attack: 0,sustain: 0.8,release: 0.2,amp: 3,rate: d,pan: p
 sleep 1.0
 3.times do
 sample :trumpetsStac,attack: 0,sustain: 0.15,release: 0.05,amp: 1,rate: d,pan: p
 sleep 0.15
 end
 sample :trumpetsSus,attack: 0,sustain: 1,release: 0.5,amp: 3,rate: d,pan: p
 sleep 1.5
end

#complete fanfare with four trumpets. One sample used with four rates
#also distrubuted in stereo spectrum using the pan parameter
define :trumpetsgo do
 #add some reverberation room: sets the amount
 with_fx :reverb, room: 0.8 do
 #play seperately...
 fanfare(1,-1)
 fanfare(1.25,1)
 fanfare(1.5,-0.5)
 fanfare(2,0.5)
 #then play together using threads...
 in_thread do
 fanfare(1,-1)
 end
 in_thread do
 fanfare(1.25,1)
 end
 in_thread do
 fanfare(1.5,-0.5)
 end
 fanfare(2,0.5)
 end
end

#build a scale using one hello sample and adjusting the rate for each note, random pan position
define :helloscale do
 sc.each do |n|
 sample :hello1,rate: (1 + n/12),pan: choose([-1,-0.5,0.5,1])
 sleep 0.5
 end
end
#play 15 hellos at random pitches and pan positions, selecting from four samples and different rates
define :randhellos do
 15.times do
 #use a case statement to select the sample at random
 p=rrand_i(1,4)
 case p
 when 1
 sample :hello1,rate: (1+sc.choose/12),pan: choose([-1,-0.5,0.5,1])
 when 2
 sample :hello2,rate: (1+sc.choose/12),pan: choose([-1,-0.5,0.5,1])
 when 3
 sample :hello3,rate: (1+sc.choose/12),pan: choose([-1,-0.5,0.5,1])
 when 4
 sample :hello4,rate: (1+sc.choose/12),pan: choose([-1,-0.5,0.5,1])
 end
 sleep rrand(0.4,1.2)
 end
end
#the samples hello1-4 were recorded at different pitches to give a chord
#this plays the chord, but can select the rate and pan position
define :schord do |r,p|
 sample :hello1,rate: r,pan: p
 sample :hello2,rate: r,pan: p
 sample :hello3,rate: r,pan: p
 sample :hello4,rate: r,pan: p
end
#this plays three hello chords going up an arpeggio, ranged round the stereo spectrum
define :chordsequence do
 schord(1,-1)
 sleep 1
 schord(1.5,0)
 sleep 1
 schord(2,1)
 sleep 1
end
#plays a drum sample
define :bass1 do |x,amp = 4|
 sample :d1c,amp: amp
 sleep x
end
#plays 4 drum notes in a rhythm. Varies the amplitude of each note
define :rbass do
 bass1(cd,4)
 bass1(q,2)
 bass1(c,3)
 bass1(c,4)
 bass1(c,4)
end

#start playing here =============
bass1(c)
startchord
lsample1(:SP2plays)
randhellos
sleep 0.5
helloscale
sleep 1
chordsequence
rbass
sleep 0.5
lsample1(:TryItIsEasy)
trumpetsgo
rbass
#===========the end =============

The End!…but do have a go yourself and experiment further. It’s great fun!

Leave a comment