Neutron Accelerators and Drum Machines with Sonic Pi

8 minute read

Published:

Working with Sonic Pi one of the exciting things about being able to code music is the potential for using algorithms. For the non-technical reader, I like to think of algorithms like following a recipe. You might start with the same ingredients but the order in which you do things can affect to outcome.

When we think about music people often understand things at an intuitive level but become uneasy about the idea of using rules or recipes to generate it. “Formulaic” is a dirty word when used to describe pop songs for example, but it doesn’t have to be that way. I’m going to look at one particular algorithm, Euclid’s algorithm, to create musical rhythms.

A method for spacing out rhythms

Despite all the complex words I’m about to use, if all boils down to this: if I have a number of beats that I want to play in a given space (say 2 bars), how do I space those out as evenly as possible?

That’s obviously easy with regular rhythms that divide cleanly. If I want to play 8 beats across two bars of 8th notes it would look like this:

x . x . x . x . | x . x . x . x .

That’s 8 x’s spaced across the 16 possible beats. Easy peasy.

But what about numbers that don’t divide cleanly, for example, 3 into 8?

Let’s start with the solution first:

x . . x . . x .

You can see we have to use a combination of long and short notes to fill the bar evenly. The above pattern uses groups of 3’s and 2’s to fill the space. You could easily notate it as:

332

if you count the distances from one beat to the next. Let’s look at a few more examples of these odd rhythms:

# 5 into 8
x . x . x x . x
22121

# 13 into 16
x . x x x x . x | x x x . x x x x
2111211121111

One thing you might notice is that these aren’t the only way to divide up the bar. To take our 3 into 8 rhythm there are actually three possible options:

x . . x . . x . #== 332
x . x . . x . . #== 233
x . . x . x . . #== 323

The last of those options appears so often in so many kinds of music that it’s often called “the mother rhythm”. Everything from Cuban clave to Elvis’ “Hound dog”. That said, if you look closely they are all variations on the same grouping: 2 lots of 3 and one lot of 2. That means we only need to generate one version and then we can rotate it to produce the others. So how are we going to generate it then?

Back to the future: Euclid’s algorithm from 300 B.C.

It turns out that Euclid thought about this too - how do you space things out as evenly as possible? Thousands of years later a scientist named Bjorklund was facing the same problem when he was setting up a Neutron Accelerator. He needed to fire neutrons evenly in a given space of time (a bit like our rhythm problem) and, being a scientist, he came up with a clever way of doing that.

    def distribute(accents, total_beats)
      res = []

      total_beats.times do |i|
        # makes a boolean based on the index
        # true is an accent, false is a rest
        res << ((i * accents % total_beats) < accents)
      end

	  res
    end

That’s the algorithm represented in Ruby. You don’t have to understand it as it’s probably not the simplest way of explaining it, but it’s there to show how concise a problem like this can be when expressed in code.

Years later a percussion-playing, scientifically-minded music professor was looking at the same problem applied to musical rhythms and came across Bjorklund’s research. He took the idea and explored it in a paper “The Euclidean Algorithm Generates Traditional Musical Rhythms” (Toussaint 2005) from which I’ve taken these ideas.

In Sonic Pi…

I’ve added a function to Sonic Pi called distribute which allows you to generate arrays that fit these evenly spaced patterns. Here’s some sample code:

def distribute(accents, total_beats, beat_rotations=0)
  res = []

  total_beats.times do |i|
    # makes a boolean based on the index
    # true is an accent, false is a rest
    res << ((i * accents % total_beats) < accents)
  end

  res.ring
end

class SonicPi::Core::RingArray
  def as_x_notation
    self.to_a.map {|x| x ? 'x' : '.'}.join(' ')
  end

  def as_beat_groups
    self.to_a.slice_before {|x| x }.to_a.map(&:count).join
  end
end

puts distribute(3, 8, 0).inspect
puts distribute(3, 8, 0).as_beat_groups
puts distribute(3, 8, 0).as_x_notation

If you paste in the above code to SonicPi and run it, you should see this in the output window:

 ├─ [true, false, false, true, false, false, true, false]
 ├─ 332
 └─ x . . x . . x .

From here it’s not too hard to map over those arrays to turn them into musical patterns. Here’s an example you can play with:

comment do
  # this function is available in SonicPi v2.4 and upwards
  # if you're using a version with this included you can 
  # delete this distribute function, otherwise uncomment it
  # above
  def distribute(accents, total_beats, beat_rotations=0)
    res = []

    total_beats.times do |i|
      # makes a boolean based on the index
      # true is an accent, false is a rest
      res << ((i * accents % total_beats) < accents)
    end

    res.ring
  end
end

# Monkey patching is never a good idea
# just say no kids...
class SonicPi::Core::RingArray
  def as_x_notation
    self.to_a.map {|x| x ? 'x' : '.'}.join(' ')
  end

  def as_beat_groups
    self.to_a.slice_before {|x| x }.to_a.map(&:count).join
  end
end

use_bpm(120)

def play_sample_for_sequence(pattern, sample_name, sleep_time = 0.25)
  pattern.each.with_index do |beat, i|
    sample sample_name if !!beat
    sleep sleep_time
  end
end

uncomment do
  live_loop(:hh) do
    with_fx :level, amp: 1 do
      cue :heartbeat
      play_sample_for_sequence(distribute(11, 16), :drum_cymbal_closed)
    end
  end

  live_loop(:bd) do
    sync :heartbeat
    play_sample_for_sequence(distribute(5, 16), :drum_bass_hard)
  end

  live_loop(:sn) do
    with_fx :level, amp: 1 do
      cue :heartbeat
      play_sample_for_sequence(distribute(2, 16).to_a.rotate(4), :drum_snare_hard)
    end
  end

  live_loop(:bass) do
    with_fx :level, amp: 1 do
      use_synth :tb303
      cue :heartbeat
      distribute(3, 8).each do |beat|
        play scale(:a2, :minor_pentatonic).choose, release: 0.3 if beat
        sleep 0.5
      end
    end
  end
end

Here’s a demo of me live coding with the above:

Why does this work?

Musically I think it’s fair to say that last example sounds pretty cool. But why? I think that the appeal of these rhythms (“Euclidean Rhythms” as Toussaint calls them) boils down to this: because the spacing is always even, when we layer these kinds of beats on top of each other we get strong “cross rhythms”. In the example above I’ve deliberately put the snare on beats 2 and 4 to provide a strong backbeat. All the other rhythms bounce off the strength of the snare rhythm.

I think another possible reason is that our brains enjoy patterns and order even if we can’t quite tell what the pattern is. Imagine the difference between two knitted jumpers, one with a completely random colour for every stitch, the other with a strong geometric pattern. Psychologically we’re more likely to prefer the geometric pattern (unless the random knit happens to be really cool).

Having these functions in our toolkit means that we can reach musical sounding results even faster, without having to rely on things being totally random but still retaining an element of surprise.

What do you think? Does it sound cool to you? Will algorithmic music ever be widely accepted? Hit me up on twitter @xavriley if you have any comments or questions.

Further rhythming

This topic has already been covered in several other places, but not as far as I know in Sonic Pi yet. There’s a very cool HTML5/Javascript version of this kind of drum machine available at http://www.groovemechanics.com/euclid/ which has been on Hacker news. Also there’s a patch for Max called Polyrhythmus which is along the same lines. I wanted to cover the musical aspects behind the algorithm in a bit more depth for this post. There’s also a good Wikipedia page on Euclidean rhythm with links to other resources.