Harp sample based voice for Sonic Pi

Harp-Clip-Art-2

Following on from work to create Grand Piano, Flute, Xylophone and Glass Armonica sample based voices for Sonic Pi, I have now completed work on producing a Harp based voice. Learning from the previous voices created, I have streamlined the process a bit, and in this case, starting with 21 harp samples provided from the Sonatina Symphonic Orchestra (SSO). I have generated a 63 note range, calculating the sample rates required to generate the remaining 42 notes within the program. Each sample is used to generate three notes. The recorded sample pitch, and notes 1 semitone below and 1 semitone above this pitch. This is done by multiplying the rate at which the sample is played by the twelfth root of 2 for the upper semitone and dividing it by the same number to get the rate for the lower semitone.
The samples are downloaded from the SSO and renamed to remove embedded – and # symbols (I used Apple’s Automator program to do this on my Mac) to give names like:
harp_c2.wav, harp_fs4.wav instead of harp-c2.wav and harp-fs#.wav
The 21 sample files were then placed in a folder named Harp which was placed in a samples folder accessible to Sonic-Pi. It was located at /home/pi/samples/Harp on my Raspberry-Pi and at /Users/rbn/Desktop/samples/Harp on my iMac. The program also contains code to play a piece transcribed for Lute or Harp by J.S. Bach. The Courante from his Suite in G minor BWV 995. The score is available here

The program is similar to what I have written previously for such instrument voices.
One essential feature, which enables me to convert midi numbers back to a note symbol is the function ntosym. Thus puts ntosym(59) prints :b4. Going the opposite direction puts note(:b4) prints 71 on the screen.

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

The lowest sample was :c2, or midi-note 36, the highest :c6 or 96. I started with an empty array called sl Using the Ruby command 36.step(96,3) this was filled with the numbers
[3,6,9,12…..96]
In the same loop, the harp samples were preloaded into memory, using load_sample(“harp_”+ntosym(i).to_s).intern This rather fearsome command generates the sample names in the form harp_c4  ntosym(i) generates the symbol corresponding to note i and .to_s converts it to a string so that it can be added to the harp_ prefix. Finally .intern converts the combined string eg harp_c6 to a symbol in its own right :harp_c6

#setup array of midi-numbers for samples, and load the samples
sl = []
36.step(96,3) do |i|
  sl << i
  load_sample ("harp_"+ntosym(i).to_s).intern
end

The next step is to make up a hash array containing details of all the notes in the harp range. Each entry has three elements. The note symbolic name, the sample name to be used for that note, and the rate at which it is to be played. eg [:b1, “harp_c2”, 0.9438743126816934]  This means that the note :b1 is to be played with the sample harp_c2 played at a rate of 0.9438743126816934  (1/twelfth root of 2)
using the Ruby << notation the sl array is traversed, giving the note values for the actual samples, and for each one three entries are added to the nhash array, thus adding in the calculated sample values for the other notes in the range.

#populate hash array with note info. Each sample supports three notes
sl.each do |n|
  nhash << [ntosym(n-1),"harp_"+ntosym(n).to_s,rmi] #typical entry [:b1, "harp_c2", 0.9438743126816934]     note,samplename, rate
  nhash << [ntosym(n),"harp_"+ntosym(n).to_s,1] #typical entry [:c2, "harp_c2", 1]
  nhash << [ntosym(n+1),"harp_"+ntosym(n).to_s,rm] #typical entry [:cs2, "harp_c2", 1.0594630943592953]
end

The nhash array at this stage contains values for all the notes, but only in terms of a note and sharp values, eg :c4,:cs4,:d4,:ds4,:e4,:f4,:fs4 etc There are no entries for flat notes eg :db4 or :gb5. Also there are no entries for :es and :bs notes. These are all catered for by adding aliases to the nhash array so that :db2 and :cs2 for example point to the same note, but each has an entry in the final nhash array.

