Fire de executie

Sa zicem ca ai obtinut un bass incredibil si un beat pe masura. Cum le redai in acelasi timp? O solutie este sa combini liniile manual - redai un pic de bas, apoi putin toba, iarasi bas… Dar potrivirea timpilor va deveni rapid greu de gestionat, mai ales daca mai ai de introdus si alte elemente.

Dar daca Sonic Pi ar putea sa faca automat intreteserea sunetelor? Ei bine, chiar poate si acest lucru se realizeaza folosind ceea ce se numeste thread (fir de executie).

Bucle infinite

Pentru simplitate, vom considera ca acestea sunt basul incredibil si beatul pe masura:

loop do
  sample :drum_heavy_kick
  sleep 1
end
loop do
  use_synth :fm
  play 40, release: 0.2
  sleep 0.5
end

Asa cum am discutat, buclele sunt gauri negre pentru program. Odata intrat intr-o bucla (loop) nu mai poti sa iesi decat apasand pe stop. Cum redam cele doua bucle in acelasi timp? Trebuie sa instruim Sonic Pi sa porneasca ceva in acelasi timp cu restul codului. Aici intervin firele de executie.

Salvarea oferita de firele de executie (threads)

in_thread do
  loop do
    sample :drum_heavy_kick
    sleep 1
  end
end
loop do
  use_synth :fm
  play 40, release: 0.2
  sleep 0.5
end

Introducand prima bucla intr-un bloc do/end ‘in_thread’, spunem Sonic Pi sa ruleze continutul blocului do/end exact in acelasi timp cu instructiunea care urmeaza dupa blocul respectiv (care se intampla sa fie a doua bucla). Incearca si vei auzi atat tobele cat si basul in acelasi timp!

Cum ar fi daca am vrea sa adaugam si un sintetizator pe deasupra? Ceva gen:

in_thread do
  loop do
    sample :drum_heavy_kick
    sleep 1
  end
end
loop do
  use_synth :fm
  play 40, release: 0.2
  sleep 0.5
end
loop do
  use_synth :zawa
  play 52, release: 2.5, phase: 2, amp: 0.5
  sleep 2
end

Avem aceeasi problema ca mai inainte. Prima bucla este redata in acelasi timp cu a doua datorita instructiunii ‘in_thread’. Totusi, a treia bucla nu este redata niciodata. Trebuie deci sa folosim un nou thread:

in_thread do
  loop do
    sample :drum_heavy_kick
    sleep 1
  end
end
in_thread do
  loop do
    use_synth :fm
    play 40, release: 0.2
    sleep 0.5
  end
end
loop do
  use_synth :zawa
  play 52, release: 2.5, phase: 2, amp: 0.5
  sleep 2
end

Rularea ca fire de executie

S-ar putea sa te surprinda faptul ca atunci cand apesi butonul Executa (Run), de fapt creezi un nou fir de executie pentru cod. De aceea, apasarea repetata a butonului va face ca sunetele sa se suprapuna. Cum rularile codului sunt fire de executie, ele vor imbina automat sunetele pentru tine.

Domeniu

Pe masura ce inveti cum sa stapanesti Sonic Pi, vei invata ca firele de executie sunt cele mai importante blocuri din care este construita muzica. Unul dintre cele mai importante roluri pe care le au este sa izoleze setarile curente de celelalte fire. Ce inseamna asta? Ei bine, cand schimbi sintetizatorul folosind ‘use_synth’, acest lucru se refera doar la firul curent - in niciun alt thread sintetizatorul nu va fi schimbat. Sa vedem un exemplu:

play 50
sleep 1
in_thread do
  use_synth :tb303
  play 50
end
sleep 1
play 50

Ai observat ca sunetul de la jumatate este diferit de celelalte? Comanda ‘use_synth’ a afectat doar thread-ul din care face parte, nu si thread-ul principal.

Mostenire

Cand creezi un nou thread folosind ‘in_thread’, acesta va mosteni automat toate setarile de la thread-ul curent. Sa vedem:

use_synth :tb303
play 50
sleep 1
in_thread do
  play 55
end

Ai observat ca a doua nota a fost redata folosind sintetizatorul ‘:tb303’ desi a fost redata intr-un thread separat? Oricare dintre parametrii modificati cu diferitele functii ‘use*’ se va comporta in acelasi mod.

Cand un thread este creat, va mosteni toate setarile de la thread-ul parinte, dar nu va transmite inapoi nicio modificare.

Numele thread-urilor

Putem da nume thread-urilor noastre:

in_thread(name: :bass) do
  loop do
    use_synth :prophet
    play chord(:e2, :m7).choose, release: 0.6
    sleep 0.5
  end
end
in_thread(name: :drums) do
  loop do
    sample :elec_snare
    sleep 1
  end
end

Priveste panoul continand jurnalul cand executi codul acesta. Ai observat ca in jurnal apare numele thread-ului?

[Run 36, Time 4.0, Thread :bass]
 |- synth :prophet, {release: 0.6, note: 47}

Un singur thread poate avea un anumit nume

Un ultim lucru de retinut despre thread-uri este ca un singur thread cu un anumit nume poate rula la un moment dat. Sa exprimentam folosind urmatorul cod:

in_thread do
  loop do
    sample :loop_amen
    sleep sample_duration :loop_amen
  end
end

Incearca sa copiezi codul asta intr-un buffer si apasa pe butonul Run. Apasa din nou de mai multe ori. Asculta cacofonia de amen break care se reiau si se suprapun. Ok, acum apasa pe Stop.

Acesta este comportamentul pe care l-am vazut de mai multe ori - apesi pe Run si sunetele se suprapun peste cele deja existente. Deci daca ai o bucla si apesi pe Run de trei ori, vei avea trei randuri de bucle care sunt redate concomitent.

Totusi, folosind thread-urile cu nume, lucrurile stau diferit:

in_thread(name: :amen) do
  loop do
    sample :loop_amen
    sleep sample_duration :loop_amen
  end
end

Incearca sa apesi butonul Run de mai multe ori pentru acest cod. Vei auzi o singura bucla cu amen break. De asemenea, vei observa asta in jurnal:

==> Skipping thread creation: thread with name :amen already exists.

Sonic Pi iti spune ca exista deja un thread cu numele ‘:amen’ care ruleaza, deci nu va crea un altul.

Acest comportament poate ca nu pare util pentru moment, dar va fi folositor cand vei incepe sa programezi in direct…