Threads

Dus je hebt je fatale baslijn en een vette beat. Hoe speel je ze op hetzelfde moment? Een oplossing is deze handmatig verweven- een beetje bas , dan wat drums, wat meer bas… Lang gaat het niet duren eer je je de timing moeilijk kan voorstellen, zeker als je nog meer elementen hierin wil verweven.

Wat als Sonic Pi deze automatisch kan verweven voor jou? Nou, het kan, en je het doet dat met wat heet een thread.

Oneindige Loops

Om het voorbeeld eenvoudig te houden, moet je je voorstellen dat dit een waanzinnige baslijn is en een vette beat:

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

Zoals we reeds eerder bespraken, zijn loops zoals zwarte gaten voor het programma. Zodra je in loop bent geraakt, kan je deze niet verlaten totdat u op stop hebt gedrukt. Hoe spelen we dan deze beide loops op hetzelfde moment? We moeten Sonic Pi vertellen dat we iets op hetzelfde moment als de rest van de code willen beginnen. Dit is waar de threads ons redding brengen.

Threads bieden redding

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

Door de eerste loop in een in_thread do/end block te plaatsen, vertellen we Sonic Pi de inhoud van de do/end block uit te voeren op exact hetzelfde moment als de volgende instructie na de do/end block (toevallig ook onze tweede lus). Probeer het en je zal de baslijn en de drums samen horen spelen!

Wat als we hieraan nog een synth willen toevoegen? Zoiets als:

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

Nu hebben we hetzelfde probleem als hiervoor. De eerste loop wordt gespeeld op hetzelfde moment als de tweede loop, door onze ‘in_thread’. De derde loop wordt weer nooit bereikt. Daarom hebben we een andere thread nodig:

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

Voert uit als threads

Wat je mischien zal verbazen is dat iedere keer je op de afspeel-knop drukt je eigenlijk een nieuwe thread aanmaakt voor de uitvoerende code. Vandaar dat, wanneer je meerdere keren op afspelen drukt, de klanken op elkaar hoort opstapelen. Vermits deze een thread op zich is, verweeft hij deze code voor jou automatisch.

Toepassingsgebied

Als je Sonic Pi leert beheersen, ga je zien dat threads de belangrijkste bouwstenen voor jouw muziek zullen zijn. Eén van de belangrijkste taak die zij hebben is het isoleren van de huidige staat van andere threads. Wat betekend dit? Wel, wanneer je van synth overschakeld door het gebruik van use_synth schakel je van synth in de huidige thread - geen enkele andere thread zal van synth veranderen. Laten we dit in actie zien:

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

Merk je op dat de middelste klank anders is dan de andere? De use_synth declaratie zal enkel invloed hebben op de thread waarin deze zich bevind en niet op de threads daarbuiten.

Erfenis

Wanneer je een nieuwe thread met in_thread creëert, zal de nieuwe thread de instellingen van de huidige thread overnemen. Laten we dit even bekijken :

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

Merk je op hoe de tweede noot met de ‘: tb303’ synth wordt gespeeld, hoewel het door een aparte thread werd aangestuurd? Elk van de met verschillende use_* functies gewijzigde instellingen, zullen zich op dezelfde manier gaan gedragen.

Wannneer threads worden gecreëerd, erven zij alle instellingen over van bovenliggende (parent) maar zij delen geen veranderingen aan hen terug.

Naamgeving aan Threads

Ook nog kunnen wij namen geven aan threads:

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

Kijk naar het log venster wanneer u deze code uitvoert. Zie je hoe het logboek de naam rapporteert van de thread met een bericht?

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

Slechts één thread per naam toegestaan

Wat je nog moet weten over benoemde threads is dat slechts één thread met een bepaalde naam tegelijkertijd kan worden uitgevoerd. Laten we dit onderzoeken met de volgende code:

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

Ga je gang en plak deze in een buffer en druk op de knop afspelen. Druk er opnieuw een paar keer. Luister naar de kakofonie van meerdere amen breaks in loop, uit de maat afspelen . OK, je mag nu op Stop drukken.

Dit soort gedrag hebben we al keer op keer gezien - dat als je de afspeelknop aanklikt, geluidslagen op bestaande klanken gaan stapelen. Dus als je een loop hebt en je klikt drie keer op afspelen zal je drie lagen loops tegelijkertijd horen afspelen.

Met benoemde threads is dat echter anders:

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

Probeer de afspeel-knop meerdere malen met deze code. Je zal nu één enkele amen break loop horen. Ook zie je dit in het logboek:

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

Sonic Pi vertelt je hier dat een thread met de naam :amen al aan het spelen is , en gaat geen andere creëren.

Dit gedrag lijkt misschien niet onmiddellijk bruikbaar voor je nu - maar het zal erg handig worden wanneer we beginnen met het spelen van code live…