Deux Miniatures coded on Sonic Pi

Following the last post in which I detailed how to set up the Sonatina Symphonic Orchestra for use with Sonic Pi, this post contains details of a pice that I have just coded with Sonic Pi which utilises the SSO library to produce the required instrument voices. The pice was written by the composer Victor Kalinnikov in 1986 and was originally scored for 2 Violins, Viola and Cello, but here I am utilising an Oboe for Violin 1 and samples for violins (plural) violas and Basses  for the other three parts,as I think the texture of the sounds produced by Sonic Pi suit this combination better. As described in the last post, the voices are set up to be played by the plarray function. However I have made several changes. First, to save space, only the instrument voices required are loaded, rather than the larger number used previously. Secondly, both the viola and bass parts need to be able to play chords. Logically one would alter the pl function which played a single note to accommodate this, but as this function is quite complex it is practically easier to make the alteration in the plarray function. This is done by testing each entry in the notes list and seeing it will respond to the .each Ruby method, using the test if n.respond_to?(:each)
If this is true, then it means that n (which is a member of the list of notes being fed to the plarray) is itself a list, and so each note in this list is sent to the pl function without any sleep commands in between, thus causing them to be played at the same time.
The second alteration I made to the pl and plarray routines was to expose the envelope sustain and decay fractions used when the sample is played, which can be used to alter the overall sound. Previously I had sustain: 0.9*d, release: d which suited the Frere Jaques example, but now I have sustain: s*d,release: r*d and s and r are included in the parameter list for each function, with default values set at 0.9 and 0.1 respectively. In the event I used the default values for this piece.
The third change made was because of the length limitations currently in force in Sonic Pi. There is a maximum buffer length which can be sent to the server, and if this is exceeded no sound results. On-going work in taking place to over come this, but at present it is possible to split programs over more than one buffer and link the parts using Sonic Pi’s cue and sync commands. Functions defined in one buffer are available for other buffers in Sonic Pi. So if the functions are defined in the first buffer used, then you only have to duplicate definitions of any variables required in subsequent parts for this to work. In fact, once the first section has been run once, the other two can be used independently to play each of the two movements are required, by commenting out the sync commands at the beginning of each of them. To play them linked together thee parts should be run in reverse order, 3,2 then 1.

There is more that can be done to the prgram, for example to add expression in the way of changes in loudness p,f,ff etc and also to add a couple of rits in the score. I may do this ;ater, but it doesn’t sound bad as it is at present.

If you are interested in seeing the music, it can be downloaded from,_Viktor)

The Deux Miniatures can be heard on here

The amended pl and plarray functions are shown below, and the three parts of the program can be downloaded here.

#define routine to play sample
define :pl do |np,d,inst,vol=1,s=0.9,r=0.1,tp=0,pan=0|
  #check if note in range of supplied samples
  #use lowest/highest sample for out of range
  change=0 #used to give rpitch for coverage outside range
  n=np+tp #note allowing for transposition
  if n.is_a?(Numeric) #allow frac tp or np
  if note(np)+tp<note(low) #calc adjustment for low note
  if note(np).to_i+tp > note(high) #calc adjustment for high note
    change = note(np).to_i+tp-note(high)
  if change < -5 or change > 5 #set allowable out of range
    #if outside print messsage
    puts 'inst: '+inst+' note '+np.to_s+' with transpostion '+tp.to_s+' out of sample range'
  else #otherwise calc and play it
    #calculate base note and octave
    oc = note(n) #do in 2 stages because of alignment bug
    oc=oc/12 -1
    #find first part of sample note
    #lookup sample to use,and rpitch offset, according to offsetclass
    case offsetclass
    when 0
      oc += 1 if base == 11 #adjust if sample needs next octave
      offset=[ 0,1,-1,0,1,-1,0,1,-1,0,1,-1]
    when 1
    when 2
      oc -= 1 if base == 0 #adjust if sample needs previous octave
    when 3
      snumber=[0,1,2,3,4,5,6,7,8,9,10,11] #this class has sample for every note
    #generate sample name
    #play sample with appropriate rpitch value
    sample paths,sname,rpitch: offset[base]+change+frac,sustain: s*d,release: r*d,pan: pan,amp: vol

#define function to play lists of linked samples/durations
define :plarray do |notes,durations,offsetclass,vol=1,s=0.9,r=0.1,tp=0,pan=0|
  puts offsetclass do |n,d|
    if n.respond_to?(:each)
      n.each do |nv|
        pl(nv,d,offsetclass,vol,s,r,tp,pan) if ![nil,:r,:rest].include? nv#allow for rests
      pl(n,d,offsetclass,vol,s,r,tp,pan) if ![nil,:r,:rest].include? n#allow for rests
    sleep d

Leave a Reply

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

You are commenting using your 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