Ch. 4 – Transformation and Process

Topics:   Musical patterns and meaning, minimalism, Steve Reich, Mod functions, musical canon, J.S. Bach, Arvo Pärt, viewing musical material, software development process, computer-aided music composition.

Being able to write down and represent music is an important skill that we have examined in some detail in the previous chapters. In this chapter we focus on another important skill, transforming music to create variations and developing it into longer and more interesting compositions.  More broadly this chapter explores foundational programming skills required to manipulate data.  More information is provided in the reference textbook.

Here is code from this chapter:


Create Steve Reich’s piece, “Piano Phase” (1967)

This code sample (Ch. 4, p. 94) creates Steve Reich’s “Piano Phase”, a minimalist piece for two pianos involving tempo differences and repetition. The speed difference is quite small, 0.5 beats-per-minute (quarter-notes per minute), so the phase shift is quite gradual, but nevertheless the result is dramatic.

# pianoPhase.py
# Plays Steve Reich's minimalist piece, Piano Phase (1967).

from music import *

pianoPart = Part(PIANO, 0) # create piano part

phrase1 = Phrase(0.0)      # create two phrases
phrase2 = Phrase(0.0)

# write music in a convenient way
pitchList    = [E4, FS4, B4, CS5, D5, FS4, E4, CS5, B4, FS4, D5, CS5]
durationList = [SN, SN,  SN, SN,  SN, SN,  SN, SN,  SN, SN,  SN, SN]

# add the same notes to both phrases
phrase1.addNoteList(pitchList, durationList)
phrase2.addNoteList(pitchList, durationList)

Mod.repeat(phrase1, 41)   # repeat first phrase 41 times
Mod.repeat(phrase2, 41)   # repeat second phrase 41 times

phrase1.setTempo(100.0)   # set tempo to 100 beats-per-minute
phrase2.setTempo(100.5)   # set tempo to 100.5 beats-per-minute

pianoPart.addPhrase(phrase1)   # add phrases to part
pianoPart.addPhrase(phrase2)

Play.midi(pianoPart)                    # play music, and
Write.midi(pianoPart, "pianoPhase.mid") # save it to a MIDI file

It plays this sound:

 

A Variation
Here is a variation of this piece utilizing more Mod functions. and adding a bass voice.

It plays this sound:

 


Create a simple musical canon, “Row Your Boat”

This code sample (Ch. 4, p. 99) demonstrates how to create a musical canon.

# rowYourBoat.py
# Demonstrates how to build a musical canon.

from music import *

# Create the necessary musical data
rowYourBoatScore = Score("Row Your Boat", 108.0)  # tempo is 108 bpm

flutePart    = Part(FLUTE, 0)        # flute part on channel 0
trumpetPart  = Part(TRUMPET, 1)      # trumpet part on channel 1
clarinetPart = Part(CLARINET, 2)     # clarinet part on channel 2

themePhrase = Phrase(0.0)            # theme starts at the beginning

# "Row, row, row your boat gently down the stream"
pitches1   = [C4, C4, C4,  D4, E4, E4,  D4, E4,  F4, G4]
durations1 = [QN, QN, DEN, SN, QN, DEN, SN, DEN, SN, HN]

# "merrily, merrily, merrily, merrily"
pitches2   = [C5,  C5,  C5,  G4,  G4,  G4,  E4,  E4,  E4,  C4,
               C4,  C4]
durations2 = [ENT, ENT, ENT, ENT, ENT, ENT, ENT, ENT, ENT, ENT,
               ENT, ENT]

# "life is but a dream."
pitches3   = [G4,  F4, E4,  D4, C4]
durations3 = [DEN, SN, DEN, SN, HN]

# add the notes to the theme
themePhrase.addNoteList(pitches1, durations1)
themePhrase.addNoteList(pitches2, durations2)
themePhrase.addNoteList(pitches3, durations3)

# make two new phrases and change start times to make a round
response1Phrase = themePhrase.copy()
response2Phrase = themePhrase.copy()

