Following on in my series of additional sample based voices for Sonic Pi, this time it is the turn of a set of Chimes. The first part of the program, dealing with setting up the sample based voice is almost identical to that used for the harp voice in my previous project here. For that reason I will not describe the program operation in detail. As before, the samples are taken from the Sonatina Symphonic Orchestra
Having set up the voice, I wondered how best to exploit it in demonstrating what it can do. I decided to emulate a set of church bells ringing a peal. I did not know much about campanology, but a quick look at wikipedia showed that it is a fascination subject. In fact a future project might be to develop programs to calculate some of the “changes” that can be done, although there are already some good programs out there to do that for you. It is in fact a vast subject. In the event I found a Bristol Surprise Major Quarter Peal which lasts about twelve and a half minutes, and I set up the chimes to use the same frequencies as those of the bells in my local Parish Church (St. Peter’s Oundle). The Peal consists of 231 changes in which the order in which the 8 bells are rung changes each time. I won’t go into the rules governing these changes, but there are plenty of sites which can give you information if you want it. I got the details of the peal form this page
The bells are numbered from 1 (the treble bell) down to 8 the Tenor bell) and the 231 8 digit numbers were stored as a continuous string ch
The program traverses this string in a loop and uses it to play the required bell numbers in turn. Unlike the harp and previous voices I have used, it was appropriate in this case not to truncate the sounding of the bells too much. I set the sustain value of each note to 0 and the release time to 1 second. This dampened it a bit, but I found that playing the sample totally unhindered rang for too long for the speed at which I wanted to ring the bells.
One final touch. I introduced a bit of human “wobble” in the timing of the bells, which I think makes it sound a little more realistic. This is done by using
sleep 0.3+rrand(0,0.2) between the bells, instead of sleep 0.4, so that the bells are not exactly timed correctly.
The program is listed below
#samples based chimes voice for Sonic Pi by Robin Newman Ocrtober 2014 #program plays a Quarter Peal of Church Bells #samples from Sonatina Symphonic Orchestra #first part of program defines the Chimes voice, and sets up functions to use it #voice range :b2, to :cs6 use_debug false #use_sample_pack '/Users/rbn/Desktop/samples/Chimes' use_sample_pack '/home/pi/samples/Chimes' set_sched_ahead_time! 4 shift = 0 #set transpose if required vol=1 s = 1.0 / 8#s is speed multiplier rm=2**(1.0/12) #rate multiplier for one semitone higher than sample (twelfth root of 2) rmi = 1.0/rm #rate multiplier for one semitone lower than sample (inverse of rm) #range :a3 to :c6 of notes covered nhash=[]#array to hold instrument note info define :ntosym do |n| #this returns the note symbol for an input integer e.g. 59 => :b4 #nb no error checking #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 #setup array of midi-numbers for samples, and load the samples sl = [] 48.step(84,3) do |i| sl << i load_sample ("chimes_"+ntosym(i).to_s).intern end #populate hash array with note info. Each sample supports three notes sl.each do |n| nhash << [ntosym(n-1),"chimes_"+ntosym(n).to_s,rmi] nhash << [ntosym(n),"chimes_"+ntosym(n).to_s,1] nhash << [ntosym(n+1),"chimes_"+ntosym(n).to_s,rm] end #puts nhash #for debugging purposes #note aliases so you can use :cb2 (plays :b1) etc flat=[:cb3,:db3,:eb3,:fb3,:gb3,:ab3,:bb3,:cb3,:db4,:eb4,:fb4,:gb4,:ab4,:bb4,:cb4,:db5,:eb5,:fb5,:gb5,:ab5,:bb5,:cb5,:db6] sharp=[:b2,:cs3,:ds3,:e3,:fs3,:gs3,:as3,:b3,:cs4,:ds4,:e4,:fs4,:gs4,:as4,:b4,:cs5,:ds5,:e5,:fs5,:gs5,:as5,:b5,:cs6] #add es and bs with aliases so you can use :es2 (plays :f2) etc flat.concat [:es3,:es4,:es5,:bs2,:bs3,:bs4,:bs5] sharp.concat [:f3,:f4,:f5,:c3,:c4,:c5,:c6] #build extra items for nhash array extra=[] flat.zip(sharp).each do |f,s| extra.concat [[f,(nhash.assoc(s)[1]),(nhash.assoc(s)[2])]] end nhash = nhash + extra #add in flat definitions #definition to play a sample "note" specify note, duration,pan,volume,damping as parameters #becuase of teh bell characteristic the default is to set a release of 1 second on the chime #can be overriden by setting nodamp to 0 #this can be adjusted to suite piece being played define :pl do |n,d=0.2,pan=0,v=0.8,nodamp=1| if nodamp!=1 sample (nhash.assoc(n)[1]).intern,rate: (nhash.assoc(n)[2]),attack: 0,sustain: d*0.95,release: d*0.05,amp: v,pan: pan else sample (nhash.assoc(n)[1]).intern,rate: (nhash.assoc(n)[2]),attack: 0,sustain: 0,release: 1,amp: v,pan: pan end 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 define :plarray do |nt,dur,shift=0,vol=0.6,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 pl(tr(n,shift),d,pan) end sleep d end end #define note durations dsq = 1 * s sq = 2 * s sqd = 3 * s q = 4 * s qd = 6 * s qt = 2.0/3*q 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 :trn do |n,num,offset=2| #produces trill sequence using n and tone (or semitone) above: num notes total @n = note_info(n).midi_note return [ntosym(@n+offset),n]*(num / 2) #trill set to start on higher note. Can be swapped over end define :trd do |d,num| #produces trill note durations. d is total duration, num number of notes return [d/num]*num end define :chromatic do#used for testing purposes puts note(:c3) puts note(:c6) 47.upto(85) do |n| pl(ntosym(n),1) sleep 0.3 end end #chromatic #uncomment to test range #=======================define note arrays. Five parts play together================= obells=[:db3,:eb3,:f3,:gb3,:ab3,:bb3,:c4,:db4] obells=obells.reverse #reverse order to fit usual number of bells (1 treble to 8 tenor) #plarray(obells,[0.5]*8)#uncomment to test puts"Sonic Pi goes bell ringing!" puts" " puts"This program uses a sample based chimes voice to simulate a peal of 8 bells" puts "Bristol Surprise Major Quarter Peal (8 bells)" puts "Bells tuned as the bells in St. Peter's Oundle" puts "Peal listed here http://ringing.org/main/pages/method?name=Bristol&class=4&stage=8" #bell sound order listed in the string ch, cosisting of 231 changes of 8 numbers 1-8 ch="123456782143658712346857214386752413685742316587241356784231576824351786234571683254178635247168534276183524678132547618234567812436587142638517462358716432851746238157426318752436815723461875324168572314658732415678231457682134758612435768213456781243658714263857142638574162837514268735416278534612873564218375461238576421358746231578426351872436157823465187326458172346857124365817426385714628375164827315684237518624731568427135648217534628713542681753246187354216837524613857421635874126537814623587412638571462837516482735164827356184725316487523618457326814752386417253681427358641237568421357648231754628135742683175248637154268735146283715648273516847253186745213876425317846521387645123867415326847512364871532468175236418725346812735641823756148325716842375614827351684725318674523186745238176543218675342817635248716534278615432871645237861425387641235867421536847123564872153467825136487523168472513867452318765432178563412758643215768341275863142785613248765314286751324687153428617543268714523861742538167243518764253816745231876543217856342178563427158362417853264715823467518326457813624751863425781643275861423785641328765142386754132685743128675342187654312785634217583624157382614537862413587261453782164573812467583216478531246875132647815362487516342781564327185462317586432718563421758362415738264157382645137284615732486513742685317248635712846531782643571862453781642573861247583164278536124873562147853264175836214573826415372846135274816325784612375481632574186352714685372418657321468753124865713284675318264571386245173684215378624517382641537284613527486135274863125476813524678312564873215467823514768321574862351784632571864352781465372186457328146752384165732486153728416352748613254768123456718243576814253671824356178234516873254617835241687532146783512476853217486351278463152876413257846315274861325476812345678" puts (ch.length / 8).to_s + " changes" puts "time taken "+(ch.length.to_f * 0.4/60).to_s+" minutes" 0.upto(ch.length - 1) do |i| #loop performs the ringing pl(obells[ch[i].to_i - 1],0.4) #bell index offset by -1 to accommodate usual array index numbering from 0 sleep 0.3+rrand(0,0.2) #put a human "wobble" on exact timing to make it sound more realistic end
You can download a zip file of the program and a folder containing the chimes samples here
You can listen to the start of the Peal recorded from Sonic Pi here
It would ring the bells of Heaven the greatest joy for years if parson lost his senses and the people came to theirs.