General Purpose Drumkit

Greatly inspired yesterday by @alexesc’s ace method for drum patterns, and in my quest to use SPi as a general purpose live and recording instrument I wrote a general purpose drumkit script.

I’ve put this in 6/4, not to annoy you but coz that’s the track I’m working on just now. Four on the floor or boots and cats are great for sure, but c’mon there’s so many other great rhythms in the world :smile: and not just random ones ha ha

This is so much easier than working out all the individual sleeps, more in tune with how I think as a drummer. I’m still trying to find a similar good way with melodies - if you got any ideas please share.

btw in real use I’m putting in choices and probablities to mix it up, take elements in and out

#General purpose drum kit

use_bpm 120

define :pattern do |pattern|
  return pattern.tick(:pattern) == "x"
end

live_loop :main do
  sleep 6
  cue :bar
end

with_fx :reverb do
  with_fx :echo, mix: 0.1 do
    
    live_loop :drumkit do
      sync :bar
      
      #Kick
      a = 0.5
      in_thread do
        6.times do
          sample :bd_ada, amp: a if pattern "x-----"
          sleep 1
        end
      end
      
      #Snare
      a = 1.0
      in_thread do
        6.times do
          sample :drum_snare_hard, amp: a if pattern "---x--"
          sleep 1
        end
      end
      
      #Pedal
      a=0.4
      in_thread do
        6.times do
          sample :drum_cymbal_pedal, amp: a*0.3 if pattern "-x--x-"
          sleep 1
        end
      end
      
      #Hihat
      a=0.1
      in_thread do
        p1 = "-xx-x-xx-xx-"
        p2 = "xx-xx-xx-xx-"
        p3 = "-xx-xx-xx-xx"
        p4 = "xxxxxxxxxxxx"
        p = [p1,p2,p3,p4].choose
        12.times do
          ac=[1,2,1,1,1,2].ring
          sample :drum_cymbal_soft, amp: a*ac.tick(:ac) if pattern p
          sleep 0.5
        end
      end
      
      #Hihat Open
      a=0.02
      in_thread do
        12.times do
          sample :drum_cymbal_open, amp: a, finish: 0.15 if pattern "-----------x"
          sleep 0.5
        end
      end
      
      #Percussion
      a=0.2
      with_fx :slicer do
        sample :perc_bell, rate: 0.5, amp: a, release: 1.0
      end
      
    end #drumkit
    
    
  end #echo
end #verb
3 Likes

holy moly, nice i will try to make melodies now. atleast i hoped to xD

1 Like

I’ve looked a quite a few scripts that play melodies, all kinds of ways. They’re amazing and all look like very hard work vs composing or recording to a DAW. Fun and v impressive but not practical for me.

The cogs are whirring about to use something like this ‘pattern’ idea with a melody. But I’d be delighted to copy someone else’s good idea :smiley:

i “found” this here, for me as a non drummer i find this more intuitiv :stuck_out_tongue_winking_eye:

live_loop :multi_beat do
  use_random_seed 122212123222221233
  12.times do
    
    sample :elec_hi_snare if one_in(3)
    sample :drum_cymbal_closed if one_in(3)
    sample :drum_cymbal_pedal if one_in(3)
    sample :bd_haus if one_in(3)
    sleep 0.25
  end
end

live_loop :multi_beat2 do
  
  
  use_random_seed 12222342112123222221233
  
  10.times do
    
    use_synth :chipbass
    
    play_pattern_timed chord(:c4, :minor).pick(1), 0.25 if one_in(18)
    
    play_pattern_timed chord(:g3, :minor).pick(1), 0.25 if one_in(14)
    play_pattern_timed chord(:f3, :minor).pick(1), 0.25 if one_in(8)
    play_pattern_timed chord(:c3, :minor).pick(1), 0.25 if one_in(2)
    
    
    sleep 0.25
    
  end
end
1 Like

That’s great! Unless you planned it all out (?) it’s like a voyage of discovery where you can come across new ideas. I’m all for that. Oftentimes though I want to code a specific known groove, then wobble it about a bit.

I’ll defo nick this pattern though - I think it needs a bit of both.

If you see my suggestion about VCV Rack, and get into that, you’ll see that ‘organised chaos’ is very much in the culture. Treading the line between totally random and deterministic there’s a lot of creativity to be mined

I think there’s a Brian Eno quote going round about this new music being more about choosing things you like (from what the machine throws up) vs writing what’s in your head

Inspired by myself…OK not melody but the same idea for loopy arpeggios. Only things is it relies on the Ruby to_i function and I’m mindful of Sam’s warning that it might be there in future.

#Arpeggios

use_bpm 120

define :melody do |scale, pattern|
  return scale[pattern.tick(:melody).to_i]
end

live_loop :main do
  sleep 4
  cue :bar
end

with_fx :reverb, mix: 0.3 do
  with_fx :echo, mix: 0.3 do
    
    live_loop :arp do
      sync :bar
      
      sample :bd_ada, amp: 0.3
      
      a = 0.5
      notes = scale :C4, :aeolian
      in_thread do
        16.times do
          n = melody(notes, "03570359")
          play n, amp: a
          sleep 0.25
        end
      end
    end #arp
    
    
  end #echo
end #verb
1 Like

Ah I hope ruby functions don’t get removed in the future, I use them all the time :smiley:

Have you seen ziffers @soxsa? It’s uses a similar number notation.

Anyways, here’s how I’ve been jamming recently. It’s not very idiomatic sonic pi, but I’ve been enjoying it.

I kinda did something similar to you for the melody but I use a ring of indexes.

require "~/samples/samplepacks.rb"

define :l2f do |samp, length|
  #converts desired length to fraction of sample length
  duration = sample_duration samp
  return 1 if length >= duration
  return length/duration
end

define :evenBar do
  return look(:bar) % 2 == 0
end

define :evenBeat do
  return look(:beat) % 2 == 0
end

define :oddBeat do
  return look(:beat) % 2 != 0
end

define :fourthBeat do
  return look(:beat) == 4
end

define :kick do
  #evenBar = look(:bar) % 2 == 0
  #return if evenBar
  length = 0.5
  sample :drum_heavy_kick, finish: length, release: 0.1
end

define :snap do
  snap = Hyperdrums[:snaps], 12
  finish = l2f(snap, 0.25)
  timeShift = 1-finish
  time_warp 0.5 do
    sample :perc_snap2, finish: finish,\
      compress: 1, pre_amp: 1
  end
end

define :hit do
  d = ring(1,1,1,2,1).tick(:hit)
  hit1 = Hyperdrums[:closedhats], 5
  fin1 = l2f(hit1, 0.5/d)
  hit2 = Hyperdrums[:closedhats], 2
  fin2 = l2f(hit2, 0.5/d)
  density d do
    time_warp 0 do
      sample hit1, finish: fin1, amp: 0.2
      sleep 0.5
      sample hit2, finish: fin2, amp: 0.3
    end
  end
end

define :funky do
  return if !fourthBeat
  len = 0.5
  vox1 = :bass_voxy_hit_c
  finish1 = l2f(vox1, len)
  vox2 = :bass_voxy_c
  finish2 = l2f(vox2, len)
  time_warp (1-len) do
    sample vox1, finish: finish1, amp: 0.5, attack: 0.1
    sample vox2, finish: finish2, amp: 0.6, attack: 0.1
  end
end

define :bd do
  sample :bd_gas, beat_stretch: 0.5  if evenBar
  time_warp 0.5 do
    sample :bd_haus, beat_stretch: 0.5
  end
end

define :melody1 do
  use_synth :fm
  use_octave -2
  idx = ring(2,4,2,4,3,5,3,5).tick(:melody1)
  timeShift = ring(0,0.25,0,0.25).look(:melody1)
  sustain = ring(0.9,0.65,0.9,0.65).look(:melody1)
  time_warp timeShift do
    play (scale :F, :minor)[idx], attack: 0.1, \
      sustain: sustain, release: 0.1
  end
end

define :hyperclap do
  claps = Hyperdrums[:claps]
  filts = ["20","22","27","29","34"]
  clap = claps, 5
  length = 0.45
  finish = l2f(clap, length)
  #timeShift = 1-length
  time_warp -0.05 do
    sample clap, amp: 0.3, pan: 0.1#, lpf: 120
    sleep 0.5
    sample clap, amp: 0.2, pan: -0.2#, lpf: 115
  end
end

define :hyperkick do
  kicks = Hyperdrums[:kicks]
  filts = ["04", "10","43"]
  kick = kicks, 6 #6,9
  finish = l2f(kick, 0.75)
  sample kick, finish: finish, amp: 0.4
end

set_sched_ahead_time! 1
tick_set :bar, 1
live_loop :conductor do
  use_bpm 120
  set_volume! 1
  tick_set :beat, 1
  4.times do
    tick(:beat)
    #kick
    hyperkick
    hyperclap
    snap
    hit
    #funky
    bd
    melody1
    sleep 1
  end
  tick(:bar)
end

(sorry it’s messy I just copy pasted a jamming session)

3 Likes

wow think that takes time to digest for me xD

1 Like

Yes me too! Lots of stuff in there, I’ll give it a play tomorrow

Thanks for the ziffers tip, that looks promising

The way I think about it in my head is that each sound/instrument describes it’s own position in relation to the beat and the bar. It also describes it’s own individual behaviour like FX or sound envelope.

The conductor loop sets them all up and controls the context of beat & bar. For example if you wanted 6 beats in a bar you you would adjust the 4.times loop. If I wanna stop using an “instrument” I comment it out. Here I also control “globals” like the bpm or master volume (which I sometimes lower if, for example, I am looking through sample packs for a sound I like).

Everything else is helper functions.

Happy to answer any questions, if needed.

Heyy glad you found my little drum thing! For a few days that I discovered this old Roland midi box I had laying around has not too bad patches and sounds I’ve been going crazy sequencing it with sonic pi and I’ve been loving it just grooving with the machine :sunglasses:

Something I recommend if you haven’t checked out yet is the spread function! It uses Euclidian math magic to generate drum patterns, the sonic pi documentation explanes it quite well I think. My daily sonic pi workout has been connect m laptop to the roland and just run a bunch of live loops with the code I posted for the acustic cover. Everyone should check out the spread function!

1 Like

That’s very cool. This is a way of thinking about rhythm I’ve simply not seen before. Maybe my old skool linear approach - well that’s how you learn drums - needs revision.

I can’t play it though without the ‘samplepacks.rb’ - is this yours or a download I’m missing???

How have you got that hooked up? Details please!!! USB-midi cable or what? And you’re sending midi notes out from Spi? I’m just looking for an excuse to buy and build in more hardware and this could be it

Spread function - thanks for the tip… another rabbit hole coming up I can tell…:smile:

What I like about your drum pattern thing is the simplicity - a single line function and all the possibilities unfold. That’s the way. If we’re embracing coding as an art form, I think it’s as much about the elegance of the code as the outputs.

Again, thanks for the ziffers shout. I don’t know - looks like there’s a ton of thought and work gone into it but I’m wary. Same as with the thoughts on Spi vs Tidal - some tools are good for one thing, others for another.

Spi just feels right for loops, arpeggio’s, working with samples. Looking at Sam’s set on Saturday, looked to me like he only had a bit more than a screen’s worth of code but kept us entertained with the thing morphing around for a full 90 mins with that kind of music - looks like something fits right there

1 Like

Yeah i am using “if spread” and “if one_in” all the time, but i have to admitt i like if_one in more because when u change the random seed u get a new melodie and a new rythm

you can also use spread and shuffle it, that is the one I use most.

“bools” is good for this as well:

use_bpm 160

live_loop :drumzz do ; tick
  sample :bd_fat, amp: 1.2 if bools(1,0,0,0, 0,0,0,0, 0,0,1,0, 0,0,0,0).look
  sample :sn_zome, amp: 0.5 if bools(0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0).look
  sample :sn_generic, amp: 0.2 if bools(0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,1).look
  
  sleep 0.25
end

live_loop :noisezz do ; tick
  with_fx :ping_pong do
    sample :loop_drone_g_97,
      amp: 0.3,
      rate: 2,
      slice: look if bools(1,0,1,0, 1,1,1,0, 0,0,1,1, 0,0,1,0).look
  end
  
  sleep 0.5
end
3 Likes

Ah ha! I like that because it doesn’t need a bespoke function so you can just code that in the moment in line. Am I becoming a SPi coding purist too much???

But wait, I like @alexesc "x-x----x--" format because of the look. So here’s what I’ve come up with, best of both worlds :smiley:

sample :bd_mehackit if "x----x"[tick]=="x"

1 Like

And this for arpeggios ha ha! Well, ok only up to ten notes of the scale, but still I like it

Thanks again for these snippets and ideas, it’s a real help

