Saturday, January 14, 2012

The Algorithmic Drum Machine

I kind of pulled a Mark Zuckerberg on this little project I made today, so I'll try to right my wrongs here:

Dan, this is your idea which I stole and implemented using Python and Csound. Please know that I did this out of love, and I am looking forward how you will implement your version of this drum machine in Supercollider.

Here is a little sample of what my drum machine sounds like:

Randomized Drum Machine Experiment by The Zebra Project

The drum sounds are just one-shot samples being triggered by the soundin opcode in Csound. No synthesis happening here. Actually, the code is so boring that I'm not going to even bother posting it. I won't be posting the samples either, but know that they are from the "Acetone Rhythm Ace" folder in this big file of drum samples I downloaded once.

The drum algorithm starts off with a base drum pattern. Inputting the base drum pattern is very similar to how you would program a traditional 16th note step sequencer. Every 16th note has a specific velocity and weight. Velocity speaks for itself. Weight is the hip algorithmic variable. Weight is a number between 0 and 1. The closer the number reaches 1, the more likely the number will be triggered. A note with a weight of .9 is going to get triggered 90% of the time with a specified velocity.

Below is the python code I used for the score generator. This code will render 4 bars of my example drum pattern to a Csound Score file. There, it can be rendered. Because this algorithm uses stochastic (random) elements, no two renders are alike.

import random

#declare the base drum pattern in a typical 16th note cell fashion

snare_note = (0,0,0,0,1,1,0,0,0,0,0,0,1,1,1,1)
snare_vel = (0,0,0,0,1,.25,0,0,0,0,0,0,1,.5,.5,.5)
snare_weight = (0,0,0,0,.9,.5,0,0,0,0,0,0,.9,.2,.1,.2)

kick_note = (1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0)
kick_vel = (1,0,0,0,0,0,0,0,1,0,0,0,0,0,.5,0)
kick_weight = (1,0,0,0,0,0,0,0,.9,0,0,0,0,0,.5,0)

hihat_note = (1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1)
hihat_vel = (1,.1,.4,.25,1,.1,.4,.25,1,.1,.4,.25,1,.1,.4,.25,)
hihat_weight = (.5,.6,.8,.8,.9,.8,.5,.6,.5,.6,.8,.8,.9,.8,.5,.6)

#open score file
score = open("drumscore.sco", "w")
tempo = 120

#write tempo
score.write("t 0 " + str(tempo) + "\n")

def drumseq(name,instr,note,vel,bar_num,weight):
score.write(";" + str(name) + "\n")
time = 0
for bar in range(bar_num):
for i in range(16):
rand_val = random.random()
if note[i] == 1 and rand_val <= weight[i]:
time = i * .25 + 4*bar
score.write("i" + str(instr) + " " + str(time) +" .25 "+ str(vel[i]) + "\n")


#write the parts
drumseq("snare", 1, snare_note, snare_vel, 4, snare_weight)
drumseq("kick", 2, kick_note, kick_vel, 4, kick_weight)
drumseq("hihat", 3, hihat_note, hihat_vel, 4, hihat_weight)
score.close()



This is a rather basic implementation of this drum algorithm and has much room for improvements. In the future, I hope to incorporate more elements of processing like reverse and pitch shift in addition to humanization to strive to generate an organic sounding drum pattern.

No comments:

Post a Comment