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.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s