Uma coisa útil a fazer no nosso código é dar nomes às coisas. Sonic Pi torna isto muito fácil: escreves o nome que queres usar, um sinal de igual (=
), e a coisa que te queres lembrar:
sample_name = :loop_amen
Aqui “lembramo-nos” do símbolo :loop_amen
na variável sample_name
. Podemos agora usar sample_name
em todo o lado que usaríamos :loop_amen
. Por exemplo:
sample_name = :loop_amen
sample sample_name
Existem 3 razões principais para usar variáveis no Sonic Pi: comunicar significado, gerir repetições e capturar o resultado de coisas.
Quando escreves código é fácil de pensar que estas a dizer ao computador como fazer coisas - desde que o computador perceba está Ok. No entanto é importante lembrar que não é só o computador que lê código. Outras pessoas podem o ler e tentar perceber o que se está a passar. Além disso, é provável que tenhas que ler o teu código no futuro e tentar perceber o que se passa. Apesar de poder parecer óbvio para ti agora - pode não o ser para outros ou o teu eu futuro!
Uma maneira de ajudar os outros a perceber o que o teu código está a fazer é adicionar comentários (como vimos na secção anterior). Outra coisa é usar nomes de variáveis com sentido. Vê este código:
sleep 1.7533
Porque usa o numero 1.7533
? De onde veio este número? O que significa? Vê agora este código:
loop_amen_duration = 1.7533
sleep loop_amen_duration
Agora é muito mais claro o que 1.7533
significa: é a duração do sample :loop_amen
! Claro, podes dizer porque não escrever simplesmente:
sleep sample_duration(:loop_amen)
Que, claro, é uma boa maneira de comunicar a intenção do código.
Frequentemente vês muita repetição no teu código e quando queres mudar as coisas, tens que as mudar em muitos sítios. Vê este código:
sample :loop_amen
sleep sample_duration(:loop_amen)
sample :loop_amen, rate: 0.5
sleep sample_duration(:loop_amen, rate: 0.5)
sample :loop_amen
sleep sample_duration(:loop_amen)
Estamos a fazer muitas coisas com o :loop_amen
! E se queremos ouvir como soa com outro loop sample como o :loop_garzul
? Teremos de encontrar e substituir todos os :loop_amen
s por :loop_garzul
. Isso pode ser Ok se tiveres muito tempo - mas se tiveres a actuar em palco? As vezes não temos o luxo de tempo - especialmente se queres manter as pessoas a dançar.
E se escreveres código como este:
sample_name = :loop_amen
sample sample_name
sleep sample_duration(sample_name)
sample sample_name, rate: 0.5
sleep sample_duration(sample_name, rate: 0.5)
sample sample_name
sleep sample_duration(sample_name)
Isto faz exactamente o mesmo que o anterior (experimenta). Também nos dá a habilidade de apenas mudar uma linha sample_name = :loop_amen
para sample_name = :loop_garzul
e mudamos em muitos sítios pela magia das variáveis.
Finalmente, uma boa motivação para usar variáveis é para capturar o resultados de coisas, podes querer fazer coisas com a duração do sample:
sd = sample_duration(:loop_amen)
Agora podemos usar sd
em qualquer lugar que necessitemos da duração do sample :loop_amen
.
Talvez mais importante, a variável permite nos capturar o resultado da chamada a play
ou a sample
:
s = play 50, release: 8
Agora capturamos e lembramos-nos de s
como variável, que nos permite controlar o synth enquanto corre:
s = play 50, release: 8
sleep 2
control s, note: 62
Veremos como controlar os synths em mais detalhe numa secção posterior.
Embora as variáveis sejam ótimas para dar nomes às coisas e capturar os resultados das coisas, é importante saber que elas normalmente só devem ser usadas localmente num segmento. Por exemplo, não faça isso:
a = (ring 6, 5, 4, 3, 2, 1)
live_loop :sorted do
a = a.sort
sleep 0.5
puts "sorted: ", a
end
live_loop :shuffled do
a = a.shuffle
sleep 0.5
end
No exemplo acima, atribuímos um anel de números a uma variável ‘a’ e usamo-lo dentro de dois ciclos “live_loop” separados. No primeiro ciclo “live_loop” a cada 0.5 segundos nós ordenamos o anel (para (anel 1, 2, 3, 4, 5, 6)
) e imprimimos no log. Se executares o código, verás que a lista impressa * nem sempre é ordenada! *. Isto pode surpreender-te - especialmente porque às vezes a lista é impressa ordenada e outras vezes não. Isto é chamado de comportamento não determinístico e é o resultado de um problema bastante desagradável chamado de condição de corrida. O problema deve-se ao fato de que o segundo ciclo “live_loop” também manipular a lista (neste caso, embaralhando-a) e no momento em que a lista é impressa, às vezes ela acaba de ser ordenada e outras vezes acaba de ser embaralhada. Ambos os ciclos “live_loop” estão concorrendo para fazer algo diferente na mesma variável e em cada vez um ciclo diferente ‘ganha’.
Existem duas soluções para isto. Primeiro, não usar as mesmas variáveis em múltiplos ciclos “live_loop” ou threads. Por exemplo, o seguinte código deverá sempre imprimir uma lista ordenada à medida que cada ciclo “live_loop” tenha as suas variáveis separadas
live_loop :shuffled do
a = (ring 6, 5, 4, 3, 2, 1)
a = a.shuffle
sleep 0.5
end
live_loop :sorted do
a = (ring 6, 5, 4, 3, 2, 1)
a = a.sort
sleep 0.5
puts "sorted: ", a
end
No entanto, ás vezes queremos partilhar coisas entre threads. Por exemplo, a tecla atual, as BPM, sintetizador, etc. Nestes casos, a solução é o utilizar as funções get
eset
que fazem parte do sistema seguro do Sonic PI para partilha entre threads.