Skip to content

Instantly share code, notes, and snippets.

@omardelarosa
Last active February 3, 2021 01:40
Show Gist options
  • Save omardelarosa/c03bed13c0682eb1a51ae2a951f08cbb to your computer and use it in GitHub Desktop.
Save omardelarosa/c03bed13c0682eb1a51ae2a951f08cbb to your computer and use it in GitHub Desktop.
Drum Pattern Duration Randomization

Example Drum Pattern Duration Randomization

This uses some slightly more interesting drum pattern randomization than I had previously been using. It kind of uses a 2-dimensional approach:

Initial hit Position

snares = [
  ringify('0000 1000 0000 1000'),
  ringify('0000 1000 0000 1010'),
  ringify('0100 1000 0000 1000')
]

Number and duration of hits

snare_dur = [
  [4, 1],
  [4, 1],
  [4, 1],
  [4, 1],
  [8, 2],
  [12, 3],
  [12, 3],
  [12, 3],
  [24, 6],
  [64, 4],
  [64, 4],
  [64, 4],
  [128, 8],
  [128, 8],
  [128, 8],
].ring

Combination

The combined result is as follows:

live_loop :bt do
  r = rhythm.tick
  p = pattern.tick # select which pattern
  
  # Kicks
  # ... kicks code
  
  # Snares
  in_thread do
    s = snares[p][r]
    if s == 1
      d = snare_dur.choose  # duration randomly checked.
      d[1].times do |n|
        l = (4.0 - n).abs * LEVELS[:snares]
        dur = M/d[0]
        sample :sn_dolf, amp: l
        sleep dur
      end
    end
  end
  
  # Claps
  ## ... claps code
end

To see this in action, checkout an example of a track I made using this system on YouTube

use_bpm 60
M = 4.0
SAMPS = "#{SAMPLE_PATH}/other_bits/"
# Array of sample volumes, etc, mapping
vox = [
['afterparty1', (M/1 * 2), 1.5, :F2],
['afterparty2', (M/2), 4, :A3],
].ring
# Initialize scale, etc
# Initial States
set :ch, 0 # chord cursor
set :rh, 0 # rhythm cursor
rt = :A3
sc_type = :aeolian
sc = scale rt, sc_type
oct = 4
rhythm = range(0,16).ring
durations = (ring, 16)
melody = make_melody(48)
# Controls the channels that are muted/not muted
LEVELS = {
hats: 0,
kicks: 0,
snares: 0,
vox: 0,
vox2: 0,
syn1: 0,
syn2: 0,
claps: 0
}
# Alternative Claps
TICKS = [:elec_blup, :elec_beep]
# Creates a ring of chords for the scale
chords = (1..7).map {|n| chord_degree n, rt, sc_type }.ring
# Chord Transitions
CH = {
0 => [3,4],
1 => [7],
2 => [5,4],
3 => [4],
4 => [3,3,0,0,0],
5 => [1,6],
6 => [2],
7 => [4]
}
# Level Transitions
LV = {
0 => [1,1,1,0],
1 => [0,0,1,1]
}
# A set of durations for the kick to choose from
kick_dur = [
[4, 1],
[8, 2],
[3, 2],
[12, 3],
]
kicks = [
ringify('1000 0000 1000 0000')
]
# A set of durations for the hats to choose from
hats = [
[16, 4],
[16, 4],
[16, 4],
[32, 8],
[48, 12],
[12, 3]
].ring
snare_dur = [
[4, 1],
[4, 1],
[4, 1],
[4, 1],
[8, 2],
[12, 3],
[12, 3],
[12, 3],
[24, 6],
[64, 4],
[64, 4],
[64, 4],
[128, 8],
[128, 8],
[128, 8],
].ring
## This will likely be expanded later. TODO
snares = [
ringify('0000 1000 0000 1000')
]
## Each of the above 1s triggers a string of hits
## The loops below read from both dimensions:
## 1/16 pattern and duration list
### STATE TRANSITIONS
# Change chords
live_loop :chrds do
# Update chord
mnote :ch, CH
sleep M/1
end
# Update Levels
live_loop :levels do
# Updates levels of each channel
LEVELS.each {|k,v| LEVELS[k] = LV[v].sample }
sleep M/1
end
# Two vocal loops to use with the sampler.
live_loop :vox1 do
v = vox[1]
l = (v[2] * LEVELS[:vox])
with_fx :reverb, mix: 0.8 do
with_fx :bitcrusher, mix: 0.9 do
sample SAMPS, v[0], amp: l/1.1
end
end
sleep v[1]
end
live_loop :vox2 do
v = vox[2] # This is cardib.
l = 0#(v[2] * LEVELS[:vox2]) # Might keep this at 0 for now.
with_fx :reverb, mix: 0.5 do
sample SAMPS, v[0], amp: l
end
sleep v[1]
end
live_loop :ht do
h = hats.tick
h[1].times do
l = 3.0 * LEVELS[:hats]
sample :elec_tick, amp: l
sleep M/h[0]
end
end
live_loop :bt do
r = rhythm.tick
# Kicks
in_thread do
s = kicks[0][r]
if s == 1
d = kick_dur.choose
d[1].times do |n|
l = (8.0 - n) * LEVELS[:kicks]
sample :bd_fat, amp: l
sleep M/(d[0])
end
end
end
# Snares
in_thread do
s = snares[0][r]
if s == 1
d = snare_dur.choose # duration randomly checked.
d[1].times do |n|
l = (4.0 - n).abs * LEVELS[:snares]
dur = M/d[0]
sample :sn_dolf, amp: l
sleep dur
end
end
end
# Claps
in_thread do
s = snares[0][r]
if s == 1
l = 4.0 * LEVELS[:claps]
sample TICKS.choose, amp: l
end
end
sleep M/16
end
live_loop :a1 do
use_synth :fm
c = get :ch
l = 1.0 * LEVELS[:syn1]
3.times do |n|
chr = chords[c]
nt = c + chr.tick - 8
play sc[nt], release: 3, amp: l
end
sleep M/4 + M/8
4.times do |n|
chr = chords[c]
nt = c + chr.tick - 8
play sc[nt], release: M/4, amp: l
end
sleep M/8
end
live_loop :a2 do
use_synth :chipbass
d = [8,6] # Sets the melody rhythm
l = LEVELS[:syn2]
d[1].times do |n|
c = get :ch
nt = c + melody[n]
play sc[nt], release: M/d[0], amp: 3.0 * l
sleep M/d[0]
end
rst = (d[0] - d[1])
if rst != 0
sleep M/(d[0] - d[1])
end
end
###
### Global Utility Functions and Constants
###
### To be loaded once in a buffer and used globally
###
SAMPLE_PATH = '~/Dropbox/Code/Music/SonicPi/Samples/'
define :markov do |a, h| h[a].sample; end # Chooses the next state at random from hash
define :g do |k| get[k]; end # simplified root note in scale getter
define :s do |k, n| set k, n; end # simplified root note setter
define :mnote do |key,chain| s key, (markov (g key), chain); g key; end
# Pattern parsing
define :ringify do |p| p.gsub(' ', '') .split('').map(&:to_i).ring; end
# Melody builder
define :make_melody do |len = 16, rng = 2|
(1..len).map{|n| ((rng*-1)..rng).to_a.sample }.ring
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment