Can someone build an efficient drum machine?

Hi there :

I’m finished chapter 5,and tried use variables,functions and

threads to build a drum machine.

All that i got was this :

Drum Machine Test

Note Durations

w = 4
h = 2
q = 1
e = 0.5
s = 0.25

use_bpm 60

Drum Samples

bd = :drum_heavy_kick
sn = :drum_snare_hard
hh = :drum_cymbal_closed

Drum Kit

in_thread(kik: :kik) do
  loop do
    kik
  end
end

in_thread(snr: :snr) do
  loop do
    snr
  end
end

in_thread(hht: :hht) do
  loop do
    hht
  end
end

Drum Pattern

define :kik do
  cue :kik
  sample bd,amp: 1,pan: -0.1
  sleep q
end

define :snr do
  sync :kik
  sample sn,amp: 0.5,pan: 0.1
  sleep q
end

define :hht do
  sample hh,amp: 0.5,pan: 0.1
  sleep e
end

Can anybody build a drum machine better than this ?

All ideas are wellcome.

Thank you.

Keep reading. The live loops section will do roughly what you’e doing here and then present a solution to simplify it. Here’s an example of how I start with a four on the floor:

live_loop :drums do
  sample :bd_ada
  sleep 1
  sample :sn_dolf
  sleep 1
end

live_loop :hats do
  sample :drum_cymbal_closed, sustain: 0, release: 0.03
  sleep 0.5
end

Hi kniknoo :

I was thinking if there’s a way to create 2 arrays:

one for time unit = [q,e,q,e]

another to say when to play = [1,0,1,0]

Perhaps: play_pattern_timed [1,0,1,0], [q,e,q,e]

Something like that would be great.

Thank you for the hint,anyway.

Hi,

there are several ways to arrange notes or samples into a sequence or a pattern the way you want. You should definitely look at chapter 9.4 Ticking of the inbuild tutorial (although in the beginning it is quite a good idea to go through the whole tutorial; it is excellent.).

Here you’ll find some other resources and examples I gathered which might be of help:

1 Like

Another technique is to hold the drum rhythm (plus volume if you like) in a ring as below

r1=(ring 4,2,0,2,0,4,0,2,0,0,4,2)
r2=(ring 0,4,2,0,2,2,4,2,2,4,2,4)

sleep 1

live_loop :drum do
  v=0.5 #set volume scale (0-1)
  sample :drum_tom_hi_hard, amp: v*r1.tick/4.0,pan: -0.8 #floating point: divide by 4.0
  sample :drum_tom_hi_hard, amp: v*r2.look/4.0,vpan: 0.8
  sleep 0.2
end

I used this to good effect in the piece below

Lots of efficient ideas,
there’s much to learn yet.
Thank you for increase my curiosity.
Now… i go read further.

1 Like

here’s another variation on essentially the same idea: to separate out the patterning and instrument definitions (data) from the execution, and give a more declarative way of working. the idea here is to make it easier to translate a beat from ableton for example, and make it as easy to type out (and modify) as possible

use_bpm 120

define :beat1 do |pat|
  (ring
   *pat
   .split(" ")
   .map{|bar| bar.split("")}
   .flatten
   .map{|d| d=="1" ? 1 : 0})
end

inst1 = Hash[
  :k0  => :bd_haus,
  :s0  => :sn_dolf,
  :h0 => :elec_blip,
  :h1 =>:perc_snap2
]

pat1 = Hash[
  :k0 => "1--- ---- 1--- ---- 1--- ---- 1--- ----",
  :s0 => "---- 1--- ---- 1--- ---- 1--- ---- 1---",
  :h0 => "1--- 1-11 11-- 1--- 1--- 1-11 11-- 1---",
  :h1 => "--1- ---- --1- --1- --1- ---- --1- --1-"
]

live_loop :main do
  tick
  sample inst1[:k0] if beat1(pat1[:k0]).look == 1
  sample inst1[:s0] if beat1(pat1[:s0]).look == 1
  sample inst1[:h0] if beat1(pat1[:h0]).look == 1
  sample inst1[:h1] if beat1(pat1[:h1]).look == 1
  sleep 0.25
