Idea for drums management

Hello, I’m new here and I want to share with you this implementation I made to handle drums inside a Sonic Pi track, in order to understand if this can be a good solution :slight_smile:

I liked the idea I found around the web (mostly here and here) to map the beats inside an array, to emulate somehow the common grids used by real drum machines and simplify the creation of beats.
I also wanted to have something that gives the possibility to manage in easy way the evolution of the beats during the track, so I used sync messages to register the measures number in order to create conditions inside the loop based on it.
Here an example of a beat that has a fill every forth measure:

use_bpm 120

# pattern: is an array of 1s and 0s. 1 = beat played, 0 = no beat
# drum_sample: the sample you want to play
define :play_drum_pattern do |pattern, drum_sample|
  # Every time this function is called a new thread is created.
  # The name of the thread is the tick number, in order to be always new
  # (I come up with this solution after some issues with timing)
  in_thread(name: tick.to_s) do
    pattern.each do |p|
      if p == 1 then
        sample drum_sample
      end
      sleep 0.25
    end
    # at the end, the thread is stopped to free the resources (don't know if necessary anyway)
    stop
  end
end

live_loop :drums do
  # loop synced with :measure
  # :measures carries the current measure of the loop, so we can use it to control the behaviour
  measure = (sync :measure)[0]
  puts measure
  if (measure % 4 != 0) then
    # pattern played for every measure but the last
    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]
    play_drum_pattern(kick, :drum_heavy_kick)
    play_drum_pattern(snare, :drum_snare_soft)
  else
    # pattern played for the last measure (the fill)
    kick     = [1,0,0,1, 0,0,0,1, 1,0,1,0, 0,0,0,0]
    snare    = [0,0,0,0, 1,0,0,0, 0,0,0,0, 0,0,0,0]
    open_hat = [0,0,0,0, 0,0,0,0, 0,0,0,0, 1,0,0,0]
    play_drum_pattern(kick, :drum_heavy_kick)
    play_drum_pattern(snare, :drum_snare_soft)
    play_drum_pattern(open_hat, :drum_cymbal_open)
  end
end

measure = 1;
live_loop :conductor do
  cue :beat
  # every 4 bars, a :measure message is sent (notifying the beginning of a new measure)
  if (tick % 4 == 0) then
    cue :measure, measure
    measure += 1
  end
  sleep 1
end

What do you think?

3 Likes

Yes this looks good. I’ve been working on similar ideas - layouts that look like a grid sequence. It’s working well for me.

There’s lots of ways of applying variation once you’ve got the grid system in place, fills, random variation, accents…all sorts.

I don’t think you need to name the thread, or do stop at the end, because it’s not a loop. I believe the thread just runs out. If I’m wrong about that I need to know as I use it all the time.

1 Like

Hi @soxsa,

I think you’re right about your observations: the thread doesn’t need a name (I thought that assigning one was mandatory) and probably also the stop at the end is useless (is legit to suppose that once the thread ends is wiped out automatically).
Apparently I overthought a bit :slight_smile:

Thank you for taking your time and gave me a feedback!

You’re welcome, this is what it’s all about. As well as the hard tech tips, one great thing I’ve picked up from people here is how they layout their code and how it makes it feel more like performance instrument. I’ll share a couple of idioms around the drums I’ve taken on board.

The bools() function like this below. My function p(i) plays each sample complete with modulations and effects to taste. Using the one_in() function optionally adds some variation, and the with_random_seed block makes it repeatable.

in_thread do
  with_random_seed 2 do
        20.times do
          tick
          p(0) if (bools 1,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0).look or one_in(24)
          p(1) if (bools 0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0, 1,0,0,0).look or one_in(16)
          p(2) if (bools 1,0,1,0, 1,0,1,0, 1,0,1,0, 1,0,1,0, 1,0,1,0).look ^  one_in(4)
          p(3) if (bools 1,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0).look or one_in(3)
          p(4) if (bools 0,0,0,0, 0,0,0,0, 0,0,0,0, 1,0,0,0, 1,0,0,0).look or one_in(5)
          sleep 1.0/4
        end
  end
end

And this is one of my favourties, same kind of thing but using pattern strings which are even more grid-sequencer like…

  with_fx :echo, mix: 0.0, phase: 0.75, decay: 6 do
    with_random_seed (ring 1,1,1,4).tick(:a) do
      in_thread do
        27.times do
          tick
          p(9) if ("1--2--3--4--5--6--7--8--9--")
          p(0) if ("x--------x--------x--------"[look]=="x") #or one_in(27)
          p(1) if ("---x--x-----x-----x--x--x--"[look]=="x") ^ one_in(36)
          p(2) if ("--x--x--x--x--x--x--x--x--x"[look]=="x") ^ one_in(12)
          p(3) if ("-----------------------xxxx"[look]=="x") ^ one_in(12)
          p(4) if ("-xx-xx-xx-xx-xx-xx-xx-xx-xx"[look]=="x") ^ one_in(24)
          p(5) if ("---x-x---x-x---x-x---x-x---"[look]=="x") ^ one_in(24)
          p(6) if ("---x--x-----x--x-----x--x--"[look]=="x") or one_in(27)
          p(7) if ("---x--------x--------x-----"[look]=="x") or one_in(27)
          sleep 1.0/3
        end
      end
    end
  end

And how about this one, which uses pattern ‘masks’ to apply fills with variations, all of which can be edited live.

#Drumkit with mask variations

use_bpm 120

live_loop :drumtest do
  cue :bar
  sleep 4
end

define :r0 do
  "1---2x--3---4-x-".ring.tick(:r0)=="x" and one_in(2)
end

define :r1 do
  "1---2---3---4xxx".ring.tick(:r1)=="x" and one_in(2)
end

live_loop :drums do
  sync :bar
  #stop
  a = 1.0
  n = tick(:bar)
  s = [:drum_bass_hard,
       :drum_snare_hard,
       :drum_cymbal_closed,
       :ambi_swoosh]
  
  
  define :p do |i|
    case i
    when 0
      sample s[i], beat_stretch: 1.5, amp: a*0.3
    when 1
      sample s[i], amp: a*0.2
    when 2
      sample s[i], beat_stretch: 1, amp: a*0.1*[2,1,1,1].ring.tick(:hat)
    when 3
      sample s[i], beat_stretch: 0.5, amp: a*0.1
    end
  end
  
  
  with_fx :echo, mix: 0.1, phase: 0.75, decay: 6 do
    with_fx :lpf, cutoff: 130 do
      in_thread do
        16.times do
          tick
          p(0) if ("x-----x-x-------"[look]=="x") ^ r0 #^ one_in(24)
          p(1) if ("----x-------x---"[look]=="x") ^ r1 #^ one_in(24)
          p(2) if ("xxxxxxxxxxxxxxxx"[look]=="x") #^ one_in(0)
          p(3) if ("-x--x--x--x-----"[look]=="x") ^ one_in(16)
          sleep 1.0/4
        end
      end
    end
  end
end

I didn’t play so much with randomness yet because for now I want all under control :slight_smile:
Anyway this is interesting! If I understood well, in these examples the patterns you define act as a guide, where randomically some beats are included to apply some variations. Was this the purpose?
Another thing: how do you chose the probabilities you defined for the various one_in() functions?

Yes that is the purpose

With these techniques, you can have anything in the spectrum between the code making all the decisions to having everything prescribed! It can all lead to lovely music.

The one_in() function is built-in, and you can choose to comment it out or set a value to give ‘no variation’ all the way to chaos… With my second ‘mask’ examples, I can prescribe a fill or variation exactly, or choose to vary it a bit. one_in(0) is always false, one_in(1) is always true for instance.

A key thing with Sonic Pi is that the randomness is repeatable - you get the same results every run. Which is a very good thing for various reasons. But you can influence that with use_random_seed and with_random_seed. A technique is to create random patterns, but you can make them repeat. The musicians job is then less to create the patterns, but to choose the ones they like.

3 Likes

Cool, this use of randomness is definitely a thing worth exploring.
Thank you very much for the advices!

3 Likes

Loving this thread. I’m also using the array method, it gives me the amount of control I want, but for less ‘important’ parts, like chh I started using a rrand_I to change the length.
I’m still looking for a way to add better variations, I like one_in but I’d like a more Elektron sequencer way, more like a 2:4 do ( if second repeat out of four do) I can do that with a variable but it’s verbose…

I’m thinking about more subtle methods than the ```one_in()`` too - although that is good. I think that using the basic structure, there’s lots of scope. For instance, did you see this idea (link below)? This one is ‘fully determinisitc’ i.e. I can toggle beats on/off live and still use the same drumkit code.