response1Phrase.setStartTime(4.0)     # start after 4 quarter notes
response2Phrase.setStartTime(8.0)     # start after 8 quarter notes

# play different parts in different registers
Mod.transpose(themePhrase, 12)         # one octave higher
Mod.transpose(response2Phrase, -12)    # one octave lower

# play each phrase twice
Mod.repeat(themePhrase, 2)
Mod.repeat(response1Phrase, 2)
Mod.repeat(response2Phrase, 2)

# add phrases to corresponding parts
flutePart.addPhrase(themePhrase)
trumpetPart.addPhrase(response1Phrase)
clarinetPart.addPhrase(response2Phrase)

# add parts to score
rowYourBoatScore.addPart(flutePart)
rowYourBoatScore.addPart(trumpetPart)
rowYourBoatScore.addPart(clarinetPart)

# play score
Play.midi(rowYourBoatScore)

# write score to MIDI file
Write.midi(rowYourBoatScore, "rowYourBoat.mid")

It plays this sound:


Create J.S. Bach’s Canon No. 1 on the Goldberg Ground (BWV 1087)

In 1741, Johann Sebastian Bach (1685 – 1750) wrote the Goldberg Variations (BWV 988) for harpsichord. In 1974 an unknown manuscript was discovered, written in Bach’s hand, containing 14 canons on the first eight notes of the Goldberg aria.

The following program (Ch. 4, p. 106) demonstrates how to use Mod functions to create the first of these 14 canons. It is built from a theme consisting of eight notes and reversed version (retrograde) played simultaneously.

# JS_Bach.Canon_1.GoldbergGround.BWV1087.py
#
# This program (re)creates J.S. Bach's Canon No. 1 of the Fourteen on
# the Goldberg Ground.
#
# This canon is constructed using the Goldberg ground as the subject
# (soggetto) combined with the retrograde of itself.
#

from music import *

# how many times to repeat the theme
times = 6

# define the data structure
score  = Score("J.S. Bach, Canon 1, Goldberg Ground (BWV1087)", 100)
part   = Part()
voice1 = Phrase(0.0)

# create musical material (soggetto)
pitches = [G3, F3, E3, D3, B2, C3, D3, G2]
rhythms = [QN, QN, QN, QN, QN, QN, QN, QN]
voice1.addNoteList(pitches, rhythms)

# create 2nd voice
voice2 = voice1.copy()
Mod.retrograde(voice2)   # follower is retrograde of leader

# combine musical material
part.addPhrase(voice1)
part.addPhrase(voice2)
score.addPart(part)

# repeat canon as desired
Mod.repeat(score, times)

# play score and write it to a MIDI file
Play.midi(score)
Write.midi(score, "JS_Bach.Canon_1.GoldbergGround.BWV1087.mid")

It plays this sound:


Create J.S. Bach’s “Trias Harmonica” canon (BWV 1072)

This code sample (Ch. 4, p. 108) creates the Trias Harmonica canon (BWV 1072). The Trias Harmonica consists of two parts with four separate voices each – a total of 8 overlaid phrases.

# JS_Bach.Canon.TriasHarmonica.BWV1072.py
#
# This program creates J.S. Bach's 'Trias Harmonica' BWV 1072 canon.
#
# This canon is constructed using two parts (choirs), each consisting
# of four voices. The first part's voices use the original theme,
# each separated by a half-note delay. The second part's voices use
# the inverted theme delayed by a quarter note, each separated by
# a half-note delay.
#

from music import *

# define the theme (for choir 1)
pitches1   = [C4,  D4, E4,  F4, G4,  F4, E4,  D4]
durations1 = [DQN, EN, DQN, EN, DQN, EN, DQN, EN]

# define the inverted theme (for choir 2)
pitches2   = [G4,  F4, E4,  D4, C4,  D4, E4,  F4]
durations2 = [DQN, EN, DQN, EN, DQN, EN, DQN, EN]

# how many times to repeat the theme
times = 8

