A useful thing to do in your code is to create names for things. Sonic Pi makes this very easy: you write the name you wish to use, an equal sign (=
), then the thing you want to remember:
sample_name = :loop_amen
Hier haben wir uns das Symbol :loop_amen
mit der Variable sample_name
gemerkt. Wir können nun sample_name
überall da benutzen, wo wir auch loop_amen
benutzen könnten. Zum Beispiel:
sample_name = :loop_amen
sample sample_name
Es gibt drei Hauptgründe, Variablen in Sonic Pi zu nutzen: Bedeutung vermitteln, Wiederholung steuern und Ergebnisse speichern.
Wenn Du Code schreibst, könntest Du denken, dass Du einfach nur dem Computer sagst, was er tun soll - solange der Computer das versteht, ist es okay. Vergiss aber nicht, dass nicht nur ein Computer Deinen Code lesen will. Andere Leute könnten den Code auch lesen und wollen verstehen, was da vor sich geht. Wahrscheinlich wirst Du den Code auch später selbst wieder lesen und möchtest verstehen, was er bedeutet. Obwohl Dir jetzt vielleicht alles offensichtlich erscheint - wahrscheinlich ist es für andere nicht ganz so offensichtlich und vielleicht nicht einmal für Dich in der Zukunft!
Mit Kommentaren hilfst Du anderen, Deinen Code zu verstehen. Und Du kannst sinnvolle Namen für Deine Variablen verwenden. Sie Dir diesen Code an:
sleep 1.7533
Warum steht hier die Zahl 1.7533
. Woher kommt diese Zahl? Was bedeutet sie? Sieh Dir zum Vergleich diesen Code an:
loop_amen_duration = 1.7533
sleep loop_amen_duration
Aha, jetzt ist viel klarer, was 1.7533
bedeutet: Es ist die Dauer des Sample :loop_amen
! Natürlich kannst Du jetzt sagen, warum nicht einfach schreiben:
sleep sample_duration(:loop_amen)
Das ist natürlich auch ein sehr guter Weg, die Absicht hinter dem Code mitzuteilen.
In Deinem Code wirst Du oft Dinge wiederholen, und wenn Du irgendwo etwas ändern willst, musst Du das an vielen Stellen tun. Schau Dir mal diesen Code an:
sample :loop_amen
sleep sample_duration(:loop_amen)
sample :loop_amen, rate: 0.5
sleep sample_duration(:loop_amen, rate: 0.5)
sample :loop_amen
sleep sample_duration(:loop_amen)
Hier machen wir eine ganze Menge mit dem :loop_amen
! Was wäre, wenn Du das Ganze mit einem anderen Loop-Sample wie zum Beispiel :loop_garzul
hören wollten? Wir müssten alle :loop_amen
suchen und mit :loop_garzul
ersetzen. Das mag in Ordnung sein, wenn Du viel Zeit hast - aber was, wenn Du gerade auf einer Bühne stehst? Manchmal hast Du nicht den Luxus, Zeit zu haben - vor allem dann nicht, wenn Du willst, dass die Leute weiter tanzen.
Was wäre, wenn Du den Code so geschrieben hättest:
sample_name = :loop_amen
sample sample_name
sleep sample_duration(sample_name)
sample sample_name, rate: 0.5
sleep sample_duration(sample_name, rate: 0.5)
sample sample_name
sleep sample_duration(sample_name)
Das tut genau dasselbe wie der Code weiter oben (probier es aus). Außerdem bekommen wir die Möglichkeit, durch die Änderung der einen Zeile sample_name = :loop_amen
in sample_name = :loop_garzul
die vielen anderen Stellen durch die Magie der Variablen zu verändern.
Wenn man schließlich die Ergebnisse von irgendwelchen Dingen speichern möchte, dann ist das ebenfalls ein guter Grund dafür, Variablen zu verwenden. Du möchtest vielleicht irgendetwas mit der Dauer eines Sample anstellen:
sd = sample_duration(:loop_amen)
Wir können nun sd
überall da einsetzen, wo wir die Länge von :loop_amen
brauchen.
Noch wichtiger vielleicht: Eine Variable erlaubt es uns, das Ergebnis eines Aufrufs von play
oder sample
zu speichern:
s = play 50, release: 8
Jetzt haben wir s
als Variable eingefangen und gemerkt. Und das erlaubt es uns, einen Synth zu steuern, während er läuft:
s = play 50, release: 8
sleep 2
control s, note: 62
Das nur als kleine Vorschau, wie man Synths steuert. Das schauen wir uns später noch genauer an.
Whilst variables are great for giving things names and capturing the results of things, it is important to know that they should typically only be used locally within a thread. For example, don’t do this:
a = (ring 6, 5, 4, 3, 2, 1)
live_loop :shuffled do
a = a.shuffle
sleep 0.5
end
live_loop :sorted do
a = a.sort
sleep 0.5
puts "sorted: ", a
end
In the above example we assign a ring of numbers to a variable a
and then used it within two separate live_loop
s. In the first live loop every 0.5
s we sort the ring (to (ring 1, 2, 3, 4, 5, 6)
) and then print it out to the log. If you run the code, you’ll find that the printed list is not always sorted!. This may surprise you - especially that sometimes the list is printed as sorted, and sometimes it is not. This is called non-deterministic behaviour and is the result of a rather nasty problem called a race-condition. The problem is due to the fact that the second live loop is also manipulating the list (in this case shuffling it) and by the time the list is printed, sometimes it has just been sorted and sometimes it has just been shuffled. Both live loops are racing to do something different to the same variable and every time round a different loop ‘wins’.
There are two solutions to this. Firstly, don’t use the same variable in multiple live loops or threads. For example, the following code will always print a sorted list as each live loop has its own separate variable:
live_loop :shuffled do
a = (ring 6, 5, 4, 3, 2, 1)
a = a.shuffle
sleep 0.5
end
live_loop :sorted do
a = (ring 6, 5, 4, 3, 2, 1)
a = a.sort
sleep 0.5
puts "sorted: ", a
end
However, sometimes we do want to share things across threads. For example, the current key, BPM, synth etc. In these cases, the solution is to use Sonic Pi’s special thread-safe state system via the fns get
and set
. This is discussed later on in section 10.