Topics: Notes, phrases, parts, scores, Python lists, Ludwig van Beethoven, scales, MIDI instruments, Harold Faltermeyer, chords, Bruce Hornsby, 2Pac, Joseph Kosma, drums and other percussion, drum machines, Deep Purple, top-down design, reading and writing MIDI files.
In this chapter, we introduce Python data structures for music making. We look at some of the Python music data objects and how they represent musical information. You should already know how to create notes and rests, and write basic Python programs that play a note, or that output a value, from chapter 2.
More information is provided in the reference textbook.
Here is code from this chapter:
- Play a simple musical theme
- Play a musical theme setting the instrument and tempo
- Play a chord progression
- Create music with various melodic lines sounding simultaneously
- A Jazz trio arrangement of “Autumn Leaves”
- Play a drum sound
- Create a drum machine pattern
- Play opening theme of “Smoke on the Water”
Play a simple musical theme
This code sample (Ch. 3, p. 56) demonstrates how to use a phrase to create a melody. It generates the theme from one of the most popular classical pieces, Ludwig van Beethoven’s Bagatelle No. 25 in A minor for solo piano, commonly known as “Für Elise” (for Elise).
# furElise.py # Generates the theme from Beethoven's Fur Elise. from music import * # theme has some repetition, so break it up to maximize economy # (also notice how we line up corresponding pitches and durations) pitches1 = [E5, DS5, E5, DS5, E5, B4, D5, C5] durations1 = [SN, SN, SN, SN, SN, SN, SN, SN] pitches2 = [A4, REST, C4, E4, A4, B4, REST, E4] durations2 = [EN, SN, SN, SN, SN, EN, SN, SN] pitches3 = [GS4, B4, C5, REST, E4] durations3 = [SN, SN, EN, SN, SN] pitches4 = [C5, B4, A4] durations4 = [SN, SN, EN] # create an empty phrase, and construct theme from the above motifs theme = Phrase() theme.addNoteList(pitches1, durations1) theme.addNoteList(pitches2, durations2) theme.addNoteList(pitches3, durations3) theme.addNoteList(pitches1, durations1) # again theme.addNoteList(pitches2, durations2) theme.addNoteList(pitches4, durations4) # play it Play.midi(theme)
It plays this sound:
Play a musical theme setting the instrument and tempo
This code sample (Ch. 3, p. 63) demonstrates how to set the instrument and tempo of a phrase. It creates Harold Faltermeyer’s electronic instrumental theme from the 1984 film Beverly Hills Cop.
# axelF.py # Generates Harold Faltermeyer's electronic instrumental theme # from the film Beverly Hills Cop (1984). from music import * # theme (notice how we line up corresponding pitches and rhythms) pitches1 = [F4, REST, AF4, REST, F4, F4, BF4, F4, EF4] durations1 = [QN, QN, QN, EN, QN, EN, QN, QN, QN] pitches2 = [F4, REST, C5, REST, F4, F4, DF5, C5, AF4] durations2 = [QN, QN, QN, EN, QN, EN, QN, QN, QN] pitches3 = [F4, C5, F5, F4, EF4, EF4, C4, G4, F4] durations3 = [QN, QN, QN, EN, QN, EN, QN, QN, DQN] # create an empty phrase, and construct theme using pitch/rhythm data theme = Phrase() theme.addNoteList(pitches1, durations1) theme.addNoteList(pitches2, durations2) theme.addNoteList(pitches3, durations3) # set the instrument and tempo for the theme theme.setInstrument(SYNTH_BASS_2) theme.setTempo(220) # play it Play.midi(theme)
It plays this sound:
Here is the complete list of MIDI instruments. You can modify the above program to get different sounds.
Advice on Setting Instrument and Tempo
Follow this for best results:
- If your program creates only a single Phrase, you should set the instrument and tempo at the Phrase level (see above).
- If your program has several Phrases, you should set the instrument at the Part level, and set the tempo at the Part or Score level. (see further below).
Setting the instrument and tempo at the highest musical subdivision ensures the MIDI synthesizer will play things correctly. If not, you may get some surprising results, as phrases with different instruments and tempos may be played via the same MIDI channel producing musically garbled output.
Play a chord progression
This code sample (Ch. 3, p. 67) demonstrates how to create chords. It plays the main chord progression from Bruce Hornsby’s “The Way It Is” (1986).
# theWayItIs.py # Plays main chord progression from Bruce Hornsby's # "The Way It Is" (1986). from music import * mPhrase = Phrase() mPhrase.setTempo(105) mPhrase.addNote(A4, SN) mPhrase.addNote(B4, SN) mPhrase.addChord([A3,E4,G4,C5], DEN) mPhrase.addChord([A3,E4,G4,C5], DEN) mPhrase.addChord([E3,D4,G4,B4], HN) mPhrase.addChord([D4,A4], SN) mPhrase.addNote(G4, SN) mPhrase.addChord([D3,D4, FS4, A4], DEN) mPhrase.addChord([D3, D4, G4, B4], DEN) mPhrase.addChord([C3, C4, D4, G4], DQN) mPhrase.addChord([C3, E4], EN) mPhrase.addNote(D4, SN) mPhrase.addNote(C4, SN) mPhrase.addChord([G2, B4, D4], DQN) mPhrase.addChord([G2, D4, G4, B4], EN) mPhrase.addChord([D3, E4, A4, C5], DEN) mPhrase.addChord([D3, D4, G4, B4], EN) mPhrase.addNote(A4, SN) mPhrase.addNote(G4, EN) mPhrase.addChord([C3,C4,D4,G4], DEN) mPhrase.addChord([C3,C4,D4,G4], DEN) mPhrase.addChord([G3,B3,D4,G4], HN) Play.midi(mPhrase)
It plays this sound:
The next code sample (Ch. 3, p. 68) demonstrates a more efficient way to create chords via Python lists. It generates the main chord progression from 2Pac’s (Tupac Shakur’s) “Changes” (1998). It so happens that this is the same chord progression as in Bruce Hornsby’s “The Way It Is”, above.
# changesByTupac.py # Plays main chord progression from 2Pac's "Changes" (1998). from music import * mPhrase = Phrase() mPhrase.setTempo(105) # section 1 - chords to be repeated pitch1 = [[E4,G4,C5], [E4,G4,C5], [D4,G4,B4], A4, G4, [D4, FS4, A4], [D4, G4, B4], [C4, E4, G4], E4, D4, C4, [G3, B4, D4]] dur1 = [DEN, DEN, HN, SN, SN, DEN, DEN, DQN, EN, SN, SN, DQN] # section 2 - embellishing chords pitch2 = [A4, B4] dur2 = [SN, SN] mPhrase.addNoteList(pitch1, dur1) # add section 1 mPhrase.addNoteList(pitch2, dur2) # add section 2 mPhrase.addNoteList(pitch1, dur1) # again, add section 1 Play.midi(mPhrase)
It plays this sound:
Create music with various melodic lines sounding simultaneously
This code sample (Ch. 3, p. 72) demonstrates how to create musical parts that contain different melodies. It plays an excerpt from Haydn’s “String Quartet”, Opus 64 no 5, which consists of four overlapping phrases, using a MIDI strings sound.
# stringQuartet.py # Demonstrates how to create concurrent musical parts. # Haydn, Opus 64 no 5 from music import * stringsPart = Part(STRINGS, 0) # create empty strings part stringsPart.setTempo(104) pitches1 = [A5, REST, A5, REST, A5, REST, A5, A6, E6, D6, D6, CS6, D6, D6, CS6, D6, E6] durations1 = [EN, EN, EN, EN, EN, EN, WN, DHN, EN, EN, HN, DEN, SN, DEN, TN, TN, QN] violin1 = Phrase(0.0) # create a phrase violin1.addNoteList(pitches1, durations1) # addnotes to the phrase stringsPart.addPhrase(violin1) # now, add phrase to part pitches2 = [FS4, G4, FS4, E4, D4, REST, G4, A4, G4, FS4, E4] durations2 = [QN, QN, QN, QN, QN, DHN, QN, QN, QN, QN, QN] violin2 = Phrase(3.0) # create a phrase violin2.addNoteList(pitches2, durations2) # addnotes to the phrase stringsPart.addPhrase(violin2) # now, add phrase to part pitches3 = [D4, E4, D4, A3, FS3, REST, E4, FS4, E4, D4, CS4] durations3 = [QN, QN, QN, QN, QN, DHN, QN, QN, QN, QN, QN] violin3 = Phrase(3.0) # create a phrase violin3.addNoteList(pitches3, durations3) # addnotes to the phrase stringsPart.addPhrase(violin3) # now, add phrase to part pitches4 = [D2, FS2, A2, D3, A2, REST, A2] durations4 = [QN, QN, QN, QN, QN, DHN, QN] violin4 = Phrase(7.0) # create a phrase violin4.addNoteList(pitches4, durations4) # addnotes to the phrase stringsPart.addPhrase(violin4) # now, add phrase to part Play.midi(stringsPart)
It plays this sound:
A Jazz trio arrangement of “Autumn Leaves”
This code sample (Ch. 3, p. 77) demonstrates how to create a score containing different parts, each containing a different phrase. This is a complete example of theScore, Part, Phrase, Note data structure. It generates the theme from “Autumn Leaves”.
# autumnLeaves.py # It plays the theme from "Autumn Leaves", in a Jazz trio arrangement # (using trumpet, vibraphone, and acoustic bass instruments). from music import * ##### define the data structure (score, parts, and phrases) autumnLeavesScore = Score("Autumn Leaves (Jazz Trio)", 140) # 140 bpm trumpetPart = Part(TRUMPET, 0) # trumpet to MIDI channel 0 vibesPart = Part(VIBES, 1) # vibraphone to MIDI channel 1 bassPart = Part(ACOUSTIC_BASS, 2) # bass to MIDI channel 2 melodyPhrase = Phrase() # holds the melody chordPhrase = Phrase() # holds the chords bassPhrase = Phrase() # holds the bass line ##### create musical data # melody melodyPitch1 = [REST, E4, FS4, G4, C5, REST, D4, E4, FS4, B4, B4] melodyDur1 = [QN, QN, QN, QN, WN, EN, DQN,QN, QN, DQN, HN+EN] melodyPitch2 = [REST, C4, D4, E4, A4, REST, B3, A4, G4, E4] melodyDur2 = [QN, QN, QN, QN, WN, EN, DQN,QN, QN, 6.0] melodyPhrase.addNoteList(melodyPitch1, melodyDur1) # add to phrase melodyPhrase.addNoteList(melodyPitch2, melodyDur2) # chords chordPitches1 = [REST, [E3, G3, A3, C4], [E3, G3, A3, C4], REST, [FS3, A3, C4]] chordDurations1 = [WN, HN, QN, QN, QN] chordPitches2 = [REST, [D3, FS3, G3, B3], [D3, FS3, G3, B3]] chordDurations2 = [DHN, HN, QN] chordPitches3 = [REST, [C3, E3, G3, B3], REST, [E3, FS3, A3, C4], [E3, FS3, A3, C4]] chordDurations3 = [QN, QN, DHN, HN, QN] chordPitches4 = [REST, [DS3, FS3, A3, B3], REST, [E3, G3, B3], [DS3, FS3, A3, B3]] chordDurations4 = [QN, QN, DHN, HN, QN] chordPitches5 = [REST, [E3, G3, B3], REST] chordDurations5 = [QN, HN, HN] chordPhrase.addNoteList(chordPitches1, chordDurations1) # add them chordPhrase.addNoteList(chordPitches2, chordDurations2) chordPhrase.addNoteList(chordPitches3, chordDurations3) chordPhrase.addNoteList(chordPitches4, chordDurations4) chordPhrase.addNoteList(chordPitches5, chordDurations5) # bass line bassPitches1 = [REST, A2, REST, A2, E2, D2, REST, D2, A2, G2, REST, G2, D2, C2] bassDurations1 = [WN, QN, EN, EN, HN, QN, EN, EN, HN, QN, EN, EN, HN, QN] bassDurations2 = [EN, EN, HN, QN, EN, EN, HN, QN, EN, EN, HN, QN] bassPitches2 = [REST, C2, G2, FS2, REST, FS2, C2, B1, REST, B1, FS2, E2] bassPitches3 = [REST, E2, E2, B1, E2, REST] bassDurations3 = [EN, EN, QN, QN, HN, HN] bassPhrase.addNoteList(bassPitches1, bassDurations1) # add them bassPhrase.addNoteList(bassPitches2, bassDurations2) bassPhrase.addNoteList(bassPitches3, bassDurations3) ##### combine musical material trumpetPart.addPhrase(melodyPhrase) # add phrases to parts vibesPart.addPhrase(chordPhrase) bassPart.addPhrase(bassPhrase) autumnLeavesScore.addPart(trumpetPart) # add parts to score autumnLeavesScore.addPart(vibesPart) autumnLeavesScore.addPart(bassPart) Play.midi(autumnLeavesScore) # play music
It plays this sound:
Play a drum sound
The MIDI standard allows us to write percussive (e.g., drum) parts. MIDI has 16 channels (numbered 0 to 15). Of these, channel 9 is reserved for percussion.
When adding notes to a Part object assigned to channel 9, the pitch of the note determines which percussive instrument to play. So, whereas for other channels (0-8 and 10-15) the MIDI pitch corresponds to the note’s frequency (or piano key number), for channel 9 the MIDI pitch corresponds to a particular percussive sound (without relationship to pitch). The General MIDI standard suggests a mapping between MIDI pitch values and percussion sounds.
This code sample (Ch. 3, p. 80) demonstrates how to play a single drum sound.
# drumExample.py # A quick demonstration of playing a drum sound. from music import * # for drums always use a part on channel 9 # when using channel 9, the instrument (2nd argument) is ignored drumPart = Part("Drums", 0, 9) # try one of these as pitch: ACOUSTIC_BASS_DRUM, BASS_DRUM_1, SIDE_STICK, # ACOUSTIC_SNARE, HAND_CLAP, ELECTRIC_SNARE, LOW_FLOOR_TOM, CLOSED_HI_HAT, # HIGH_FLOOR_TOM, PEDAL_HI_HAT, LOW_TOM, OPEN_HI_HAT, LOW_MID_TOM, # HI_MID_TOM, CRASH_CYMBAL_1 (for more, see Appendix A) note = Note( PEDAL_HI_HAT, QN ) drumPhrase = Phrase() drumPhrase.addNote(note) drumPart.addPhrase(drumPhrase) Play.midi( drumPart )
It plays this sound:
Create a drum machine pattern
Given its power as a musical instrument, a drum machine may form an integral part of a composition or performance, in that it can play complex rhythmic patterns that could not possibly be performed by a single human drummer, or could be hard to perform (without errors) even by a group of drummers. Drum machines can easily be programmed to generate rhythmic patterns, from the simple to the intricate, using a wide variety of percussive sounds. Let’s see how.
This code sample (Ch. 3, p. 81) implements a drum machine pattern consisting of bass (kick), snare, and hi-hat sounds. It uses many notes, three phrases, a part and a score, with each layer adding additional rhythms.
# drumMachinePattern1.py # # Implements a drum-machine pattern consisting of bass (kick), # snare and hi-hat sounds. It uses notes, three phrases, a part and # a score, with each layer adding additional rhythms. from music import * repetitions = 8 # times to repeat drum pattern ##### define the data structure score = Score("Drum Machine Pattern #1", 125.0) # tempo is 125 bpm drumsPart = Part("Drums", 0, 9) # using MIDI channel 9 (percussion) bassDrumPhrase = Phrase(0.0) # create phrase for each drum sound snareDrumPhrase = Phrase(0.0) hiHatPhrase = Phrase(0.0) ##### create musical data # bass drum pattern (one bass + one rest 1/4 note) x 4 = 2 measures bassPitches = [BDR, REST] * 4 bassDurations = [QN, QN] * 4 bassDrumPhrase.addNoteList(bassPitches, bassDurations) # snare drum pattern (one rest + one snare 1/4 note) x 4 = 2 measures snarePitches = [REST, SNR] * 4 snareDurations = [QN, QN] * 4 snareDrumPhrase.addNoteList(snarePitches, snareDurations) # hi-hat pattern (15 closed 1/8 notes + 1 open 1/8 note) = 2 measures hiHatPitches = [CHH] * 15 + [OHH] hiHatDurations = [EN] * 15 + [EN] hiHatPhrase.addNoteList(hiHatPitches, hiHatDurations) ##### repeat material as needed Mod.repeat(bassDrumPhrase, repetitions) Mod.repeat(snareDrumPhrase, repetitions) Mod.repeat(hiHatPhrase, repetitions) ##### combine musical material drumsPart.addPhrase(bassDrumPhrase) drumsPart.addPhrase(snareDrumPhrase) drumsPart.addPhrase(hiHatPhrase) score.addPart(drumsPart) ##### view and play View.sketch(score) Play.midi(score)
It plays this sound:
Play opening theme of “Smoke on the Water”
Now that we know how to create drum parts, we can write programs that create more complete music from various genres.
This code sample (Ch. 3, p. 83) demonstrates how to combine drums with other instruments by creating the opening of Deep Purple’s “Smoke on the Water,” a rock riff from 1972. It combines melody, chords (actually, power-chords constructed from two simultaneous pitches), and drums.
The emphasis here is on demonstrating how to combine the various building elements we have seen so far.
# DeepPurple.SmokeOnTheWater.py # # Demonstrates how to combine melodic lines, chords, and # percussion. This is based on the intro of "Smoke On the Water" # by Deep Purple. from music import * ##### define the data structure score = Score("Deep Purple, Smoke On The Water", 110) # 110 bpm guitarPart = Part(OVERDRIVE_GUITAR, 0) bassPart = Part(ELECTRIC_BASS, 1) drumPart = Part(0, 9) # using MIDI channel 9 (percussion) guitarPhrase1 = Phrase() # guitar opening melody guitarPhrase2 = Phrase() # guitar opening melody an octave lower bassPhrase = Phrase() # bass melody drumPhrase = Phrase() # drum pattern ##### create musical data # guitar opening melody (16QN = 4 measures) guitarPitches = [G2, AS2, C3, G2, AS2, CS3, C3, G2, AS2, C3, AS2, G2] guitarDurations = [QN, QN, DQN, QN, QN, EN, HN, QN, QN, DQN, QN, DHN+EN] guitarPhrase1.addNoteList(guitarPitches, guitarDurations) # create a power-chord sound by repeating the melody an octave lower guitarPhrase2 = guitarPhrase1.copy() Mod.transpose(guitarPhrase2, -12) # bass melody (32EN = 4 measures) bassPitches1 = [G2, G2, G2, G2, G2, G2, G2, G2, G2, G2, G2, G2, G2, G2, G2, G2, G2, G2] bassDurations1 = [EN, EN, EN, EN, EN, EN, EN, EN, EN, EN, EN, EN, EN, EN, EN, EN, EN, EN] bassPitches2 = [AS2, AS2, C3, C3, C3, AS2, AS2, G2, G2, G2, G2, G2, G2, G2] bassDurations2 = [EN, EN, EN, EN, EN, EN, EN, EN, EN, EN, EN, EN, EN, EN] bassPhrase.addNoteList(bassPitches1, bassDurations1) bassPhrase.addNoteList(bassPitches2, bassDurations2) # snare drum pattern (2QN x 8 = 4 measures) drumPitches = [REST, SNR] * 8 drumDurations = [QN, QN] * 8 drumPhrase.addNoteList(drumPitches, drumDurations) ##### arrange material in time guitarPhrase1.setStartTime(0.0) # start at beginning guitarPhrase2.setStartTime(32.0) # start after two repetitions bassPhrase.setStartTime(64.0) # start after two more repetitions drumPhrase.setStartTime(96.0) # start after two more repetitions ##### repeat material as needed (based on how it's arranged in time) Mod.repeat(guitarPhrase1, 8) Mod.repeat(guitarPhrase2, 6) Mod.repeat(bassPhrase, 4) Mod.repeat(drumPhrase, 2) ##### combine musical material guitarPart.addPhrase(guitarPhrase1) guitarPart.addPhrase(guitarPhrase2) bassPart.addPhrase(bassPhrase) drumPart.addPhrase(drumPhrase) score.addPart(guitarPart) score.addPart(bassPart) score.addPart(drumPart) ##### write score to a MIDI file Play.midi(score)
It plays this sound: