If you read many 20thC composition texts on pitch classes you could be forgiven for thinking pitch class sets a rather dry subject and of limited compositional value.  Oh, how wrong you would be!  Pitch classes are fantastically useful for the music programmer and trivial to understand and use effectively.

A brief reminder (or introduction for those unfamiliar with pitch classes).  Pitch classes are based around the 12 semitones of the chromatic scale where each semitone is given it’s own class.  C C# D D#/Eb F F# etc.  Now remove all octave reference and enharmonic signature, we talk about pitch classes as having enharmonic and octave equivalence (i.e. D#/Eb are the same pitch class in any octave).  Of course in a programming space we use numbers to represent pitches (because numbers are easier for us to work with - which is why impromptu does not by default include pitch symbols) so instead of B#/C/Db for example we use 0, C#/Db is 1, D is 2 ... through to A#/B/Cb at 11 which rounds out the complete set of available pitch classes 0-11.

Now, the observant reader will note that we can use modulo arithmetic to find midi pitches of octave equivalence by using mod 12.  Try running this example (check the log view for results).

(dotimes (i 12)
(print 'modulo (+ i 60) 12 '=> (modulo (+ i 60) 12)))

Ok so that has to be useful right!  It sure is.  Now as previously discussed, impromptu does not include much in-built musical support, however, there is a libraries page and on that page you will find pc-lib.scm.  You’ll need to download that file before proceeding.  I encourage you to take a look at the pc-lib.scm file and extend and replace things as you see fit (you’ll probably find that you can do a better job than I have :-)

Let’s start with something simple.  We can define a pitch class set by creating a list of pitch classes that belong to the set.  We can then test a pitch against that set by using pc:?