# choir 1 - 4 voices separated by half note
choir1 = Part()

voice1 = Phrase(0.0)
voice1.addNoteList(pitches1, durations1)
Mod.repeat(voice1, times)           # repeat a number of times
voice1.addNote(C4, DQN)             # add final note

voice2 = voice1.copy()              # voice 2 is a copy of voice 1
voice2.setStartTime(HN)             # separated by half note

voice3 = voice1.copy()              # voice 3 is a copy of voice 1
voice3.setStartTime(HN*2)           # separated by two half notes

voice4 = voice1.copy()              # voice 4 is a copy of voice 1
voice3.setStartTime(HN*3)           # separated by three half notes

choir1.addPhrase(voice1)
choir1.addPhrase(voice2)
choir1.addPhrase(voice3)
choir1.addPhrase(voice4)

# choir 2 - 4 voices inverted, delayed by quarter note,
# separated by a half note.
choir2 = Part()

voice5 = Phrase(QN)                 # delayed by quarter note
voice5.addNoteList(pitches2, durations2)
Mod.repeat(voice5, times)           # repeat a number of times
voice5.addNote(G4, DQN)             # add final note

voice6 = voice5.copy()              # voice 6 is a copy of voice 5
voice6.setStartTime(QN + HN)        # separated by half note

voice7 = voice5.copy()              # voice 7 is a copy of voice 5
voice7.setStartTime(QN + HN*2)      # separated by two half notes

voice8 = voice5.copy()              # voice 8 is a copy of voice 5
voice8.setStartTime(QN + HN*3)      # separated by three half note

choir2.addPhrase(voice5)
choir2.addPhrase(voice6)
choir2.addPhrase(voice7)
choir2.addPhrase(voice8)

# score
canon = Score("J.S. Bach, Trias Harmonica (BWV 1072)", 100)
canon.addPart(choir1)
canon.addPart(choir2)

# play score, and write it to a MIDI file
Play.midi(canon)
Write.midi(canon, "JS_Bach.Canon.TriasHarmonica.BWV1072.mid")

It plays this sound:


Create Arvo Part’s “Cantus in Memoriam Benjamin Britten” (1977)

This code sample (Ch. 4, p. 110) creates Arvo Pärt’s “Cantus in Memoriam Benjamin Britten”.  It creates a single voice descending in stepwise motion the A natural minor scale. This voice is repeated at half tempo several times, creating beautiful harmonic combinations stemming from all permutations of aeolian-scale notes. This is a slightly simplified version of the original piece.

# ArvoPart.CantusInMemoriam.py
#
# Recreates a variation of Arvo Part's "Cantus in Memoriam Benjamin
# Britten" (1977) for string orchestra and bell, using Mod functions.

from music import *

# musical parameters
repetitions = 2   # length of piece
tempo = 112       # tempo of piece
bar = WN+HN       # length of a measure

# create musical data structure
cantusScore = Score("Cantus in Memoriam Benjamin Britten", tempo)

bellPart = Part(TUBULAR_BELLS, 0)
violinPart = Part(VIOLIN, 1)

# bell
bellPitches   = [REST,  A4,    REST, REST,  A4,    REST, REST,  A4]
bellDurations = [bar/2, bar/2, bar,  bar/2, bar/2, bar,  bar/2, bar/2]

bellPhrase = Phrase(0.0)
bellPhrase.addNoteList(bellPitches, bellDurations)
bellPart.addPhrase(bellPhrase)

# violin - define descending aeolian scale and rhythms
pitches   = [A5, G5, F5,  E5,  D5,  C5,  B4,  A4]
durations = [HN, QN, HN, QN, HN, QN, HN, QN]

# violin 1
violin1Phrase = Phrase(bar * 6.5)  # start after 6 and 1/2 measures
violin1Phrase.addNoteList(pitches, durations)

# violin 2
violin2Phrase = violin1Phrase.copy()
violin2Phrase.setStartTime(bar * 7.0)  # start after 7 measures
Mod.elongate(violin2Phrase, 2.0)       # double durations
Mod.transpose(violin2Phrase, -12)      # an octave lower

