Sonic Pi hat einen global Speicherbereich, der “Time State” genannt wird. Die beiden wesentliche Dinge, die man auf ihn an anwendet sind das Setzen von Informationen (Set) und das Holen von Information (Get).
Um Informationen innerhalb des Time States zu speichern, benötigen wir zwei Dinge:
Wir möchten beispielsweise die Zahl 3000
unter dem Schlüsselnamen :intensity
speichern. Dies können wir mit der set
function erreichen:
set :intensity, 3000
Für den Schlüssel können wir jeden Namen verwenden, den wir möchten. Haben wir einmal einen Wert unter diesem Schlüsselnamen gespeichert, wird ein neuer set
-Befehl diesen überschreiben:
set :intensity, 1000
set :intensity, 3000
In dem obigen Beispiel haben wir beide Werte unter dem gleichen Schlüssel abgespeichert. Der letzte Befehl ‘gewinnt’, so dass die Zahl, die mit :intensity
assoziert ist, die 3000
sein wird und somit die erste (1000) damit überschrieben wurde.
Um Informationen aus dem Time-State wieder zu holen, benötigen wir nun unseren Schlüssel, den wir bei set
verwendet haben, also in diesem Fall :intensity
. We müssen jetzt einfach get[:intensity]
aufrufen und bekommen dies im log angezeigt:
print get[:intensity] #=> prints 3000
Achte darauf, dass Aufrufe von get
Informationen zurückgeben kann, die bei einem vorherigem Programm-Lauf gesetzt wurden. Wurde eine Information einmal mit Hilfe von set
gespeichert, ist sie solange verfügbar bis sie entweder überschrieben wird (wie oben die 1000
mit 3000
) oder Sonic Pi beendet wird.
Der wesentliche Vorteil des Time State Systems ist, dass man auf sichere Art und Weise Informationen zwischen Threads und live-loops austauschen kann. Beispielsweise könnte eine live-loop Informationen bereitstellen, die die andere live-loop ausliest:
live_loop :setter do
set :foo, rrand(70, 130)
sleep 1
end
live_loop :getter do
puts get[:foo]
sleep 0.5
end
Das Schöne an get
und set
bei Threads ist, dass sie immer das gleiche Ergebnis produzieren, wenn das Programm erneut gestartet wird. Versuch’s einfach mal selbst und prüfe nach, ob die folgende Ausgabe im Log erscheint:
{run: 0, time: 0.0}
└─ 125.72265625
{run: 0, time: 0.5}
└─ 125.72265625
{run: 0, time: 1.0}
└─ 76.26220703125
{run: 0, time: 1.5}
└─ 76.26220703125
{run: 0, time: 2.0}
└─ 114.93408203125
{run: 0, time: 2.5}
└─ 114.93408203125
{run: 0, time: 3.0}
└─ 75.6048583984375
{run: 0, time: 3.5}
└─ 75.6048583984375
Starte das Programm einfach mal mehrere Male - du wirst sehen, dass das Ergebnis immer das Gleiche ist. Das bezeichnen wir als “deterministisches” Verhalten und das ist sehr wichtig, wenn wir unsere Musik an andere weitergeben möchten und sicher sein wollen, dass die Person, die die Musik anhört, genau das gleiche Ergebnis hören wird wie wir, die es erstellt haben (ähnlich wie das Spielen einer MP3 oder das Streamen von Musik).
In Abschnitt 5.6 haben wir gezeigt, wie die Nutzung von Variablen in verschiedenen Threads zu zufälligen Resultaten führen kann. Das verhindert wiederum die sichere Reproduktion von bestimmten Code wie diesem:
## Ein Beispiel für ein nicht-deterministisches Verhalten
## (aufgrund von Race Conditions, die durch
## mehrfache live-loops ausgelöst werden, die auf
## gleiche Variable zugreifen).
##
## Wenn Du den Code startest, siehst Du,
## dass die Liste nicht immer sortiert ist.
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
Schauen wir mal, wie wir get
und set
einsetzen können:
## Ein Beispiel für ein deterministisches Verhalten
## (trotz des gleichzeitigen Zugriff auf einen gemeinsamen Zustand)
## mit Hilfe des Sonic Pi Time State Systems
##
## Wenn dieser Code gestartet wird,
## wird die Liste immer sortiert ausgegeben.
set :a, (ring 6, 5, 4, 3, 2, 1)
live_loop :shuffled do
set :a, get[:a].shuffle
sleep 0.5
end
live_loop :sorted do
set :a, get[:a].sort
sleep 0.5
puts "sorted: ", get[:a]
end
Beachte, dass dieser Code fast identisch zu dem Code ist, der die Variable zum Teilen der Information verwendet hat. Allerdings verhält er sich so, wie man es sich bei einem typischen Sonic Pi Code erwartet - er verhält sich immer gleich in diesem Fall dank des Time State Systems.
Deshalb verwende immer get
und set
, wenn Du Daten zwischen live-loops und Threads teilen möchtest anstatt Variablen zu verwenden.