Explanation of the Code

The twelve piano pieces in this collection are all variations on a common theme. They may not sound similar to your ears, but there is only a very small amount of computer code (about 20-30 lines) to distinguish one piece from any other. (The pieces do rely on quite a bit more than 20-30 lines of code, but they all have that remaining part in common.)

Each piece has exactly the same musical structure: an intro, followed by some number R of rounds, followed by an end. These are called phases. So each piece has R+2 phases. The number R is specified in the file for the piece like this:

   (define rounds 3)          ; this piece will have an intro, 3 rounds, an end

Each phase consists of some number of segments. The intro and end each consist of just one segment, and each round is made up of some number of segments called the legs of the round, with chord transitions between them. A round begins in degree "i" and finishes when the degree returns to "i". (The intro segment and end segment also begin in "i".) The chord transitions from one leg to another within a round are chosen at random but constrained according a fixed pattern. The pattern used in all twelve pieces in the collection here is this:

      ((i iii vii ii) (iii vi iv) (iv v vii) (vi ii) (vii i i iii) (ii v) (v i)))

This pattern says that from "i", the possible transitions are to "iii", "vii" or "ii"; from "iii", the possible transitions are to "vi" or "iv"; and so on. So the simplest round has two legs: a segment in "i" followed by one in "vii". A more complex round has five legs: "i", "iii", "vi", "ii", and "v".

So here is how a piece with R=3 might be played:

      phase=0
        intro segment, deg=i
      phase=1 
        leg segment, deg=i
        leg segment, deg=vii
      phase=2 
        leg segment, deg=i
        leg segment, deg=iii
        leg segment, deg=iv
        leg segment, deg=v
      phase=3 
        leg segment, deg=i
        leg segment, deg=vii
      phase=4
        end segment, deg=i

So this performance involves ten segments over the five phases (the intro has one segment, the end has one segment, and the three rounds have the remaining eight segments). A second performance of the very same piece might have a different structure if some of the rounds do more or fewer chord transitions starting at degree "i" before heading back to degree "i".

A degree like "iv" does not determine an actual chord. That depends on the key of the piece (like F or Bb) and whether the piece is in a major or minor mode. All the twelve pieces here are done in minor keys. For variety, each of the twelve pieces is in a different key, specified like this:

   (define key Ab)             ; this piece will be in Ab-minor

A piece is made up of all its segments, so how long the piece takes to play depends on how many segments there will be overall. Each segment is made up of some number of musical beats, specified in advance for the piece as follows:

   
   (define intro-length 18)    ; the intro segment will take 18 beats
   (define leg-length 8)       ; each leg segment will take 8 beats
   (define tempo 120)          ; the tempo will be 120 beats per min
(The length of the end segment is not specified.)