#note aliases so you can use :cb2 (plays :b1) etc
flat=[:cb2,:db2,:eb2,:fb2,:gb2,:ab2,:bb2,:cb3,:db3,:eb3,:fb3,:gb3,:ab3,:bb3,:cb3,:db4,:eb4,:fb4,:gb4,:ab4,:bb4,:cb4,:db5,:eb5,:fb5,:gb5,:ab5,:bb5,:cb5,:db6,:eb6,:fb6,:gb6,:ab6,:bb6,:cb7]
sharp=[:b1,:cs2,:ds2,:e2,:fs2,:gs2,:as2,:b2,:cs3,:ds3,:e3,:fs3,:gs3,:as3,:b3,:cs4,:ds4,:e4,:fs4,:gs4,:as4,:b4,:cs5,:ds5,:e5,:fs5,:gs5,:as5,:b5,:cs6,:ds6,:e6,:fs6,:gs6,:as6,:b6]

#add es and bs with aliases so you can use :es2 (plays :f2) etc
flat.concat [:es2,:es3,:es4,:es5,:es6,:bs1,:bs2,:bs3,:bs4,:bs5,:bs6]
sharp.concat [:f2,:f3,:f4,:f5,:f6,:c2,:c3,:c4,:c5,:c6,:c7]
#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

In order to play the new voice, we need to have equivalents to the Sonic Pi play and play_pattern_timed commands configured to use the sample voice instead of synth notes. I have also written things so that a function  tr can be used instead of the with_transpose command to allow for transposition. This function takes two arguments. The note value as a symbol eg :c4 and a number representing the shift in semitones. It returns the symbolic name of the shifted note.
The pl command accepts arguments for the symbolic note, the duration, the pan value and the volume, although there are defaults set for all but the note.
The plarray command plays two associated arrays, one of notes the other their durations. Extra parameters allow the shift (transpose),volume and pan position to be set. The command uses a technique that allows two arrays to be zipped together, and their corresponding elements to be accessed together. It also caters for a symbolic rest :r, just sleeping for the relevant duration.

#definition to play a sample "note" specify note, duration,volume as parameters
define :pl do |n,d=0.2,pan=0,v=0.8|
  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
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

To enable note durations to be added conveniently I define durations in terms of standard notes eg q (quaver) qd (dotted quaver), c crotchet, m minim etc. The durations can all be adjusted with a speed (or tempo) factor s set at the start of the program.

#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

Two further functions aid the entering of trills. trn generates trill notes and trd trill note durations. Trills can be adjusted for semitone or tone variation and to start on the upper or lower note.

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

The longest part of the program is the note entry section, which sets up arrays for the 5 parts used to play the Bach Courante. Arrays are built up for the notes in each part, and their durations. e.g. in n1 and d1
The five parts are played together using threads so that they sound concurrently. This is accomplished in the function sec. Apart from the single upbeat note, the piece consists of two halves, with each half having a first time and a second time bar to finish it. To keep the programming efficient, all of the parts and their corresponding durations are listed in an array called ar This is then indexed with different starting points to pick out the 5 pairs of values required for a particular section. When the index is 0, then n1,d1,n2,d2,n3,d3,n4,d4 and n5,d5 are fed into sec and played together using threads. If the index is 10, then the values fed in are those for the first repeat bar, if 20 then the values for the first repeat bar second time and so on. By this means the whole piece can be played.

#sec selects and plays 5 related parts and durations from the ar array
define :sec do |n|
  in_thread do
    plarray(ar[n],ar[n+1],shift,vol,0.2)
  end
  in_thread do
    plarray(ar[n+2],ar[n+3],shift,vol,0.2)
  end
  in_thread do
    plarray(ar[n+4],ar[n+5],shift,vol,0.1)
  end
  in_thread do
    plarray(ar[n+6],ar[n+7],shift,vol,-0.2)
  end
  plarray(ar[n+8],ar[n+9],shift,vol,-0.2)
end

The complete program is shown below: [an error in the location of the samples on a Raspberry Pi has been corrected in the program and the download zip file. Harp samples should be located at /home/pi/samples/Harp on this platform. (Different on a Mac)]

#program sets up a sample based harp instrument, and plays Courante from Bach BWV995 Suite in G minor
#by Robin Newman, October 2014, on Sonic Pi 2
#score from http://conquest.imslp.info/files/imglnks/usimg/f/fc/IMSLP218574-WIMA.d9fa-SuiteBWV995Courante.pdf
#under creative commons 3 licence. Arranger Robin Ward