end
4 Likes

Congratulations,this is exactly what

i was looking for !

There’s some parts on your code that i don’t understand yet.

They’re all contained in user’s manual ?

Am reading chapter 8,and your code seems a distant dream

for me right now.Hope i can learn it.

Thank you very much !

thanks! glad it helped

i actually don’t know ruby too well, i’m kind of translating from other things i know… so i’m not too sure how close it might be to what you might see in the other ruby material. but here’s what’s happening:

you want to turn "1--- 1---" into (ring 1,0,0,0, 1,0,0,0)

"1--- 1---"                           #start with this. split(" ") turns it into
["1---","1---"]                       #then you want to split each bar, so bar.split("") turns them into
[["1","-","-","-"],["1","-","-","-"]] #now you want to put them all in one list instead of two, so that's what flatten does
["1","-","-","-","1","-","-","-"]     #now you want to turn the "1"s into 1 and the hyphens into 0's
[1,0,0,0,1,0,0,0]                     #now you have this, but if you used it like this you would have (ring [1,0,0,0,1,0,0,0]) and that wouldn't work
(ring *[1,0,0,0, 1,0,0,0])            #if you put the * in front of the [], it takes them out of the [], so then you have
(ring 1,0,0,0, 1,0,0,0)               #ta da! all done

maybe you could try this too. paste this in, and run it

puts ["Ernie", "Bert", "Elmo"].map{|d| "Hi #{d}!"}

in the log, you should see

["Hi Ernie!", "Hi Bert!", "Hi Elmo!"]

so map is pretty much just like a loop

now watch this! right-click here, and click on Inspect. now in your browser console, paste this in and press enter

["Ernie", "Bert", "Elmo"].map(d => `Hi ${d}!`)

and you should see

["Hi Ernie!", "Hi Bert!", "Hi Elmo!"]

same! hehe

2 Likes

Now … some upgrade from my previous post:

Drum Machine I

Note Durations

w = 4
h = 2
q = 1
e = 0.5
s = 0.25

use_bpm 100

With Fx

with_fx :reverb, mix: 0.5, room: 0.3 do

Drum Pattern

kik patt = kpt

live_loop :kik do
kpt = (ring q, 0, 0, 0, q, e, 0, 0,
q, 0, 0, 0, q, e, 0, q)
if kpt.tick > 0
sample:drum_heavy_kick,amp: kpt.look
use_sample_defaults pan: -0.1,pre_amp: 0.75
end
sleep e
end
end

snr patt = spt

live_loop :snr do
spt = (ring 0, 0, q, 0, 0, 0, q, 0,
0, 0, q, 0, 0, 0, q, 0)
if spt.tick > 0
sample :drum_snare_hard, amp: spt.look
use_sample_defaults pan: 0.1,pre_amp: 0.75
end
sleep e
end

hht patt = hpt

live_loop :hht do
hpt = (ring q, q, q, q, q, q, q, q,
q, q, q, q, q, q, q, q)
if hpt.tick > 0
sample :drum_cymbal_closed, amp: hpt.look
use_sample_defaults pan: 0.05,pre_amp: 0.5
end
sleep e
end

P.S : You can have subdivisions altering "sleep"values.

It’s not much sophisticated,but works.

*To those that had helped me on this topic,a souvenir :

VCV Rack - A modular synth extremely powerful,and… free !

https://vcvrack.com/

Hi @GWB70,

well done!

Just a hint: If you are going by this approach, you can make it even simpler and skip the if-condition because if you have a 0 you won’t hear anything:

q = 1
e = 0.5
kpt = (ring q, 0, 0, 0,    q, e, 0, 0,    q, 0, 0, 0,    q, e, 0, q)

live_loop :kick do 
  sample :drum_heavy_kick, amp: kpt.tick, pan: -0.1
  sleep e
end
1 Like

Thank you Martin.

Could you give me a hint on other issue too ?

