Variáveis

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_amenna variável sample_name. Podemos agora usar sample_nameem 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.

Comunicar sentido

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.7533significa: é 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.

Gerindo a repetição

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_amens 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_garzule mudamos em muitos sítios pela magia das variáveis.

Capturando resultados

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 playou 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.

Aviso: Variáveis and Threads

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.