Algorithmic Composition

generative music systems

don’t flinch

leave a comment »

Essentially, “don’t flinch” for guitar and computer is a three-part mensural
canon, but similar to late Mediaeval and Renaissance isorhythmic techniques,
melodic material is repeated along with a rhythmic sequence of differing
length. This is one of the simplest algorithms for a piece I’ve ever written,
so I thought it would make a good candidate for my first blog post on the
topic.

A basic 13-note scale ranging over an octave and a minor 6th (i.e. not an
octave repeating scale) is mapped over the full MIDI range starting from C0
(middle C being C4). With the guitar beginning in the third octave, an (also)
13-note melodic pattern is repeated 18 times, each time starting on a different
note. The transposition is determined by an offset curve mapped over the
duration of the piece and generally, but not linearly, moving from the lowest
to the highest registers of the guitar.

The same process runs in two other voices: a higher keyboard type voice, two
octaves higher, and moving four times as fast; and a bass line, moving at the
same speed as the guitar but in a lower octave and with accompanying chords.

Each voice also selects or rejects notes from the sequence according to a
fixed-seed random procedure controlled by a curve. Each voice uses the same
curve, which begins with 100% note inclusion, moves non-linearly down to 40% at
70% through the piece, and back up to 100% at the end. This, along with the
additive and subtractive rhythmic techniques (see below), accounts for the
transition from unbroken continuity to rather long stretches of short
punctuations or phrases separated by silence.

A voice consists of single notes or two-note chords (diads). The frequency of
the diads is controlled by a double cycle of lengths 17 and 19. This means that
if the note count is a multiple of either of these two numbers, a diad will be
created instead of a single note. The choice of note is determined by the same
pitch selection algorithm as for the guitar, but with the degree pattern
reversed and wrapped by one element e.g. wrap 1 2 3 4 5 6 7 8 9 10 by 1 –> 2 3
4 5 6 7 8 9 10 1.

Rhythms

Unlike a lot of contemporary music, including my own earlier work, I’m working
more and more these days with extremely simple rhythms, often involving nothing
more complex than a triplet. The general aim in doing this is to invest my
music with a renewed sense of pulse and perhaps even meter, though meter plays
no significant role in this piece. A driving pulse is perhaps also not so
evident here but the techniques used lead to the much more metrically and pulse
driven music found in, for example, “you are coming into us who cannot
withstand you”, for eight piece ensemble, from 2011.

The rhythms are derived from a six-element sequence repeated as many times as
necessary to accommodate the number of melodic sequences. Rhythmic values are
modified by an arithmetic procedure which adds or subtracts a 1/16th note to
various rhythms in the list depending on which part of the piece we’re in; this
contributes to the transition from unbroken to broken rhythmic flow as
described above. In addition, the three voices generated here use the rhythms
1) unaltered (guitar); 2) reversed (bass and chords); 3) wrapped by 4 (high
keyboard).

Chords

The bass line is rhythmically doubled by mainly three- to four-note harmonies,
extending to a six notes by the end. Chords are selected according to a simple
Lindenmayer sequence of eight rules/chords. In keeping with my obsession with
transition, each chord is presented in two variations, the second of which is
more cluster-like than the first. Over the course of the piece there is a
move from more open to more closed, cluster-based harmonies.

Post-generation modifications

After generating the piece and reading the MIDI file into Sibelius to create
the score, the normal guitar pitches were modified according to my goal of
using a bottleneck and other extended techniques. There is an overall
transition, though again not linear, from using the bottleneck continuously to
not using it at all. This is accomplished on a note-by-note basis using my
Fibonacci transition algorithm; this essentially splices in and swaps new
event types for old. A similar procedure was used to move from non-tremolo to
tremolo plucking techniques.

In addition, extended techniques for the guitar were added ad libitum,
including different plucking positions, from near the bridge to on the
fingerboard; tremolo with and without a plectrum; glissandi; exaggerated
vibrato with and without the bottleneck; various single and double harmonics;
pitch bends; and string rattles created by delicately touching a vibrating open
string with either the fingernail or the bottleneck.

The bottleneck sound was utmost in my mind from the very beginning of working
on this piece. I have a very strong and fond memory of watching Ry Cooder play
the guitar with a bottleneck on the now defunct UK TV music show “The Old Grey
Whistle Test” when I was about three or four years old. The sound of this has
remained with me my whole life and is strongly associated with the guitar for
me personally. Its connection to old blues recordings was further strengthened
during the composition of this piece when Yvonne Zehner, for whom it was
written, sent me ad hoc recordings she’d made of these techniques on a standard
PC. The poor quality of the PC’s soundcard, with all its crackles and band
limited spectral content, led to a recording that sounded not unlike blues
records from the 1930s, despite the fact that they were made in 2011 (no
criticism of Yvonne intended: it’s the sound card that’s at fault).

Use of the computer in performance

Viewed historically, this is essentially an instrument-plus-tape piece. The
computer is used only to trigger stereo sound files, sometimes at the push
of a pedal, other times once the onset of a guitar note is detected (but after
a gate is opened with another pedal, in order to avoid false triggerings).

The piece is definitely out of the ordinary in having what is essentially a
conventionally notatable computer part. Most electronic accompaniments in
pieces of this kind consist mainly of sounds that could only be made– perhaps
especially rhythmically–with computers or other electronic equipment. I was
attracted in this piece to the idea of creating an almost acoustic instrumental
trio, but having the luxury of continuously modifying, refining, and spectrally
shaping two of the voices through digital production techniques.

I will be curious to see how chamber music-like Yvonne’s approach to the piece
is. Ideally I would imagine her playing with the computer much in the same way
she would with other musicians. Naturally, I’m aware that the computer here
offers no sophisticated score following techniques which would allow tempo
fluctuations etc. I did investigate score following, but in order to take real
advantage of this I would have had to use MIDI sequences driving a sampler or
synthesiser. I know that I wouldn’t have been content with the output of these
without severe modifications and the cross fading of many different sound banks
per contrapuntal voice. So I elected instead to generate as many different
versions of each voice as I needed and then process them much as I would any
other audio data. The cutting of the final mix into shorter sound files that
are then triggered by Yvonne during the performance seemed to be the best
solution given my aims, and the desire for temporal flexibility at particular
points.

The two main voices in the computer then i.e. the high keyboard and the bass
line with chords, were generated with standard samples and software
synthesisers from Native Instruments, and the Ultra Analog VA-1 software
synthesiser by Applied Acoustics Systems. In addition, several sound files were
mixed in, including a recording of myself improvising on tenor saxophone;
myself reciting Rich’s poem; recordings of sheep; and Artaud’s “Pour en finir
avec le jugement de dieu”. The latter was used purely for its sonic and not its
semantic content.

dont flinch: score
(Recording to follow)

Common Lisp code

If you’re fluent in Lisp the following might be useful. To be run it requires
modules and classes from my algorithmic composition package “slippery chicken”
(to be released soon, I hope) but reading the comments and code should give a
good idea of how the piece was made.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; File:             yvonne.lsp
;;;
;;; Project:          don't flinch: composition for guitar and computer
;;;
;;; Purpose:          Code for generation of score
;;;
;;; Author:           Michael Edwards: m@michael-edwards.org
;;;
;;; Creation date:    August 2010 (Edinburgh)
;;;
;;; $$ Last modified: 15:48:30 Thu Jun 30 2011 BST
;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;; The title is lifted from a poem by Adrienne Rich:
;;; Don't Flinch
;;; Adrienne Rich
;;; Granta 111, 2010
;;; 
;;; Lichen-green lines of shingle pulsate and waver
;;; when you lift your eyes. It's the glare.  Don't flinch
;;; The news you were reading
;;; (who tramples whom) is antique
;;; and on the death pages you've seen already
;;; worms doing their normal work
;;; on the life that was: the chewers chewing
;;; at a sensuality that wrestled doom
;;; an anger steeped in love they can't
;;; even taste. How could this still
;;; shock or sicken you?  Friends go missing, mute
;;; nameless.. Toss
;;; the paper.  Reach again
;;; for the Iliad.  The lines
;;; pulse into sense.  Turn up the music
;;; Now do you hear it? can you smell smoke
;;; under the near shingles?

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; the slippery-chicken guitar object
(defparameter +yvonne-guitar+
  (make-instrument 
   'guitar :staff-name "guitar" :lowest-written 'e3 
   :highest-written 'b6 :chords t :midi-program 28 :starting-clef 'treble 
   :subset-id 'guitar :transposition-semitones -12 :microtones nil
   :chord-function 'guitar-chord-selection-fun :largest-fast-leap 31))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Use a Lindenmayer System to create the intervallic structure (in semitones)
;;; of chords above a given bass note.
(defun get-yvonne-chord-seq (num)
  (let ((lfl (make-l-for-lookup 
              'yvonne-chord-seq
              ;; what the rules keys will actually translate to in terms of
              ;; interval structure. We progress from one list to another over
              ;; the course of the lookup procedure, so we tend towards more
              ;; cluster-like material.
              '((1 (((1 3 4)) ((1 2 3 4))))
                (2 (((1 2 4)) ((1 2 4 5))))
                (3 (((2 3 7)) ((2 3 5 6 7))))
                (4 (((3 4 6)) ((3 4 5 6))))
                (5 (((2 5 6)) ((2 4 5 6))))
                (6 (((3 5 6 8)) ((3 4 5 6 7 8))))
                (7 (((1 2 4 6)) ((1 2 3 4 5 6))))
                (8 (((1 3 4 6)) ((1 2 3 4 5 6)))))
              ;; the l-sys rules
              '((1 (4 5))
                (2 (3 6))
                (3 (4 2 6))
                (4 (1 8))
                (5 (7 4))
                (6 (8))
                (7 (3))
                (8 (5 2))))))
    (do-lookup lfl 1 (round num))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; The main function.  This generates a MIDI file with three voices and four
;;; channels: the guitar (channel 1), the keyboard (channel 3), and the bass
;;; (channel 2) with the optional (but selected for don't flinch) chords
;;; (channel 4).  See the let at the very bottom of the function where the
;;; voice local function is called).
;;; 
;;; <num-seqs> is the number of times we loop through the degree-pattern 'tune'
;;; -- 18 in don't flinch
;;;
;;; <scale-structure> has a default set of pitches but we actually use
;;; +yvonne-scale+ (defined below) for don't flinch.
;;;
;;; <degree-pattern> is the melodic structure that is repeated num-seqs times;
;;; numbers here refer to step-by-step moves up and down the scale, using
;;; <start-octave> and <offset-curve>.  See sc-scale.lsp::sc-scale-note for
;;; details, but basically we start with the C in the <start-octave> and move
;;; up/down from there.  We start with C no matter what the 'tonic' of the
;;; scale is because it's the octave that's important in determining tessitura.
;;; <rhythms>.  We don't change this 13-note sequence for don't-flinch.
;;;
;;; <rhythms> is the 6-element sequence we cycle around (also not changed in
;;; don't flinch) as many times as necessary to accommodate <num-seqs> of
;;; <degree-patterns>.  These are modified however by an additive procedure
;;; which adds or subtracts a 1/16th note depending on which part of the
;;; 'piece' we're in (see the portion algorithm below).  In addition, the three
;;; voices generated here use the rhythms 1) unaltered 2) reversed 3) wrapped
;;; by 4 e.g. (wrap-list '(1 2 3 4 5 6 7 8 9 10) 4) -> (5 6 7 8 9 10 1 2 3
;;; 4). NB The latter's wrapping is independent of the <wrap> key argument.
;;;
;;; <offset-curve> (which is modified in the call which generates don't flinch)
;;; specifies in degrees the transposition of the degree-pattern.  This offset
;;; is calculated once per sequence, not on a note-by-note basis.  So we can
;;; create tessitura curves easily.
;;;
;;; <inclusion-curve> determines the percentage chance of a note being included
;;; or not. 100 means will be included, 0 not.  So this introduces randomness
;;; into the equation, but in order to get repeatable results we use a fixed
;;; seed random generator.
;;;
;;; <start-octave> is the pitch octave in which the pitch selection algorithm
;;; will begin.
;;;
;;; <min-amp> and <max-amp> are values between which random amplitudes will be
;;; selected (merely to liven up the playback of the MIDI file).
;;;
;;; <chord-intervals> selects the cycle of notes before a 2-note chord is
;;; selected in a generated part.  The default is changed in don't flinch.  If
;;; the note count is a cycle of one of these numbers (mod count x) == 0 then a
;;; chord will be generated.  NB After 2/3 of the piece we increase the
;;; number of 2-note chords by decrementing the numbers of this list until we
;;; hit <chord-int-min>.
;;;
;;; <wrap> After generating the complete part, should we start somewhere in the
;;; middle, loop back to the beginning and up to the start point?  If so, give
;;; a portion here e.g. 1/3.  NB This was experimented with but not actually
;;; used in don't flinch.
;;; 
;;; <chord-int-min> we'll reduce the chord-intervals as we progress but don't
;;; go less than this number (see <chord-intervals> above).

(defun yvonne (num-seqs midi-file &key 
               (tempo 60)
               (scale-structure '(d e gs as d ef g a bf cs d ef gf))
               ;; the repeating 'melodic' pattern: 13 notes
               (degree-pattern 
                '(-1 2 4    3 6 -2 -1 2 6    7 3 6 2))
               (rhythms '(q w+e q. h. h+s e.+w.))
               (offset-curve '(0 0 30 6 50 10 60 8 80 3 90 16 
                               100 17))
               (inclusion-curve '(0 100 100 100))
               (start-octave 3)
               (min-amp 0.5)
               (max-amp 0.65)
               ;; play a 2-note chord in cycles of these numbers
               (chord-intervals '(14 8))
               (wrap nil)
               (print-chords nil)
               (chord-int-min 4))
  ;; reset the fixed-seed random generator
  (random-rep 100 t)
  (let* ((scale (make-sc-scale 'c0 scale-structure))
         (lowest-degree (loop for deg in degree-pattern minimize deg))
         (rthms nil)
         (offset-curve-stretched (new-lastx offset-curve num-seqs))
         (highest-pitch nil)
         ;; get the transposition we'll need to fit the pattern into the lowest
         ;; notes possible 
         (initial-offset 
          (loop for offset from 0 
             for note = (sc-scale-note scale (+ offset lowest-degree)
                                       start-octave)
             do
             (when (in-range +yvonne-guitar+ note)
               ;; so we can get higher notes below
               (setf highest-pitch note)
               (format t "~&Lowest note will be ~a" (data note))
               (return offset))))
         ;; this will hold the number of pitches the get-pitches function
         ;; generated
         (num-pitches 0)
         ;; the basic unit used for additive/subtractive rhythms
         (add-rthm (make-rhythm 's)))
    (labels 
        ;; get all the pitches for a complete part with a sub-list for each
        ;; sequence. The interp-scaler stretches the offset so that the pitch
        ;; offset curve for the whole piece becomes more exaggerated.
        ((get-pitches (pattern &optional (offset 0) (interp-scaler 1.0)
                               (num-passes 1))
           ;; floating point so arithmetic is good in voice call
           (setf num-pitches 0.0)
           (loop for i below num-seqs by (/ num-passes)
              for coffset = (round (interpolate i offset-curve-stretched
                                                :scaler interp-scaler))
              collect
              (loop for degree in pattern and count from 0 
                 do (incf num-pitches)
                 collect
                 ;; so the offset is degrees, not semitones, and is a
                 ;; combination, in order, of the overall offset for this
                 ;; voice, the offset derived from the main offset curve, the
                 ;; offset calculated to make the lowest sequence fit onto the
                 ;; guitar, and the current degree.
                 (sc-scale-note scale (+ offset coffset initial-offset degree)
                                start-octave))))
         ;; depending on 2nd arg, this adds or subtracts <add-rthm> (1/16th) to
         ;; one of the rhythms in our list 
         (additive (index add &optional print)
           (let ((rthm (get-nth index rthms)))
             ;; min dur of add-rthm (1/16th)
             (when (or add (> (duration rthm) (duration add-rthm)))
               (let ((new (arithmetic rthm add-rthm (if add #'+ #'-) t)))
                 (if new
                     (progn 
                       (set-nth index new rthms)
                       (when print 
                         (format t "~&~a ~a"
                                 (if add "+" "-") (duration new))))
                     (error "can't do additive (dur ~a, add ~a)" 
                            (duration rthm) add))))))
         ;; return t if seq is a certain proportion through the total
         ;; i.e. between start and stop fractions (0->1) NB determines where we
         ;; are in the piece according to sequence count, not note count.
         (portion (seq start num-passes &optional (stop 1) print)
           (let* ((fraction (/ seq (* num-passes num-seqs)))
                  (result (and (>= fraction start) (<= fraction stop))))
             (when print
               (format t "~&seq ~a start ~a stop ~a fraction ~a result ~a"
                       seq start stop fraction result))
             result))
         ;; check whether a pitch is in the guitar range.  if we pass the
         ;; sequence-number, we assume note and time and issue a warning if out
         ;; of range.
         (check-range (pitch &optional seq note time)
           (let ((result (in-range +yvonne-guitar+ pitch)))
             (when (and seq (not result))
               (warn "out of range @ ~a (~a:~a): ~a" 
                     (secs-to-mins-secs time)
                     seq note (data pitch)))
             result))
         ;; from the bass note and intervals returned by get-yvonne-chord-seq,
         ;; get a chord object
         (extra-chord (bass-pitch intervals midi-channel
                                  &optional (octaves-up 1))
           (let* ((bass-up (sc-scale-degree 
                            scale bass-pitch (+ octaves-up (octave bass-pitch))
                            t)))
             (make-chord 
              (loop for interval in intervals 
                 ;; there will be some strangely high chords generated here
                 ;; but that's because the bass-note might not actually
                 ;; re-occur until quite high up in the scale. but they're a
                 ;; nice structural feature :) 
                 for pitch = (nth (+ interval bass-up) (scale-pitches scale))
                 when pitch collect pitch)
              :midi-channel midi-channel :force-midi-channel t)))
         ;; generate one part for the whole piece.
         (voice (midi-channel the-rhythms &key (pitch-offset 0) 
                              (interp-scaler 1.0) (time-scaler 1.0)
                              ;; basically a scaler for num-seqs 
                              (num-passes 1) (wrap-em wrap)
                              ;; produce another midi channel of chords to be
                              ;; played with this voice; either nil or the
                              ;; midi-channel for the chords
                              (extra-chords-channel nil) 
                              (do-additive t) (check-range t))
           ;; rthms is declared above so we can use them in additive
           (setf rthms (rhythm-list the-rhythms t))
           (let* ((pitches (get-pitches degree-pattern pitch-offset 
                                        interp-scaler num-passes))
                  (pitches-rev (get-pitches 
                                ;; start with the second element and
                                ;; wrap-around to the first at the end
                                (wrap-list (reverse degree-pattern) 1)
                                pitch-offset interp-scaler num-passes))
                  (incl-curve (new-lastx inclusion-curve (1- num-pitches)))
                  (chord-seq (when extra-chords-channel
                               (get-yvonne-chord-seq num-pitches)))
                  (events '()))
             (loop for seq in pitches and seq-rev in pitches-rev
                and seq-count from 1 
                with event-count = 0
                with time = 0 
                for print-first = t
                do
                ;; double bar at end of each top-level repeat?
                (loop 
                   for p in seq
                   for p2 in seq-rev
                   for p-highest = (pitch-max p p2)
                   for rhythm = (scale (get-next rthms) time-scaler)
                   for count from 0
                   for chord-possible = (or (not check-range) 
                                            (and (check-range p)
                                                 (check-range p2)))
                   for chord = (and chord-possible 
                                    (mods (1+ event-count) chord-intervals))
                   for include = (<= (random-rep 100.0)
                                     (interpolate event-count incl-curve))
                   do
                   (when include
                     (push (make-event p rhythm :start-time time
                                       :midi-channel midi-channel
                                       :amplitude (between min-amp max-amp))
                           events)
                     (when extra-chords-channel
                       (let* ((chord (extra-chord
                                      p (pop chord-seq) extra-chords-channel))
                              (echord (make-event 
                                       chord rhythm :start-time time
                                       :amplitude (between min-amp max-amp))))
                         (unless (has-notes chord)
                           (warn "no notes in extra chord"))
                         (push echord events)))
                     ;; main voice chord time?
                     (when chord
                       (when print-chords
                         (format t "~&chord (~a, ~a) @ ~a:~a"
                                 (data p) (data p2) seq-count count))
                       ;; start increasing chord regularity after a certain
                       ;; time  
                       (when (portion seq-count 2/3 num-passes)
                         (loop for int in chord-intervals and i from 0 do
                              (when (> int chord-int-min)
                                (decf (nth i chord-intervals))
                                (return))))
                       (push
                        ;; we use time no matter what the tempo as this will
                        ;; end up expressing time in 1/4 notes, which we want.
                        ;; we then write the tempo in the midi-file (below) so
                        ;; this takes care of everything
                        (make-event p2 rhythm :start-time time
                                    :midi-channel midi-channel
                                    :amplitude (between min-amp max-amp))
                        events))
                     (when print-first
                       (setf print-first nil)
                       (format t "~&~a: ~a ~a" (secs-to-mins-secs time)
                               (data p) (if chord (data p2) "")))
                     ;; uncomment to print every pitch in pattern
                     ;; (print (data p))
                     (when (pitch> p-highest highest-pitch)
                       (setf highest-pitch p-highest))
                     (when check-range
                       (check-range p seq-count count time)))
                   (when (mods count 13)
                     (incf time 5))
                   (incf event-count)
                   (incf time (compound-duration rhythm)))
                do
                ;; additive/subtractive rhythms during different parts of
                ;; the piece but always +/- 16th.
                (when do-additive
                  ;; from 1/4 to 2/3 of piece, subtractive on first rhythm,
                  ;; additive on second
                  (when (portion seq-count 1/4 num-passes 2/3)
                    (additive 0 nil)
                    (additive 1 t))
                  ;; from 1/4 to end, additive on 6th (so we get some quite
                  ;; long rests by the end)
                  (when (portion seq-count 1/4 num-passes)
                    (additive 5 nil))
                  ;; from 2/3 subtractive on the 2nd and 4th, additive on the
                  ;; first 
                  (when (portion seq-count 2/3 num-passes)
                    (additive 1 nil)
                    (additive 3 nil)
                    (additive 0 t))))
             (setf events (nreverse events))
             ;; just check which note classes we've used in this voice
             (loop for e in events with used-notes = '() do
                  (when (is-single-pitch e)
                    (pushnew (no-8ve (pitch-or-chord e)) used-notes))
                  finally (format t "~&~a used notes: ~a" 
                                  (length used-notes) used-notes))
             (format t "~&Duration: ~a, Highest Note: ~a"
                     (secs-to-mins-secs (start-time (first (last events))))
                     (data highest-pitch))
             (if wrap-em
                 (wrap-events-list events (floor (* wrap-em num-pitches)))
                 events))))
      ;; although we've been explicit about do-additive in the keyboard voice
      ;; (bg3) it defaults to t anyway in the other voices.
      (let ((bg1 (voice 1 rhythms)) ;; guitar
            ;; bass & organ chords: at the mo high/low 118/48 bf8/c3
            (bg2 (voice 2 (reverse rhythms) :check-range nil 
                        ;; bass: at the mo high/low 70/43 bf4/bf1
                        :extra-chords-channel 4 
                        :pitch-offset -12))
            ;; keyboard: at the mo high/low 120/72 c9/c5
            (bg3 (voice 3 (wrap-list rhythms 4) :pitch-offset 12 
                        ;; high keyboard voice has an exaggerated pitch offset
                        ;; curve applied to the sequences
                        :interp-scaler 1.2 :do-additive t
                        ;; rhythms are scaled by 1/4 so we'll need 4x as many
                        ;; passes 
                        :num-passes 4
                        :check-range nil :time-scaler 1/4)))
        (cm::event-list-to-midi-file 
         (append bg1 bg2 bg3) midi-file tempo 0)))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Generation of piece.

;;; NB The 13 notes here are only there to represent the interval structure of
;;; the scale.  The actual 'tonic' is give in the sc-make-scale call above
;;; (c0).
(defparameter +yvonne-scale+ '(d e f g af bf c d ef f gf af bf))

(yvonne 18 "/Users/medward2/Dropbox/dont-flinch/yvonne.mid" :tempo 52
        :chord-int-min 4 ;;:min-amp .2 :max-amp .35
        :chord-intervals '(17 19)
        :print-chords nil
        ;; this means missing B through whole piece (has BF6 as highest): use
        ;; that at end against final bass notes?  or as tremolo at various
        ;; points in piece?  D# missing in bass btw
        :offset-curve '(0 0 1 5 2 8 3 3 4 6 7 2 11 10 12 11 13 4 14 2 15 13 
                        16 3 17 10 19 1 20 16 21 17)
        ;; so this means some notes won't be used i.e. there might be < 13
        ;; notes per sequence 
        :inclusion-curve '(0 100 20 100 40 90 50 80 55 95 60 60 65 100 
                           70 40 75 70 90 100 100 100)
        :scale-structure +yvonne-scale+)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Some post-generation fiddling/interference/quibbling.

(defun yvonne-transition (event1 event2 fib-trans-data)
  (let* ((midi-events
          ;; only count chords once
          (remove-duplicates 
           (cm::midi-file-to-events-list 
            "/Users/medward2/Dropbox/dont-flinch/score/yvonne-sib.mid" 1)
           :test #'(lambda (x y) (= (cm::object-time x) (cm::object-time y)))))
         (num-events (length midi-events))
         (fib-trans (fibonacci-transitions num-events fib-trans-data)))
    (loop for ft in fib-trans and me in midi-events do
         (when (typep me 'cm::midi)
           (format t "~&~a: ~a ~a" 
                   (if (zerop ft) event1 event2)
                   (secs-to-mins-secs (cm::object-time me))
                   (midi-to-note (cm::midi-keynum me)))))))

;;; transition between bottle-neck and ord.
(yvonne-transition 
 "NO" "YES"
 ;; the point of '(1 0 1 0) is we have 3 transitions: 1->0 0->1 and 1->0
 '(1 0 1 0))

;;; transition between no tremolo and tremolo
(yvonne-transition 
 "ord" "trem"
 '(0 1 0))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;; EOF yvonne.lsp

Written by Michael Edwards

June 30, 2011 at 5:22 pm

Leave a Reply

University of Edinburgh Privacy Policy and cookie information