Let’s say,that i have 2 samples to choose:

Bass Drum 1, and Bass Drum 2.

I’d like that BD1 play only at q notes,

and BD2,only at e notes,how i can solve that ?

Once again, i thank you for your expertise and help.

Hi @GWB70,

well looking at your code above I guess you have enough knowledge to figure it out on you own … but here is one way:

I think you will have to reintroduce the if again:

live_loop :kick do
  if kpt.tick == q
    sample :drum_heavy_kick, amp: kpt.look, pan: -0.1
  else
    sample :bd_ada, amp: kpt.look, pan: -0.1
  end
  sleep e
end

You might come up with another way to achieve what you want. I myself try to keep things as simple as possible (while as complex as necessary) because I tend to forget complex solutions. And as my interest is to code as much live as possible, I need to rely on easy and logical-to-remember-stuff.

Hi Martin :

Think it’s all to have a decent drum.

I feel a lack of examples on certain topics at user manual :

  • How to manipulate lists, arrays and rings

  • Practical examples on conditionals

  • I don’t know exactly what (==) stands for,
    what the meaning of firt (=),but i’ll write as you show me.

When i finish my reading, will be able to refer at

exact points where i think it need to be more clear;

always priorizing a musician perspective,and not so

from a programmer point of view.

Thank you very much again !

Hey @ds604, just stumbled across this, and really like it as a very short & simple yet still visually intuitive drum machine.

Here’s an even shorter version, inspired by yours:

use_bpm 120

def beats(pat)
  pat.delete(' ').split('').map(&:to_i).ring
end

patt = {
  :bd_klub         => "9--5 --5- ---- -3-- 9--- ---- 9--- ----",
  :drum_snare_soft => "---- ---3 9--- ---- ---- ---- --33 9---",
  :perc_snap2      => "9--- --11 11-- ---- 5--- 5-11 11-- 9---",
  :elec_flip       => "--5- ---- --5- --5- --5- ---- --5- --5-",
  # :bd_boom => "9-3--"
}

live_loop :main do
  tick  #(step: [0,1,1,1,1,1,2].choose)  # change step of tick randomly for nice effect
  patt.each{ |key, val|  sample key, amp: beats(val).look / 10.0  }
  sleep 0.25
end

Turning the beat numbers directly into amplitudes makes it easy to control accents for each beat. I also used a loop to play each sample, shortened the beat-parsing function, and folded the separate instruments hash into keys of the pattern hash.

Thanks for a drum machine short and clear enough to teach in a class session!

Update: okay, I think this is the shortest and clearest version I can come up with. Gets rid of the pattern hash entirely; everything is laid out in the loop body (this makes it easier to control the volume of each instrument individually, for example by adding “* 0.5” at the end of one of the “amp:” values:

def pat(p)
  p.delete(' ').split('').map{ |v|  v.to_f / 10  }.ring
end

live_loop :machine do
  use_bpm 60
  tick
  sample :bd_klub,         amp: pat(" 9--5 --5- ---- -3-- ").look
  sample :drum_snare_soft, amp: pat(" ---- ---3 9--- ---5 ").look
  sample :perc_snap2,      amp: pat(" 9--- --11 11-- ---- ").look
  sample :elec_flip,       amp: pat(" --5- ---- --5- --5- ").look
  sleep 0.25
end
7 Likes

Sorry, I’m totally new to this fantastic new world so maybe my question is trivial but : what is this book that you (@GWB70) are talking about when you say that you just finished chapter 5? I just finished the tutorial but I’m looking for a book to get deeper in the concepts. Thanks for all again
Patrick

Hi everyone, I’m playing with a slightly modified version of this drum-machine.

def pat(p) ; p.delete(' ').split('').map{ |v|  v.to_f / 10  }.ring ; end

live_loop :a do ; use_bpm 140 ; tick(:count) ; tick ; use_real_time
  with_fx :level, amp: 2 do
    
    
    
    tick(:one)
    seq = ["5--- 5--- 5--- 5---", # 0
           "5--- 5--- 5--- 5---", # 1
           "5--- 5--- 5--- 5---", # 2
           "5--- 5--- 5--- 5---"] # 3
    sample da, 4, finish: 0.5, amp: pat(seq[a]).look(:one)
    
    tick(:two)
    seq = ["9--- --0- 9--- 0000", # 0
           "9--- 9--- 9--- 9---", # 1
           "9--- 00-- 9--- ----", # 2
           "9--- -0-- 9--- -0--"] # 3
    sample feedback,[1, 5, 10].choose, finish: 0.01, amp: pat(seq[a]).look(:two)
    
    tick(:three)
    seq2 = ["8888 ---- 8888 ----", # 0
            "9--- 5--- 9--- 9---", # 1
            "9--- 9--- 5--- 9---", # 2
            "9--- 9--- 9--- 3---"] # 3
    sample feedback, 20, finish: 0.01, amp: pat(seq2[a]).look(:three)
    
    
    
    puts("Playing step: " + counter.to_s + " on seq: " + a.to_s)
    sleep 0.25 ; counter += 1
    if (counter > 15) ; a = a + 1 ; counter = 0 ; end
    if (a == 4) ; a = 0 ; end
end ; end

As you can see, I was playing with the idea of implementing multiple 16*4 step sequencers to build a piece based on rhythm. I was thinking of implementing something that would allow me to tick the pat-patterns not at every iteration, but every 2, 3, 4, 5, etc iterations of the loop. I’ve tried different methods but nothing is really working so far.

Does someone have an idea about how to implement this?

I love this pat function. You can really do interesting sequences using it, even changing amp to vel for midi sequencing, and so on.

Was reading over topics about drum patterns just now and saw this. @bubo (and anyone else that finds this useful) if you’re still interested how to approach ticking through the above patterns only every X times through the loop, here’s how you can: (I simplified the above example slightly and made it a tiny bit more ‘Sonic Pi-like’).

define :pat do |p|
  p.delete(' ').split('').map{ |v|  v.to_f / 10  }
end

live_loop :a do ; use_bpm 140
  with_fx :level, amp: 2 do
    tick(:one)
    if (look(:one) % 64 == 0); tick(:a); end
    puts("Playing step: #{look(:one) % 16} on seq: #{look(:a) % 4}" )
    
    seq1 = ["5--- 5--- 5--- 5---", # 0
            "5--- 5--- 5--- 5---", # 1
            "5--- 5--- 5--- 5---", # 2
            "5--- 5--- 5--- 5---"] # 3
    sample :elec_beep, 4, finish: 0.5, amp: pat(seq1.look(:a)).look(:one)
    
    tick(:two)
    seq2 = ["9--- --0- 9--- 0000", # 0
            "9--- 9--- 9--- 9---", # 1
            "9--- 00-- 9--- ----", # 2
            "9--- -0-- 9--- -0--"] # 3
    sample :elec_chime, finish: 0.01, amp: pat(seq2.look(:a)).look(:two)
    
    tick(:three)
    seq3 = ["8888 ---- 8888 ----", # 0
            "9--- 5--- 9--- 9---", # 1
            "9--- 99-- 5--- 9---", # 2
            "9--- 9--- 9--- 3---"] # 3
    sample :elec_bong, amp: pat(seq3.look(:a)).look(:three)
    
    
    sleep 0.25
    
end ; end

The main technique to do what you want was to use the % (mod operator) to only do something if the current beat was an even multiple of something. (Such as only advancing the current pattern if the script has played 64 beats, (ie 4 lots of the last pattern of 16).
The above of course advances the patterns of all 3 sample sequences together. It’s left as an exercise to the reader to allow each sequence to advance independently :slight_smile:

2 Likes

I would also like to offer up a solution for this topic. I made a youtube tutorial about making drum patterns using arrays to emulate using a basic grid GUI that is found with a lot of online drum sequencers. It is a bit more beginner friendly, but might not have some of the funtionality of some other solutions posted here.

3 Likes