Self similar patterns


#1

Stumbled to this great Sonic Pi youtube video describing algorithm that produces self similar patterns that are musically more interesting than completely random patterns.

Tinkered with the code a bit and created this oneliner that plays the same thing. You can change the notes by changing the random seed. Melody also changes if you pick less or more numbers from the list of random numbers. Number of picked random numbers also affects the length. 10 random numbers = 2^10 = 1024 notes

use_synth :saw
use_bpm 300
use_random_seed 234
play_pattern (-12..12).to_a.pick(10).inject([60]) {|acc,j| acc.flat_map{|n|[n,n+j]}}

You could also inject array containing multiple numbers to make the pattern more predictable in some way. I also used this idea to create musical patterns based on key and scale like in ziffers project. I’v tried to include rhythmic pattern to make the melody more interesting, but i think there are better ways to combine melodic patterns and the rhythm.

use_synth :tri
use_bpm 150

use_random_seed 3

def selfSimilar(start, range, pow)
  range.to_a.pick(pow).inject(start) {|acc,j| acc.flat_map{|n|[n,n+j]}}
end

def getNoteFromDgr(dgr, zkey, zscale,scaleDegrees)
  dgr=-1 if dgr==0
  return degree(scaleDegrees[dgr],zkey,zscale)+dgr/scaleDegrees.length*12 if dgr>scaleDegrees.length || dgr<0
  return degree(dgr,zkey,zscale)
end

zkey = :g3
zscale = :minor
scaleDegrees = Array.new(scale(zkey,zscale).length){ |i| (i+1) }.ring
melody = selfSimilar([1],(-3..4),10)
rhythm = (spread 7,14)

melody.each do |n|
  play getNoteFromDgr(n, zkey, zscale, scaleDegrees)
  sample :bd_haus if rhythm.look
  sleep rhythm.tick ? 0.25 : 0.5
end

#2

This is cool! I’ve enjoyed playing with it this morning.
I’ve reduced the pow variable and put the melody play section into a live loop.
This allows you to generate varying tunes whilst playing continuously altering such things as the random seed, the power, the scale etc. You can then generate some tunes with greater variety as you go along.
I also played with the rhythm. I liked a 1:3 split using 0.25 : 0.75
There are so many things you can tweak as it plays that it give a nice live_coding experience with little effort.

Here’s one setup, but you can vary most things.

#explore algorithmic tune generation
#using Amiika's algorithms
#modified to play melody in live loop, allowing you to tweak many settings and rerun
#in a live performance.

set :synth, :pluck # try changing synth

set :bpm,200 # try changing bpm


use_random_seed 234 # try changing seed

def selfSimilar(start, range, pow)
  range.to_a.pick(pow).inject(start) {|acc,j| acc.flat_map{|n|[n,n+j]}}
end

def getNoteFromDgr(dgr, zkey, zscale,scaleDegrees)
  dgr=-1 if dgr==0
  return degree(scaleDegrees[dgr],zkey,zscale)+dgr/scaleDegrees.length*12 if dgr>scaleDegrees.length || dgr<0
  return degree(dgr,zkey,zscale)
end

zkey = :c3 # chage key as it runs
zscale = :minor_pentatonic
scaleDegrees = Array.new(scale(zkey,zscale).length){ |i| (i+1) }.ring
puts scaleDegrees
melody = selfSimilar([1],(-3..4),5) #vary last parameter (pow) for shoeter or logner tunes
puts melody
set :rhythm,(spread 1,4) #can vary for some funky effects

live_loop :playIt do
  melody.each do |n|
    use_synth get(:synth)
    use_bpm get(:bpm)
    play getNoteFromDgr(n, zkey, zscale, scaleDegrees)
    sample :bd_haus if get(:rhythm).look
    sleep get(:rhythm).tick ? 0.25 : 0.75 #can vary these
  end
end


#3

Yeah, it creates fun melodies with little effort. This time i added melody generation inside the loop and generated the melody from existing array with little variation. Also added random bar function that creates more rhythmic variety, but perhaps its getting too random. There must be some other ways to build rhytmic melody patterns. Maybe it would be better to build the rhytmic pattern first and then generate the melody.

set :synth, :pluck
set :bpm, 120

use_random_seed 13

def selfSimilar(start, range, pow)
  range.to_a.pick(pow).inject(start) {|acc,j| acc.flat_map{|n|[n,n+j]}}
end

def getNoteFromDgr(dgr, zkey, zscale,scaleDegrees)
  dgr=-1 if dgr==0
  return degree(scaleDegrees[dgr],zkey,zscale)+dgr/scaleDegrees.length*12 if dgr>scaleDegrees.length || dgr<0
  return degree(dgr,zkey,zscale)
end

def randomBar(max,l)
  arr = []
  sum = 0
  while(sum<max) do
      l = l.delete_if {|x| x+sum > max}
      rLength = l.choose
      arr.push(rLength)
      sum +=rLength
    end
    arr.ring
  end
  
  zkey = :e
  zscale = :major
  scaleDegrees = Array.new(scale(zkey,zscale).length){ |i| (i+1) }.ring
  set :bass,(spread 6,9)
  set :hihat,(spread 2,4)
  set :snare, (spread 1,4)
  
  live_loop :playMelody do
    sync :drums
    melody = selfSimilar([1,3,2,-1],(-3..3),3)
    melody.each_with_index do |n,i|
      bar = randomBar(1,[0.25,0.5]) if bar==nil || ((i + 1) % bar.length == 0 && one_in(2))
      use_synth get(:synth)
      use_bpm get(:bpm)
      play getNoteFromDgr(n, zkey, zscale, scaleDegrees)
      sleep bar.tick
    end
  end
  
  live_loop :drums do
    sample :bd_haus, amp: 0.5 if get(:bass).look
    sample :drum_cymbal_closed, amp: 0.5 if get(:hihat).look
    sample :drum_snare_soft, amp: 0.5 if get(:snare).look
    tick
    sleep 0.25
  end