Algorithmische Komposition mit Common Lisp
Vertiefungen
Exkurs Exponentialfunktionen
Exponentielle accelerandi und ritardandi
.Videobeispiel für ein exponentielles accelerando [#fig:bouncing-ball-video] ./resources/video/bouncing-ball.m4v
.Beispiel für ein exponentielles accelerando
[#fig:bouncing-ball]
Beschleunigungen, wie sie in beim wiederholten Aufschlagen eines selbsttätig springenden Balls auf dem Boden erzeugt werden, sind exponentiell. Exponentiell bedeutet, dass das Zeit/verhältnis/ zweier benachbarter Aufschläge des Balls konstant ist. Im folgenden Beispiel sind die Zeitdifferenzen (Einsatzabstände bzw. Rhythmen) aus der Abbildung und darunter deren Verhältnisse dargestellt:
;; Einsatzabstände eines springenden Balls: 1.000 0.774 0.599 0.464 0.359 0.278 0.215 0.167 0.129 0.1 ;; Zeitverhältnisse benachbarter Einsatzabstände: 0.774/1.000 = 0.77 0.599/0.774 = 0.77 0.464/0.599 = 0.77 0.359/0.464 = 0.77 0.278/0.359 = 0.77 0.215/0.278 = 0.77 0.167/0.215 = 0.77 0.129/0.167 = 0.77 0.100/0.129 = 0.77
Um es allgemein zu formulieren, ist in Clamps die Funktion n-exp
definiert, die eine exponentielle Interpolation von min
nach
max
für einen x
Wertebereich von [0..1]
errechnet. Bei x=0
ist das Ergebnis min
und bei x=1
ist das Ergebnis
max
. Dazwischenliegende Werte für x ergeben Zwischenwerte, die
exponentiell verteilt sind. Exponentiell bedeutet in diesem Fall,
dass für jede beliebige gleichmäßige Teilung der x-Werte von 0 bis
1 gilt, dass das Verhältnis zwischen benachbarten Werten konstant
ist.
(defun n-exp (x min max) (* min (expt (/ max min) x))) ;; Beispiel: ;; ;; min = 0.1, max = 1 ;; ;; x = 0: (n-exp 0 0.1 1) ;; -> 0.1 ;; x = 1: (n-exp 1 0.1 1) ;; -> 1.0 ;; x = 0.5: (n-exp 0.5 0.1 1) ;; -> 0.31622776 ;;; Teilung in 5 exponentiell gleichmäßig verteilte Werte: (loop for x from 0 to 1 by (/ 4) collect (n-exp x 0.1 1)) ;;; => (0.1 0.17782794 0.31622776 0.56234133 1.0) (loop for (x y) on (loop for x from 0 to 1 by (/ 4) collect (n-exp x 0.1 1)) while y collect (/ y x)) ; => (1.7782794 1.7782794 1.7782794 1.7782794) ;;; Teilung in 7 exponentiell gleichmäßig verteilte Werte: (loop for x from 0 to 1 by (/ 6) collect (n-exp x 0.1 1)) ;;; -> (0.1 0.14677994 0.21544348 0.31622776 0.46415886 0.68129206 ;;; 1.0) (loop for (x y) on (loop for x from 0 to 1 by (/ 6) collect (n-exp x 0.1 1)) while y collect (/ y x)) ;;; => (1.4677994 1.4677992 1.4677992 1.4677992 1.4677993 1.4677993) ;;; das Verhältnis zwischen benachbarten Werten direkt errechnen: (let ((min 0.1) (max 1) (anzahl-werte 5)) (expt (/ max min) (/ (1- anzahl-werte)))) ; => 1.7782794 (let ((min 0.1) (max 1) (anzahl-werte 7)) (expt (/ max min) (/ (1- anzahl-werte)))) ; => 1.4677993
Das Beispiel mit dem springenden Ball vom Anfang des Kapitels lässt
sich also mit Hilfe der Funktion n-exp
folgendermaßen
errechnen:
(defparameter *testreihe* (loop for x from 0 to 9 collect (n-exp (/ x 9) 1 0.1))) ;;; -> (1.0 0.7742637 0.59948426 0.4641589 0.35938138 0.27825594 ;;; 0.21544348 0.16681005 0.12915497 0.1) (loop for (x y) on *testreihe* while y collect (/ y x)) ;;; -> (0.7742637 0.7742637 0.7742637 0.7742637 0.7742637 0.77426374 ;;; 0.7742636 0.7742637 0.7742637)
Daraus lässt sich ein common music Prozess formulieren, der ein acc von einem gegebenen Anfangswert zu einem gegebenen Endwert in einer gegebenen Anzahl von Schritten durchführt.
(defun acc (anzahl anfang ende) (process for rhythm in (loop for x from 0 to anzahl collect (n-exp (/ x anzahl) anfang ende)) output (new midi :duration (- rhythm 0.01)) wait rhythm)) (sprout (acc 40 0.5 0.04))