# violin 3
violin3Phrase = violin2Phrase.copy()
violin3Phrase.setStartTime(bar * 8.0)  # start after 8 measures
Mod.elongate(violin3Phrase, 2.0)       # double durations
Mod.transpose(violin3Phrase, -12)      # an octave lower

# violin 4
violin4Phrase = violin3Phrase.copy()
violin4Phrase.setStartTime(bar * 10.0) # start after 10 measures
Mod.elongate(violin4Phrase, 2.0)       # double durations
Mod.transpose(violin4Phrase, -12)      # an octave lower

# repeat phrases enough times
Mod.repeat(violin1Phrase, 8 * repetitions)
Mod.repeat(violin2Phrase, 4 * repetitions)
Mod.repeat(violin3Phrase, 2 * repetitions)
Mod.repeat(violin4Phrase, repetitions)

# violin part
violinPart.addPhrase(violin1Phrase)
violinPart.addPhrase(violin2Phrase)
violinPart.addPhrase(violin3Phrase)
violinPart.addPhrase(violin4Phrase)

# score
cantusScore.addPart(bellPart)
cantusScore.addPart(violinPart)

# fade in, and fade out
Mod.fadeIn(cantusScore, WN)
Mod.fadeOut(cantusScore, WN * 12)

# view, play, and write
View.sketch(cantusScore)
Play.midi(cantusScore)
Write.midi (cantusScore, "ArvoPart.CantusInMemoriam.mid")

It plays this sound:


Create variations automatically from a theme

The following program (Ch. 4, p. 121) demonstrates how to use various Mod functions to create novel music from existing material.  For simplicity, this aims for a piece with reasonable (but not perfect) musicality – a piece that can be used to explore musical possibilities, and get new ideas.  Since randomness is involved, every time this program runs will create new variations (some better than others).

# themeAndVariations.py
#
# Demonstrates how to automatically develop musical material
# using a theme and transforming it through Mod functions.
#
# Here we start with the theme, continue with a few interesting
# variations, and close with the theme for recapitulation.

from music import *

# create theme
pitches = [C4, E4, G4, A4, B4, A4, B4, C5]
rhythms = [EN, EN, QN, SN, SN, SN, SN, QN]

theme = Phrase()
theme.addNoteList(pitches, rhythms)

# variation 1
# vary all pitches in theme, but keep the same
# rhythmic pattern (for stability)
var1 = theme.copy()    # preserve original (make a copy)
Mod.randomize(var1, 3) # randomize each pitch (max of +/- 3)
                       # (later we force them into the scale)
# variation 2
# slow down theme, and change two pitches using
# a random value from within the theme range
var2 = theme.copy()
Mod.elongate(var2, 2.0)  # double its time length
Mod.mutate(var2)         # change one pitch and rhythm value
Mod.mutate(var2)         # and another (could be the same)

# variation 3
# reverse the theme, and lower it one octave
var3 = theme.copy()
Mod.retrograde(var3)     # reverse notes
Mod.transpose(var3, -12) # lower one octave

# recapitulation
# repeat the theme for closing (i.e., return home)
var4 = theme.copy()      # we need a copy (a phrase can be added
                         # to a part only once)

# add theme and variations in the right order (since we
# didn't specify start times, they will play in sequence)
part = Part()
part.addPhrase(theme)
part.addPhrase(var1)
part.addPhrase(var2)
part.addPhrase(var3)
part.addPhrase(var4)

# now, fix all notes to be in the C major scale,
# and with rhythm values that line up with SN intervals
Mod.quantize(part, SN, MAJOR_SCALE, 0)

# provide alternate views
View.pianoRoll(part)
View.internal(part)
View.sketch(part)
Mod.consolidate(part)   # merge phrases into one phrase, so that...
View.notate(part)       # ...notate() can display all the notes

# and play
Play.midi(part)

It plays this sound: