Sonic Pi 2 plays Maple Leaf Rag

If you have followed my previous articles on Sonic Pi 2, especially Developing music for Sonic Pi 2, then you will have seen the technique that I use to transcribe music from the printed staff to Sonic Pi. This program, which plays the first half of Scott Joplin’s Maple Leaf Rag without the trio section, follows the same pattern, although there are one or two differences which I will point out. First here are the two pages of music involved.

Ragp1 Ragp2

 

Looking at the music you will see that even though it is played on a (honkytonk) piano there are essentially 5 parts to be transcribed. Two in the right hand and three in the left hand. Also apart from the obvious repeat sections, you may notice that the very last section is the same as the beginning taking the second time bar, if we alter the very last note. As before the note pitches and durations are written into arrays which are then played using a function here named playarray. I don’t intend on this occasion to give a full explanation of how the program works, as it is similar to what has gone before. However there are one or two differences that are worthy of discussion. First here is a listing of the complete program.

#Scott Joplin Maple Leaf Rag coded by Robin Newman September 2014
#music score http://conquest.imslp.info/files/imglnks/usimg/9/98/IMSLP270188-PMLP06700-Maple_Leaf_Rag.pdf
#on a Raspberry Pi turn OFF Print output in Sonic Pi Prefs
set_sched_ahead_time! 2
s = 1.0 / 12 #makes a crotchet (8) last 0.53s or about 112 crotchets/minute
with_fx :reverb,room: 0.6 do
  #first set up synth and note lengths
  #not all note lengths used in this piece
  use_synth :pretty_bell

  dsq = 1 * s #demi-semi-quaver
  sq = 2 * s #semi-quaver
  sqd = 3 * s #semi-quaver dotted
  q = 4 * s #quaver
  qd = 6 * s #quaver dotted
  qdd = 7 * s #quaver double dotted
  c = 8 * s #crotchet
  cd = 12 * s #crotchet dotted
  cdd = 14 * s #crotchet double dotted
  m = 16 * s #minim
  md = 24 * s #minim dotted
  mdd = 28 * s #minim double dotted
  b = 32 * s #breve
  bd = 48 * s #breve dotted
  define :playarray do |narray,darray,ratio = 0.9,vol=0.4|
    narray.zip(darray).each do |n,d|
      if n != :r
        play n,amp: vol,sustain: d * ratio,release: d * (1-ratio) #play note
      end
      sleep d #gap till next note
    end
  end
  p1n = [:r,:ab4,:eb5,:ab4,:c5,:eb5,:g4,:eb5,:g4,:bb4,:eb5,:r,:ab4,:eb5,:ab4,:c5,:eb5,:g4,:eb5,:g4,:bb4,:eb5,:r,:eb5]
  p1d = [sq,sq,sq,sq,sq,q,sq,sq,sq,sq,(sq+c),sq,sq,sq,sq,sq,q,sq,sq,sq,sq,(sq+q),sq,sq]
  p2n = [:r,:eb4,:r,:eb4,:r,:eb4,:r,:eb4,:r,:eb4,:r,:eb4,:r,:eb4,:r,:eb4,:r,:eb4]
  p2d = [q,sq,q,q,sq,sq,q,(sq+c),q,sq,q,q,sq,sq,q,(sq+q),sq,sq]
  p3n = [:ab3,:c4,:c4,:a3,:bb3,:db4,:db4,:eb3,:ab3,:c4,:c4,:a3,:bb3,:db4,:db4,:eb3]
  p3d = [q,q,q,q,q,q,q,q,q,q,q,q,q,q,q,q]
  p4n = [:r,:ab3,:ab3,:r,:g3,:g3,:r,:ab3,:ab3,:r,:g3,:g3,:r]
  p4d = [q,q,q,c,q,q,c,q,q,c,q,q,q]
  p5n = [:ab2,:eb3,:eb3,:a2,:bb2,:eb3,:eb3,:eb2,:ab2,:eb3,:eb3,:a2,:bb2,:eb3,:eb3,:eb2]
  p5d = [q,q,q,q,q,q,q,q,q,q,q,q,q,q,q,q]
  #bar 5
  p1n.concat [:r,:ab4,:cb4,:fb5,:r,:eb5,:r,:eb5,:r,:ab4,:cb4,:fb5,:r,:eb5,:r,:ab2,:cb2,:ab3,:r,:ab3,:cb3,:ab4,:r,:ab4,:cb4,:ab5,:r,:ab5,:cb5,:ab6]
  p1d.concat [sq,sq,sq,sq,sq,sq,sq,sq,sq,sq,sq,sq,sq,sq,q+sq,sq,sq,sq,sq,sq,sq,sq,sq,sq,sq,sq,sq,sq,sq,sq]
  p2n.concat [:r,:fb4,:r,:eb4,:r,:eb4,:r,:fb4,:r,:eb4,:r]
  p2d.concat [qd,sq,sq,sq,sq,sq,qd,sq,sq,sq,(q+b)]
  p3n.concat [:fb3,:eb3,:eb3,:fb3,:eb3,:r]
  p3d.concat [c,q,q,c,q,(q+b)]
  p4n.concat [:r]
  p4d.concat [2*b]
  p5n.concat [:fb2,:eb2,:eb2,:fb2,:eb2,:r,:eb1,:r,:ab2,:r,:ab3,:r,:ab4,:r]
  p5d.concat [c,q,q,c,q,q,q,q,q,q,q,q,q,q]
  #bar 9
  p1n.concat [:ab6,:ab6,:ab6,:ab6,:ab6,:eb6,:f6,:c6,:eb6,:f6,:ab5,:bb5,:cb5,:ab5,:bb5,:c6,:ab5,:c6,:ab5,:bb5,:ab5,:r,:ab5]
  p1d.concat [q,q,q,sq,q,sq,sq,sq,sq,q,q,sq,sq,sq,sq,q,sq,sq,sq,q,q,sq,qd]
  p2n.concat [:ab5,:ab5,:ab5,:ab5,:ab5,:r,:ab5,:fb5,:r,:fb5,:r,:eb5,:r,:eb5,:r,:eb5,:eb5,:r,:ab4]
  p2d.concat [q,q,q,sq,q,c,q,q,sq,sq,q,q,sq,sq,sq,q,q,sq,qd]
  p3n.concat [:b4,:b4,:b4,:b4,:c5,:c5,:c5,:c5,:cb4,:cb4,:c5,:c5,:c5,:db5,:c5,:r,:b4]
  p3d.concat [q,q,q,q,q,q,q,q,q,q,q,q,q,q,q,q,q]
  p4n.concat [:ab4,:ab4,:ab4,:ab4,:ab4,:ab4,:ab4,:ab4,:ab4,:ab4,:ab4,:ab4,:ab4,:g4,:ab4,:r,:f4]
  p4d.concat [q,q,q,q,q,q,q,q,q,q,q,q,q,q,q,q,q]
  p5n.concat [:d4,:d4,:d4,:d4,:eb4,:eb4,:eb4,:eb4,:fb4,:fb4,:eb4,:eb4,:eb4,:eb4,:ab4,:r,:d3]
  p5d.concat [q,q,q,q,q,q,q,q,q,q,q,q,q,q,q,q,q]
  #bar 13b2
  p1n.concat [:ab5,:ab5,:ab5,:ab5,:eb5,:f5,:c5,:eb5,:f5,:ab4,:bb4,:cb4,:ab4,:bb4,:c5,:ab4]
  p1d.concat [q,q,sq,q,sq,sq,sq,sq,q,q,sq,sq,sq,sq,q,sq]
  p2n.concat [:ab4,:ab4,:ab4,:ab4,:r,:ab4,:r,:ab4,:fb4,:r,:fb4,:r,:eb4,:r]
  p2d.concat [q,q,sq,q,sq,sq,q,q,q,sq,sq,q,q,sq]
  p3n.concat [:b4,:b4,:b4,:c5,:c5,:c5,:c5,:cb4,:cb4,:c5,:c5]
  p3d.concat [q,q,q,q,q,q,q,q,q,q,q]
  p4n.concat [:f4,:f4,:f4,:ab4,:ab4,:ab4,:ab4,:ab4,:ab4,:ab4]
  p4d.concat [q,q,q,q,q,q,q,q,q,q]
  p5n.concat [:d3,:d3,:d3,:eb3,:eb3,:eb3,:eb3,:fb3,:fb3,:eb3,:eb3]
  p5d.concat [q,q,q,q,q,q,q,q,q,q,q]
  #repeat bar 1
  p1nrb1a = [:c5,:ab4,:bb4,:ab4,:r]
  p1drb1a = [sq,sq,q,q,q]
  p1nrb1b = p1nrb1a
  p1drb1b = p1drb1a
  p2nrb1a = [:eb4,:eb4,:eb4,:r]
  p2drb1a = [q,q,q,q]
  p2nrb1b = p2nrb1a
  p2drb1b = p2drb1a
  p3nrb1a = [:c4,:db4,:c4,:eb3]
  p3drb1a = [q,q,q,q]
  p3nrb1b = [:c4,:db4,:c4,:a3]###=>:ab5 on second page
  p3drb1b = [q,q,q,q]
  p4nrb1a = [:ab3,:g3,:ab3,:r]
  p4drb1a = [q,q,q,q]
  p4nrb1b = p4nrb1a
  p4drb1b = p4drb1a
  p5nrb1a = [:eb3,:eb3,:r,:eb2]
  p5drb1a = [q,q,q,q]
  p5nrb1b = [:eb5,:eb5,:r,:a2]#####=>:ab4 on second page
  p5drb1b = [q,q,q,q]
  lar =[[p1n,p1d],[p2n,p2d],[p3n,p3d],[p4n,p4d],[p5n,p5d]] #0-4
  define :sec do|n|
    in_thread do
      playarray(lar[n][0],lar[n][1])
    end
    in_thread do
      playarray(lar[n+1][0],lar[n+1][1])
    end
    in_thread do
      playarray(lar[n+2][0],lar[n+2][1])
    end
    in_thread do
      playarray(lar[n+3][0],lar[n+3][1])
    end
    playarray(lar[n+4][0],lar[n+4][1])
  end
  lar.concat [[p1nrb1a,p1drb1a],[p2nrb1a,p2drb1a],[p3nrb1a,p3drb1a],[p4nrb1a,p4drb1a],[p5nrb1a,p5drb1a]] #5-9
  lar.concat [[p1nrb1b,p1drb1b],[p2nrb1b,p2drb1b],[p3nrb1b,p3drb1b],[p4nrb1b,p4drb1b],[p5nrb1b,p5drb1b]] #10-14
  #play the first page of music here
  play_chord [:eb2,:eb3],amp: 0.4,sustain: q*0.9,release: q * 0.1 #upbeat
  sleep q
  sec(0)
  sec(5)
  sec(0)
  sec(10)
  #start second page of music
  #bar17
  p1n2 = [:r,:g5,:eb6,:g5,:bb5,:d6,:g5,:db6,:g5,:bb5,:c6,:eb5,:bb5,:eb5,:r,:c5,:ab5,:c5,:eb5,:f5,:c5,:ab5,:c5,:eb5,:f5,:c5,:f5]
  p1d2 = [sq,sq,sq,sq,sq,q,sq,sq,sq,sq,q,sq,sq,sq,sq,sq,sq,sq,sq,q,sq,sq,sq,sq,q,sq,q]
  p2n2 = [:r,:eb5,:r,:d5,:r,:db5,:r,:c5,:r,:bb4,:r,:ab4,:r,:f4,:r,:ab4,:r,:f4,:r,:f4]
  p2d2 = [q,sq,q,q,sq,sq,q,q,sq,sq,qd,sq,q,q,sq,sq,q,q,sq,q]
  p3n2 = [:bb3,:db4,:eb3,:db4,:bb3,:db4,:eb3,:g3,:ab3,:c4,:eb3,:c4,:ab3,:c4,:ab3,:a3]
  p3d2 = [q,q,q,q,q,q,q,q,q,q,q,q,q,q,q,q]
  p4n2 = [:r,:g3,:r,:g3,:r,:g3,:r,:ab3,:r,:ab3,:r,:ab3,:r]
  p4d2 = [q,q,q,q,q,q,cd,q,q,q,q,q,c]
  p5n2 = [:bb2,:eb3,:eb2,:eb3,:bb2,:eb3,:eb2,:g2,:ab2,:eb3,:eb2,:eb3,:ab2,:eb3,:ab2,:a2]
  p5d2 = [q,q,q,q,q,q,q,q,q,q,q,q,q,q,q,q]
  #bar21
  p1n2.concat [:r,:eb5,:g5,:bb4,:db5,:f5,:eb5,:g5,:bb4,:db5,:f5,:db5,:f5,:r,:c5,:ab5,:c5,:eb5,:f5,:c5,:ab5,:c5,:eb5,:f5,:c5,:f5]
  p1d2.concat [sq,sq,sq,sq,sq,q,sq,sq,sq,sq,q,sq,q,sq,sq,sq,sq,sq,q,sq,sq,sq,sq,q,sq,q]
  p2n2.concat [:r,:g4,:r,:f4,:r,:g4,:r,:db4,:r,:f4,:r,:ab4,:r,:f4,:r,:ab4,:r,:f4,:r,:f4]
  p2d2.concat [q,sq,q,q,sq,sq,q,q,sq,sq,qd,sq,q,q,sq,sq,q,q,sq,q]
  p3n2.concat [:bb3,:db4,:eb3,:db4,:bb3,:db4,:bb3,:b3,:c4,:c4,:eb3,:c4,:ab3,:c4,:ab3,:a3]
  p3d2.concat [q,q,q,q,q,q,q,q,q,q,q,q,q,q,q,q]
  p4n2.concat [:r,:g3,:r,:g3,:r,:g3,:r,:ab3,:r,:ab3,:r,:ab3,:r]
  p4d2.concat [q,q,q,q,q,q,cd,q,q,q,q,q,c]
  p5n2.concat [:bb2,:eb3,:eb2,:eb3,:bb2,:eb3,:bb2,:b2,:c3,:eb3,:eb2,:eb3,:ab2,:eb3,:ab2,:a2]
  p5d2.concat [q,q,q,q,q,q,q,q,q,q,q,q,q,q,q,q]
  #bar25
  p1n2.concat [:r,:g5,:eb6,:g5,:bb5,:d6,:g5,:db6,:g5,:bb5,:c6,:eb5,:bb5,:eb5,:r,:c5,:ab5,:c5,:eb5,:f5,:c5,:ab5,:ab5,:g5,:gb5]
  p1d2.concat [sq,sq,sq,sq,sq,q,sq,sq,sq,sq,q,sq,sq,sq,sq,sq,sq,sq,sq,q,sq,q,q,q,q]
  p2n2.concat [:r,:eb5,:r,:d5,:r,:db5,:r,:c5,:r,:bb4,:r,:ab4,:r,:f4,:r,:ab4,:ab4,:g4,:gb4]
  p2d2.concat [q,sq,q,q,sq,sq,q,q,sq,sq,qd,sq,q,q,sq,q,q,q,q]
  p3n2.concat [:bb3,:db4,:eb3,:db4,:bb3,:db4,:eb3,:g3,:ab3,:c4,:eb3,:c4,:ab3,:ab3,:g3,:gb3]
  p3d2.concat [q,q,q,q,q,q,q,q,q,q,q,q,q,q,q,q]
  p4n2.concat [:r,:g3,:r,:g3,:r,:g3,:r,:ab3,:r,:ab3,:r]
  p4d2.concat [q,q,q,q,q,q,cd,q,q,q,m]
  p5n2.concat [:bb2,:eb3,:eb2,:eb3,:bb2,:eb3,:eb2,:g2,:ab2,:eb3,:eb2,:eb3,:ab2,:ab2,:g2,:gb2]
  p5d2.concat [q,q,q,q,q,q,q,q,q,q,q,q,q,q,q,q]
  #bar29
  p1n2.concat [:r,:f4,:a4,:c5,:f5,:c5,:a4,:f4,:r,:f4,:bb4,:db5,:f5,:db5,:c5,:r,:c5,:r,:bb4,:eb4]
  p1d2.concat [sq,sq,sq,sq,sq,sq,sq,sq,sq,sq,sq,sq,q,q,q,sq,sq,sq,q,sq]
  p2n2.concat [:r,:f4,:bb4,:ab4,:r,:ab4,:r,:db4,:r]
  p2d2.concat [m+c,q,q,q,sq,sq,sq,q,sq]
  p3n2.concat [:f3,:f3,:a3,:a3,:bb3,:db4,:db4,:db4,:bb3,:bb3,:eb3,:g3]
  p3d2.concat [q,q,q,q,q,q,q,q,q,q,q,q,q,q,q,q]
  p4n2.concat [:r,:bb3,:bb3,:bb3,:f3,:f3,:r]
  p4d2.concat [m+q,q,q,q,q,q,c]
  p5n2.concat [:f2,:f2,:a2,:a2,:bb2,:f3,:f3,:f3,:bb2,:bb2,:eb2,:g2]
  p5d2.concat [q,q,q,q,q,q,q,q,q,q,q,q]
  #rb2
  p1nrb2a = [:ab4,:eb5,:eb5,:eb5]
  p1drb2a = [q,q,q,q]
  p1nrb2b = [:r,:ab4,:c5,:eb5,:ab5,:r]
  p1drb2b = [sq,sq,sq,sq,q,q]
  p2nrb2a = [:c4,:eb4,:eb4,:eb4]
  p2drb2a = [q,q,q,q]
  p2nrb2b = [:r,:ab4,:r]
  p2drb2b = [c,q,q]
  p3nrb2a = [:ab3,:c4,:c4,:a3]
  p3drb2a = [q,q,q,q]
  p3nrb2b = [:ab3,:eb4,:eb4,:eb3]
  p3drb2b = [q,q,q,q]
  p4nrb2a = [:r,:ab3,:ab3,:r]
  p4drb2a = [q,q,q,q]
  p4nrb2b = [:r,:c4,:c4,:r]
  p4drb2b = [q,q,q,q]
  p5nrb2a = [:ab2,:eb3,:eb3,:a3]
  p5drb2a = [q,q,q,q]
  p5nrb2b = [:ab2,:ab3,:ab3,:eb2]
  p5drb2b = [q,q,q,q]
  lar.concat [[p1n2,p1d2],[p2n2,p2d2],[p3n2,p3d2],[p4n2,p4d2],[p5n2,p5d2]] #15-10
  lar.concat [[p1nrb2a,p1drb2a],[p2nrb2a,p2drb2a],[p3nrb2a,p3drb2a],[p4nrb2a,p4drb2a],[p5nrb2a,p5drb2a]] #20-24
  lar.concat [[p1nrb2b,p1drb2b],[p2nrb2b,p2drb2b],[p3nrb2b,p3drb2b],[p4nrb2b,p4drb2b],[p5nrb2b,p5drb2b]] #25-29
  #play the second page of music
  sec(15)
  sec(20)
  sec(15)
  sec(25)
  sec(0)
  #alter the last note of section p3nrb1b and p5nrb1b
  p3nrb1b[3]=:ab5
  p5nrb1b[3]=:ab4
  sec(10)
