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
- Create a simple musical canon, “Row Your Boat”
- Create J.S. Bach’s Canon No. 1 on the Goldberg Ground (BWV 1087)
- Create J.S. Bach’s “Trias Harmonica” canon (BWV 1072)
- Create Arvo Part’s “Cantus in Memoriam Benjamin Britten” (1977)
- Create variations automatically from a theme
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:
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: