Parameterized Random Rhythm Generation, Part 1

So ~back in the day~, I created a little drum machine with Arduino. It would generate a pattern based on certain parameters and then send MIDI signals to trigger drums. It was pretty cool, but it doesn’t work well with the new SuperCollider framework, and it had some limitations that I was never happy with. So I’ve decided to rebuild it with new capabilities.

Requirements

In a nutshell, what I want is to be able to build Converter-style power noise tracks that I can modulate and control with my gloves and possibly a new interface (I have some ideas about that, but they’re too preliminary to talk about right now). I’ve also figured out some things about the types of beats I prefer when I do sit down to write things from scratch, so I want to incorporate those into the design.

Repeating patterns

Obviously, this is the core requirement. I want the output to be consistent but not repetitive, which means I need to be able to store a drum pattern and repeat it, but also to change the pattern up at certain points. So I need to be able to translate a rhythmic pattern into a data structure.

The obvious answer, to me, is that one bar of rhythm can translate into an array of 16 booleans, one for each 16th note. For example, in a basic rock beat with the kick on 1 and 3 and the snare on 2 and 4, the arrays would look like:

kick =  [1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0];
snare = [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0];

Per-note probabilities

For a basic rock beat, there’s a probability of 1 that kick[0] = 1 and kick[7] = 1, and a probability of 0 that any other note = 1. That’s pretty boring, but I also don’t want it to be totally random — I’m not making free jazz here. So I’m going to draw some parameters for the program to work inside of. I’m representing a bar as an array of 16 notes, but those notes aren’t identical. They’re sixteenth notes:

        1  e  &  a  2  e  &  a  3  e  &  a  4  e  &  a
kick = [1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0];

So each type of drum — right now I’ll do kick, snare and hat — should have a certain probability of hitting depending on its position within the bar. I want something that sounds at least vaguely like a rock beat. So for each type of drum, I’ll build an array of probabilities:

kick_probs =  [95, 10, 20, 10, 40, 10, 20, 10, 95, 10, 20, 10, 40, 10, 20, 10];
snare_probs = [30, 10, 30, 10, 90, 10, 30, 10, 30, 10, 30, 10, 90, 10, 30, 10];

Then I’ll build out the arrays using these probabilities. Pseudocode:

kick = Array.fill(16, false);
for(var i = 0; i < 16; i++) {
  var rand = random(0, 100);
  if(rand < kick_probs[i]) {
    kick[i] = true;
  }
}

Making noises

Now I have the ability to generate a bounded, randomized rhythm pattern for different types of drums, I need to be able to actually play the sounds. I want to have a bank of different drum sounds to choose from randomly, and to be able to trigger those sounds based on the pattern arrays and on the tempo. The previous sections have been pretty general, but this starts to get into SuperCollider-specific information.

I’ve decided to store the files as “kick0.wav”, “kick1.wav”, etc. I’ll load each of these sounds into a buffer in SuperCollider in a particular order, and store a dictionary of the file names to the buffer numbers:

b = Buffer.read(s, thisProcess.nowExecutingPath.dirname +/+ "kick0.wav");
b = Buffer.read(s, thisProcess.nowExecutingPath.dirname +/+ "kick1.wav");
...etc

e = Dictionary.new();
e.add("kick0" -> 0);
e.add("kick1" -> 1);
...etc

Then, all I need is one synth, which I can trigger with a specific bufNum every time I want to play a drum sound:

SynthDef("drum", { arg bufNum = 0, t_trig = 0;
 Out.ar(0,
 PlayBuf.ar(2, bufNum, BufRateScale.kr(bufNum), t_trig)
 )
}).add;

Then all I need to do is to play a loop — essentially an interval timer that waits 0.25*tempo seconds — and trigger the synth for each drum that registers a “hit” on that note:

loop {
  x = (x + 1) % 16;

// For each drum, check if it's "on" for this note
  d.keysValuesDo { | key, value |
    bar = value[x];
    if (y == 0, {
      bar = ~newBar.value(key, x);
    });
    if (bar, {
      // Trigger the right synth
      drum.set(\bufNum, e[key], \t_trig, 1);
    });
  };
  ~wait.wait;
};

Switching up the pattern

So far I can generate a pattern and play it back using a variety of wav files. The pattern is random but has a set of parameters that it works within to keep it sounding familiar. But even if the generated beat is interesting in itself, it’ll get boring if I don’t switch it up. I’ve decided to do this in two ways:

1. Every fourth bar is randomized

I’ve noticed that whenever I write my own beats, I like it when the same pattern is repeated three times, then it switches up for the fourth bar. So I decided to add this capability; the stored pattern is repeated for three bars, then the fourth is random, generated on the fly.

2. New patterns

I want to generate new patterns every so often — not too often, but not too rarely. If every [3x patterns, 1x random] is a “block”, from experimenting with my old drum machine, I know I like to switch up the pattern about every 8-12 blocks. I want there to be a chance of it changing on every other block, and for it to be absolutely guaranteed at 16. In SC notation, that looks like…

x = (x + 1) % 16;
if (x == 0, {
  y = (y + 1) % 4;
  y.postln;
  // Chance of creating new patterns
  if (y == 0, {
    z = z + 1;
    if (z > 0 && (z % 2) == 0, {
      rand = rand(1, 16);
      rand.postln;
      if (rand  ~newBar.value(key));
      });
      d.postln;
      z = 0;
      });
    });
  });
});

Next steps

You can see the finished code here. This is the MVP based on my concept of the tool. However, this is definitely just a first step, and over the next few weeks I’m going to be working on building out the new features. These include:

  • Triplets instead of 16th notes
  • Save two sets of patterns, like “verse” and “chorus”
  • Use interface to add or remove drums
  • Use interface to modify the randomness or density of the patterns
  • Use interface to add effects to the drums (definitely bit crushing, maybe reverb, maybe FM)

I’ll be adding more posts as I get these things done.

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 )

Google+ photo

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

w

Connecting to %s