end

As before, I specify the note durations in variables which are defined at the start of the program. Note pitches and durations are added into arrays for each of the five parts initially for the first page of music. At the end of the first page are two repeat bars. The first time the page is played the first time bar is used. The page is then repeated this time finishing with the second time bar. Each of these repeat bars is stored in a separate range of arrays named for example p1nrb1a and p1nrb1b for the firstpart notes and p1drb1a and p1drb1b for the durations for these bars in the first part.
To play the part arrays a function called playarray is defined.This is similar to the tune function in the Developing music for Sonic Pi 2 article, but is in fact a bit more efficient in the logic used to play a rest.

  define :playarray do |narray,darray,ratio = 0.9,vol=0.4|
    narray.zip(darray).each do |n,d|
      if n != :r
        play n,amp: vol,sustain: d * ratio,release: d * (1-ratio) #play note
      end
      sleep d #gap till next note
    end
  end

as before the line narray.zip(darray).each do |n,d| traverses the note and duration arrays taking matching items in turn which are then processed in the following loop. The logic is changed and tests if the value in n is NOT a rest. If that is the case, i.e. it IS a note, then the play command plays it. In ALL cases the following sleep d command is observed, giving the duration of the note or the rest.
One reason for not including the Trio section is that the length of the program is approaching 9000 characters, and on the Mac version at least, there is currently a maximum of just over 9000 that will play, so I knew that it would not fit in under that limit. In fact I had to take some steps to make the program more efficient and shorter, and I did this in making section play defined function more general than before. I wrote it in such a way that it could apply to any section of the piece without having to hard code in the actual arrays to be played in each section. This was done by including a list of arrays which I called lar. When the part arrays for the first page had been coded in I set up the initial values in lar and the section function as shown below

  lar =[[p1n,p1d],[p2n,p2d],[p3n,p3d],[p4n,p4d],[p5n,p5d]] #0-4
  define :sec do|n|
    in_thread do
      playarray(lar[n][0],lar[n][1])
    end
    in_thread do
      playarray(lar[n+1][0],lar[n+1][1])
    end
    in_thread do
      playarray(lar[n+2][0],lar[n+2][1])
    end
    in_thread do
      playarray(lar[n+3][0],lar[n+3][1])
    end
    playarray(lar[n+4][0],lar[n+4][1])
  end