;; four examples tested against the pitch class set representation of a c major chord
(pc:? 60 '(0 4 7))
(pc:? 84 '(0 4 7))
(pc:? 35 '(0 4 7))
(pc:? 79 '(0 4 7))

We can also choose a random number between a lower and upper bounds from a pitch class set.

;; this chooses a C in any octave
(pc:random 0 127 '(0))

;; this chooses any note from a dminor chord in octave 4
(pc:random 60 73 '(2 5 9))

;; this chooses any note from a c pentatonic octaves 3-6
(pc:random 48 97 '(0 2 4 7 9))

Let’s write a little organum piece (how 13C of me)  We’re going to write a strict parallel organum where we take a melody part and then transpose up a perfect forth or fifth (you can try both) to supply a harmony.  So why is this in the PC section, well you can’t just transpose up a fifth by adding 7 to everything.  Why not you ask, take a look.

;; up 7 semitones or a perfect fifth
(map (lambda (p)
(pc:? (+ p 7) '(0 2 4 5 7 9 11)))
(list 60 62 64 65 67 69 71))

;; up 5 semitones or a perfect forth
(map (lambda (p)
(pc:? (+ p 5) '(0 2 4 5 7 9 11)))
(list 60 62 64 65 67 69 71))

;; up 4 semitones or a major third
(map (lambda (p)
(pc:? (+ p 4) '(0 2 4 5 7 9 11)))
(list 60 62 64 65 67 69 71))

Based on a C-major key pitch class set => B up 7 semitones (a perfect 5th) gives us F#.  F up by 5 semitones (a perfect 4th) gives Bb and if we have the audacity to try 4 semitones (a major 3rd) - well basically nothing works.  Notice that we do use map here instead of for-each because we do want to return a list (of boolean values).  So the answer is to use pc:relative which will choose a pitch value from the pitch class relative to our current pitch.

;; this gives us 62
(pc:relative 60 1 '(0 2 4 5 7 9 11))

;; this gives us 67
(pc:relative 60 4 '(0 2 4 5 7 9 11))

;; this gives us 67 as well
(pc:relative 67 0 '(0 2 4 5 7 9 11))

;; this gives us 57 (yes you can go backwards)
(pc:relative 60 -2 '(0 2 4 5 7 9 11))

On with the organum.  Oh, one more rule, we need our melody and harmony to start and finish on the same note - C.  Here’s one way we could go about the task.

;; define a melody
(define melody (make-list-with-proc 24
(lambda (i)
(pc:random 60 73 '(0 2 4 5 7 9 11)))))

;; define harmony up a perfect 5th (4 places away in the pitch class set)
(define harmony (map (lambda (p)
(pc:relative p 4 '(0 2 4 5 7 9 11)))
melody))

;; set c at start and end
(set! melody (cons 60 melody))
(set! harmony (cons 60 harmony))
(set! melody (reverse (cons 60 (reverse melody))))
(set! harmony (reverse (cons 60 (reverse harmony))))

;; random rhythm
(define rhythm (make-list-with-proc 24 (lambda (i) (random '(44100 22050)))))

;; set long start and end to rhythm
(set! rhythm (cons 88200 rhythm))
(set! rhythm (reverse (cons 88200 (reverse rhythm))))

(define organum
(lambda (time mlst hlst rlst)
(play-note time piano (car mlst) 60 (car rlst))
(play-note time piano (car hlst) 60 (car rlst))
(if (not (null? (cdr mlst)))
(callback (+ time (* .5 (car rlst))) 'organum (+ time (car rlst))
(cdr mlst)
(cdr hlst)
(cdr rlst)))))

;; start
(organum (now) melody harmony rhythm)

Here’s what my organum sounds like organum.mp3.  It was also a little out of character for the melody to leap around so much so let’s also use pc:relative to implement a random walk melody (the rest of your code can stay the same but remember to reevaluate everything that the change effects - everything to do with creating melody and harmony).

;; define a random walk melody seeded with 60 (we remove this at the end with cdr)
(define melody (let loop ((i 0)
(lst '(60)))
(if (< i 24)
(loop (+ i 1)
(cons (pc:relative (car lst)
(random '(-1 1))
'(0 2 4 5 7 9 11))
lst))
(cdr (reverse lst)))))

Of course we could easily use larger leaps by changing (random ‘(-1 1)) to (random ‘(-2 -1 1 2 3) for example.  pc-relative can be a useful way of constraining (and then later releasing) melodic invention.

Ok, that’s enough 13thC hip hop, let’s go hard core 20thC and make a I IV V progression :)  But first a crazy 21stC chord.  Once crazy-chord is running, slowly start removing pitch classes from the end of the set (I’m not going to remind you to re-evaluate anymore :-) and listen to the c-major chord that starts to evolve.  If your machine will handle a higher callback rate then go for it, we’re after a wash of sound here.  Try choosing a sound with a delay for extra impact (don’t let my piano symbol fool you ;)

(define crazy-chord
(lambda (time)
(play-note time piano (pc:random 24 97 '(0 4 7 10 2 3 5 9 6 11 1)) 80 500)
(callback (+ time 1000) 'crazy-chord (+ time 2000))))

(crazy-chord (now))

Here’s a recording of my machine going absolutely gang-busters until it finally resolves to the last pitch class 0 (c). crazy-chord.mp3

Ok, so we’ve seen how we can use a pitch class to represent a chord.  pc-lib also includes a useful little function pc:make-chord for returning a ‘random’ chord based on a pitch class set.  Let’s take a look at this in action.

;; C-major and repeat
(define chords
(lambda (time)
(for-each (lambda (p)
(play-note time piano p 80 10000))
(pc:make-chord 60 72 3 '(0 4 7)))
(callback (+ time 10000) 'chords (+ time 11025))))

(chords (now))

Hey, our friend for-each is back.  Now while this is playing start expanding the range (i.e. drop the 60 down and raise the 72 up).  pc:make-chord returns as many notes as we request (3 in this example) from the pitch class and it tries to evenly distribute the number of notes across the chosen range.  It also attempts to use each class in the pitch class set.  However, it does not make any guarantees about what order to choose classes from the pitch class set (i.e. i i6 i64 are all possible in this example).  You might also like to change the number of notes being generated for our chord - try changing 3 to 1 now try 2, 4, 5 etc..

Getting a little sick of C-major, let’s add IV (f major) and V (g major) to the progression and make a random chord change one in five callbacks.  Note that random can just as easily choose a list from a list as an atom from a list.

;; I IV V
(define chords
(lambda (time chord)
(for-each (lambda (p)
(play-note time piano p 80 10000))
(pc:make-chord 48 90 3 chord))
(callback (+ time 10000) 'chords (+ time 11025)
(if (> (random) .8)
(random '((0 4 7) (5 9 0) (7 11 2)))
chord))))

(chords (now) '(0 4 7))

Here’s what this sounds like on my machine.  chords.mp3  There’s a lot more we can do with pitch classes so go and explore, we’ll also see plenty more examples in the coming pages.

Time to move onto some serious composition, and what could be more serious than diatonic harmony ;)