Next: Ausgabe in eine svg Datei und Abspielen mit dem Browser , Previous: Erste Version des Codes für das gesamte Stück , Up: Steve Reich: Piano Phase , Home: Allgemeine Einführung

Algorithmische Komposition mit Common Lisp

Crescendo und Decrescendo

Für die Implementierung von crescendo und decrescendo wird eine Funktion get-amp-values definiert, die als erstes Argument die Werte :cersc, :decresc, oder nil und als zweites Argument die anzahl von Noten, über die eine Lautstärkeänderung bei Bedarf generiert werden soll. Die Funktion liefert den Anfangswert der Amplitude in dB und den Wert für die Amplitudenänderung zwischen benachbarten Tönen zurück.

(defun get-amp-values (ampchange num-notes)
  (case ampchange
    (:cresc (list -26 (/ 20.0 (1- num-notes))))
    (:decresc (list -6 (/ -20.0 (1- num-notes))))
    (otherwise (list -6 0.0))))


Asnchließend muss die Funktion shift-collect-pno-events mit der Funktion amp-shift-collect-pno-events um die Amplitudenanpassung erweitert werden. Auch die Funktionen zum Erstellen der Section und Part Events werden entsprechend angepasst um umbenannt.

(defun amp-shift-collect-pno-events (pattern num-repeats start-time dtime start-shift shift pan amp)
  (let* ((pattern-length (length pattern))
         (num-notes-no-shift (* pattern-length num-repeats))
         (num-notes-shift (+ shift num-notes-no-shift))
         (real-dtime (* dtime (/ num-notes-no-shift num-notes-shift))))
    (loop
      for i below num-notes-shift
      with (start-amp delta-amp) = (get-amp-values amp num-notes-shift)
      for curr-amp = start-amp then (+ curr-amp delta-amp)
      collect (new sfz
                :time (+ start-time (* i real-dtime))
                :keynum (elt pattern (mod (+ i start-shift) pattern-length))
                :duration dtime
                :amplitude curr-amp
                :preset :yamaha-grand-piano
                :pan pan))))

(defun amp-rest-shift-collect-section-events (patterns num-repeats start-time dtime start-shifts shifts amps)
  (let ((amps (if amps amps '(nil nil))))
    (loop
      for pattern in patterns
      for start-shift in start-shifts
      for shift in shifts
      for amp in amps
      for pan from 0 by (/ 1 (1- (length shifts)))
      append (if shift (amp-shift-collect-pno-events pattern num-repeats start-time dtime start-shift shift pan amp)))))

(defun amp-rest-collect-part-events (part patterns dtime start)
  "Collect all events of one part of the piece."
  (loop
    for start-time = start then (+ start-time section-duration)
    for start-shifts = '(0 0) then (rest-calc-shift start-shifts shifts)
    for (num-repeats shifts amps) in part
    for section-duration = (section-duration num-repeats (first patterns) dtime)
    append (amp-rest-shift-collect-section-events patterns num-repeats start-time dtime start-shifts shifts amps)))

Zusätzlich definieren wir eine Funktion expand-patterns, die aus den Patterns der Partitur eine Liste von zwei Patterns macht, auch wenn in der Partitur der Patterns in einem Teil nur ein Pattern notiert ist:

(defun expand-pattern (pattern)
  (if (numberp (first pattern)) (list pattern pattern) pattern))

Abschießend nun die Funktion, die Piano Phase komplett mit crescendi und decrescendi spielt. Die Funktionsargumente sind optional und dadurch bei Aufruf der Funktion nicht erforderlich.

(defun piano-phase-amp (&optional (score (get-real-score *piano-phase-score*))
                             (patterns *piano-phase-patterns*) (tempo '(3/8 72)))
  "Return a list of all sfz instances for Piano Phase with given /score/,
/patterns/ and /tempo/."
  (loop
    with dtime = (get-duration 1/16 tempo)
    for start-time = 0 then (+ start-time part-duration)
    for part in score
    for pattern in patterns
    for expanded-pattern = (expand-pattern pattern)
    for part-duration = (part-duration part pattern dtime)
    append (amp-rest-collect-part-events part expanded-pattern dtime start-time)))

;;; Beispiel für einen Aufruf, der die gesamte Komposition über die
;;; Audioausgänge ausgibt:

(sprout (piano-phase-amp))