Un lucru util pe care il putem face in cadrul codului nostru este sa cream nume pentru obiecte. In Sonic Pi asta se face foarte usor, scrii un nume pe care vrei sa-l folosesti, semnul egal (‘=’), apoi obiectul pe care vrei sa-l memorezi:
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.
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.
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.
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.
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_loop
s. In the first live loop every 0.5
s 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.