Nun hast du also eine Killer-Basslinie und einen krassen Beat gebaut. Wie kannst du beide zur selben Zeit spielen lassen? Eine Möglichkeit ist, beide Sounds per Hand miteinander zu verweben - spiele erst den Bass ein bisschen, dann das Schlagzeug, dann den Bass etwas mehr… Beides zeitlich aufeinander abzustimmen wird jedoch gedanklich bald immer schwieriger, vor allem, wenn noch mehr Klänge dazukommen sollen.
Was, wenn Sonic Pi Klänge automatisch für dich miteinander verweben könnte? Nun, das kann es, und zwar erreichst du das mit einem besonderen Ding, welches Thread (Strang) genannt wird.
Damit dieses 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, 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 auf Stopp 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 Reihenfolgen (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
Indem 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 nächste Anweisung nach dem do/end-Block ausführen (und das ist in diesem Fall die zweite Schleife). Probiere es aus, und du wirst den Beat und die Basslinie miteinander verwoben hören!
Mal angenommen, wir wollten darüber noch einen Synth hinzufügen. 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 auf Ausführen klickst, erzeugst du eigentlich einen neuen Thread, innerhalb dessen der Programm-Code abläuft. Deshalb entstehen immer neue Klangschichten, wenn du wiederholt auf Ausführen klickst. Weil diese Abläufe jeweils für sich Threads sind, werden sie automatisch die Klänge für dich miteinander verweben.
Während du Sonic Pi besser zu meistern lernst, wirst du auch herausfinden, dass Threads die wichtigsten Bausteine für deine Musik sind. Eine der wichtigen Aufgaben die sie ausführen, ist die aktuellen Einstellungen, die für einen Thread gelten, von anderen Threads zu isolieren. Was genau bedeutet das? Nun, wenn du etwa einen Synth mit use_synth
durch einen anderen ersetzt, dann veränderst du den Synth lediglich für den aktuellen Thread - bei keinem anderen der laufenden Threads wird der Synth ersetzt. 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
Bemerke, wie 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 ausführenden Thread.
Wenn du einen neuen Thread mit in_thread
erzeugst, wird der neue Thread alle Einstellungen automatisch vom vorherigen Thread erben. Sehen wir uns das an:
use_synth :tb303
play 50
sleep 1
in_thread do
play 55
end
Achte darauf, dass der zweite Ton mit dem :tb303
-Synth gespielt wird, obwohl er in einem anderen Thread läuft? Jede der Einstellungen, vorgenommen mit den unterschiedlichen use_*
-Ausdrücken, wird sich genauso verhalten.
Wenn neue Threads erzeugt werden, erben sie alle Einstellungen von ihren Eltern. Aber Änderungen der Einstellungen innerhalb dieser neuen Threads haben umgekehrt keinen Einfluss auf die Eltern.
Und schließlich können wir unseren Threads auch 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
Achte auf das Protokoll-Fenster, wenn du diesen Code laufen lässt. Siehst du, wie das Protokoll mit den Nachrichten auch die Namen der Threads ausgibt?
[Run 36, Time 4.0, Thread :bass]
|- synth :prophet, {release: 0.6, note: 47}
Eine letzte Anmerkung zu Threads mit Namen: Es kann nur ein Thread gleichen Namens zur selben Zeit laufen. Probieren wir das aus. Sieh dir 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 auf Ausführen
. Klicke noch ein paar mal darauf. Hör dir diese Kakophonie mehrerer Amen-Breaks an, die rhythmisch nicht unbedingt passend zueinander ablaufen. Ok, du kannst jetzt Stopp klicken.
Dieses Verhalten haben wir bereits öfter gesehen - wenn du die Ausführen
-Schaltfläche klickst, legen sich Klänge über alle bereits laufenden Klänge. Wenn du eine Schleife hast und dreimal auf Ausführen
klickst, bekommst du drei Ebenen mit Schleifen, die gleichzeitig spielen.
Bei benannten Threads jedoch ist das anders:
in_thread(name: :amen) do
loop do
sample :loop_amen
sleep sample_duration :loop_amen
end
end
Versuche bei diesem Code den Ausführen
-Schalter 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 teilt dir mit, dass ein Thread mit dem Namen :amen
bereits läuft und es deshalb keinen weiteren erzeugt.
Vielleicht erscheint dir dieses Verhalten im Moment noch nicht nützlich - aber es wird sehr nützlich sein, wenn wir ins Live-Coding einsteigen …