Imaginary Gamelan


#1

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

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.


#3

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!


#4

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