An interesting spin on standard lists are rings. If you know some programming, you might have come across ring buffers or ring arrays. Here, we’ll just go for ring - it’s short and simple.
In the previous section on lists we saw how we could fetch elements out of them by using the indexing mechanism:
puts [52, 55, 59][1]
Now, what happens if you want index 100
? Well, there’s clearly no element at index 100 as the list has only three elements in it. So Sonic Pi will return you nil
which means nothing.
However, consider you have a counter such as the current beat which continually increases. Let’s create our counter and our list:
counter = 0
notes = [52, 55, 59]
We can now use our counter to access a note in our list:
puts notes[counter]
Great, we got 52
. Now, let’s increment our counter and get another note:
counter = (inc counter)
puts notes[counter]
Super, we now get 55
and if we do it again we get 59
. However, if we do it again, we’ll run out of numbers in our list and get nil
. What if we wanted to just loop back round and start at the beginning of the list again? This is what rings are for.
We can create rings one of two ways. Either we use the ring
function with the elements of the ring as parameters:
(ring 52, 55, 59)
Or we can take a normal list and convert it to a ring by sending it the .ring
message:
[52, 55, 59].ring
Once we have a ring, you can use it in exactly the same way you would use a normal list with the exception that you can use indexes that are negative or larger than the size of the ring and they’ll wrap round to always point at one of the ring’s elements:
(ring 52, 55, 59)[0] #=> 52
(ring 52, 55, 59)[1] #=> 55
(ring 52, 55, 59)[2] #=> 59
(ring 52, 55, 59)[3] #=> 52
(ring 52, 55, 59)[-1] #=> 59
Let’s say we’re using a variable to represent the current beat number. We can use this as an index into our ring to fetch notes to play, or release times or anything useful we’ve stored in our ring regardless of the beat number we’re currently on.
A useful thing to know is that the lists returned by scale
and chord
are also rings and allow you to access them with arbitrary indexes.
In addition to ring
there are a number of other functions which will construct a ring for us.
range
invites you specify a starting point, end point and step size.bools
allows you to use 1
s and 0
s to succinctly represent booleans.knit
allows you to knit a sequence of repeated values.spread
creates a ring of bools with a Euclidean distribution.Take a look at their respective documentation for more information.