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 ineinander flechten könnte? Kann es auch, und zwar mit einem besonderen Ding, welches Thread heißt.
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.
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
Wenn wir die erste Schleife in einen in_thread
-do/end-Block hinein packen, 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!
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 wie 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. Threads können die aktuellen Einstellungen, die für einen Thread gelten, von anderen Threads 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 Aktion 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, aber 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 Threads 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: Es können nicht zwei gleichzeitig laufende Threads den gleichen Namen haben. 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 einen Puffer und klicke den Ausführen
-Button. Klick in noch ein paar mal. Hör Dir diese Kakophonie mehrerer Amen-Breaks an, die rhythmisch 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 weiteren startet.
Vielleicht erscheint Dir dieses Verhalten im Moment noch nicht sinnvoll - aber es wird sehr nützlich sein, wenn wir ins Live-Coding einsteigen…