Imaginary Gamelan

Using the concept of state machines (thanks to @nabisco) on polyphonic lines, this resembles some stuff I heard Gamelan ensembles play.

Due to the changing random seed, this will sound different each time you execute the code.

#MarkovChains Freequencies

t = Time.now.to_i
use_random_seed t
puts rand

live_loop :temp do
  set :temp, 52
  sleep 10
end

#Setting the range of possible pitches, in this case one octave
set :min_pitch, 66
set :max_pitch, 78


#Setting the pitches to be used; not quantized to any kind of scale so microtonality or strange constellations may come up
set :eins, rrand(get[:min_pitch], get[:max_pitch])
set :zwei, rrand(get[:min_pitch], get[:max_pitch])
set :drei, rrand(get[:min_pitch], get[:max_pitch])
set :vier, rrand(get[:min_pitch], get[:max_pitch])
set :fünf, rrand(get[:min_pitch], get[:max_pitch])


#Setting the rhythmic values to be used
RHYTHMIC_VALUES = [0.125, 0.25, 0.375, 0.5, 0.75, 1, 1.5].pick(6)


#initial state
set :current_state, 0


#Setting the actual notes to be used; in this case, the fifth note is omitted while the first and third each occur twice
NOTES = [get[:eins], get[:zwei], get[:drei], get[:vier], get[:drei], get[:eins]]

#Multiplying the rhythmic values to achieve different rhythmic levels - not sure if this actually works?!
live_loop :rhythm do
  INTERONSET = (RHYTHMIC_VALUES * (ring 1, 2, 3, 4, 6, 8).choose)
  sleep rrand_i(4, 64)
end

# State machine rules
RULES = {
  0 => [1],
  1 => [2],
  2 => [3, 3, 0],
  3 => [1, 2, 4, 4],
  4 => [5, 0],
  5 => [1]
}

#Setting a value for transposition of the sequence of notes resulting from the state machine rules
#Due to the four zeros, no transposition is most likely
live_loop :trnsp do
  set :transpose, [-12, 0, 0, 0, 0, 12].choose
  sleep rrand_i(4, 64)
end

with_fx :reverb, mix: 0.2 do
  live_loop :update_state do
    # get current state value -- 0 on the first run
    use_bpm get[:temp]
    s = get[:current_state]
    play NOTES[s] + get[:transpose], amp: rrand(0.2, 0.8), release: rrand(0.1, 0.5), pan: rrand(-1, 1)
    sleep INTERONSET[s]
    
    # update to next state
    set :current_state, RULES[s].choose
    
  end
  
  #Setting a different value for transposition of the second voice
  live_loop :trnsp1 do
    set :transpose1, [-12, 0, 0, 0, 12].choose
    sleep rrand_i(4, 64)
  end
  
  #initial state for the second voice
  set :current_state1, 0
  
  #As above
  live_loop :rhythm1 do
    INTERONSET1 = (RHYTHMIC_VALUES * (ring 1, 2, 3, 4, 6, 8).choose)
    sleep rrand_i(4, 64)
  end
  
  
  live_loop :update_state1 do
    # get current state value for second voice -- 0 on the first run
    use_bpm get[:temp]
    s = get[:current_state1]
    play NOTES[s] + get[:transpose1], amp: rrand(0.2, 0.8), release: rrand(0.1, 0.5), pan: rrand(-1, 1)
    sleep INTERONSET1[s]
    
    # update to next state for second voice
    set :current_state1, RULES[s].choose
    
  end
  
  #and the same for a third voice...
  live_loop :trnsp2 do
    set :transpose2, [-12, 0, 0, 0, 12].choose
    sleep rrand_i(4, 64)
  end
  
  set :current_state2, 0
  
  live_loop :rhythm2 do
    INTERONSET2 = (RHYTHMIC_VALUES * (ring 1, 2, 3, 4, 6, 8).choose)
    sleep rrand_i(4, 64)
  end
  
  
  live_loop :update_state2 do
    use_bpm get[:temp]
    s = get[:current_state2]
    play NOTES[s]  + get[:transpose2], amp: rrand(0.2, 0.8), release: rrand(0.1, 0.5), pan: rrand(-1, 1)
    sleep INTERONSET2[s]
    
    set :current_state2, RULES[s].choose
    
  end
  
  
end
2 Likes

I don’t think this works either

