Can someone build an efficient drum machine?


#1

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.


#2

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

#3

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.


#4

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:


#5

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


#6

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


#7

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

#8

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 !


#9

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


#10

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/


#11

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

#12

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.


#13

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.


#14

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 !


#15

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