use_debug false
#use_sample_pack '/Users/rbn/Desktop/samples/Harp'
use_sample_pack '/home/pi/samples/Harp'
set_sched_ahead_time! 5
shift = 0 #set transpose if required
vol=1
s = 1.0 / 19 #s is speed multiplier
rm=2**(1.0/12) #rate multiplier for one semitone higher than sample (twelth 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 = []
36.step(96,3) do |i|
  sl << i
  load_sample ("harp_"+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),"harp_"+ntosym(n).to_s,rmi] #typical entry [:b1, "harp_c2", 0.9438743126816934]     note,samplename, rate
  nhash << [ntosym(n),"harp_"+ntosym(n).to_s,1] #typical entry [:c2, "harp_c2", 1]
  nhash << [ntosym(n+1),"harp_"+ntosym(n).to_s,rm] #typical entry [:cs2, "harp_c2", 1.0594630943592953]
end

#puts nhash #for debugging purposes

#note aliases so you can use :cb2 (plays :b1) etc
flat=[:cb2,:db2,:eb2,:fb2,:gb2,:ab2,:bb2,:cb3,:db3,:eb3,:fb3,:gb3,:ab3,:bb3,:cb3,:db4,:eb4,:fb4,:gb4,:ab4,:bb4,:cb4,:db5,:eb5,:fb5,:gb5,:ab5,:bb5,:cb5,:db6,:eb6,:fb6,:gb6,:ab6,:bb6,:cb7]
sharp=[:b1,:cs2,:ds2,:e2,:fs2,:gs2,:as2,:b2,:cs3,:ds3,:e3,:fs3,:gs3,:as3,:b3,:cs4,:ds4,:e4,:fs4,:gs4,:as4,:b4,:cs5,:ds5,:e5,:fs5,:gs5,:as5,:b5,:cs6,:ds6,:e6,:fs6,:gs6,:as6,:b6]

#add es and bs with aliases so you can use :es2 (plays :f2) etc
flat.concat [:es2,:es3,:es4,:es5,:es6,:bs1,:bs2,:bs3,:bs4,:bs5,:bs6]
sharp.concat [:f2,:f3,:f4,:f5,:f6,:c2,:c3,:c4,:c5,:c6,:c7]
#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,volume as parameters
define :pl do |n,d=0.2,pan=0,v=0.8|
  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
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(:c2)
  puts note(:c6)
  36.upto(96) do |n|
    pl(ntosym(n),0.2)
    sleep 0.3
  end
end

#chromatic #uncomment to test range

#=======================define note arrays. Five parts play together=================
#upbeat
nu=[:d4]
du=[q]
#first section: part 1
n1 = [:d4,:e4,:f4,:g4,:a4,:bb4,:a4,:g4,:a4,:f4,:f4,:g4,:f4,:f4,:e4,:d4,:cs4,:cs4,:d4,:e4,:a3,:d4,:cs4,:d4,:e4,:g4,:f4,:e4,:d4]
d1 = [cd,q,q,q,q,q,q,q,q,q,q,c,q,q,q,q,q,q,c,q,cd,sq,sq,c,c,q,q,q,q]
#4
n1.concat [:bb4,:g4,:d5,:cs5,:d5,:f4,:e4,:a4,:f4,:e4,:d4,:e4,:f4,:g4,:a4,:bb4,:d5,:c5,:c5,:bb4,:a4,:g4,:f4,:e4,:e4,:f4,:g4]
d1.concat [cd,q,q,q,c,q,c,q,cd,q,q,q,q,q,q,q,q,q,cd,q,q,q,q,q,q,c,q]
#7
n1.concat [:f4,:e4,:d4,:c4,:c5,:d5,:c5,:b4,:c5,:a4,:d5,:c5,:c5,:b4,:a4,:gs4,:gs4,:a4,:b4]
d1.concat [q,q,q,q,cd,q,q,q,q,q,cd,q,q,q,q,q,q,c,q]
#9
n1.concat [:a4,:gs4,:fs4,:e4,:e5,:d5,:cs5,:d5,:e5,:f5,:e5,:d5,:c5,:b4,:c5,:a4]+trn(:gs4,4,1)+[:a4,:r,:gs4,:d5,:e5,:d5,:c5,:b4]+trn(:b4,6,1)+[:a4]
d1.concat [q,q,q,q,c,q,q,c,c,cd,q,q,q,q,q,q]+trd(c,4)+[q,q,q,q,q,q,c,q]+trd(cd,6)+[q]
n1ra = [:a4,:g4,:f4,:e4,:f4,:d4,:a4,:r,:d4]
d1ra = [c+sq,sq,sq,sq,qd,sq,m,q,q]
n1rb = [:a4,:g4,:f4,:e4,:f4,:d4,:a4,:r,:e5]
d1rb=d1ra
#part 2
n2 = [:r,:cs4,:r,:g4,:r,:f4,:r,:d4,:r,:f4,:r,:a4,:r,:a4,:r,:a4,:gs4]
d2 = [bd,m,b+bd,c,m,c,m,m,b,cd,q+b+bd,cd,q+b+bd,m,b+m,m,m]
n2ra = n2rb = [:r,:e4,:r]
d2ra = d2rb = [md,m,c]
#part 3
#b14
n3= [:r,:d4,:r,:c4,:r,:d4,:r,:e4,:r]
d3 = [3*bd,c,c+b+bd,cd,q+b+bd,cd,q+b+bd*2+m,m,m]

n3ra = n3rb = [:r,:cs4,:r]
d3ra = d3rb = [md,m,c]
#part 4
n4=[:r,:cs4,:a3,:r,:b3,:r,:d4,:r]
d4 = [bd*3+b,m,m,b+bd*3,m,b,m,b+bd]
n4ra=n4rb=[:r]
d4ra=d4rb=[bd]
#part 5
n5=[:d3,:r,:e3,:r,:f3,:r,:a3,:d3,:f3,:g3,:r,:a3,:d3,:r,:a3,:r,:b2,:c3,:r,:e3,:a3,:g3,:f3,:r,:e3,:f3,:d3,:e3,:r,:g3,:f3,:e3]
d5=[m,b,m,b,m,c,c,c,c,m,m,m,m,b,m,md,c,m,c,c,c,c,m,c,c,c,c,m,c,c,c,c]
#10
n5.concat [:d3,:r,:d3,:e3,:f3,:b3,:e3,:a3,:d3,:e3]
d5.concat [m,c,c,c,c,c,c,c,c,m]
n5ra = n5rb = [:a3,:a2,:r]
d5ra = d5rb = [md,m,c]
#end of first part
#set up array to hold play list  of parts
ar=[n1,d1,n2,d2,n3,d3,n4,d4,n5,d5,n1ra,d1ra,n2ra,d2ra,n3ra,d3ra,n4ra,d4ra,n5ra,d5ra,n1rb,d1rb,n2rb,d2rb,n3rb,d3rb,n4rb,d4rb,n5rb,d5rb]
#define second half note arrays
#part 1b
#14
n1b = [:e5,:f5,:e5,:d5,:c5,:d5,:e5,:a4,:bb4,:a4,:bb4,:a4,:g4,:f4,:e4,:f4,:g4,:bb4,:a4,:g4,:g4,:f4,:e4,:d4,:bb4,:a4,:a4,:d5,:eb5]
d1b = [cd,q,q,q,q,sq,sq,sq,sq,c,q,q,c,q,q,q,q,q,c,c,q,q,q,q,c,c,c,qd,sq]
#17
n1b.concat [:g4]+trn(:fs4,4,1)+[:g4,:a4,:bb4,:c5,:d5,:eb5,:d5,:c5,:bb4,:a4,:g4,:f4,:eb4,:f4,:d4,:e4,:g4,:a4,:bb4,:bb4,:a4,:g4,:f4,:f5,:g5]+trn(:c5,6)+[:bb4]
d1b.concat [q]+trd(c,4)+[q,q,q,q,q,c,c,q,q,q,q,q,q,q,q,q,q,q,q,q,q,q,q,cd,q]+trd(cd,6)+[q]
#20
n1b.concat [:bb4,:a4,:bb4,:c5,:d5,:e5]+trn(:e5,6,1)+[:d5,:e5,:f5,:c5,:d5,:a4,:a4,:bb4,:c5,:bb4,:a4,:g4,:f4,:a4,:b4,:a4,:b4]+trn(:b4,6,1)+[:a4,:b4]
d1b.concat [cd,q,q,q,q,q]+trd(cd,6)+[sq,sq,cd,q,c,c,q,c,q,q,q,q,q,q,q,q,q]+trd(cd,6)+[sq,sq]
#23
n1b.concat [:c5,:b4,:d5,:cs5,:d5,:c5,:bb4,:a4,:g4,:g4,:a4,:g4,:f4,:e4,:e4,:d4]
d1b.concat [cd,q,q,q,c,q,q,q,q,cd,q,q,c,q,cd,q]

n1bra = [:d4,:a3,:g3,:f3,:g3,:a3,:d5,:r,:e5]
d1bra = [cd,sq,sq,sq,sq,q,m,q,q]
n1brb = [:d4,:a3,:g3,:f3,:g3,:a3,:d5]
d1brb = [cd,sq,sq,sq,sq,q,md]
#part 2b, b14
n2b = [:cs5,:r,:e4,:r,:d4,:r,:f4,:r,:bb4,:a4,:f4,:r,:e4,:d4,:cs4]
d2b = [cd,q+b+b,c,c,m,b+b+c,c,bd+m,m,m,m,b+3*bd,m,m,m]
n2bra=[:r,:a4,:r]
d2bra=[md,m,c]
n2brb=[:r,:a4]
d2brb=[md,md]
#part 3b
n3b=[:a4,:r,:g3,:r,:d4,:r]
d3b=[cd,q+b+bd*3,m,b+bd,m,b+4*bd]
n3bra=[:r,:f4,:r]
d3bra=[md,m,c]
n3brb=[:r,:f4]
d3brb=[md,md]
#part 4b
n4b=[:e4,:r,:a3,:f4,:eb4,:d4,:c4,:r,:d3,:r]
d4b=[m,b+bd,m,c,c,m,m,b,m,b+6*bd]
n4bra=[:r,:d3,:r]
d4bra=[md,m,c]
n4brb=[:r,:d3]
d4brb=[md,md]
#part 5b
n5b=[:a3,:r,:a3,:f3,:d3,:b2,:r,:e3,:c3,:a2,:d3,:d4,:c4,:bb3,:a3,:r,:d3,:g2,:r,:g2,:c3,:d3,:eb3,:c3,:d3,:eb3,:f3,:bb2,:r,:bb2,:a2,:g2]
d5b=[m,c,c,c,c,m,c,c,c,c,m,c,c,m,m,md,c,m,c,c,cd,q,c,c,c,c,m,m,c,c,c,c]
#21
n5b.concat [:a2,:r,:f3,:g3,:e3,:f3,:r,:f3,:e3,:d3,:e3,:r,:f3,:g3,:e3,:cs3,:a2,:d3,:g2,:a2]
d5b.concat [m,c,c,c,c,m,c,c,c,c,m,c,c,c,c,c,c,c,c,m]
n5bra=[:d4,:d3,:r]
d5bra=[md,m,c]
n5brb=[:d4,:d3]
d5brb=[md,md]
#add second half arrays tro playlist
ar.concat [n1b,d1b,n2b,d2b,n3b,d3b,n4b,d4b,n5b,d5b,n1bra,d1bra,n2bra,d2bra,n3bra,d3bra,n4bra,d4bra,n5bra,d5bra]
ar.concat [n1brb,d1brb,n2brb,d2brb,n3brb,d3brb,n4brb,d4brb,n5brb,d5b]

#sec selects and plays 5 related parts and durations from the ar array
define :sec do |n|
  in_thread do
    plarray(ar[n],ar[n+1],shift,vol,0.2)
  end
  in_thread do
    plarray(ar[n+2],ar[n+3],shift,vol,0.2)
  end
  in_thread do
    plarray(ar[n+4],ar[n+5],shift,vol,0.1)
  end
  in_thread do
    plarray(ar[n+6],ar[n+7],shift,vol,-0.2)
  end
  plarray(ar[n+8],ar[n+9],shift,vol,-0.2)
end
#================play the piece here===============
plarray(nu,du) #upbeat
sec(0) #first section
sec(10)#first time bar
sec(0)#first section repeat
sec(20)#second time bar

sec(30)#second section
sec(40)#first time bar
sec(30)#second section repeat
sec(50)#second time bar
#================end==============================

You can download a zip file containing the program and a folder containing the Harp samples here

You can listen to an mp3 file of the complete pice recorded from Sonic Pi here

(the .wav file converted to .mp3 with the  open source program Audacity)

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