Variables

Algo útil para hacer con tú código es crear nombres para las cosas. Sonic Pi hace esto muy fácil, escribes el nombre que deseas utilizar, un símbolo de (=), después lo que quieres recordar:

sample_name = :loop_amen

Aquí hemos ‘recordado’ el símbolo :loop_amen en la variable sample_name. Ahora podemos usar sample_name en cualquier lugar donde pudiésemos usar :loop_amen. Por ejemplo:

sample_name = :loop_amen
sample sample_name

Hay tres razones principales para utilizar variables en Sonic Pi: comunicar significado, administrar repeticón y capturar los resultados de las cosas.

Comunicando significado

Cuando escribes código, es fácil pensar que sólo le dices a la computadora cómo hacer algo - mientras ella entienda, todo está bien. Sin embargo, es importante recordar que no sólo la computadora leerá el código. Otra gente podría leerlo e intentar comprender qué es lo que sucede. También sucede que quieras entender tu propio código en el futuro. Aunque pueda ser obvio para tí, ahora, ¡podría no serlo para otros o para tí en el futuro! When you write code it’s easy to just think you’re telling the computer how to do stuff - as long as the computer understands it’s OK. However, it’s important to remember that it’s not just the computer that reads the code. Other people may read it too and try to understand what’s going on. Also, you’re likely to read your own code in the future and try to understand what’s going on. Although it might seem obvious to you now - it might not be so obvious to others or even your future self!

Una forma de ayudar a otras personas a entender lo que hace tu código es escribir comentarios (como vimos en una sección anterior). Otra forma es utilizar nombres de variables significativos. Mira este código:

sleep 1.7533

¿Por qué utiliza el número 1.7533? ¿de dónde proviene? ¿qué significa? Miremos el código siguiente:

loop_amen_duration = 1.7533
sleep loop_amen_duration

Ahora está mucho más claro lo que significa 1.7533: es la duración del sample :loop_amen! claro que puedes pensar que es mucho más simple escribir:

sleep sample_duration(:loop_amen)

Lo cual es una muy buena manera de comunicar la intención del código.

Administrando las repeticiones

a menudo ves muchas repeticiones en tu código y cuando quieres cambiar algo, tienes que cambiarlo enmuchos lugares. Mira 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 haciendo muchas cosas con :loop_amen! ¿qué pasa si quisiésemos escuchar otro sampleo como :loop_garzul? tendríamos que buscar y reemplazar todos los :loop_amen con :loop_garzul. Eso estaría bien si tienes mucho tiempo - pero ¿qué si está en vivo? algunas veces no tendrás el lujo del tiempo - especialmente si quieres que la gente se mantenga bailando.

¿qué tal si hubieses escrito tú código, así?:

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)

Eso hace exactamente lo mismo que lo anterior (pruébalo). también nos da la habilidad de cambiar una línea sample_name = :loop_amen a sample_name = :loop_garzul y podemos hacerlo en muchos lugares gracias a las variables.

Capturando los resultados

Un buen motivo para utilizar variables es capturar los resultados de las cosas. Por ejemplo, quizás desees hacer algo con la duración de un sampleo:

sd = sample_duration(:loop_amen)

Ahora podemos usar sd en cualquier lugar donde necesitemos la duración del sampleo :loop_amen.

Más importantemente, una variable nos permite capturar el resultado de una llamada a play o sample:

s = play 50, release: 8

Ahora hemos atrapado y recordado s como una variable, lo que nos permite controlar el sintetizador mientras corre:

s = play 50, release: 8
sleep 2
control s, note: 62

Veremos en más detalle cómo controlar los sintetizadores más tarde.

Advertencia: Variables e Hilos

Si bien las variables son buenas para nombrar cosas y guardar los resultados de estas, es importante saber que, por lo general, solo deben usarse localmente dentro de un hilo. Por ejemplo, no hagas esto:

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

In the above example we assign a ring of numbers to a variable a and then used it within two separate live_loops. In the first live loop every 0.5s we sort the ring (to (ring 1, 2, 3, 4, 5, 6)) and then print it out to the log. If you run the code, you’ll find that the printed list is not always sorted!. This may surprise you - especially that sometimes the list is printed as sorted, and sometimes it is not. This is called non-deterministic behaviour and is the result of a rather nasty problem called a race-condition. The problem is due to the fact that the second live loop is also manipulating the list (in this case shuffling it) and by the time the list is printed, sometimes it has just been sorted and sometimes it has just been shuffled. Both live loops are racing to do something different to the same variable and every time round a different loop ‘wins’.

Hay dos soluciones a este problema. Primero, no utilices el miso variable en multiples live loops o subprocesos. Por ejemplo, el siguiente código imprimirá siempre una lista ordenada, porque cada live loop tiene su variable separado:

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

Sin embargo, a veces queremos compartir datos entre hilos. Por ejemplo, la clave actual, la velocidad, el sintetizador etc. En estos casos, se puede utilizar el sistema especial “thread-safe” de Sonic Pi a través de los fns get y set. Esto sera discutido luego en la sección 10.