There was a new idea from @amiika in this post: Req: a function that indexes all Markov matrices.
The idea is to build matrices not by explicitely defining probabilities but by writing down the sequences you would like to hear. I have used this idea to demonstrate how it could be used as an alternative to the above code:
use_debug false
use_bpm 110
use_random_seed 9
# base drum
live_loop :bd do
4.times do
sample :bd_sone
sleep 1
end
end
# helper functions for matrix creation
define :to_mm do |idx, mm=8|
# Length of the markov matrix
length = mm.is_a?(Integer) ? mm : mm.length
# Init with random matrix if mm=integer
mm = Array.new(length) { Array.new(length, 0.0) } if mm.is_a?(Integer)
# Treat integer as a markov chain: 121 = 1->2, 2->1
degrees = idx.split("-")
degrees.push(degrees[0]) if degrees.length==1
degrees.each_with_index do |d,i|
if degrees[i+1]
# Overflow depending on mm length: 8->0, 9->1, 0->2
row = (d.to_i==0 ? length : d.to_i-1)%length
column = (degrees[(i+1)].to_i-1)%length
mm[row][column] += 1.0
end
end
mm
end
define :print_matrix do |mm|
mm.each do |row|
r = ""
row.each do |c|
r += "#{c.round(1)} "
end
puts r
end
end
define :normalize do |mm|
mm.length.times do |row|
pp = 0.0
mm[row].each do |p|
pp += p
end
mm[row].length.times do |i|
if pp == 0.0 then
puts "warning: no transition defined for row #{row+1}!" if i == 0
mm[row][i] = 1.0/mm[row].length
else
mm[row][i] /= pp
end
end
end
mm
end
# function to get next index according to markov matrix mm
define :next_idx do |current_idx, mm|
n = 0
r = rand
pp = 0
mm[current_idx].each do |p|
pp += p
break if pp > r
n += 1
end
return n
end
# drum patterns for snare
patterns = [
#1 2 3 4
"--------------xx", # 1
"--x---x---x---x-", # 2
"--x---x---x---xx", # 3
"--x---x---xx--xx", # 4
"--x---xx--xx--xx", # 5
"--xx--xx--xx--xx", # 6
"--x---x---x----x", # 7
"--x---x----x---x", # 8
"--x----x---x---x", # 9
"---x---x---x---x", # 10
"---x---x---x--xx", # 11
"---x---x--xx--x-", # 12
"---x--xx--x---x-", # 13
"--xx--x---x---x-", # 14
"-xxx-xxx-xxx-xxx", # 15
"xxxxxxxxxxxxxxxx", # 16
"--x---x---xx--x-", # 17
"--x---xx--x---x-", # 18
"--xx--x---x---x-", # 19
"--x---x----x--x-", # 20
"--x----x--x---x-", # 21
"---x--x---x---x-", # 22
]
mm = to_mm "1-2-2-2-3-4-5-6-2", patterns.length # initialization with number of patterns = 22
mm = to_mm "2-7-8-9-10-11-12-13-14-2", mm
mm = to_mm "7-2", mm
mm = to_mm "2-11-12-13-14-2", mm
mm = to_mm "14-15-16-2", mm
mm = to_mm "16-15-16-2", mm
mm = to_mm "2-3-17-18-19-2", mm
mm = to_mm "7-20-21-22-2", mm
mm = to_mm "10-15", mm
mm = to_mm "16-1", mm
mm = to_mm "14-6", mm
mm = to_mm "10-6", mm
mm = to_mm "6-15", mm
normalize mm
#print_matrix mm
n = 0
live_loop :snare do
with_fx :gverb, room: 20, dry: 2, mix: 0.1 do
puts patterns[n]
tick_reset(:p)
16.times do
#sample :elec_hi_snare, rate: 1.8, amp: 0.8 if (patterns[n][tick(:p)]=="x")
#sample :drum_snare_soft if (patterns[n][tick(:p)]=="x")
sample :sn_dolf, sustain: 0.05, release: 0.03, hpf: 70 if (patterns[n][tick(:p)]=="x")
sleep 4.0/16
end
n = next_idx(n, mm)
end
end
The actual composition is concentrated in these lines (other code is just needed for the execution) :
patterns = [
#1 2 3 4
"--------------xx", # 1
"--x---x---x---x-", # 2
"--x---x---x---xx", # 3
"--x---x---xx--xx", # 4
"--x---xx--xx--xx", # 5
"--xx--xx--xx--xx", # 6
"--x---x---x----x", # 7
"--x---x----x---x", # 8
"--x----x---x---x", # 9
"---x---x---x---x", # 10
"---x---x---x--xx", # 11
"---x---x--xx--x-", # 12
"---x--xx--x---x-", # 13
"--xx--x---x---x-", # 14
"-xxx-xxx-xxx-xxx", # 15
"xxxxxxxxxxxxxxxx", # 16
"--x---x---xx--x-", # 17
"--x---xx--x---x-", # 18
"--xx--x---x---x-", # 19
"--x---x----x--x-", # 20
"--x----x--x---x-", # 21
"---x--x---x---x-", # 22
]
mm = to_mm "1-2-2-2-3-4-5-6-2", patterns.length # initialization with number of patterns = 22
mm = to_mm "2-7-8-9-10-11-12-13-14-2", mm
mm = to_mm "7-2", mm
mm = to_mm "2-11-12-13-14-2", mm
mm = to_mm "14-15-16-2", mm
mm = to_mm "16-15-16-2", mm
mm = to_mm "2-3-17-18-19-2", mm
mm = to_mm "7-20-21-22-2", mm
mm = to_mm "10-15", mm
mm = to_mm "16-1", mm
mm = to_mm "14-6", mm
mm = to_mm "10-6", mm
mm = to_mm "6-15", mm
First, you define the patterns that are possible and then you write down the sequences that should be used. The more often a certain transition is used (such as 2-2
) the more likely it is to happen.