Randomness and repeatability

Hi everyone.

I came across Sonic Pi at the weekend and I’ve been having great fun so far. Wanting to make something musical without actually specifying the notes to play, I thought repetitions and patterns were the way to go. This picks random notes in a key and repeats the random length phrases a random number of times.

I thought I’d share, I hope you like it:

use_bpm 102
use_random_seed Time.now.to_i
base = ':es1'
type = [:minor, :m, :m6]
key = scale(base, :minor, num_octaves: 5)
$note=32

live_loop :ageis do
  $sleepmax = dice(4)
  $intervalmax = dice(7)
  use_synth :tri
  with_fx :reverb, room: 0.99999 do
    with_fx :distortion, distort: 0.99 do
      ##| what's a tidier way of writing this?
      $sleeps = [rrand_i(1,$sleepmax)*0.25,
                 rrand_i(1,$sleepmax)*0.25,
                 rrand_i(1,$sleepmax)*0.25,
                 rrand_i(1,$sleepmax)*0.25,
                 rrand_i(1,$sleepmax)*0.25,
                 rrand_i(1,$sleepmax)*0.25,
                 rrand_i(1,$sleepmax)*0.25,
                 rrand_i(1,$sleepmax)*0.25].ring
  
      $interval = [rrand_i(-$intervalmax,$intervalmax),
                   rrand_i(-$intervalmax,$intervalmax),
                   rrand_i(-$intervalmax,$intervalmax),
                   rrand_i(-$intervalmax,$intervalmax),
                   rrand_i(-$intervalmax,$intervalmax),
                   rrand_i(-$intervalmax,$intervalmax),
                   rrand_i(-$intervalmax,$intervalmax),
                   rrand_i(-$intervalmax,$intervalmax)].ring
     
      ##| arp = chord_degree(rrand_i(1,7), base, :minor, rrand_i(3,8))
      arp = chord(:es4, :m, num_octaves: 2)
      n = dice(2)*dice(8)
      n.times do
        play (arp.tick)+12,release: 0.1,amp: 0.3
        sleep 0.25
      end
      n = dice(4)*dice(8)
      n.times do
        play key[$note+$interval.tick(:one)], amp: 0.1, release: 0.15
        sleep $sleeps.tick(:two)
      end
      $note = $note+$interval[8]
      ##| tick_reset_all
    end
  end
end

##| rhythm section from:
##| https://github.com/soundslikedata/open_source_music
live_loop :bass, sync: :ageis do
  use_sample_defaults amp: 7
  3.times {sample :bd_boom; sleep 0.75}
  sleep 0.25; sample :bd_boom; sleep 1.5
end

live_loop :hats, sync: :bass do
  10.times {sample :drum_cymbal_closed, rate: 1.75, amp: 2; sleep 0.25}
  sample :drum_cymbal_open, sustain: 0.15, rate: 1.88, amp: 2
  6.times {sample :drum_cymbal_closed, rate: 1.75, amp: 2; sleep 0.25}
end

live_loop :snare, sync: :bass do
  sleep 1
  sample :drum_snare_hard, amp: 0.8, rate: 1.05, sustain: 0.1
  sleep 1
end

live_loop :pads, sync: :bass do
  use_synth :tb303
  use_synth_defaults sustain: 5, release: 2.5, amp: 0.5, cutoff: 70
  play chord(:es5, :m); play chord(:es4, :m)
  sleep 8
  play chord(:es4, :m6); play chord(:es3, :m6)
  sleep 8
end

##|  simpler slow melody
live_loop :polis, sync: :bass do
  use_synth :beep
  $sleep2 = rrand_i(5,8)*0.25
  $interval2 = rrand_i(-7,7)
  play key[$note+$interval2], sustain: $sleep2 - 0.2, amp: 2
  sleep $sleep2
end

\aR/

3 Likes

yep

$sleeps = rrand_i(1,$sleepmax)*0.25

as all the values of this ring are the same. the rrand_i function will be executed each time with a randomize result. So no need for a ring here.

i miss something ?

2 Likes

@nlb is right, there is not strictly any need to construct an array of random values, other than perhaps personal taste. Merely replacing the $sleeps definition with $sleeps = rrand_i(1,$sleepmax)*0.25 would not be quite enough however, as in the n.times loop it would play the same note assigned to $sleeps n times.

Instead, if you are already iterating over a number of calls to play, then you can just call the random number functions once inside that loop, and a new pseudo-random number will be provided at each call. So instead of defining $sleeps = [ ... ] and using tick/look to step through the values, you can just write something like:

      n.times do
        play key[$note+rrand_i(-$intervalmax, $intervalmax)], amp: 0.1, release: 0.15
        sleep rrand_i(1,$sleepmax)*0.25
      end

That can work just as well.

Incidentally, from glancing through your code, I can’t immediately see any need to use global variables there, other than perhaps $note (unless there is more code to your composition than what you have shared above). Even so, once you have had a good read through the tutorial, you will find that you also have the option of using more ‘idiomatic’ Sonic Pi syntax to do things that you might otherwise do with global variables, like sharing variables such as note between loops :slight_smile: worth checking it out if you’re interested: That’s all explained in Chapter 10.

Regardless of all that, some nice sounds! Looking forward to hearing anything else you may be happy to share :slight_smile: - do feel free to ask any questions you may have as you continue to explore Sonic Pi :smile:

1 Like

Thanks both :+1:. Yes, I should tidy up my variables - I wasn’t really thinking about it - oops.

And thanks for spotting my question in the code there. Unfortunately neither suggestion quite gets what I’m after. My initial code creates an array that can be addressed again in the loop, so you get the repeatable aspect of music:

puts "ring repetition"
use_random_seed 123
1.times do
  sleeps = [rrand_i(1,4)*0.25,
             rrand_i(1,4)*0.25,
             rrand_i(1,4)*0.25,
             rrand_i(1,4)*0.25].ring
  2.times do
    puts sleeps
  end
end

"ring repetition"
 ├─ (ring 1.0, 0.25, 0.75, 0.75)
 ├─ (ring 1.0, 0.25, 0.75, 0.75)

Setting the variable once means it doesn’t change in the loop and setting the value at point of use means you can’t address it again to get the repeated pattern - you just get the new value each time.

It turns out I needed to use the map function!, so I’ll put that here is case anyone finds it useful:

puts "ring repetition"
use_random_seed 123
1.times do
  sleeps = 4.times.map{rrand_i(1,4)*0.25 }.ring
  2.times do
    puts sleeps
  end
end

"ring repetition"
 ├─ (ring 1.0, 0.25, 0.75, 0.75)
 └─ (ring 1.0, 0.25, 0.75, 0.75)

ah ok ! i missed something :slight_smile: i misunderstood your goal !

you want some random values, put them into a ring and then use this ring.

so i propose this

use_random_seed Time.now.to_i


sleeps = (ring )
4.times do
  sleeps = sleeps + (ring rrand_i(1,4)*0.25 )
end
puts sleeps


sleeps_02 = (ring )
4.times do
  sleeps_02 = sleeps_02 + (ring [0.25, 0.5, 0.75, 1].choose)
end
puts sleeps_02

cheers

1 Like

Thanks! I’ll save those snippets.