Adding Play option affects order of random seed sequence

Hello,

I have this bit of code
which produces two random sequences based on the two arguments passed to the use_random_seed function.

use_bpm 133

live_loop :melle  do
  with_fx :octaver, sub_amp: 2, amp: 1.5, mix: 0.4 do
    with_fx :reverb, room: 0.6, mix: 0.6 do
      use_synth :saw
      use_random_seed (knit, 9864, 4, 235, 4).tick(:a)
      sleep 1
      14.times do
        play scale(:c3, :ionian, num_octaves: 2).choose, amp: 0.15, release: 0.5 if spread(10, 14).tick
        sleep 0.5
      end
    end
  end
end

However, if I add the pan option which passes a random argument to this line of code:

  play scale(:c3, :ionian, num_octaves: 2).choose, amp: 0.15,  pan: rrand(-1, 1), release: 0.5 if spread(10, 14).tick

It will now produce two different random sequences of notes even though I haven’t changed the numbers in the use_random_seed function.

Can someone explain why it behaves this way and what I can do to keep it from changing the random sequence I have chosen while still using the pan option?

Thanks

Hello, this happens because a new randomly generated data has been added.

You can generate a sequence before, and then use it with tick

use_bpm 133
pa = []

100.times do |i|
  pa[i] = rrand(-1,1)
end

live_loop :melle  do
  with_fx :octaver, sub_amp: 2, amp: 1.5, mix: 0.4 do
    with_fx :reverb, room: 0.6, mix: 0.6 do
      use_synth :saw
      use_random_seed (knit, 9864, 4, 235, 4).tick(:a)
      sleep 1
      14.times do
        play scale(:c3, :ionian, num_octaves: 2).choose, amp: 0.15,  pan: pa.tick, release: 0.5 if spread(10, 14).tick
        sleep 0.5
      end
    end
  end
end

Hi there,

in Sonic Pi, random streams are thread-local. This means that each thread (such as a live_loop) has its own independent stream of random numbers to work with. This is kind of analogous to having a pre-shuffled deck of cards to work with.

Every time a random function such as rand, rrand, choose, etc is used, it will consume one rand from the random stream - or take one card from the pre-shuffled deck to determine the random value. This means that having just one call to, say, rand in each live_loop will mean the random choices in each live loop are isolated from each other as they are each working with their own independent deck of pre-shuffled cards.

If you put more than one call to rand in a live_loop, then you’re taking more than one card from the deck. This means that the calls will interfere with each other. So, say you’re using the specific order of the top 8 cards on the deck to drive your bassline, if you were to then use every other card (because you started consuming cards for the pan opt) then your bassline will inevitably change.

To fix this, you should either work with a new thread, or if that’s not possible (as it seems to be in your case) you can use the handy rand_back function to undo a call to rand and effectively put a card back on the deck.

So, try this:

use_bpm 133

live_loop :melle  do
  with_fx :octaver, sub_amp: 2, amp: 1.5, mix: 0.4 do
    with_fx :reverb, room: 0.6, mix: 0.6 do
      use_synth :saw
      use_random_seed (knit, 9864, 4, 235, 4).tick(:a)
      sleep 1
      14.times do
        play scale(:c3, :ionian, num_octaves: 2).choose, amp: 0.15,   pan: rrand(-1, 1), release: 0.5 if spread(10, 14).tick
        rand_back
        sleep 0.5
      end
    end
  end
end

However, this won’t completely work as expected as you’re only calling the rrand for pan: if the spread value suggests it should. This is because you’re adding an if at the end of the line so it doesn’t execute every time - whereas the rand_back is executed every time.

For it to work as expected, we need to only call rand_back when the rrand is called.

We can therefore either put both the play and rand_back within the same larger if statement:

if spread(10, 14).tick
  play scale(:c3, :ionian, num_octaves: 2).choose, amp: 0.15,   pan: rrand(-1, 1), release: 0.5 
  rand_back
end

Or my prefered solution is to use on: instead of if. That way the call to rrand happens every time, but the synth is only triggered when the spread value is appropriate (whereas with the call to if not only is the synth not triggered, but the whole line isn’t executed and therefore not the rrand).

use_bpm 133

live_loop :melle  do
  with_fx :octaver, sub_amp: 2, amp: 1.5, mix: 0.4 do
    with_fx :reverb, room: 0.6, mix: 0.6 do
      use_synth :saw
      use_random_seed (knit, 9864, 4, 235, 4).tick(:a)
      sleep 1
      14.times do
        play scale(:c3, :ionian, num_octaves: 2).choose, amp: 0.15,  pan: rrand(-1, 1), release: 0.5, on: spread(10, 14).tick
        rand_back
        sleep 0.5
      end
    end
  end
end

Hope that this helps!

1 Like