I’m a big fan of the Elektron Analog Rytm drum computer. I wrote a script to model a general drum computer. You can make patterns, change the volume (or another parameter) per step (called parameter lock in the Rytm), and combine several patterns into a song.
I added live controls, like easy ways to do live coding:
- swing
- mixer. Instead of starting and stopping a loop, you can also set the volume to 0. In this way you can avoid sync issues.
- changing start position of pattern, reverse, tempo
I hope you enjoy it!
# Live controls. This is an attempt to have several interesting modifications to the sound close at hand, to make
# live coding during a performance easier. However, it makes the code a bit more complex.
use_bpm 126
# These controls are to easily transpose the pitch (tune) of the melodies. 12 is the number
# musical notes in an octave. Changing whole octaves makes sure that the melody stays in harmony
# with the other melodies.
pitch_low = 0 * 12
pitch_mid = 1 * 12
# This makes sure that the sleep time is not constant, but swinging.
# To make the rhytm straight, change the swing_amount to 4. Go to 5 and it sounds laidback.
# Going close to 8 reduces the second sleep in swing to almost 0, thus almost skipping the note altogether
# Make to add 1 decimal to the swing_amount, which turns to data type into a floating number.
swing_amount = 4.8
swing = [ swing_amount / 64*4 , (8.0 - swing_amount) /64*4 ].ring
# This is an easy approach to mute or change the volume of the different tracks. It's like a audio mixer.
# The values work on the amp: function. Another option to turn instruments on/off is to start and stop
# the live_loops, but somehow I feel the result is less easy to control. In this situation, you let all
# the loops play all the time, avoiding out of sync problems altogether.
# bd = bassdrum, sn =snaredrum, cy = cymbal, pe = cymbal pedal, low = instr low, mid = instr mid, ran = instr random
#loops_mute =[bd sn cy pe dr5 dr6 dr7 dr8 low mid]
loops_mute = [ 0.8, 1, 1, 1, 1, 0, 1, 0, 1, 1]
# You can play the arrays for drum patterns and melody from start till end, but why not play with time?
# The first array defines the start position in the loop (melody or rhythm). Change it to create more complex
# intertwined rhythms. The second array defines how many steps for sleep you take every cycle. The default for drums is
# 2, because the first step in the drum pattern array is the note, and the second defines the volume of the note (see
# more specific comments at the sections of the drum patterns). You can also use a negative number, which reverses the pattern.
# pos= [drums low mid] (only use even number)
pos = [0, 0, 4]
# time_tweak = [drums low mid]
time_tweak = [2, 1, -1]
#-----------------------------------------------------------------------------------------------------------------
# This section is a method to create drums like a drum computers. The rhythms in a drum computer are called patterns.
# This usually involves a row of 16 buttons and a cursor looping through this row. For each drum sound, you press the
# buttons on the positions where you want the drum sound to play. Usually the pattern plays from 1 to 16 and repeats.
# However, in Sonic Pi you can also skip steps or reverse the pattern (also see the array above called time_tweak.
# In some modern drum computers (like Elektron Analog Rytm), you can also change a parameter of a drum sound for each
# step. This is called parameter lock. In our drum computer, we can change the volume of each separate drum for each step.
# Finally, you can connect different patterns to a song.
# This defines the drum samples (drum kit). The only reason to do this, is to make the names shorter, so that they take less
# space in the arrays of the drum patterns. s0 stands for sample 0.
define :s0 do
:bd_klub
end
define :s1 do
:sn_dub
end
define :s2 do
:drum_cymbal_closed
end
define :s3 do
:drum_cymbal_pedal
end
define :s4 do
:elec_flip
end
# These are the drum patterns. The drum looper (next part) will cycle through these arrays. These arrays consist of
# 32 steps. However, 2 steps are taken at the same time, before a sleep command. The first step of this duo is to place
# a sample (e.g. s0) or a rest (:r). The second one is used to set the volume (:amp) of the sample. However, the second one
# can also be used for other parameters of a sample. For example the length of the sample (:finish) or the pitch.
kick_1 = [s0,1, :r,1, :r,1, :r,1, s0,1, :r,1, :r,1, :r,1, s0,1, :r,1, :r,1, :r,1, s0,1, :r,1, :r,1, :r,1].ring
kick_2 = [s0,1, :r,1, :r,1, :r,1, s0,1, s0,0.5, :r,1, s0,1.2, :r,1, :r,1, :r,1, :r,1, :r,1, :r,1, :r,1, :r,1].ring
snare_1 = [:r,1, :r,1, :r,1, :r,1, s1,0.6, :r,1, :r,1, :r,1, :r,1, :r,1, :r,1, :r,1, s1,0.6, :r,1, :r,1, :r,1].ring
snare_2 = [:r,1, :r,1, :r,1, :r,1, s1,0.6, :r,1, :r,1, :r,1, :r,1, s1,0.7, :r,1, s1,0.5, s1,0.4, :r,1, :r,1, :r,1].ring
cym_1 = [s2,0.3, s2,0.5, s2,1, :r,1, s2,1, s2,0.3, s2,1, :r,1, :r,1, :r,1, s2,1, :r,1, :r,1, :r,1, :r,1, :r,1].ring
ped_1 = [:r,1, :r,1, :r,1, :r,1, :r,1, :r,1, :r,1, :r,1, :r,1, :r,1, :r,1, :r,1, s3,0.3, s3,0.6, s3,0.8, s3,1.2].ring
elec_1 = [:r,1, :r,1, :r,1, :r,1, :r,1, :r,1, :r,1, :r,1, s4,1, :r,0.3, :r,2, :r,1, :r,1, :r,1, :r,1, :r,1].ring
mute = [:r,1, :r,1, :r,1, :r,1, :r,1, :r,1, :r,1, :r,1, :r,1, :r,1, :r,1, :r,1, :r,1, :r,1, :r,1, :r,1].ring
# This function uses the drum patterns to create the movement through the patterns. A total of 4 patterns are defined here.
# Each pattern is a variable (e.g. drumname1). In the following live_loop :playdrum, you actually choose which patterns you
# want to play. This makes it very dynamic and easy to add more patterns, and play different variations of drums in the
# live_loop. Because the patterns contain 2 steps (note, volume) before a sleep command, the pattern skips 2 places each
# loop (pos[0]+=2). With a straight rhythm, this would do the job. But if you use the swing function, there is always a pair
# of steps with different duration. So if you would skip 2 steps, this would mean you always take either the shorter one or
# the longer one of the pair. That's why there is a ring which puts the swing position one place back 1 out of every 2
# loops.
define :choosedrum do |drumname1,drumname2,drumname3,drumname4,drumname5,drumname6,drumname7,drumname8|
16.times do
sample drumname1[pos[0]], amp: 0 + drumname1[pos[0]+1] * loops_mute[0]
sample drumname2[pos[0]], amp: 0 + drumname2[pos[0]+1] * loops_mute[1], finish: 0.2
sample drumname3[pos[0]], amp: drumname3[pos[0]+1] * loops_mute[2], finish: 0.1
sample drumname4[pos[0]], amp: 0 + drumname4[pos[0]+1] * loops_mute[3], finish: 0.2
sample drumname5[pos[0]], amp: 0 + drumname5[pos[0]+1] * loops_mute[4]
sample drumname5[pos[0]], amp: 0 + drumname6[pos[0]+1] * loops_mute[5]
sample drumname5[pos[0]], amp: 0 + drumname7[pos[0]+1] * loops_mute[6]
sample drumname5[pos[0]], amp: 0 + drumname8[pos[0]+1] * loops_mute[7]
sleep swing[pos[0] - (ring 0,1).tick]
pos[0]+= time_tweak[0]
end
end
# This is where you can make a song with patterns.
live_loop :playdrum do
choosedrum(kick_1,snare_1,cym_1,mute,mute,mute,mute,mute)
choosedrum(kick_1,snare_1,cym_1,ped_1,mute,mute,mute,mute)
choosedrum(kick_1,snare_1,cym_1,mute,mute,mute,mute,mute)
choosedrum(kick_2,snare_2,cym_1,ped_1,elec_1,mute,mute,mute)
end
# This is the basic array to draw the melodies from.
melody_1 = [:c4,:r,:ds4,:ds4,:g4,:c4,:r,:gs4,:r,:r,:gs3,:r,:g3,:r,:r,:r].ring
live_loop :instr_low do
use_synth :subpulse
use_synth_defaults cutoff: 70, release: 1.2, amp: 1 * loops_mute[8], pulse_width: rrand(0,1)
16.times do
play melody_1[pos[1]] + pitch_low
sleep swing[pos[1]]
pos[1]+= time_tweak[1]
end
end
live_loop :instr_mid do
use_synth :blade
use_synth_defaults cutoff: rrand(75,120), release: 0.2, amp: rrand(1.0, 1.4) * loops_mute[9]
with_fx :reverb, mix: 0.55, room: 0.85, damp: 0.8 do
16.times do
play melody_1[pos[2]] + pitch_mid
sleep swing[pos[2]]
pos[2]+= time_tweak[2]
end
end
end