I often use Frere Jaques as a simple tune with which to introduce new users to using Sonic Pi. Over the last year Sonic Pi has had many new features added and recently I decided to use some of these new features to revisit Frere Jaques. One of the nice things about Sonic Pi is that there is no right way to do anything, and many musical outputs can be obtained by different means.
This version makes use of the powerful features offered by rings and the tick and look functions. The program is show below, followed by comments bout how it works, which I have described in detail in the hope that it may be helpful to beginners getting to grips with coding on Sonic Pi. It is written for Sonic Pi 2.8 but can be amended to work on 2.6 and 2.7 as described below. I have tested it on B+,Pi2 MacOSX and Windows. I think it MAY work on a model B if you add a set_sched_ahead_time! for several seconds.
#Frere Jaques 4 part round using live loops by Robin Newman, December 2015 #Syntax here needs SP2.8 Will work on 2.6 and 2.7 if you follow the instructions below #The delay parameter in the live_loops will be ignored in versions before 2.8 #The program will work in 2.6 and 2.7 if you uncomment the sleep 8*c lines #which are before the last three live_loops s=0.2 #adjust the tempo here by varying s Try 0.1 to 0.4 q=1*s #define quaver (q) duration c=2*q #define crotchet (c) duration m=2*c #define minim (m) duration #define rings for the tune notes and the note durations #by using rings, we can access the contents with the tick and look functions t = (ring :c4,:d4,:e4,:c4,:c4,:d4,:e4,:c4,:e4,:f4,:g4,:e4,:f4,:g4, \ :g4,:a4,:g4,:f4,:e4,:c4,:g4,:a4,:g4,:f4,:e4,:c4,:c4,:g3,:c4,:c4, \ :g3,:c4) d= (ring c,c,c,c,c,c,c,c,c,c,m,c,c,m,q,q,q,q,c,c,q,q,q,q, \ c,c,c,c,m,c,c,m) define :fplay do |syn| use_synth syn #set the current synth #now play the next note selected by tick and the duration selected by look play t.tick,sustain: d.look*0.9,release: d.look*0.1 #sustains note for 90% of duration #sleep for the duration of the note selected by d.look sleep d.look stop if look >= d.length*2 - 1 #stop after 2 cycles. Note look starts from 0, so subtract 1 end live_loop :tune do fplay(:tri) end #sleep 8*c #uncomment if using sp 2.6 or 2.7 live_loop :tune2,delay: 8*c do fplay(:saw) end #sleep 8*c #uncomment if using sp2.6 or 2.7 live_loop :tune3,delay: 16*c do fplay(:fm) end #sleep 8*c #uncomment if using sp2.6 or 2.7 live_loop :tune4,delay: 24*c do fplay(:tb303) end
The program starts by defining variables which hold details of the note durations to be used. There are three: quavers, crotchets and minims. These have relative values 1,2 and 4. That is to say a crotchet lasts twice as long as a quaver and a minim 4 times as long as a quaver or twice as log as a crotchet. This is expressed by the four lines
s is used a a scaling factor to give the actual duration of each of these notes. They will last for 0.2, 0.4 and 0.8 seconds (assuming the default set up for Sonic Pi)
The next two lines define all the notes in Frere Jaques and their lengths.
t holds the notes in a special kind of list called a ring. The notes are expressed using built in symbols that Sonic Pi understands. thus :c4 means middle c ( the c at the bottom of the 4th octave of the piano), :d4 represents the d immediately above that and so on. We’ll come on to what the ring means in a bit.
d holds a list of the note durations for Frere Jaques, again in a ring format. The position of each duration symbol, whether c, m or q, relates directly to the note in the corresponding position in the t tune ring.
If we ignore the next section define :fplay for the moment, and look further down, you can see
live_loop :tune do
This is going to carry out repeatedly executing fplay(:tri)
So what is fplay(:tri) ?
it is an additional function which we just jumped over. Lets look at its definition.
It starts by choosing the synthesizer to be used, which is fed in by the :tri when it is called.
So having sent the command use_synth :tri (in this case)
it now executes the line
play t.tick,sustain: d.look*0.9,release: d.look*0.1
Understanding this, will explain how the whole program works.
t remember was defined as a ring, and consisted of a list of symbols to give the notes to be played. So how does this tie up with the tick and where does the ring bit come in?
tick is a counter which is automatically set up when a live loop is created. The first time the command tick is issued it assumes the value 0. Every time make a call to tick it will increase by 1. In certain programs tick MAY be called several times in a loop, but it is more usual for it to be called once within the loop. The associated function look will give the CURRENT value of tick when it is called, but will NOT increase its value.
So lets look at this line. The first time round the loop tick will be 0 and t.tick will get the first note in the ring which is :c4. The next time round the loop ∫ will go to 1 and t.tick will get the second note in the ring which is :d4, The third time tick will go to 2, and t.tick will get the third note in the ring which is :e4, and so on.
We have to tell Sonic Pi TWO other things about the note. How long is the note to sound for, and how long should elapse before the next note is to be played.
Well we know that the d ring holds details of the LENGTHS of the notes. In order to get these lengths we can use the same mechanism as for the t ring. However we want to use the SAME value of tick as we used to get the note, so we CAN’T say d.tick as that would change the value of tick. Instead we use d.look and that uses the CURRENT value of tick to look at the d ring. The first 10 times round the loop it will give the value of c a crotchet, and then the next time when tick has reached 10, and therefore look will also be 10 it will return the value of m the minim variable and so on. Now we can get to grips with the ring bit. tick has no idea how long Frere Jaques is, and just goes relentlessly on increasing in value each time it is called. Eventually t.tick will teach the end of the tune. When it is increased once more what happens? If we just has a simple list of notes it would have nothing further to point to, and the program would stop with an error. But because we defined the notes in a ring, what happens is that it wraps round and points at the beginning of the tune again. The list becomes circular with no end, and hence it is called a ring.
Before we go on, lets look at the second half of the play line:
sustain: d.look*0.9,release: d.look*0.1
This sets the shape of the note. It says sustain: the note at full volume for 90% of the note duration given by d.look (a crotchet first time round), and then let it fade away or release: during the remaining 10% of the duration given by d.look Notice we can use look TWICE in this line and it maintains the same value. If we tried to use d.tick it would change each time as tick would keep on getting bigger). This will give quite legato or joined up notes.
The next line in the program says sleep d.look (Again we use look so we are still pointing at the same note duration). This tells Sonic Pi to wait for the duration of the note before going back to start the loop again and get the next note.
That leaves one line in the function fplay
stop if look >= d.length*2 - 1
Remember we said that the notes and durations were stored in rings. That means that there is no end to the loop, as it will keep on and on reading and playing notes until we press the stop button or quit Sonic Pi. To prevent this we tell it to stop after a suitable number of notes has been played.We can calculate the number of notes by seeing how many entries there are in either the t or d rings (They will both have the same number hopefully, one duration for each note). d.length will give us this number (it is actually 32) If you want you could add a line just before the define :flpay line with the command
This will print 32 on the screen when the program is run.
I have said stop if look >= d.length*2 -1
d.length*2 is twice the number of notes in the tune. Because tick starts counting from 0 and not 1, I have subtracted 1 from this total and asked the loop to stop when tick gets to this value, i.e. after the tune has played through twice.
So why did I put all this code inside the function fplay and not directly into the live_loop :tune? Purely for a matter of expediency and conciseness. We will actually have FOUR live loops which will all be identical apart from their names, and the synth used to play the tune. It is much shorter to write out the definition for fplay ONCE and then just to put fplay(:tri) or fplay(:fm) in the live_loop rather than several more lines of code.
So how do the other loops work?
There is actually another important difference I didn’t mention in the loops. That is, that all apart from the first one have a parameter delay: 8*c or delay: 16*c or delay: 24*c added. This means that they are not all started at the same time when the program is run. The second loop is delayed by the time of 8*c or 8 crotchet notes before it starts running the first time, the third by 16 crotchets and the fourth by 24 crotchets. Now 8 crotchets is the time taken for the first repeated line of Frere Jaques to complete. So the four parts (each part played by a separate loop) are staggered in entry, giving the traditional “round”.
If you are using an earlier version of Sonic Pi like 2.6, where this extra parameter is not supported, you can obtain the same effect by putting a line sleep 8*c before the start of EACH live loop (except the first one) instead.
One final point that people often find confusing is the use of the : at various points in the program. : is used before a word when that word is a symbol eg :c4 or :tri, or when you define a new function name as in define :fplay. : is used AFTER a word when that word is a parameter or option in a function, eg sustain: and release: are parameters (or otions) applied to the play function, and delay: is a parameter or option applied to the live_loop function call, whereas :tune1 is the name defined for a particular live_loop function. It is confusing at first, but you quickly get used to how it works.
If you are using Sonic Pi version 2.6 or 2.7 amend the program as described in the comments at the beginning to get it to work.
You can download the program from my gist site here