7.1 Listen

Eine Liste ist eine Aneinanderreihung von Werten oder Variablen oder einer Mischung von Beidem umschlossen von eckigen Klammern und getrennt durch Komma und sieht beispielsweise so aus:

[50, 55, 60]

Diese Liste enthält 3 Werte. Mit dem Befehl

play [52, 55, 59]

kann man alle Werte gleichzeitig spielen. Diese eine Zeile ersetzt also die drei Zeilen:

play 52
play 55
play 59

Mit einer Liste kann man also schon einmal Tipparbeit sparen. Listenwerte müssen aber keine Zahlen sein. Ich kann auch Variablen vorab einem Wert zuweisen und dann in eine Liste stecken:

eton = 50
play [eton, 60]

Manchmal hat man eine größere Liste und will aus dieser nur einen einzigen Wert benutzen. Dafür gibt es in Sonic Pi eine einfache Möglichkeit. Will man beispielsweise den 3. Wert einer Liste benutzen, dann setzt man in eckigen Klammern eine 2 dahinter:

play [eton, 60, 66, 70][2]

Eigentlich könnte man denken, da gehört eine 3 hin. Aber wir fangen hier bei 0 an. [0] zeigt also auf des erste Element, [1] auf das zweite, [2] auf das dritte und sofort…

Das erscheint auf den ersten Blick etwas umständlich, denn statt

play [eton, 60, 66, 70][2]

hätte man ja auch gleich

play 70

schreiben können. Aber Listen machen immer dann Sinn, wenn die Elemente ohnehin zusammengesammelt wurden und man über eine Variable, zum Beispiel den Zähler in einer Schleife, auf sie zugreifen will.

In der Abbildung rechts ist ein Beispiel zu der Verwendung von Listen zu sehen.

Und hier die dazu passende Erklärung:

Zuerst kommt die Definition von 3 einzelnen Listen. Wir werden erst einmal nur eine davon benutzen, aber gleich alle 3 erzeugen, um sie später zu verwenden. Alle Listen sind gleich aufgebaut und enthalten jeweils 8 Zahlen

bar1_4 = [60, 64, 67, 72, 76, 67, 72, 76]
bar2 = [60, 62, 69, 74, 77, 69, 74, 77]
bar3 = [59, 62, 67, 74, 77, 67, 74, 77]

Und hier startet die erste Schleife, die 2 mal durchlaufen wird:

2.times do

Für die Schleife wird ein Zähler benötigt, um den richtigen Ton aus der Liste auszusuchen. Also wird der Zähler erzeugt und auf null gesetzt. Der Zähler wird genau dort erzeugt, wo er auch benötigt wird. Auf diese Weise muss man nicht erst lange suchen.

iterator = 0

Und hier startet die zweite Schleife, die 8 mal durchlaufen wird:

8.times do

Aus der Liste „bar1_4“ wird ein Ton gespielt. Da der Wert der Variablen „iterator“ im ersten Durchlauf 0 ist, wird der erste Ton der Liste gespielt, also der MIDI-Ton 60.

play bar1_4[iterator]
sleep 0.25

Nach der Pause wird auf der rechten Seite des Gleichheitszeichens eine Addition des Iterators mit der 1 durchgeführt und dann wird das Ergebnis der gleichen Variablen „iterator“ auf der linken Seite wieder zugewiesen.

iterator = iterator +1

Hier ist die innere Schleife zu Ende und Sonic Pi springt zurück zum Beginn der inneren Schleife und macht dort weiter.

end

Hier ist die äußere Schleife zu Ende und Sonic Pi springt zurück zum Beginn der außeren Schleife und durchläuft sie ein zweites Mal.

end

Das Programm spielt also den ersten Takt zweimal. Und der Vorteil einer Liste ist sofort ersichtlich: Da ich einen Zähler vor einer Schleife erzeugen kann und in der Schleife hochzähle, kann ich die einzelnen Elemente über den Index ansprechen. Hätte jedes Element einen einzelnen Namen, beispielsweise „c5“, „e5“ und so weiter, dann kann ich die eben nicht mit einem Index ansprechen, sondern nur mit ihrem Variablennamen.

Das Beispiel – übrigens der Anfang eines Stückes von Bach – sieht für 3 Takte aus wie dargestellt in Code 13.
Insgesamt werden in dem Beispiel 8*2*4 = 64 Töne gespielt.

