O Sonic Pi tem um armazenamento global chamado de Time State. As duas coisas principais que fazes com ele é armazenar (set
) informação e ler ( get
) informação. Vamos aprofundar…
Para armazenar informação no Time State são necessárias duas coisas:
Por exemplo, nós podemos querer armazenar o numero 3000
com a chave :intensity
. Isto é possível utilizando a função set
:
set :intensity, 3000
Podemos usar qualquer nome para a nossa chave. Se alguma informação já foi armazenada anteriormente com a mesma chave, o nosso novo set
vai substitui-la:
set :intensity, 1000
set :intensity, 3000
No exemplo em cima, nós guardamos ambos os números utilizando a mesma chave, a ultima chamada set
ganha , por isso o numero associado a :intensity
vai ser 3000
já que a primeira chamada ao set
é efetivamente substituída.
Para tirar informação do Time State só precisamos de utilizar a chave que utilizamos para armazena-la (set
) , que no nosso caso e :intensity
. Só precisamos então de chamar get[:intensity]
que podemos ver ao imprimirmos o resultado para o log:
print get[:intensity] #=> prints 3000
Notice that calls to get
can return information that was set
in a previous run. Once a piece of information has been set
it is available until either the information is overridden (just like we clobbered the :intensity
value of 1000
to 3000
above) or Sonic Pi is closed.
The main benefit of the Time State system is that it can be safely used across threads or live loops. For example, you could have one live loop setting information and another one getting it:
live_loop :setter do
set :foo, rrand(70, 130)
sleep 1
end
live_loop :getter do
puts get[:foo]
sleep 0.5
end
The nice thing about using get
and set
across threads like this is that it will always produce the same result every time you hit run. Go on, try it. See if you get the following in your log:
{run: 0, time: 0.0}
└─ 125.72265625
{run: 0, time: 0.5}
└─ 125.72265625
{run: 0, time: 1.0}
└─ 76.26220703125
{run: 0, time: 1.5}
└─ 76.26220703125
{run: 0, time: 2.0}
└─ 114.93408203125
{run: 0, time: 2.5}
└─ 114.93408203125
{run: 0, time: 3.0}
└─ 75.6048583984375
{run: 0, time: 3.5}
└─ 75.6048583984375
Try running it a few times - see, it’s the same every time. This is what we call deterministic behaviour and it’s really very important when we want to share our music as code and know that the person playing the code is hearing exactly what we wanted them to hear (just like playing an MP3 or internet stream sounds the same for all listeners).
Atrás na secção 5.6 discutimos a razão pela qual utilizar variáveis através de diferentes threads pode levar a comportamento aleatório. Isto faz-nos desconfiar de código como este:
## Um exemplo de comportamento não-determinístico
## (devido a race conditions causadas por múltiplos
## loops manipulando a mesma variárvel ao mesmo tempo).
##
## Se você executar este código você notará
## que a lista que é impressa
## nem sempre sai ordenada!
a = (ring 6, 5, 4, 3, 2, 1)
live_loop :shuffled do
a = a.shuffle
sleep 0.5
end
live_loop :sorted do
a = a.sort
sleep 0.5
puts "sorted: ", a
end
Let’s take a look at how this might look using get
and set
:
## Um exemplo de comportamento determinístico
## (apesar do acesso simultâneo ao estado compartilhado)
## usando o novo sistema Time State do Sonic Pi.
##
## Quando este código é executado, a lista
## sempre é impressa de forma ordenada!
set :a, (ring 6, 5, 4, 3, 2, 1)
live_loop :shuffled do
set :a, get[:a].shuffle
sleep 0.5
end
live_loop :sorted do
set :a, get[:a].sort
sleep 0.5
puts "sorted: ", get[:a]
end
Notice how this code is pretty much identical to the version using a variable before it. However when you run the code, it behaves as you would expect with any typical Sonic Pi code - it does the same thing every time in this case thanks to the Time State system.
Therefore, when sharing information across live loops and threads, use get
and set
instead of variables for deterministic, reproducible behaviour.