play notes["01357530".ring[tick].to_i], amp: a
#Arpeggios

use_bpm 120

live_loop :main do
  sleep 4
  cue :bar
end

live_loop :arp do
  sync :bar  
  sample :bd_ada, amp: 0.1  
  a = 0.2
  use_synth :beep
  notes = scale :C4, :aeolian, num_octaves: 2
  in_thread do
    16.times do
      play notes["01357530".ring[tick].to_i], amp: a
      sleep 0.25
    end
  end
end #arp

That was just a little helper so I can access my local samples without writing the long sample path.

Here’s something you can run without adjustment:

# House-y beat
## Helper Functions
define :l2f do |samp, length|
  #converts desired length to fraction of sample length
  duration = sample_duration samp
  return 1 if length >= duration
  return length/duration
end

define :evenBar do
  return look(:bar) % 2 == 0
end

define :evenBeat do
  return look(:beat) % 2 == 0
end

define :oddBeat do
  return look(:beat) % 2 != 0
end

define :fourthBeat do
  return look(:beat) == 4
end

## Instrument descriptions

define :kick do
  length = 0.75
  sample :drum_heavy_kick, beat_stretch: length, amp: 0.6
end

define :offset_clap do
  length = 0.99
  if evenBeat
    time_warp -0.05 do
      sample :perc_snap, amp: 0.75, lpf: 100, \
        beat_stretch: length
    end
  end
end

define :tonal do
  return if !fourthBeat
  tonal = :bass_voxy_hit_c
  length = 0.5
  timeShift = 1-length
  finish = l2f(tonal, length)
  
  time_warp timeShift do
    sample tonal, lpf: 80,finish: finish, amp: 0.65
  end
end

define :toms do
  if oddBeat
    tom = :drum_tom_mid_soft
    length = 0.25
    swing = ring(0,0.05,0,0,0.05).tick
    amp = ring(0.65,0.5,0.5,0.5).look
  else
    tom = :drum_tom_hi_soft
    length = 0.5
    swing = 0
    amp = 0.6
  end
  finish = l2f(tom, length)
  info = {amp: amp, finish: finish}
  timeShift = 1-length+swing
  time_warp timeShift do
    sample tom, info
  end
end

define :tom2 do
  tom = :drum_tom_lo_soft
  if oddBeat
    length = 0.25
    swing = ring(0,0.05,0,0,0.05).tick
  else
    length = 0.5
    swing = 0
  end
  finish = l2f(tom, length)
  info = {amp: 0.4, finish: finish, hpf: 60, lpf: 100}
  timeShift = 1-length+swing
  time_warp timeShift do
    sample tom, info
  end
end

define :hat1 do
  hat = :drum_cymbal_closed
  length = 0.5
  timeShift = 1-length
  amp = ring(0.7,0.8,0.8,0.8).tick
  time_warp timeShift do
    sample hat, beat_stretch: length, amp: amp, \
      lpf: 110
  end
end

define :hat2 do
  return if fourthBeat
  hat = :drum_cymbal_pedal
  length = 0.25
  finish = l2f(hat, length)
  timeShift = ring(0.75,0.75,0.25).tick
  amp = ring(0.1,0.1,0.2).look
  time_warp timeShift do
    sample hat, finish: finish, amp: amp, \
      attack: 0.05
  end
end

define :hat3 do
  hat = :drum_cymbal_closed
  length = 0.5
  timeShift = 1-length
  #amp = ring(0.7,0.8,0.8,0.8).tick
  amp = 0.4
  time_warp timeShift do
    sample hat, beat_stretch: length, amp: amp
  end
end


## Conductor
set_sched_ahead_time! 1
tick_set :bar, 1
live_loop :conductor do
  use_bpm 120
  set_volume! 1
  tick_set :beat, 1
  4.times do
    tick(:beat)
    kick
    offset_clap
    tonal
    toms
    tom2
    hat1
    hat2
    hat3
    sleep 1
  end
  tick(:bar)
end

Definitely agreed there, I think just my approach to learning has made this make the most sense for me (for now). Hopefully I can get to a point where I can make pretty music with pretty code :smiley:

This is nice, I like it. I think doing things the way I currently am it would go something like:

define :bd do
  return if !(firstBeat || sixthBeat)
  sample :bd_mehackit
end

or maybe

sample :bd_mehackit if firstBeat || lastBeat