Variabile

A useful thing to do in your code is to create names for things. Sonic Pi makes this very easy: you write the name you wish to use, an equal sign (=), then the thing you want to remember:

sample_name = :loop_amen

Aici, am ‘memorat’ simbolul ‘:loop_amen’ in variabila ‘sample_name’. Putem folosi ‘sample_name’ oriunde am fi folosit ‘:loop_amen’. De exemplu:

sample_name = :loop_amen
sample sample_name

Exista trei motive principale pentru utilizarea variabilelor in Sonic Pi: comunicarea rolului, gestiunea repetitiilor si stocarea rezultatului unor actiuni.

Comunicarea rolului

Cand scrii cod e usor sa te gandesti doar ca tu ii spui calculatorului sa faca un anumit lucru - cat timp computerul intelege este OK. Totusi, e important sa te gandesti ca nu doar computerul citeste codul. Alte persoane ar putea sa il citeasca de asemenea si sa incerce sa inteleaga despre ce este vorba. De asemenea, e posibil ca tu sa iti recitesti propriul cod mai tarziu si sa vrei sa intelegi cum functioneaza. Desi ar putea sa ti se para evident acum, acest lucru ar putea sa nu fie valabil pentru altii sau chiar pentru tine in viitor!

Un mod de a-i ajuta pe altii sa inteleaga ce face codul tau este sa scrii comentarii (asa cum am vazut intr-o sectiune anterioare). Alt mod este sa folosesti variabile cu nume alese conform rolului lor. Priveste acest cod:

sleep 1.7533

De ce foloseste acest numar, 1.7533? De unde vine acest numar? Ce inseamna? Priveste acum acest cod:

loop_amen_duration = 1.7533
sleep loop_amen_duration

Acum este mult mai clar ce inseamna 1.7533: este durata esantionului ‘:loop_amen’! Desigur, ai putea spune ca e mai simplu sa scrii:

sleep sample_duration(:loop_amen)

Care este, desigur, un mod potrivit de a comunica scopul codului respectiv.

Gestionarea repetitiilor

Adesea poti vedea multe repetitii in cod si cand vrei sa schimbi ceva trebuie sa schimbi intr-o multime de locuri. Priveste acest cod:

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)

Folosim in multe locuri ‘:loop_amen!’ Sa presupunem ca vrem sa auzim cum ar suna daca am folosi un alt esantion, cum ar fi ‘:loop_garzul’. Ar trebui sa inlocuim peste tot :loop_amen cu :loop_garzul. Ar putea fi OK daca ai avea o gramada de timp la dispozitie, dar daca interpretezi live pe scena? Uneori nu iti permiti luxul de a pierde timpul, mai ales cand vrei ca lumea sa danseze in continuu.

Daca ai fi scris codul astfel:

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)

Codul ar face acelasi lucru ca cel de mai sus (poti incerca). Dar ne da si posibilitatea de a modifica o singura linie din sample_name = :loop_amen in sample_name = :loop_garzul, ceea ce va schimba esantionul redat in mai multe locuri folosind magia variabilelor.

Stocarea rezultatelor

In fine, un motiv bun pentru a utiliza variabilele este acela ca ajuta la stocarea rezultatelor. De exemplu, ai putea dori sa faci anumite lucruri cu durata esantionului:

sd = sample_duration(:loop_amen)

Acum putem folosi ‘sd’ oriunde avem nevoie de durata esantionului ‘:loop_amen’.

Poate mai important, o variabila ne permite sa stocam rezultatul unui apel al unei functii ‘play’ sau ‘sample’:

s = play 50, release: 8

Acum am memorat ‘s’ ca o variabila, ceea ce ne permite sa controlam sintetizatorul in timpul functionarii:

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

Vom intra in detalii privind controlul sintetizatoarelor intr-o sectiune ulterioara.

Warning: Variables and Threads

Whilst variables are great for giving things names and capturing the results of things, it is important to know that they should typically only be used locally within a thread. For example, don’t do this:

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

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

There are two solutions to this. Firstly, don’t use the same variable in multiple live loops or threads. For example, the following code will always print a sorted list as each live loop has its own separate variable:

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

However, sometimes we do want to share things across threads. For example, the current key, BPM, synth etc. In these cases, the solution is to use Sonic Pi’s special thread-safe state system via the fns get and set. This is discussed later on in section 10.