#Multiplying the rhythmic values to achieve different rhythmic levels - not sure if this actually works?!
live_loop :rhythm do
  INTERONSET = (RHYTHMIC_VALUES * (ring 1, 2, 3, 4, 6, 8).choose)
  sleep rrand_i(4, 64)
end

Not entirely sure what you were trying to achieve.

Instead I used

  INTERONSET = RHYTHMIC_VALUES.shuffle
  sleep rrand_i(4, 64)

for each of the three instances where the code appears. with the appropriate index INTERONSET1 2 or 3
This merely resorts the rhythm values.

1 Like

Thanks @robin.newman!

What I´m trying to achieve is this: all three instances should share the same repertoire of rhythm values, but not necesarilly in the same tempo. By multiplying the rhythm values by e.g. 2 , I want them to play out in half time.
So one instance might play sixtheenth and eight notes, while another might play whole and half notes: the same temporal relationship, but in a different tempo.

This is actually a thing in traditional Gamelan music, would be cool to be able to do this.
Cheers!

OK. I’ll think how to do this.
EDIT
does this do what you want? (Intorduced function to scale INTERONSET values
Also declared them outside the live_loop so they act as global

#MarkovChains Freequencies

t = Time.now.to_i
use_random_seed t
puts rand

live_loop :temp do
  set :temp, 52
  sleep 10
end

#Setting the range of possible pitches, in this case one octave
set :min_pitch, 66
set :max_pitch, 78


#Setting the pitches to be used; not quantized to any kind of scale so microtonality or strange constellations may come up
set :eins, rrand(get[:min_pitch], get[:max_pitch])
set :zwei, rrand(get[:min_pitch], get[:max_pitch])
set :drei, rrand(get[:min_pitch], get[:max_pitch])
set :vier, rrand(get[:min_pitch], get[:max_pitch])
set :fünf, rrand(get[:min_pitch], get[:max_pitch])


#Setting the rhythmic values to be used
RHYTHMIC_VALUES = [0.125, 0.25, 0.375, 0.5, 0.75, 1, 1.5].pick(6)
INTERONSET=INTERONSET1=INTERNOSET2=RHYTHMIC_VALUES #set intial values for all outside liveloops

#initial state
set :current_state, 0

define :rset do |values| #function to scale INTERONSET values
  sc=[1,2,3,4,5,6].choose
  vout=[]
  values.each do |x|
    vout << x*sc
  end
  return vout
end

#Setting the actual notes to be used; in this case, the fifth note is omitted while the first and third each occur twice
NOTES = [get[:eins], get[:zwei], get[:drei], get[:vier], get[:drei], get[:eins]]

#Multiplying the rhythmic values to achieve different rhythmic levels - not sure if this actually works?!
live_loop :rhythm do
  INTERONSET = rset(RHYTHMIC_VALUES)
  puts "INTERNOSET = #{INTERONSET}"
  sleep rrand_i(4, 64)
end

# State machine rules
RULES = {
  0 => [1],
  1 => [2],
  2 => [3, 3, 0],
  3 => [1, 2, 4, 4],
  4 => [5, 0],
  5 => [1]
}




#Setting a value for transposition of the sequence of notes resulting from the state machine rules
#Due to the four zeros, no transposition is most likely
live_loop :trnsp do
  set :transpose, [-12, 0, 0, 0, 0, 12].choose
  sleep rrand_i(4, 64)
end

with_fx :reverb, mix: 0.2 do
  live_loop :update_state do
    # get current state value -- 0 on the first run
    use_bpm get[:temp]
    s = get[:current_state]
    play NOTES[s] + get[:transpose], amp: rrand(0.2, 0.8), release: rrand(0.1, 0.5), pan: rrand(-1, 1)
    sleep INTERONSET[s]
    
    # update to next state
    set :current_state, RULES[s].choose
    
  end
  
  #Setting a different value for transposition of the second voice
  live_loop :trnsp1 do
    set :transpose1, [-12, 0, 0, 0, 12].choose
    sleep rrand_i(4, 64)
  end
  
  #initial state for the second voice
  set :current_state1, 0
  
  #As above
  live_loop :rhythm1 do
    INTERONSET1 = rset(RHYTHMIC_VALUES)
    puts "INTERNOSET1 = #{INTERONSET1}"
    sleep rrand_i(4, 64)
  end
  
  
  live_loop :update_state1 do
    # get current state value for second voice -- 0 on the first run
    use_bpm get[:temp]
    s = get[:current_state1]
    play NOTES[s] + get[:transpose1], amp: rrand(0.2, 0.8), release: rrand(0.1, 0.5), pan: rrand(-1, 1)
    sleep INTERONSET1[s]
    
    # update to next state for second voice
    set :current_state1, RULES[s].choose
    
  end
  
  #and the same for a third voice...
  live_loop :trnsp2 do
    set :transpose2, [-12, 0, 0, 0, 12].choose
    sleep rrand_i(4, 64)
  end
  
  set :current_state2, 0
  
  live_loop :rhythm2 do
    INTERONSET2 = rset(RHYTHMIC_VALUES)
    puts "INTERNOSET1 = #{INTERONSET2}"
    sleep rrand_i(4, 64)
  end
  
  
  live_loop :update_state2 do
    use_bpm get[:temp]
    s = get[:current_state2]
    play NOTES[s]  + get[:transpose2], amp: rrand(0.2, 0.8), release: rrand(0.1, 0.5), pan: rrand(-1, 1)
    sleep INTERONSET2[s]
    
    set :current_state2, RULES[s].choose
    
  end
    
end
1 Like

Hey @robin.newman, sorry for never giving feedback on this, I just tested your suggestion in a simplified context and yes, it works fine. Thanks!

#MarkovChains Freequencies
#Robin Newman fix test

use_random_seed 1


live_loop :temp do
  set :temp, 60
  sleep 10
end


#Setting the pitches to be used
set :eins, 60
set :zwei, 62
set :drei, 63
set :vier, 65
set :fünf, 67


#Setting the rhythmic values to be used
RHYTHMIC_VALUES = [0.125, 0.5, 1, 2].pick(6)
INTERONSET=INTERONSET1=INTERNOSET2=RHYTHMIC_VALUES #set intial values for all outside liveloops

#initial state
set :current_state, 0

live_loop :rhythmic_levels do
  define :rset do |values| #function to scale INTERONSET values
    sc=[1,2,4,8].choose
    vout=[]
    values.each do |x|
      vout << x*sc
    end
    return vout
  end
  sleep (ring 4, 8, 16).choose
end

#Setting the actual notes to be used; in this case, the fifth note is omitted while the first and third each occur twice
NOTES = [get[:eins], get[:zwei], get[:drei], get[:vier], get[:fünf], get[:zwei]]

#Multiplying the rhythmic values to achieve different rhythmic levels - not sure if this actually works?!
live_loop :rhythm do
  INTERONSET = rset(RHYTHMIC_VALUES)
  puts "INTERONSET = #{INTERONSET}"
  sleep rrand_i(4, 64)
end

# State machine rules
RULES = {
  0 => [1],
  1 => [2],
  2 => [3, 3, 0],
  3 => [1, 2, 4, 4],
  4 => [5, 0],
  5 => [1]
}


with_fx :reverb, mix: 0.1 do
  
  live_loop :update_state do
    # get current state value -- 0 on the first run
    use_bpm get[:temp]
    s = get[:current_state]
    play NOTES[s] -12, amp: rrand(0.2, 0.8), sustain: 0.8, release: rrand(0.2, 0.3), pan: -0.3
    sleep INTERONSET[s]
    
    # update to next state
    set :current_state, RULES[s].choose
    
  end
  
  #initial state for the second voice
  set :current_state1, 0
  
  #As above
  live_loop :rhythm1 do
    INTERONSET1 = rset(RHYTHMIC_VALUES)
    puts "INTERONSET1 = #{INTERONSET1}"
    sleep rrand_i(4, 64)
  end
  
  
  live_loop :update_state1 do
    # get current state value for second voice -- 0 on the first run
    use_bpm get[:temp]
    s = get[:current_state1]
    play NOTES[s], amp: rrand(0.2, 0.8), sustain: 0.8, release: rrand(0.2, 0.3), pan: 0
    sleep INTERONSET1[s]
    
    # update to next state for second voice
    set :current_state1, RULES[s].choose
    
  end
  
  
  set :current_state2, 0
  
  live_loop :rhythm2 do
    INTERONSET2 = rset(RHYTHMIC_VALUES)
    puts "INTERONSET1 = #{INTERONSET2}"
    sleep rrand_i(4, 64)
  end
  
  
  live_loop :update_state2 do
    use_bpm get[:temp]
    s = get[:current_state2]
    play NOTES[s] + 12, amp: rrand(0.2, 0.8), sustain: 0.8, release: rrand(0.2, 0.3), pan: 0.3
    sleep INTERONSET2[s]
    
    set :current_state2, RULES[s].choose
    
  end
  
end