Mal angenommen, Du hast eine Killer-Basslinie und einen krassen Beat gebaut. Wie kannst Du beides zur selben Zeit spielen lassen? Eine Möglichkeit ist es, beide Sounds per Hand ineinander zu weben - erst spielt der Bass ein bisschen, dann das Schlagzeug, dann der Bass etwas mehr… Beides aufeinander zeitlich abzustimmen wird jedoch immer schwieriger, vor allem, wenn noch mehr Sounds dazukommen sollen.
Was, wenn Sonic Pi Sounds automatisch für Dich ineinanderflechten könnte? Kann es auch, und zwar mit einem besonderen Ding, welches Thread heisst.
Damit das nächste Beispiel nicht zu kompliziert wird, musst Du Dir einfach vorstellen, dass dies Deine Killer-Basslinie und Dein krasser Beat sind.
loop do
sample :drum_heavy_kick
sleep 1
end
loop do
use_synth :fm
play 40, release: 0.2
sleep 0.5
end
Wir haben das früher schon besprochen, Endlos-Schleifen sind wie
schwarze Löcher für ein Programm; läuft es einmal in die Schleife
kommt es da nicht mehr raus, bis Du den Stopp
-Button klickst. Wie also
können wir beide Schleifen zur selben Zeit abspielen? Wir müssen Sonic
Pi sagen, dass wir einen bestimmten Abschnitt gleichzeitig mit
dem Rest des Codes starten möchten. Hierbei helfen uns Threads.
Wenn wir die erste Schleife in einen in_thread
-do/end-Block
hineinpacken, sagen wir Sonic Pi, es soll den Inhalt dieses
do/end-Blocks genau zur selben Zeit wie die nachfolgende Anweisung
ausführen. Und das ist in diesem Fall die zweite Schleife. Probier es
aus, und Du wirst den Beat zusammen mit der Basslinie hören!
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
Angenommen, dass wir obendrauf noch einen Synth legen wollten. Ungefähr so:
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
Jetzt haben wir das gleiche Problem wir vorhin. Die erste Schleife wird
durch das in_thread
zur selben Zeit wie die zweite gespielt. Aber
die dritte Schleife wird nie erreicht. Also brauchen wir einen
weiteren 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
Was Dich vielleicht erstaunt: Wenn Du den Ausführen-Button klickst, erzeugst Du tatsächlich einen neuen Thread, innerhalb dessen der Code abläuft. Deshalb entstehen immer neue Soundschichten, wenn Du den Ausführen-Button wiederholt klickst. Weil die Abläufe Threads sind, werden sie automatisch die Sounds verflechten.
Wenn Du Dich besser mit Sonic Pi auskennst, wirst Du herausfinden, dass
Threads die wichtigsten Bausteine für Deine Musik sind. Eine
wesentliche Funktion, die Threads haben, ist es, aktuelle
Einstellungen, die für einen Thread gelten, von anderen Threads zu
isolieren. Was genau bedeutet das? Wenn Du etwa einen Synths mit
use_synth
durch einen anderen austauschst, dann veränderst Du den
Synth lediglich für den aktuellen Thread - kein anderer der laufenden
Threads bekommt den neuen Synth. Sehen wir uns das mal in der Praxis an:
play 50
sleep 1
in_thread do
use_synth :tb303
play 50
end
sleep 1
play 50
Hast Du gehört, dass sich der mittlere Klang von den anderen beiden
unterschieden hat? Die use_synth
-Anweisung hat sich nur auf den Thread
ausgewirkt, in dem sie auch stand und nicht auf den äußeren
Haupt-Thread.
Wenn Du einen neuen Thread mit in_thread
erzeugst, wird dieser vom
vorherigen alle Einstellungen automatisch erben. Sehen wir uns das an:
use_synth :tb303
play 50
sleep 1
in_thread do
play 55
end
Hast Du bemerkt, dass der zweite Ton mit dem :tb303
-Synth gespielt
wird, obwohl er in einem anderen Thread läuft? Jede der Einstellungen,
die mit den unterschiedlichen use_*
-Ausdrücken vorgenommen wird,
verhält sich genauso.
Wenn neue Threads starten, erben sie alle Einstellungen von ihren Eltern. Wenn Du aber Einstellungen innerhalb dieser neuen Threads änderst, haben diese keine Einfluss auf die Eltern-Threads.
Schließlich kannst Du Deinen Treads Namen geben.
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
Sieh Dir das Protokoll-Fenster an, wenn Du diesen Code laufen lässt. Siehst Du, wie die Namen der Threads ausgeben werden?
[Run 36, Time 4.0, Thread :bass]
|- synth :prophet, {release: 0.6, note: 47}
Eine letzte Anmerkungen zu Threads mit Namen: Ein mit einem bestimmten Namen benannter Thread kann zur Zeit nur einmal laufen. Probieren wir das aus. Sieh Dir den folgenden Code an:
in_thread do
loop do
sample :loop_amen
sleep sample_duration :loop_amen
end
end
Kopiere das einmal in ein Arbeitsfenster und klicke den
Ausführen
-Button. Klick in noch ein paarmal. Hör Dir diese Kakophonie
mehrerer Amen Breaks an, die ryhtmisch nicht unbedingt passend
zueinander ablaufen. Ok, jetzt kannst Du auf Stopp
klicken.
Dieses Verhalten konnten wir schon woanders sehen - wenn Du den
Ausführen
-Button klickst, legen sich die neuen Klänge über die schon
laufenden. Wenn eine Schleife abläuft, und Du den Ausführen
-Button
dreimal klickst, bekommst Du drei Ebenen mit Schleifen, die gleichzeitig
ablaufen.
Mit benannten Threads ist das jedoch anders:
in_thread(name: :amen) do
loop do
sample :loop_amen
sleep sample_duration :loop_amen
end
end
Versuche mit diesem Code den Ausführen
-Button mehrmals zu klicken. Du
wirst immer nur eine Amen-Break-Schleife hören. Das kannst Du auch im
Protokoll sehen:
==> Skipping thread creation: thread with name :amen already exists.
Sonic Pi sagt Dir, dass ein Thread mit dem Namen :amen
bereits läuft
und es deshalb keinen neuen startet.
Vielleicht erscheint Dir dieses Verhalten im Moment noch nicht sinnvoll - aber es wird sehr nützlich sein, wenn wir ins Live-Coding einsteigen…
Im Deutschen bedeutet thread Kette, Faden oder Strang. Der Thread ist jedoch für Programmiersprachen eine so typische Idee, dass eine Übersetzung nur insoweit sinnvoll ist, dass Du Dir die Idee, die hinter diesem Begriff steht, besser merken kannst. Weil niemand beim Programmieren über eine Kette oder einen Faden spricht, werden wir hier nur den englischen Begriff benutzen. ↩
Scope bedeutet ins Deutsche übersetzt soviel wie Geltungsbereich. Für die Zukunft ist es aber ganz gut, sich den englischen Begriff zu merken, da auch dieser im Programmierbereich häufig vorkommt. ↩