In order to play the first section we use the command sec(0). This substitutes array names from lar into the relevant place in the various threads inside the definition. So the first thread has playarray(lar[n][0],lar[n][1]) This will substitute to playarray(lar[0],[0],lar[0],[1]) which is turn becomes playarray(p1n,p1d). In the same way the second thread which has playarray(lar[n+1],[0],lar[n+1],[1]) becomes playarray(p2n,p2d) and so on. The important thing to remember is that all arrays number their elements from 0 not from 1.
Once this first section has played further elements are added to the lar array.

  lar.concat [[p1nrb1a,p1drb1a],[p2nrb1a,p2drb1a],[p3nrb1a,p3drb1a],[p4nrb1a,p4drb1a],[p5nrb1a,p5drb1a]] #5-9
  lar.concat [[p1nrb1b,p1drb1b],[p2nrb1b,p2drb1b],[p3nrb1b,p3drb1b],[p4nrb1b,p4drb1b],[p5nrb1b,p5drb1b]] #10-14

this concatenates or adds the array elements to play the first and second time bars. The first time bars entries will be in position 5 to 9 (remember they number from position zero), and the second time bar entries from position 10-14. So subsequently we can use the commands sec(10 and sec(15) to play these. In the program we now play the first page of music. To start we play a chord of two notes :eb2 and :eb3 This is the upbeat shown in the music before the first repeated section starts. We sleep for a quaver, the duration of the note and then play sec(0) the first section, sec(5) the first time repeat bar, sec(0) the repeated first section (minus the upbeat) and sec(10) the second time bar.
Unlike in previous programs, I have here played the music as we progress through the program, as it is easier to follow than leaving it all to the end in this case.

  #play the first page of music here
  play_chord [:eb2,:eb3],amp: 0.4,sustain: q*0.9,release: q * 0.1 #upbeat
  sleep q
  sec(0)
  sec(5)
  sec(0)
  sec(10)

The remainder of the program more or less does the same thing again, defining the note and duration arrays for the second page of music, and concatenating the relevant arrays to be played onto the end of the lar array. So sec(15) plays the repeated section at the start of the second page, followed by sec(20) which plays the first time of the repeat bar on page 2 (referred to as rb2a) followed by sec(15) again and then sec(25) which plays the second time of the repeat bar on page 2 (rb2b).
The second half of the second page of music is in fact a repeat of the first section on page 1, selecting the second time of the repeat bar on that page with the last note changed. This is done directly on the two relevant arrays p3nrb1b and p5nrb1b and then sec(0) and sec(10) are repeated to finish the piece, with the final end statement closing off the with fx :reverb, room: 0.6 do at the beginning of the piece.

  #play the second page of music
  sec(15)
  sec(20)
  sec(15)
  sec(25)
  sec(0)
  #alter the last note of section p3nrb1b and p5nrb1b
  p3nrb1b[3]=:ab5
  p5nrb1b[3]=:ab4
  sec(10)
end

You can download the piece as a text file here and then paste it into a blank workspace in Sonic Pi 2 to play it. If using a Raspberry Pi, remember to turn off Print output in the Prefs or the computer will not be able to cope. If you do it is fine.

Here is a link to hear what it sounds like, played by Sonic Pi 2

One thought on “Sonic Pi 2 plays Maple Leaf Rag

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