13 Algorithms and Processes

Example 13-1. The strum1 definition.

(define (strum1 key1 key2 rate dur amp)
  (let ((step (if (< key2 key1) -1 1))
        (diff (abs (- key2 key1))))
    
    (loop repeat (+ diff 1)
          for key from key1 by step
          for beg from 0 by rate
          collect (new midi :time beg 
                       :duration dur
                       :amplitude amp
                       :keynum key))))

Example 13-2. Converting the strum algorithm into a process description.

(define (strum2 key1 key2 rate dur amp)
  (let ((step (if (< key2 key1) -1 1))
        (diff (abs (- key2 key1))))
    (process repeat (+ diff 1)
             for key from key1 by step
             for beg from 0 by rate
             output (new midi :time beg 
                         :duration dur
                         :amplitude amp 
                         :keynum key))))

Interaction 13-1. Using the strum2 process.

cm> (events (strum2 48 60 .1 1 .4) "strum.mid")
"strum-1.mid"
cm>

Example 13-3. A process with changing run times.

(define (strum3 key1 key2 rate dur amp)
  (let ((step (if (< key2 key1) -1 1))
        (diff (abs (- key2 key1))))
    (process repeat (+ diff 1)
             for key from key1 by step
             for beg = (now)
             output (new midi :time beg 
                         :duration dur
                         :amplitude amp  
                         :keynum key)
             wait rate)))

Interaction 13-2. Listening to strum3.

cm> (events (strum3 84 72 .1 1 .4) "strum.mid")
"strum-2.mid"
cm> (events (list (strum3 48 60 .1 1 .4)
                  (strum3 84 72 .1 1 .2))
            "strum.mid")
"strum-3.mid"
cm>

Example 13-4. Helper Functions from Chapter 6 and 8.

(define (chance? prob)
  (< (random 1.0) prob))

(define (keynum->pc k)
  (mod k 12))

(define (retrograde-row row)
  (reverse row))

(define (transpose-row row to)
  (loop for pc in row 
        collect (keynum->pc (+ pc to))))

(define (invert-row row)
  (loop for pc in row 
        collect (keynum->pc (- 12 pc))))

Example 13-5. Basic row playing.

(define row1 '(0 1 6 7 10 11 5 4 3 9 2 8))
(define row2 (invert-row row1))
(define row3 (retrograde-row row1))
(define row4 (retrograde-row row2))

(define (ttone1 reps row key beat amp)
  (process with len = (length row)
           repeat reps
           for i from 0
           for p = (mod i len)
           for pc = (list-ref row p) ; i mod 12
           for k = (+ key pc)
           output (new midi :time (now)
                       :duration (* beat 2)
                       :keynum k
                       :amplitude amp)
           wait beat))

Interaction 13-3. Listening to ttone1

cm> (events (ttone1 36 row1 60 .2 .5) 
            "ttone.mid")
"ttone-1.mid"
cm>

Example 13-6. A pointillistic row.

(define (ttone2 len row key beat amp)
  (process repeat len
           for i from 0
           for pc = (list-ref row (mod i 12))
           for n = (if (chance? .5)
                     (+ key 12)
                     (- key 12))
           for k = (+ n pc)
           output (new midi :time (now)
                       :duration (* beat 2)
                       :keynum k
                       :amplitude amp)
           wait beat))

Interaction 13-4. Listening to ttone2.

cm> (events (ttone2 36 row1 60 .2 .5) 
            "ttone.mid")
"ttone-2.mid"
cm>

Example 13-7. A pointillistic row with random waits.

(define (ttone3 len row key beat amp)
  (process repeat len
           for i from 0
           for pc = (list-ref row (mod i 12))
           for w = (* beat (random 4))
           for n = (if (= w 0)
                     (if (chance? .5)
                       (+ key 12)
                       (- key 12))
                     key)
           for k = (+ n pc)
           output (new midi :time (now)
                       :duration (* beat 2)
                       :keynum k
                       :amplitude amp)
           wait w))

Interaction 13-5. Listening to ttone3

cm> (events (list (ttone3 36 row1 40 .2 .5)
                  (ttone3 36 row1 60 .2 .5))
            "ttone.mid"
            '(0 4))
"ttone-3.mid"
cm>

Example 13-8. Defining sections and instrumentation.

(define (ttone4 dur row key beat amp chan)
  (process while (< (now) dur)
           for i from 0
           for pc = (list-ref row (mod i 12))
           for r = (* beat (random 4))
           for n = (if (= r 0)
                     (if (chance? .5)
                       (+ key 12)
                       (- key 12))
                     key)
           for k = (+ n pc)
           output (new midi :time (now)
                       :duration (* beat 2)
                       :keynum k
                       :amplitude amp
                       :channel chan)
           wait r))

Interaction 13-6. Playing a little 12-tone etude.

cm> (events (list (ttone4 16 row1 40 .2 .5 0)
                  (ttone4 12 row2 60 .2 .5 1)
                  (ttone4 8 row3 80 .2 .5 2))
            "ttone.mid"
            '(0 4 8))
"ttone-4.mid"
cm>

Example 13-9. Helper functions for ghosts.

(define (pick-list lst)
  (let* ((len (length lst))
         (pos (random len)))
    (list-ref lst pos)))

(define (pick-range low high)
  (let ((rng (- high low)))
    (if (> rng 0) 
      (+ low (random rng))
      0)))

(define (bpm->seconds bpm)
  (/ 60.0 bpm))

(define (rhythm->seconds rhy tempo)
  (* rhy 4.0 (bpm->seconds tempo)))

Example 13-10. Defining gestures for ghosts.

(define (hitone knum at) 
  ;; create a long tone two octave above knum
  (new midi :time at
       :keynum (+ knum 24)
       :duration at
       :amplitude .5))

(define (thump knum at)
  ;; make two percussive events below knum
  (list (new midi :time at
             :keynum (- knum 18)
             :duration .05 :amplitude .4)
        (new midi :time at
             :keynum (- knum 23)
             :duration .05 :amplitude .4)))

(define (riff knum rhy)
  ;; generate an upward strum of notes
  (let ((rate (rhythm->seconds (/ rhy 4) 60)))
    (process repeat 5
             for k from (+ 39 (mod knum 13)) by 13
             output 
             (new midi :time (now) :keynum k 
                  :amplitude .3 :duration 10)
             wait rate)))

Interaction 13-7. A sample thump and strum.

cm> (events (hitone 60 0) "thump.mid")
"thump-1.mid"
cm> (events (thump 60 0) "thump.mid")
"thump-2.mid"
cm> (events (riff 60 1/8) "riff.mid")
"riff-1.mid"
cm>

Example 13-11. The ghosts process.

(define (ghosts)
  (process repeat 12
           for here = (now)
           for ahead = (* (+ here .5) 2)
           for main = (pick-range 53 77)
           for high? = (>= main 65)
           for amp = (if high? .6 .4)
           for rhy = (pick-list '(1/16 1/8 3/16))
           ;; output main melody
           output (new midi :time here
                       :keynum main
                       :duration (rhythm->seconds rhy 60 ) 
                       :amplitude amp)
           when high?
           sprout (hitone main ahead)
           and sprout (riff main rhy) at (* ahead 2)
           when (= rhy 3/16)
           sprout (thump main (+ here .5))  
           wait (rhythm->seconds rhy 60 )))

Interaction 13-8. Listening to ghosts.

cm> (events (ghosts ) "ghosts.mid")
"ghosts-1.mid"
cm> (events (ghosts ) "ghosts.mid")
"ghosts-2.mid"
cm>

Chapter Source Code

The source code to all of the examples and interactions in this chapter can be found in the file processes.cm located in the same directory as the HTML file for this chapter. The source file can be edited in a text editor or evaluated inside the Common Music application.