Natürlich taucht hier die Frage auf, ob man die Listen und Schleifen auch anders aufbauen hätte können, und die Antwort ist ja. Aber der aktuelle Aufbau hat schon einige Vorteile.

1. Es ist immer gut, wenn sich die Wirklichkeit, und der zugehörige Code im Aufbau gleichen, oder zumindest ähneln. Das ist tatsächlich relativ oft schon automatisch der Fall. Und auch der Aufbau des Musikstückes und des Codes ist ähnlich. Pro Takt im Stück gibt es genau eine Liste.

2. Während die innere Schleife einfach durch alle einzelnen Noten durchgeht, spiegelt die äußere Schleife , die ja 2mal durchlaufen wird, die Wiederholung der jeweils 8 Noten im Musikstück wieder. Damit spiegelt nicht nur die Liste, sondern auch der Schleifenaufbau das Musikstück wieder.

3. Nach jedem Loop-Block ändert sich etwas in den Tönen. Wäre das nicht der Fall, dann wäre es besser gewesen, mehrere Blöcke zu einem zusammenzufassen.

Eine Liste kann in Sonic Pi Zahlen enthalten, genau wie im letzten Beispiel. Sie kann aber auch Text, also Strings enthalten. Im folgenden Beispiel soll der Sound in jedem Schleifendurchlauf geändert werde.

Dafür habe ich ich 4 Sounds in eine Liste gepackt und ändere dann mit dem Befehl „use_synth“ innerhalb einer zusätzlichen Schleife den Sound. Das Beispiel ist in 21 rechts zu sehen.

Die Einzelheiten können wir gemeinsam noch einmal in Code 14 durchgehen.
Der Code startet mit einem Befehl, der etwas abseits vom Thema liegt:

use_debug false

Damit wird Sonic pi angewiesen, die automatische Ausgabe im Log-Viewer zu unterbinden. Es wird nur ausgegeben, was mit „puts“ explizit angewiesen wird.

sound = [:piano, :fm, :pulse, :prophet]

Die Liste enthält also 4 Sounds, schon zusammen mit dem Doppelpunkt. Denn den benötige ich bei der Verwendung der „use_synth“ sowieso.

iSound = 0

Hier wird einem Zähler der Startwert 0 zugewiesen.

use_synth sound[iSound]
puts iSound
puts sound[iSound]

Zuerst wird aus der Liste der Sound ausgewählt, der an der iSound-Stelle steht, wobei wie immer die Liste bei 0 beginnt. Zur Kontrolle wird dann der Zähler selbst und anschließend der Sound ausgegeben.

iSound = iSound + 1

Und dann fast am Ende das Erhöhen den Zählers um den Wert 1.
Der gesamte Code ist in Code 14 zu sehen.

################################
# Lists and Loops
# Hans Gruendel
# 10.4.2016
################################use_synth :pianobar1_4 = [60, 64, 67, 72, 76, 67, 72, 76]
bar2 = [60, 62, 69, 74, 77, 69, 74, 77]
bar3 = [59, 62, 67, 74, 77, 67, 74, 77]# loop 1
2.times do
iterator = 0
8.times do
play bar1_4[iterator]
sleep 0.25
iterator = iterator +1
end
end

# loop 2
2.times do
iterator = 0
8.times do
play bar2[iterator]
sleep 0.25
iterator = iterator +1
end
end

# loop 3
2.times do
iterator = 0
8.times do
play bar3[iterator]
sleep 0.25
iterator = iterator +1
end
end

# loop 4
2.times do
iterator = 0
8.times do
play bar1_4[iterator]
sleep 0.25
iterator = iterator +1
end
end
Code 13. Listen und Loops

lists_2

 

 

 

use_debug false

sound = [:piano, :fm, :pulse, :prophet]

bar1_4 = [60, 64, 67, 72, 76, 67, 72, 76]

iSound = 0
4.times do
use_synth sound[iSound]
puts iSound
puts sound[iSound]
2.times do
iterator = 0
8.times do
play bar1_4[iterator]
sleep 0.25
iterator = iterator +1
end
end
iSound = iSound + 1
end
Code 14. Listen und Loops II

lists_1