Algorithmische Komposition mit Common Lisp
Prozesse
Prozesse sind in Common Music Abstraktionen zur Steuerung zeitlicher Abläufe, vergleichbar mit einem Midi-Sequencer der Midi- bzw. Klangereignisse im Zeitverlauf steuert.
In Common Music ist das process Makro der zentrale Baustein, um
zeitliche Ereignisfolgen zu definieren. Dieses Makro hat eine sehr
ähnliche Struktur und Syntax, wie das loop Makro von Common
Lisp. Beide Makros definieren Iterationen, d.h. Wiederholungen,
deren Parameter (Anzahl der Wiederholungen, Variablen,
Rückgabewerte, Abbruchbedingungen, etc.) über spezielle Symbole,
sogenannte Klauseln definiert werden können. Diese Klauseln
ermöglichen sehr mächtige und flexible Steuerungsmechanismen und
bilden eine eigene, in Common Lisp eingebettete Sprache zur
Beschreibung von Iterationen. Es erfordert einige Zeit, mit den
Feinheiten vertraut zu werden, lohnt aber die Mühe und den
Zeitaufwand des Lernens. Neben dem oben verlinkten Kapitel zu
loop
aus dem Sprachstandard (cltl2) gibt es in dem Kapitel Loop
for Black Belts in Peter Seibel's Buch Practical Common Lisp
eine sehr empfehlenswerte Einführung in das loop
Makro 1.
Im process
Makro von Common Music sind über die von loop
bekannten Klauseln hinaus vor allem die Schlüsselwörter output
und wait
wichtig, da sie die Voraussetzung für die zeitlich
strukturierte Ausgabe von Ereignissen bilden. output
erzeugt die
Ausgabe eines Ereignisses und wait
bezeichnet die Zeitdifferenz
zwischen den Iterationen. Die Klausel repeat
gibt die Anzahl der
Iterationen an.
;; Definition eines Prozesses mit zwei Midiereignissen mit 1 Sekunde Zeitabstand (process repeat 2 output (new midi) wait 1)
Das process
Makro definiert dabei lediglich den Prozess, ohne
irgendwelche Ereignisse zu erzeugen. Um die Ereignisse eines
Prozesses tatsächlich auszulösen, werden die Funktionen sprout
oder events
verwendet:
;; Verschiedene Ausgabemöglichkeiten eines Prozesses mit zwei ;; Midiereignissen mit 1 Sekunde Zeitabstand ;;; Echtzeit (rts muss zuvor gestartet worden sein): (sprout (process repeat 2 output (new midi) wait 1)) ;;; Echtzeit unter Verwendung der events funktion: (events (process repeat 2 output (new midi) wait 1) *rts-out*) ;;; Ausgabe in eine Midi-Datei: (events (process repeat 2 output (new midi) wait 1) "/tmp/test.midi") ;;; Ausgabe in eine Lilypond Datei: (events (process repeat 2 output (new midi) wait 1) "/tmp/test.ly")
Die Klausel output
hat eine ähnliche Funktionsweise, wie die
oben beschriebene Funktion output, allerdings eine andere
Syntax: Hinter der Klausel output
muss ein Ausdruck stehen,
dessen Wert ein Ereignis ist. Im Unterschied hierzu steht die
Funktion output
in Klammern und erwartet als Argument einen
Ausdruck, dessen Wert ein Ereignis ist, das ausgegeben werden
kann.
Die Funktion output
kann allerdings in Verbindung mit der do
Klausel auch innerhalb des process
Makros verwendet werden. Das
nachfolgende Beispiel beschreibt den gleichen Prozess wie zuvor
mit Hilfe der Klausel do
und der Funktion output
.
(sprout (process repeat 2 do (output (new midi)) wait 1))
Auch innerhalb eines process
ist es möglich, die Ausgabe von
output
auf einen speziellen Stream/Port umzuleiten. Hierzu dient
die Klausel to
:
;;; Erzeugung eines incudine-streams mit 4-Kanal channel-tuning (make-mt-stream *mt-out01* *midi-out1* '(4 0)) ;;; Umleitung von output in einem process mit der Klausel "to" (sprout (process repeat 2 output (new midi :keynum 60.5) to *mt-out01* wait 1)) ;;; Alternative Lösung mit der Funktion output, der Klausel "do" und ;;; dem Keywort :to: (sprout (process repeat 2 do (output (new midi :keynum 60.5) :to *mt-out01*) wait 1))
Fußnoten:
Es
sollte an dieser Stelle allerdings erwähnt werden, dass process
nicht den vollen Umfang von loop
implementiert. So fehlen
beispielsweise die Iteration über Hash-Tables,
argument-destructuring und anderes mehr.