Sonic Pi: Randomization
Now that we know how to play notes and samples, as well as how to fine tune their playback using ADSR envelopes, it's time to jump into a fun topic: randomization. This will be of particular use when it comes to generative music production.
You would think that choosing a random note to play would be as easy as something like:
play rrand(60, 80)
At first glance, this should choose a random note somewhere between 60
and 80
each time you hit Play. But that's not the case. If you try playing that several times you'll realize you get the same note over and over. That's because Sonic Pi prioritizes consistency between machines, so if you share your code (including "random" tones), that code should produce the same music on one machine as it does on another.
So how do we generate random tones? One way to do this is by putting the code above in a loop. On each iteration of the loop, a new tone will get selected:
loop do play rrand(60, 80) sleep 0.5end
As an aside, you need the sleep
in that loop, otherwise it throws an error. This is probably predictable as you would essentially be trying to call the same loop at the exact same time an infinite number of times. That said, shortening the sleep
value produces some interesting results:
loop do play rrand(60, 80) sleep 0.1end
Another famous example of this sort of thing is the Haunted Bells example within Sonic Pi:
loop do sample :perc_bell, rate: rrand(0.125, 1.5) sleep rrand(0.2, 2)end
This takes the basic :perc_bell
sample and changes its pitch and playback length using the rate
opt in conjunction with random numbers. It also passes sleep
a random value.
Here's another example of using rrand
to pass random values to opts:
use_synth :tech_saws
loop do play 60, release: 0.2, cutoff: rrand(65, 92) sleep 0.1end
By the way, the nature of this randomization, where numbers are referred to as random, but are actually predictable and consistent between machines, is called pseudo-randomization.
If you don't like the random tones produced by default, for example:
5.times do play rrand(66, 77) sleep 0.2end
...then you tell the pseudo-random numbers to change by changing the seed value. The seed value is the deterministic sequence of bits that are used to approximate randomness. You can change the seed value using use_random_seed
:
use_random_seed 4205.times do play rrand(66, 77) sleep 0.2end
Other Randomization Approaches
rrand_i
It's worth noting that rrand
picks random float values between the two values you pass it, meaning it is most likely producing microtones (i.e. 60.123834684
instead of 60
).
If you want to only play whole numbers (aka "integers"), use rrand_i
:
5.times do play rrand_i(60, 80) sleep 0.2end
rand
rand
lets you specify a range between 0
and the value you pass it to choose a number from. The default value is 1
. This range includes 0
but excludes 1
(or the specified value).
loop do play rand(66) sleep 1end
This is useful for amp
, attack_level
, sustain_level
, or release_level
.
loop do play 60, amp: rand, attack_level: rand, sustain_level: rand, release_level: rand sleep 1end
It also works with negative values:
loop do play 60, pan: rand(-1) sleep 1end
rand_i
rand_i
behaves the same as rand
, but for whole integers.
choose
choose
allows you to choose an item randomly from a list:
loop do play choose([60, 65, 72, 76, 80]) sleep 1end
This would be helpful for specifying a key or arpeggio to choose random notes from.
dice
You can use dice
to simulate rolling a dice with the number of sides you specify. dice(6)
is the same as rolling a traditional, 6-sided dice; dice(20)
is the same as rolling a d20.
This is basically the same as rand_i
, but instead of the lower bound being 0
, the lower bound is 1
.
loop do play dice(80) sleep 1end
All of the above could be used for some fun generative sonic creations!