Sonic Pi plays church bells, using a sample based chimes voice

1280px-StHelenWittonBelfry1

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

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