What happens during a segment is specified by a procedure for the piece called "song-segment" for that piece:

   
   (define (song-segment ...   ; this says what to do in each segment

Each time this procedure is invoked, it will play a certain number of notes on the piano (and maybe use the sustain pedal).

This is the full specification for a piece.

Here is how the piece would be played for the run given above:

      song-segment will be invoked at beat=0 with phase=0, deg=i
      song-segment will be invoked at beat=18 with phase=1, deg=i
      song-segment will be invoked at beat=26 with phase=1, deg=vii
      song-segment will be invoked at beat=34 with phase=2, deg=i
      song-segment will be invoked at beat=42 with phase=2, deg=iii
      song-segment will be invoked at beat=50 with phase=2, deg=iv
      song-segment will be invoked at beat=58 with phase=2, deg=v
      song-segment will be invoked at beat=66 with phase=3, deg=i
      song-segment will be invoked at beat=74 with phase=3, deg=vii
      song-segment will be invoked at beat=82 with phase=4, deg=i

Note that the intro segment (at phase=0) occupies 18 beats, but the leg segments during each round (at phase=1, 2 or 3) only occupy 8 beats. When the end segment (at phase=4) terminates, the piece is over. (How long all of this will take depends on the tempo. For tempo=T, the elapsed time in seconds is the total number of beats times 60/T.)

To know how a piece is actually going to sound, the only remaining piece of the puzzle is how the "song-segment" procedure is defined. This will determine what piano notes are actually played during each segment of the piece.

(Warning: The following assumes some familiarity with MIDI and a willingness to look at some computer code. Otherwise, this should be considered to be the end of the explanation and a good time to bail.)


Typically, "song-segment" is defined by something of the following form:

   
   (define (song-segment beat phase deg pc)
      ...local declarations...
      (cond
        ((= phase 0) ...what to do in the intro segment...)
        ((> phase rounds) ...what to do in the end segment...)
        (else ...what to do during a leg segment of a round...)))

Each time "song-segment" is invoked, it will schedule a certain number of MIDI notes to be played, by invoking a function invoked "note1":

  
   (note1 beat pitch velocity duration)

This schedules an individual MIDI note to be played on whatever instrument is listening to MIDI channel 1 (assumed here to be a MIDI piano.) For example, if "song-segment" is invoked with beat=B, then an expression like

   
   (note1 (+ beat 3) (+ key 60) 64 2)

in the body of "song-segment" schedules a note to be played on the piano at beat B+3. The MIDI pitch played is (+ key 60) so, if the key is C, then the note is middle-C; the MIDI velocity here is 64, (halfway to the maximum of 128); the duration here is 2 beats (what is called a "half-note"). The other significant MIDI event taking place during a segment is

   (pedal1 beat duration)

so that the expression

   (pedal1 (+ beat 2) 3)

in the body of "song-segment" schedules the sustain pedal (MIDI controller 64) on MIDI channel 1 to be depressed on beat B+2 and released on beat B+5. (It is possible to cause other MIDI events to occur, but this is enough for now.)

Putting all these pieces together, here is the complete listing for the piece called "moonie", in the file "moonie.scm" (number six in the collection):


(include "../lib/common-code.scm")

(define key C#)
(define tempo 170)

(define intro-length 16)
(define leg-length 8)
(define rounds 11)

(define (song-segment beat phase deg pc) 
  (define (pno beat p v d) (note1 beat p (cosr v 20 1/64) d))
  (define chd (pitches (sinr 46 -6 1/256) 7 pc))            ; 7 notes: chd[0;6]
  (define hi (list-ref chd (if (< (rand) .1) 6 5)))         ; chd[5] or chd[6]
  (define (right beat)
    (pno beat hi 62 7)                                      ; RH single note
    (when (eq? deg 'v) (pno (+ beat 7) hi 50 1)))           ; RH pick-up on "v"
  (define (left beat)
    (pedal1 (+ beat 3) 5)                                   ; sustain pedal
    (pno beat (car chd) 52 4)                               ; LH chd[0] 
    (for ((i 4) (p (cdr chd))) (pno (+ beat 1 i) p 45 1))   ; LH arpeg chd[1;4]
    (for ((i 3) (p (cddr chd))) (pno (+ beat 5 i) p 32 1))) ; LH arpeg chd[2;4]
  (cond
   ((= phase 0) (left beat) (left (+ beat 8)))              ; intro in LH only
   ((> phase rounds) (for ((p chd)) (pno beat p 25 8)))     ; the final chord
   ((<= phase rounds) (left beat) (right beat))))           ; use both hands

Here's what all this means. The first line says to use the code that is common to all the pieces. The next two lines set things up as described above: a key of C#-minor (like the Moonlight Sonata), and a tempo of 170 beats per minute. (It is easier to have a fast tempo and to use expressions like "(+ beat 1)" than to have a slower tempo and have to use fractional beats.) The next three lines say that the piece has an intro of 16 beats, followed by 11 rounds, each made up of some number of leg segments at 8 beats per segment, and concluding with the end segment.

So what exactly happens during these segments? This is what is specified by the last sixteen lines of the file, the body of "song-segment".

This "pno" business says we're going to be playing notes on MIDI channel 1 with pitch p, velocity v, duration d, but where the given velocity v is changed according to the beat: (cosr v 20 1/64) means the velocity will start at v+20 for beat=0, work its way slowly down to v-20 at beat=32, and then back up to v+20 by beat=64 and then back down again.

Next, "chd" defines a chord to use in the segment with 7 note pitches in it. The actual 7 pitches depend on the starting point and the given pitch class argument "pc". (For a key of C# and deg "i", the "pc" would be C#-minor, so we would get pitches like C#3, E3, G#3, C#4, E4, etc; or maybe E2, G#2, C#3, etc. depending on the starting point.) The actual starting point used depends on the beat: (sinr 46 -6 1/256) means the starting pitch is MIDI 46 at beat=0, then works its way down to MIDI 40 at beat=64 then up to MIDI 52 at beat=192 and then back down, and on like this.

The next line says that a "hi" pitch will be either the last or second last pitch from the "chd" chord. (90% of the time, it will be the second last.)

The next two definitions specify what should happen in the virtual "right" hand and the virtual "left" hand (and pedal) in a segment.

     For the right:
       Play the hi note of the chord on the given beat.                (RH)
       When deg=v, play hi again with an offset of 7 beats.            (RH)
     For the left: 
       Play the note 0 of the chord with no offset.                    (LH)
       Play notes 1,2,3,4 of the chord with offsets 1,2,3,4.           (LH) 
       Play notes 2,3,4 of the chord with offsets 5,6,7.               (LH)

These are all the local declarations. The last four lines of the file make the final decison about what to play in each segment: for the intro segment, play the LH part twice with no RH; at the end of the piece, quietly play the 7-note "chd" chord with no arpeggio; finally, for each leg of a round, play both the LH and the RH parts;

This is the entire "moonie" piece! Basically, it's 5-note arpeggios in the LH and single notes in the RH, going through the various chord changes, up and down the piano, quieter and louder. Listen to the piece "6.moonie.mp3" and you should hear those LH arpeggios and single notes in the RH as well as the chord transitions within each round.

Return to the main text.