Chapter 3

Organization and Data

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.

3.1 Overview

In this chapter, we introduce Python data structures for music making. In particular, we look at some of the Python music data objects and how they represent musical information. It is assumed that you already know how to create notes and rests, and that you can write basic Python programs that play a note, or that output a value, as introduced in Chapter 2.

3.2 Musical Organization

Music is made up of structured sounds and silence. But music, as sound or ideas, is not a concrete thing that we can see, grasp, or capture. In order to do so, we create musical representations. Typically, these representations stand for particular musical events and describe the characteristics of these events. But what are music representations for? They serve a number of functions. First, they make music concrete and stable so that it can be stored and manipulated; we can capture music as scores, data structures, and files that we can process and modify. Second, they allow music to be communicated in ways other than aurally; we can transfer files to other software packages, send the musical representations over the Internet, and print them out using music-publishing software. Third, a representation is a conception of music, a way of understanding it, a way to think about it. The way we represent music says a lot about what we conceive music to be, and it influences the types of musical transformations that will be evident and available to us. Importantly, the music representation is not the music, and so the way a representation is interpreted by software, hardware, or human beings can vary the outcome significantly by making assumptions about aspects not represented and amplifying any distortions present in the representation system.

Over time there have been many schemes for representing music, and in Western cultures the most prevalent has been the use of the notated score. The data structure in the Python music library takes this tradition as a starting point and builds upon it. A computer representation of music, or anything else, needs to be quite detailed because the computer’s interpretation is very literal, even moronic. As we proceed through this book the level of detail in the music representations will become more fine-grained, but for now many assumptions are made to keep the task manageable.

3.2.1 Music Data Structure

Musical information in Python (through the music library) is stored in a hierarchical fashion, mimicking a conventional score on paper.

Score (contains any number of Parts)

|

+— — Part (contains any number of Phrases)

|

+— — Phrase (contains any number of sequential Notes—e.g., a melody)

|

+— — Note (contains a single musical event)

This is a highly orthodox structure where a piece of music is represented as a score, that score has several parts (e.g., a flute part and a percussion part), each part contains phrases (e.g., melodies, riffs, grooves, sequences, patterns), and each phrase is made up of a series of one or more notes (individual sound events). The structure and naming conventions are designed to be familiar to those with some musical knowledge however, this hierarchy is only a starting point and, as will become clear later, you can represent any style of music within this structure.

Notes (and rests) are the most important and most basic music event. Actually, notes (or note objects) are created from the Note class (as seen in the previous chapter):

n = Note(C4, QN)

Below we see how we can organize notes into larger structures, using more classes from the music library, to organize and perform realistic musical pieces.

3.3 Phrases

In music theory, a phrase refers to a grouping of consecutive notes. A phrase typically contains a melody—a linear list of notes that acts as a musical unit.

Definition: A phrase consists of a sequence of notes, the start time (in the piece), timbre (instrument used), and tempo (speed).

The Python music library provides the Phrase class, which allows storing and manipulating a series of Note objects. Phrase objects have other attributes, including:

  • start time, that is, when it is supposed to be played in a piece (a real number—0.0 or greater)
  • instrument—the instrument number used to play the phrase (0 to 127)
  • tempo—the speed of playback (a real number—0.0 or greater, in quarter note beats per minute)

Notes in a phrase are played sequentially (i.e., one after the other). If a gap is desired between two notes, then a Rest note should be introduced. Phrases may also contain chords (i.e., sets of concurrent notes).

3.3.1 Creating Phrases

We can create a new phrase like this:

phr = Phrase() # create an empty phrase

Alternatively, a new phrase can be created using an argument, the start time.

phr = Phrase(0.0)

A phrase created like this is set to start at time 0.0, that is, the beginning of the piece.

Fact: Musical time in Python is measured in quarter notes (i.e., time 1.0 equals the length of a quarter note).

There is a subtle, but big difference between Phrase() and Phrase(0.0). If no start time is specified, then when added to a part (more on this later) the phrase starts at the end of the previous phrase (or at the beginning of the piece, if this is the first phrase). On the other hand, phrases with start times are placed within the part at exactly the time specified. While it is convenient to create phrases without start times, when creating larger songs it is necessary to create phrases with start times.

As mentioned earlier, the music library provides constants for common time values, such as QN for 1.0 (one quarter note), or WN for 4.0 (four quarter notes). These constants are listed in Appendix A.

3.3.2 Adding Notes

Once an empty phrase has been created (as above), you can add notes to it using the addNote() function.

n = Note(C4, HN)
phr.addNote(n)

Since adding notes to phrases is very common, you can combine these two statements into one:

phr.addNote(C4, HN)

For example, here is the first theme of Beethoven’s fifth symphony:

phr = Phrase()	# create an empty phrase
# the first note is an eighth note rest, so
r = Rest(EN)	# create it
phr.addNote(r)	# and add it to the phrase
g = Note(G4, EN)	# create a G4 note eighth note
phr.addNote(g)	# and add it to the phrase three times
phr.addNote(g)
phr.addNote(g)
ef = Note(EF4, QN)	# create an EF4 note
phr.addNote(ef)	# add it to the phrase
phr.addRest(r)	# add the eighth note rest again
f = Note(F4, EN)	# create an F4 note
phr.addNote(f)	# and add it to the phrase three times
phr.addNote(f)
phr.addNote(f)
d = Note(D4, QN)	# create a D4 note
phr.addNote(d)	# add it to the phrase
Play.midi(phr)	# play phrase

Notice how we create a note once and may add it several times to the phrase. This results in several notes of the same pitch and duration played back to back.

Also, notice how time consuming it becomes to add notes like this. Given that many more notes are needed to construct a piece, the music library provides a much more economical way for adding notes by using lists.

3.4 Python Lists

In addition to integers, floats, and strings, Python has a very useful data type, called a list. As its name suggests, the list data type is a collection of values in a specific order. To create a list, simply put the values in square brackets and separate them by commas. For example, here is a list of integers assigned to the variable x:

>>> x = [1, 2, 3]
>>> x
[1, 2, 3]

Here is a list of strings:

>>> y = ["Python", "is", "fun!"]
>>> y
['Python', 'is', 'fun!']

A list may contain values of different data types, for example:

>>> z = [2, "be or not", 2.0, "be?"]
>>> z
[2, 'be or not', 2.0, 'be?']

In this chapter, we use lists to store pitch and durations. For example,

>>> pitches = [C4, E4, G4]
>>> pitches
[60, 64, 67]
>>> durations = [QN, HN, EN]
>>> durations
[1.0, 2.0, 0.5]

By the way, notice how the interpreter replaces music library constants (e.g., C4) with the corresponding numerical values (e.g., 60).

3.4.1 List Concatenation

Python uses the “+” operator with lists for concatenation (i.e., joining of two lists). If you think about it, this makes sense—concatenation is equivalent to addition; that is, we are adding lists together. For example,

>>> [1, 2] + [3, 4]
[1, 2, 3, 4]
>>> [2, "be or not"] + [2.0, "be?"]
[2, 'be or not', 2.0, 'be?']
>>> [2] + ["be or not"] + [2.0, "be?"]
[2, 'be or not', 2.0, 'be?']

3.4.2 List Repetition

Python uses the “*” operator with lists for repetition (i.e., duplicating a list a number of times). In a way, this makes sense, as “multiplying” a list results in many copies of the list concatenated together. This is consistent with the concept of multiplication with numbers, that is, multiplication is a shorthand for multiple additions. For example,

>>> [0] * 10
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
>>> [1, 2] * 4
[1, 2, 1, 2, 1, 2, 1, 2]
>>> ["hello"] *6
['hello', 'hello', 'hello', 'hello', 'hello', 'hello']
>>> [2, 'be or not', 2.0, 'be?'] * 2
[2, 'be or not', 2.0, 'be?', 2, 'be or not', 2.0, 'be?']

Finally, we can mix list concatenation (“+”) and repetition (“*”) operators. Repetition operations are performed first, followed by concatenation (similarly to multiplication and addition with numbers). For example,

>>> ["Python"] + ["is"] + ["fun"]*3
['Python', 'is', 'fun', 'fun', 'fun']
>>> [0]*2 + [1]*3 +[2]*5
[0, 0, 1, 1, 1, 2, 2, 2, 2, 2]
>>> [WN] + [HN]*2 + [QN]*4
[4.0, 2.0, 2.0, 1.0, 1.0, 1.0, 1.0]

Notice how the duration constants WN, HN, and QN are converted to the corresponding numbers before list operations are performed.

3.5 Adding Notes with Lists

A faster way to enter notes is to use Python lists. Instead of adding one note at a time to a Phrase, you can store the pitch and durations for each note in parallel lists:

pitches = [E4, E4, E4, C4, REST, D4, D4, D4, B3]
durations = [ENT, ENT, ENT, HN, QN, ENT, ENT, ENT, HN]

Then, in one step, you can add all these pitches and durations into a Phrase, as follows:

phr.addNoteList(pitches, durations)

The first list contains the pitches of the notes, while the second list contains the corresponding durations. In other words, the first note has a pitch of E4 (the first element of the pitches list) and a duration of ENT (the first element of the durations list), and so on. The last note has a pitch of B3 and HN. The number of items in both lists must be the same, otherwise you get an error.

Definition: Two lists are called parallel if every item of the first list corresponds to the item, at the same position, in the second list.

To demonstrate how much faster this way is, here is the opening of Beethoven’s fifth again, this time using lists of pitches and durations:

phr = Phrase()	# create an empty phrase
# create parallel lists of pitches and durations
pitches = [E4, E4, E4, C4, REST, D4, D4, D4, B3]
durations = [ENT, ENT, ENT, HN, QN, ENT, ENT, ENT, HN]
# add notes to phrase
phr.addNoteList(pitches, durations)
Play.midi(phr)	# play phrase

Compare the number of lines (and typing effort) between this and the earlier example.

Fact: The addNoteList() function supports up to four parallel lists, namely, pitches, durations (these two are required), dynamics, and pannings (these two are optional, but in this order).

Good Style: In parallel lists, align the corresponding pitches and durations.

As seen above, aligning the corresponding pitches and durations by adding additional white space improves the readability of your musical data but does not affect the list.

Appendix B contains additional Phrase functions. These will be very useful when creating algorithmic music in later chapters (or in your own endeavors).

3.6 Case Study: Ludwig van Beethoven—“Für Elise”

Now that we have learned about creating phrases and adding notes to them, let’s create a 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, A4, REST, C4, E4, A4, B4, REST, E4]
durations1 = [SN, SN, SN, SN, SN, SN, SN, SN, EN, SN, SN, SN, SN, EN, SN, SN]
pitches2 = [GS4, B4, C5, REST, E4]
durations2 = [SN, SN, EN, SN, SN]
pitches3 = [C5, B4, A4]
durations 3 = [SN, SN, EN]
# create an empty phrase, and construct the theme from the above motifs
theme = Phrase()
theme.addNoteList(pitches1, durations1)
theme.addNoteList(pitches2, durations2)
theme.addNoteList(pitches1, durations1) # again
theme.addNoteList(pitches3, durations3)
# play it
Play.midi(theme)

When looking at the musical score for this piece (available online), you may notice that the theme has some repetition in it. As we have done in this example, try to take advantage of such repetitions in musical material to reduce the effort in representing it in code. You can create a list of notes one time and add it many times. In the above, pitches1 and durations1 were constructed to capture such material, and thus are added twice.

3.6.1 Exercise

Transcribe one of your favorite melodies into Python. It should be at least as long as “Für Elise.”

3.7 Musical Scales

The collection of notes from one pitch to the next one of the same name, say C4 to C5, is an octave. All the notes within an octave (including sharps or flats) make up the notes of the “chromatic” scale (12 notes).

Definition: A scale is a set of pitches within an octave.

Definition: The first pitch of a scale is called the root, or the tonic.

Any set of pitches within an octave can be a scale. Some are more consonant (pleasant) or dissonant than others. This is partially due to cultural conditioning (i.e., the types of music we were born into); however, there are some aspects that are controlled by the structure of our inner ear.*

Scales can be described by specifying the process of playing them on a piano (or guitar). All you need to do is specify the distances (or intervals) between consecutive pitches in the scale.

Definition: An interval is the distance between two pitches.

Intervals are specified in terms of the number of chromatic steps (piano keys or guitar frets) you have to move through to get from one note to the other.

Fact: The music library uses integers to describe intervals.

For example, the interval between C and D is 2 steps (i.e., two piano keys, or two guitar frets). The interval between E and F is 1. Can you say why?

Patterns of interval steps can form scales. In Western music we have two very common scales, called the “major” and “minor” scales.

3.7.1 The Major Scale

The major scale is very important in Western music. For example, the C major scale consists of the seven white keys on a piano starting on C (i.e., C, D, E, F, G, A, B). In other words, to step through the C major scale, you have to start at C, move up two keys to D (interval of 2), move up two more keys to E (again, interval of 2), move up one key to F (interval of 1), and so on. Put together, this pattern of intervals is 2, 2, 1, 2, 2, 2, and 1 (the last interval to bring us to the root pitch, one octave higher, for closure/completeness). This pattern (2, 2, 1, 2, 2, 2, and 1) gives this scale its “major” sound characteristic. If the pattern is transposed (i.e., shifted) to start on D (i.e., D, E, F#, G, A, B, C#), it gives us the D major scale, and so on, for each note.

Definition: The relative intervals in a major scale are 2, 2, 1, 2, 2, 2, 1.

Another useful way to describe a scale is in terms of absolute intervals, that is, intervals from the root. To do so, for every pitch in the scale, we state the distance from the root. For example, using absolute intervals, the C major scale becomes 0 (i.e., C—the root), 2 (i.e., D), 4 (i.e., E), 5 (i.e., F), and so on.

Definition: The absolute intervals in a major scale are 0, 2, 4, 5, 7, 9, 11.

These intervals hold for any major scale, regardless of the root note. Accordingly, the Python music library defines a major scale as follows:

MAJOR_SCALE = [0, 2, 4, 5, 7, 9, 11]

This representation is convenient, because it allows us to easily create a major scale from any given root.

3.7.2 The Minor Scale

The minor scale is another common scale in Western music. The minor scale follows a slightly different pattern. All the white keys starting from A (A, B, C, D, E, F, G) make up a minor scale—the A natural minor scale. Transposing this pattern to start at C generates the C natural minor scale (C, D, E, F, G, A, B), and so on.

Definition: The relative intervals in a natural minor scale are 2, 1, 2, 2, 1, 2, 2.

Definition: The absolute intervals in a natural minor scale are 0, 2, 3, 5, 7, 8, 10.

These intervals hold for any natural minor scale, regardless of the root note. Accordingly, the Python music library defines a natural minor (or Aeolian) scale as follows:

AEOLIAN_SCALE = [0, 2, 3, 5, 7, 8, 10]

3.7.3 Other Scales

For convenience, the Python music library defines several scales as constants (see Appendix A, Scale and Mode Constants). These include BLUES_SCALE, CHROMATIC_SCALE, DORIAN_SCALE, MIXOLYDIAN_SCALE, and PENTATONIC_SCALE.

These scales do not contain specific notes (e.g., C5, D5, etc.). Instead, they are defined as absolute intervals. The first interval is always 0, denoting the root. Subsequent intervals are calculated by subtracting MIDI pitches of scale notes from the root. There is wisdom in this representation; it is very efficient. It allows you to use any MIDI note (pitch) as the starting note and, through arithmetic, find the other notes in the scale. For example, the following piece of code outputs the pitches for a major scale starting at C4:

# printScale.py
# Outputs the pitches of the major scale pitches,
# starting at a given root.
from music import *
root = C4	# starting pitch
scale = MAJOR_SCALE	# scale
# output the pitches in this scale
print "The major scale starting at pitch", root, ":"
for interval in scale:
pitch = root + interval	# add the interval to the root
print pitch
# after the loop
print "Done."

The scales in Appendix A are not exhaustive by any means. There is a wide variety of scales used in contemporary as well as non-Western styles of music. By representing a scale using a list of intervals, the music library makes it possible to define any scale you wish in your programs.

3.7.4 Exercise

In the above example, change the C4 to another pitch, or change MAJOR_SCALE to another scale (see Appendix A), to get the pitches of that scale.

Can you tell the difference between major and minor scales when you hear them? Some people can describe this difference, in terms of the feelings each one generates—can you? Give it a try.

3.8 Musical Instruments

Musical instruments are as old as human culture. The musical instruments used to perform a composition dictate its sound. Each instrument has a specific timbre, or tone quality. On a computer we use electronic (virtual) instruments to play back our music. These can have sounds sampled (recorded) from acoustic instruments or produced by synthesizers.

Definition: Timbre is the sound color or tone quality that distinguishes different instruments.

Computers support playback of music with a wide variety of instruments, as discussed below.

3.8.1 MIDI Instruments

In the 1980s the electronic musical instrument industry developed the MIDI (Musical Instrument Digital Interface) standard. MIDI specifies how computers and other digital musical instruments can generate and exchange musical data in a standard, unified way. A numbered list of instruments and sounds is part of the General MIDI specification. Your computer is equipped with a music synthesizer, which plays the notes generated by your Python programs with sounds corresponding to this instrument list.

Fact: The Play.midi() function plays music through your computer’s MIDI synthesizer.

A couple of decades ago, such a synthesizer was a very expensive, avant garde instrument. Today, it is routinely packaged with standard computers. This powerful synthesizer remains hidden from the casual user. It is through your Python programs (and the music library) that you can easily access this synthesizer to generate algorithmic music.

The Python music library provides 128 different instruments as specified by the General MIDI standard. These instruments are divided into 15 different families (Table 3.1).

Table 3.1

The 128 MIDI Instruments (grouped by family).

Family

Instruments

Piano

Acoustic grand piano, bright acoustic piano, electric grand piano, honky-tonk piano, electric piano 1 (Rhodes), electric piano 2 (Dx7), harpsichord, clavinet.

Chromatic Percussion

Celesta, glockenspiel, music box, vibraphone, marimba, xylophone, tubular bells, dulcimer.

Organ

Drawbar organ, percussive organ, rock organ, church organ, reed organ, accordion, harmonica, tango accordion.

Guitar

Acoustic guitar (nylon), acoustic guitar (steel), electric guitar (jazz), electric guitar (clean), electric guitar (muted), overdriven guitar, distortion guitar, guitar harmonics.

Bass

Acoustic bass, electric bass (finger), electric bass (pick), fretless bass, slap bass 1, slap bass 2, synth bass 1, synth bass 2.

Strings and Timpani

Violin, viola, cello, contrabass, tremolo strings, pizzicato strings, orchestral harp, timpani.

Ensemble

String ensemble 1, string ensemble 2, synth strings 1, synth strings 2, choir aahs, voice oohs, synth voice, orchestra hit.

Reed

Soprano sax, alto sax, tenor sax, baritone sax, oboe, English horn, bassoon, clarinet.

Pipe

Piccolo, flute, recorder, pan flute, blown bottle, shakuhachi, whistle, ocarina.

Synth Lead

Lead 1 (square), lead 2 (sawtooth), lead 3 (calliope), lead 4 (chiff), lead 5 (charang), lead 6 (voice), lead 7 (fifths), lead 8 (bass + lead).

Synth Pad

Pad 1 (new age), pad 2 (warm), pad 3 (polysynth), pad 4 (choir), pad 5 (bowed), pad 6 (metallic), pad 7 (halo), pad 8 (sweep).

Synth Effects

Fx 1 (rain), fx 2 (soundtrack), fx 3 (crystal), fx 4 (atmosphere), fx 5 (brightness), fx 6 (goblins), fx 7 (echoes), fx 8 (sci-fi).

Ethnic

Sitar, banjo, shamisen, koto, kalimba, bag pipe, fiddle, shanai.

Percussive

Tinkle bell, agogo, steel drums, woodblock, taiko drum, melodic tom, synth drum, reverse cymbal.

Sound Effects

Guitar fret noise, breath noise, seashore, bird tweet, telephone ring, helicopter, applause, gunshot.

The General MIDI specification also supports a set of drum and percussion instruments, specifically for drum tracks and drum-machine programs. We’ll address these in more detail in the next chapter.

3.9 Setting the Instrument

Phrases normally are played on a MIDI piano instrument. You can change the instrument for some phrase, phr, by calling its setInstrument() function:

phr.setInstrument(CLARINET)

The music library provides constants for all MIDI instruments (see Table 3.1.). These include piano, guitar, bass, and violin. They also include vibraphone, marimba, and tubular_bells. Other possibilities include organ, bandoneon, jazz_guitar, contrabass, voice, trombone, alto_sax, clarinet, and ocarina. Finally, examples of more exotic instruments include ice_rain, goblins, shamisen, reverse_cymbal, seashore, bird, helicopter, and applause.

See Appendix A for the complete list of MIDI instrument constants.

3.9.1 Exercise

Experiment with different sounds (MIDI instruments). Which ones do you like better?

  • Identify four favorite instruments.
  • Create three unique lists of instruments that go together well, for different styles of music. Be creative, experiment, discover.
  • Remember that these computer instruments (similarly to the physical instruments they represent) have a “sweet spot,” that is, a range of pitches they were intended to play (usually encompassing a couple of octaves). Try getting out of those ranges. For instance, how does voice sound at very low or very high pitches? Do the same for other instruments. Find combinations of unusual timbres and write them down.

This exploration may provide inspiration in your future music-making activities.

3.9.1.1 Setting the Tempo

To play back the phrase at a faster or slower speed, you can set the tempo.

The tempo is specified in beats per minute (BPM). Higher values indicate faster playback. The default tempo is 60 BPM, which is quite slow—but has the advantage that 1 beat equals one quarter note duration and lasts for 1 second. Typical musical tempos range from 80 to 150 BPM.

To set the tempo of a phrase, use the setTempo() function. For example,

phr.setTempo(120)

sets the phrase’s tempo to 120 BPM.

3.10 Case Study: Harold Faltermeyer—“Axel F”

Now that we have seen how to set the instrument and tempo of a phrase, here is an example where they are useful: 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 durations)
pitches1	= [F4, REST, AF4, REST, F4, F4, BF4, F4, EF4, F4, REST, C5, REST]
durations1	= [QN, QN, QN, EN, QN, EN, QN, QN, QN, QN, QN, QN, EN]
pitches2	= [F4, F4, DF5, C5, AF4, F4, C5, F5, F4, EF4, EF4, C4, G4, F4]
durations2	= [QN, EN, QN, QN, QN, QN, QN, QN, EN, QN, EN, QN, QN, DQN]
# create an empty phrase, and construct theme using pitch/duration data
theme = Phrase()
theme.addNoteList(pitches1, durations1)
theme.addNoteList(pitches2, durations2)
# set the instrument and tempo for the theme
theme.setInstrument(SYNTH_BASS_2)
theme.setTempo(220)
# play it
Play.midi(theme)

Notice how the theme has been divided into two groups of pitches and durations—nothing special here. Also, notice the setting of the instrument and tempo at the bottom. This new functionality allows you to write code to play any musical theme possible.

3.10.1 Exercises

Select two diverse but well-known musical themes.

  • Write code that generates them.
  • Try to match the instruments and tempos as closely as possible.
  • Using the more exotic MIDI instruments (e.g., see Synth Pad, Synth Effects, and Sound Effects families), create an interesting musical theme of your own. You might explore possibilities using a regular musical instrument (if you know how to play one), or your voice, before you implement your code. Remember, 2 hours of design (in this case, about 5 minutes) can save you 20 hours of coding (i.e., about 50 minutes of work).

3.11 Chords

In music, chords occur when two or more notes sound together. Instruments that can play many notes at one time, such as guitars and pianos, often play chords. So far in our introduction to Phrase objects, we have focused on melodies where only one note sounds at one time—monophonic music. Here, we discuss chords and how to enter them—homophonic music.

Definition: Monophonic music refers to a sequence of nonoverlapping notes, that is, melody.

Definition: Homophonic music refers to two or more simultaneous sequences of notes with different pitches, but the same durations, that is, chords.

Definition: Polyphonic music refers to two or more simultaneous sequences of notes having both distinct pitches and durations.

Harmony is produced when two or more notes sound together. Therefore, both homophonic and polyphonic music produce harmony.

Chords are often made up of three or more notes. The chord is often named after the lowest note (also known as the root). For example:

  • The C major chord consists of note pitches C, E, and G.
  • The G major chord consists of note pitches G, B, and D.
  • The A minor chord consists of note pitches A, C, and E.

Figure 3.1 shows some commonly used chords and the pitch constants to use in your programs.

Figure 3.1

Image of Common chords in staff notation and music library pitches

Common chords in staff notation and music library pitches.

A common chord progression is C, F, G, C (many folk singers, e.g., Woody Guthrie and Bob Dylan, made their early careers using only these chords). Another common chord progression is C, Am, F, G, C.§

Many other possible chord progressions exist. Some locations within a progression sound more stable than others. Composers use these relative places of rest (stability) and unrest to create interest within a piece. When this is done well, we get musical hits. When this is not done well, we get boring or awkward pieces. Let your ear guide you.

3.11.1 Adding Chords

Earlier we saw that a phrase can contain a grouping of consecutive notes. Typically, this can be a melody or a bass line.

For convenience, the music library also allows chords to be added to a phrase. This is done with the addChord() function. For example,

phrase = Phrase()	# create an empty phrase
aMinorChord = [A3, C4, E4]	# create chord pitch list
phrase.addChord(aMinorChord, WN)	# add chord to phrase
Play.midi(phrase)	# play it back

Notice how, when adding a chord to a phrase, we provide a list of pitches and a single duration. This duration applies to all pitches—they will be played together as one block of notes.

We can continue adding chords to a phrase to create a chord progression. Chords added this way will sound sequentially. For example, the following creates a C, F, G, C chord progression:

phrase = Phrase()	# create an empty phrase
C4MajorPitches = [C4, E4, G4]	# C major (4th octave)
F4MajorPitches = [F4, A4, C5]	# F major
G4MajorPitches = [G4, B4, D5]	# G major
C5MajorPitches = [C5, E5, G5]	# C major (5th octave)
# add chord progression to phrase
phrase.addChord(C4MajorPitches, WN)
phrase.addChord(F4MajorPitches, WN)
phrase.addChord(G4MajorPitches, WN)
phrase.addChord(C5MajorPitches, WN)
Play.midi(phrase)	# play it back

The addChord() function allows two additional (yet optional) parameters, namely, dynamic level and panning position. Similarly to the duration, only one value for these is needed per chord.**

Fact: It is possible to mix chords and notes in the same phase. They will be played sequentially in the order added.

Good Style: Keep chords separate from melody, that is, create a separate phrase for each.

Keeping chords and melodic lines separate makes it easier to maintain your work. Music composers do the same thing when writing music on paper.

One exception is when single notes are part of the chord progression; sometimes, such notes are used as embellishment. This is the case in the following example.

3.11.2 Case Study: Bruce Hornsby—“The Way It Is”

Now that we have seen how to create chords, here is 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)

Notice how this particular chord progression includes single notes interspersed into the sequence. Each item (chord or note) will be played in the order added.

Also, notice the repetition of chords in the chord progression above. Adding chords with the addChord() function may be convenient for smaller chord progressions. But it does not allow us to take advantage of chord repetitions to reduce the size of our programs. Below, we address this concern.

3.11.3 Adding Chords with Lists

A faster way to enter chords is to use Python lists. Instead of adding one chord at a time into a Phrase, you can store the pitch and durations for each note in parallel lists. For example, the following code will create a simple melody intertwined with chords:

pitches =	[C4, [D4, F4, G4], E4, [F4, A4, C5], G4]
durations = [QN, QN,	QN, QN,	QN]

Then, in one step, you can add all these pitches and durations into a Phrase, as follows:

phr.addNoteList(pitches, durations)

Again, it advisable to keep chords separate from melody, that is, to use two (or more) phrases to store such music. This is accomplished using a Part object, which may contain many Phrase objects. (More on this soon.)

3.11.4 Case Study: 2Pac—“Changes”

Now that we have seen how to use lists to create chord progressions more quickly, here is the main chord progression from 2Pac’s (Tupac Shakur’s) “Changes” (1998). It so happens that this is the same chord progression as in the last case study. This is convenient, as it allows us to compare the two alternate ways of entering chords.

# 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
pitches1	= [[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]]
durations1	= [DEN,	DEN,	HN,	SQ,	SQ,	DEN,
		DEN,	DQN,	EN,	SN,	SN,	DQN]
# section 2 - embellishing chords
pitches2	= [A4, B4]
durations2	= [SN, SN]
mPhrase.addNoteList(pitches1, durations1) # add section 1
mPhrase.addNoteList(pitches2, durations2) # add section 2
mPhrase.addNoteList(pitches1, durations1) # re-add section 1
Play.midi(mPhrase)

Clearly, this is a more economical representation compared to adding chords one by one. Also, notice how using lists allows us to further reduce the size of the program, by taking advantage of repetition in the original musical material.

Good Style: When creating long parallel lists, wrap around and indent by one space, to maintain visual connection between pitches and corresponding durations.†† Doing this allows us to create very long lists and still keep track of which pitch(es) goes with which duration.

It is possible to add additional parallel lists for dynamics and pannings. These would be constructed similarly to the durations list in the example above. In other words, provide one dynamic and one panning value per chord.

3.12 Parts

So far we have used notes and phrases for musical organization. Notes are the elemental music objects. Many notes can be stored within a Phrase. Phrases have a start time in a piece. Many phrases can be added to a Part (polyphonic music). Parts can have an instrument associated with them.

In musical arrangements, it is common to refer to a musical part, such as the trumpet part or the guitar part. For this reason, the music library provides the Part class. A Part object contains a set of Phrase objects to be played by a particular instrument. These Phrase objects are played in series (i.e., one after another) or in parallel (i.e., simultaneously) and thus may overlap (according to their start times and durations).

For example, Figure 3.2 shows a Part which consists of three Phrase objects. Two of the phrases are sequential (one follows the other) and contain monophonic material (i.e., a sequence of nonoverlapping notes—melody). The third phrase contains monophonic material. Together, the three phrases make up polyphonic material (i.e., simultaneous sequences of notes having both distinct pitches and durations).

Figure 3.2

Image of A Part object contains Phrases, which in turn contain Notes

A Part object contains Phrases, which in turn contain Notes.

This example demonstrates that, even if the particular instrument does not allow for polyphony (e.g., a flute), a Part using this instrument can have different simultaneous melodies. In other words, a Part can be thought of as a group of several instruments of the same type (e.g., flute section), each playing a different melody (a Phrase).

Part objects have several attributes. These include:

  • Title—a descriptive string (e.g., “Violin 1”)
  • Instrument—an integer from 0 to 127 (or MIDI instrument constant)
  • Channel—an integer (0–15)

Parts are also the best place to set the panning for the notes that it contains, because all the notes in a part are assigned for playback on the same MIDI channel, and panning is a channel attribute in the MIDI specification. To change the stereo pan location for the part use Part.setPan(position) where position is a value between 0 and 127, with 64 (center) being the default.

3.12.1 Creating Parts

Creating a part is done using the Part() function, and passing the desired values for a title, instrument, and channel. For example,

part1 = Part("a flute part on channel 0", FLUTE, 0)

creates a part with the title “a flute part on channel 0”, using a FLUTE instrument, and assigns that instrument to MIDI channel 0.

You may also create a part with only instrument and channel. For example,

part2 = Part(DISTORTION_GUITAR, 1)

creates a part using a DISTORTION_GUITAR instrument, and assigns that instrument to MIDI channel 1.

3.12.2 MIDI Channels

The MIDI standard provides 16 channels (0–15). Each MIDI channel is capable of playing any of the 128 different MIDI instruments, but only one at a time.

Parts may be assigned to a particular MIDI channel.

Good Style: Keep parts with different instruments on different MIDI channels.

It is important to keep parts using different instruments in different MIDI channels. If you assign two parts with different instruments to the same MIDI channel, only the second instrument will be used.

If two parts are using the same instrument, it is OK to assign them to the same MIDI channel.

There is a special case among MIDI channels: MIDI channel 9 is reserved for drum kit and percussion sounds. Regardless of a part’s selected instrument, if that part is assigned to MIDI channel 9, its notes will generate percussion sounds, based on the notes’ pitches. We will explore MIDI channel 9 and percussion later in this chapter.

3.12.3 Adding Phrases

Once an empty part has been created (as above), you can add phrases to it using the addPhrase() function.

n = Note(C4, HN) # create a note
phrase1 = Phrase() # create an empty phrase
phrase1.addNote(n) # add note to phrase - nothing new, so far...
part1 = Part(DISTORTION_GUITAR, 1) # create a part
part1.addPhrase(phr)	# add phrase to part

Now, part1 contains phrase1, which in turn contains note1. When this part is played, it will generate a C4, a half note using a distorted guitar sound.

Good Style: If you are using parts, set instruments only at the part level.‡‡

Although it is possible to add phrases which have been assigned different instruments (e.g., a violin and a piano) to the same part, this is really bad style and may not work as you might expect. Even worse, the part could have been assigned yet another instrument (e.g., a DISTORTION_GUITAR).

What happens in that case? Which instrument takes precedence? The part instrument? Each phrase’s own instrument? The fact that we even have to ask this question is proof that such code style would be unnecessarily complicated.

The main reason for having parts is to provide a container for multiple phrases that need to be played by a single instrument. So, if you are using parts, simply create phrases without an instrument and add them to a part. The part takes care of the instrument for these phrases.

3.12.4 Creating Ensembles

Now that we know how to create parts that contain phrases, we can create bands and ensembles with several instruments.

Here is a simple program that demonstrates how to create a string quartet. It plays four overlapping notes (each in a separate phrase), using a MIDI strings sound.

# stringQuartet.py
# Demonstrates how to create concurrent musical parts.
# Hayden, 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 the phrase to the 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 the phrase to the 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 the phrase to the 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 the phrase to the part
Play.midi(stringsPart)

Notice how the notes begin sounding at different times (i.e., they are not chords). Creating separate phrases with different start times and durations is possible when we create ensembles.

Also, notice that, if we explicitly specify the start time of phrases, the order in which we add them to a part does not dictate their playback order.§§

Fact: When phrases have explicit start times (i.e., their start time has been set), the order that you add them to a part is independent of their start time. Such phrases play back according to their start time (and they may overlap).

Fact: When phrases do not have explicit start times, that is, phrase = Phrase(), the order that you add them to a part is critical. Such phrases play back sequentially (one after another), in the order added.

3.13 Scores

If we consider the music of a jazz trio, with its multiple parts (i.e., instruments), these parts need to be collected together into a score. Scores are essential in multipart music composition and performance.

The Score object is the highest-level musical structure provided by the Python music library. Score objects have several attributes. These include:

  • Title—a descriptive string (e.g., “My New Composition”)
  • tempo—the speed of playback (a positive real number — in quarter note beats per minute)

3.13.1 Creating Scores

Creating a score is done using the Score() function and passing the desired attribute values. There are several Score() functions (see Appendix B). The most common one takes arguments for title and tempo. If the tempo is omitted, it is set to 60 bpm (beats-per-minute) by default. This is quite a slow tempo but has the advantage that each beat (quarter note duration) equals one second of time, which is useful for music that is time-based rather than based on note durations.

This Python statement creates a score with title “Opus 1” and a default tempo of 60 bpm. The score is assigned to the variable s:

s = Score("Opus 1")

whereas this statement creates a similar score but with a tempo of 135 bpm:

s = Score("Opus 1", 135.0)

Once a score has been created, to insert parts into it use the addPart() function, as follows:

s.addPart(part1)

You can also set the tempo of the score with the setTempo() function, as follows:

s.setTempo(120.0) # set score's tempo to 120 bpm

There are various other functions available for Scores, as well as for Parts, Phrases, and Notes. For more information on this and other music library functions, see Appendix B.

3.13.2 Putting It All Together

The music library data structure (Score, Part, Phrase, Note) has been designed to accommodate a wide variety of musical forms and structures. Note objects describe musical events; they are arranged in a list within phrases. Phrase objects have a start time, which positions them within a part. Part objects can contain many phrases and also have an instrument assigned to them. That instrument plays all notes within the part. Parts are added to a Score object, which also sets the tempo of music within it.

The Python music data structure can be thought of as a set of concentric rings, one within another, as shown in Figure 3.3.

Figure 3.3

Image of The Python music data structure

The Python music data structure.

The “target” view of the score is a simplification, because in reality a score can contain many parts, a part many phrases, and a phrase many notes. More realistically, the Python music data structure can also be represented visually as in Figure 3.4.

Figure 3.4

Image of A view of a simple musical segment represented in the Python music data structure

A view of a simple musical segment represented in the Python music data structure.

Creating music of significant complexity involves coordinating several musical lines at one time. Typical examples include multipart vocal chorales or string quartets. However, such multipart coordination is required by music of almost any style. For example, consider a jazz arrangement, where you have to coordinate a melody, a bass part, and a percussive accompaniment. The music library Part and Score classes help with this structuring.

Good Style: Reflecting compositional practices in acoustic music, there are two ways to write polyphonic music with the music library data structure:

  1. If you are dealing with polyphonic material to be played by a single instrument (e.g., a harpsichord fugue) or by a group of monophonic instruments of the same kind (e.g., a flute ensemble), you can create multiple Phrases (voices) and assign them to a single Part (with that instrument).
  2. If you are dealing with polyphonic material to be played by a group of monophonic instruments of different kinds (e.g., a woodwind quintet), you can create a single Phrase and add it to different Parts (one for each instrument) — as demonstrated by the following case study.

Obviously, nothing prevents you from combining both techniques in a computer music piece. In any case, follow the stylistic guidelines and examples of this book.

3.14 A Complete Example

3.14.1 Case Study: Joseph Kosma—“Autumn Leaves” (Jazz Trio)

Now that we have seen the all the data structures available for creating music in Python, let’s see a complete example. The following program plays the theme from “Autumn Leaves.” This song was composed by Joseph Kosma in 1945 and has become a jazz standard. It has been performed by many famous musicians, such as Jo Stafford, Edith Piaf, Nat King Cole, Frank Sinatra, Bill Evans, John Coltrane, Manfred Mann, and Eric Clapton, among others.

# 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	= [R EST, [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	= [R EST, [C3, E3, G3, B3], REST, [E3, FS3, A3, C4], [E3, FS3, A3, C4]]
chordDurations3	= [Q N,	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	= [R EST, A2, REST, A2, E2, D2, REST, D2, A2, G2, REST, G2, D2, C2]
bassDurations1	= [W N,	QN, EN,	EN, HN, QN,	EN,	EN, HN, QN,	EN,	EN, HN, QN]
bassDurations2 = [E N,  EN, HN, QN, EN,  EN, HN, QN, EN,  EN, HN,	QN]
bassPitches2  = [R EST, 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

Notice how variables names (e.g., trumpetPart, chordPhrase, etc.) capture two things: the type of information it holds (e.g., instrument or chord) and the data structure used (i.e., score, part, or phrase). By doing this, the name itself tells you everything you need to know.

Good Style: Create meaningful names for variables. Be consistent.

Meaningful variable names minimize confusion and the need to go back and forth in your code to remember the name or purpose of a particular variable. Accordingly, this minimizes programming errors and, in the long run, saves time. The longer the program, the more you will benefit from this stylistic guideline.

Also notice how, in the melody line, we added two durations (HN+EN) to construct an unusual duration value. In the notated score for this piece, this would be called a tie.

Definition: A musical tie connects two notes of the same pitch into a single note (with the combined duration).

Fact: In Python, a musical tie is implemented by adding two duration values.

Finally, this piece needs a drum kit track. We will see how to do this in the next section.

3.14.2 Exercise

The form of “Autumn Leaves” is AABC (meaning that theme A repeats once and then it is followed by two other themes, B and C). The case study above includes only theme A. Find a score of the complete piece online and add the remaining code. Follow the above coding style—it will save you time in the long term.

3.15 MIDI Drums and Percussive Sounds

The MIDI standard allows us to write percussive (e.g., drum) parts. As mentioned above, 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.

For example, here are MIDI pitches for some percussive instruments found in a drum kit:

36 (C2) Bass Drum

38 (D2) Snare Drum

42 (FS2) Closed Hi Hat

46 (AS2) Open Hi Hat

49 (DF3) Crash Cymbal

Appendix A provides the complete list of General MIDI Drum and Percussion constants. For example, this list includes: BASS_DRUM, SIDE_STICK, SNARE, HAND_CLAP, CLOSED_HI_HAT, OPEN_HI_HAT, LOW_FLOOR_TOM, CRASH_CYMBAL_1, RIDE_CYMBAL_1, COWBELL, MARACAS, SHORT_WHISTLE, and many others.

Also, the library provides abbreviated constants, for easier inclusion in pitch lists. For the percussion instruments above, the corresponding abbreviations are BDR, STK, SNR, CLP, CHH, OHH, LTM, CC1, RC1, CBL, MRC, and SWH.

For example, the following program 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)
note = Note(ACOUSTIC_BASS_DRUM, QN) # a bass drum strike
drumPhrase = Phrase()
drumPhrase.addNote(note)
drumPart.addPhrase(drumPhrase)
Play.midi(drumPart)

3.15.1 Exercises

  1. Modify the above program to generate different percussive sounds. Possibilities include: ACOUSTIC_BASS_DRUM, BASS_DRUM_1, ACOUSTIC_SNARE, HAND_CLAP, CLOSED_HI_HAT, OPEN_HI_HAT, HIGH_FLOOR_TOM, LOW_TOM, and CRASH_CYMBAL_1. See Appendix A for a complete list of constants for percussive sounds.
  2. Create a program that plays different percussive sounds in sequence (Hint: Create a phrase where you add a sequence of notes corresponding to the desired instruments.)

3.15.2 Case Study: Drum Machines

Drum machines are electronic musical instruments which can play rhythmic patterns using drums and other percussion instrument sounds. Drum machines are popular in many musical genres, such as rock, dance, and electronic music. They are routinely used in practice or jamming sessions, replacing (the sometimes difficult to find) human drummers. Drum machines are also used in live performances, especially in electronic dance music.

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.

3.15.2.1 Drum Machine Pattern #1

Here is a program that 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.

Notice how the program is structured using comments to follow the general algorithm for creating music. Most importantly, note how the comments capture the calculations of how many notes to have per measure. This style of commenting allows you to do the work once (i.e., calculate how many notes to have per measure) to make sure that everything lines up metrically, and then never have to think about it again. Just read the comments and make appropriate changes to the code below it. Then, if you like the outcome, go back and update the comment accordingly.

# 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 a 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 notes) 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 notes) 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)

Notice how we use the list repetition operator (*), e.g., [BDR, REST]*4, to repeat list items (instead of typing them out repeatedly). The former is more economical, easier to understand (especially when constructing drum machine patterns), and definitely less error prone.

Notice how we use both list repetition and concatenation operators (* and +) to construct the hi-hat pattern, e.g., [CHH]*15 + [OHH].

Finally, notice the statement

Mod.repeat(bassDrumPhrase, repetitions)

This has the effect of repeating the notes in bassDrumPhrase a number of times, namely, repetition times (i.e., 8). This and other Mod functions will be covered in the next chapter in more detail.

3.15.2.2 Exercise

Modify the above program to play various other drum machine patterns. Try different dance patterns. Try some exotic rhythms.

3.15.3 Case Study: Deep Purple—“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. To demonstrate combining drums with other instruments, here is 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. Again, 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  = [G 2, AS2, C3, G2, AS2, CS3, C3, G2, AS2, C3, AS2,	G2]
guitarDurations = [Q N, 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)
##### repeat material as needed
Mod.repeat(guitarPhrase1, 8)
Mod.repeat(guitarPhrase2, 6)
Mod.repeat(bassPhrase, 4)
Mod.repeat(drumPhrase, 2)
##### 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
##### 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

3.16 Top-Down Design

Notice how the above code has been written out conceptually from the highest level of detail to the lowest. First, we define the musical data structures, starting from the score, to the parts, to the phrases. Then we create the musical data (notes of the melody, chords, bass line). Finally, we combine the musical material to form the complete song.

Top-down design and implementation is a strategy for constructing programs. It starts from the biggest and goes to the smaller—in our case, from the score, to the parts, to the phrases, and so on. By specifying the highest-level pieces of our program first, and then dividing them into successively smaller pieces, we gain perspective, and the structure of the program is clearly defined. Then it is easy to go back and fix problems or update the program to perform slightly different tasks (e.g., extend a particular song by adding another part, add some more functionality to the program, and so on). Top-down design will make more sense when we introduce functions. Until then, use the above program as a model when constructing larger pieces of music.

Fact: By being systematic in your programming, you save time.

Top-down design and implementation is most beneficial when developing larger programs. But by applying it even to the smallest programs you write, you gain flexibility to make them grow easily, at a later time, without wasting effort on added complexity and the potential logical errors that this added complexity may introduce.

3.17 Input and Output

In the last line of the previous case study the program saved the composition as a MIDI file. In previous examples we have played back compositions directly. In addition to keyboard input and screen or speaker output (seen earlier), the Python music library allows you to read and write standard MIDI files.

A standard MIDI file is a common format for storing musical compositions. By convention, files saved in this format have a “.mid” suffix appended to their name. Many music software applications can read and/or write MIDI files. By saving your Python music compositions as MIDI files you will be able to open them for further editing, display, or playback in other software.

3.17.1 Reading MIDI Files

You can read a MIDI file into your program using the Read.midi() function. This function expects an empty score and the name of a MIDI file (saved in the same folder as your program). For example,

Read.midi(score, "song.mid")

inputs the musical data from the MIDI file “song.mid”, and stores them into score. Once the file has been read in, you can manipulate or play back the score. For example,

from music import *
score = Score()	# create an empty score
Read.midi(score, "song.mid")	# read MIDI file into it
Play.midi(score)	# play it back

This program reads the MIDI file called “song.mid” into the variable score. Notice how a score has been already created—it is an empty Score object. The program then plays the score, to demonstrate that the musical data has been successfully read into the program.

The Read.midi() function works only with standard MIDI files.¶¶

Reading musical data into our programs opens the door for some interesting possibilities. We will discuss them below.

3.17.2 Writing MIDI Files

You can create a MIDI file from your program using the Write.midi() function. This function expects a score (part, phrase, or note) and the name of a MIDI file. For example,

Write.midi(score, "song.mid")

writes the musical data in score into the MIDI file called “song.mid”. This file is saved in the same folder as your program. If the MIDI file already exists, it will be overwritten.

For example, if you replace the statement, Play.midi(autumnLeavesScore), in the last case study with the statement, Write.midi(autumnLeavesScore, “autumnLeaves.mid”), your program will save the generated music into a MIDI file.***

This is very useful. It is likely that you will want to save your music as a file for storage, playback, or use in another application. Adding Write.midi() to any of the examples so far will give you an external MIDI file that you can share with (e.g., email to) others—without the need for Python.††† This way, Python becomes your music production environment—and the MIDI file a vehicle with which you share your results. (You could use other external tools, e.g., GarageBand, to convert your MIDI files to MP3 or other formats, if you wish.)

Another possibility is to use writing and reading of MIDI files to modularize/compartmentalize your work. There is no need for one program to do ALL the work. Programs can become short and self-contained, i.e., they do one thing—and they do it well. This makes the idea of design (as discussed in Chapter 1) even more important and relevant, and opens up many creative possibilities for you to explore.

3.17.3 Exercises

  1. Modify the “Smoke on the Water” case study above to output a MIDI file. Then play it back. Do you notice any differences in timbre? If so, that’s due to the different synthesizers used (Play.midi() uses the Java synthesizer. MIDI files use the operating system’s default synthesizer.)
  2. Select a simple classical piece consisting of two voices. Transcribe it into a Python music representation using a phrase for each voice.
  3. Select a favorite popular song consisting of a chord progression and a melody. Transcribe it, using one phrase for the chords and another for the melody. Ensure that the two phrases line up in terms of note (and chord) durations. If it helps to better organize the music, consider using several phrases, as in the “Smoke on the Water” case study above.
  4. Transcribe a favorite piece of music (or an excerpt of it) in Python. You may do so by ear or from a notated musical score. Since most music has repetitions, use these to minimize duplication of effort (data entry). Duplicate material can be entered once and reused (one phrase being a copy of another). Another possibility is to repeat material using Mod.repeat() and, possibly, Mod.transpose()—see the next chapter.

3.18 Summary

This chapter introduced the remaining components of the Python music data structure. This now gives us Notes, Phrases, Parts, and Scores. Together, they allow us to transcribe arbitrary musical material into Python programs. This format is equivalent to (and some may argue easier to learn than) traditional Western musical notation. The end result is the same, that is, a representation of musical material. What makes this Python representation more powerful is that, in addition to being able to listen to the music it describes (via your computer’s MIDI synthesizer), it also allows you to write algorithms that implement various musical and other processes that modify and transform this material, as shown in the next chapter. This leads to the development of tools that can contribute to composing and/or performing music (as shown in the rest of the book).

Additionally, we learned about another data type, that is, the Python list, and its operations. We discussed musical scales and explored MIDI instruments. We discussed chords and how to efficiently model them via Python lists. We explored MIDI drums and percussion, and learned how to combine drums with other musical material (through Parts playing on MIDI channel 9). Finally, we discussed how to read and write MIDI files, which allows us to send our musical material to other people (e.g., musical collaborators), so that they may easily play it or use it with standard MIDI editors and sequencers.


* The cochlea in the inner ear (the place where sound waves are broken up into individual frequencies) is constructed in terms of the golden ratio. This clearly incorporates certain “biases” as to what sounds go together well. Ear anatomy and its aesthetic implications are beyond the scope of this book. But we invite you to explore more. Musical beauty is partially in the ear of the beholder (and the fact that we all share the same general ear design).

This example uses a Python construct called a for-loop. We will explore for-loops in Chapter 5.

Chord construction and harmony are beyond the scope of this book. If you are unfamiliar with these topics, there are many good references, such as Surmani et al. (1999), and Levine (1995).

§ As you may know already, an upper case letter chord (e.g., C) is a major chord. Adding a lower case “m” to the chord name (e.g., Cm) makes it a minor chord. Again, chord construction is beyond the scope of this book.

The alternative would be to create parallel phrases each containing a separate note in a chord. You can still do this, but it is awkward and time consuming.

** Dynamic and panning values are discussed in the previous chapter, under Notes.

†† Python is very particular with indentation, except with items in lists. We can use as much vertical or horizontal space as we wish, as long as we keep the brackets (open and close) balanced.

‡‡ In other words, it is stylistically okay to set a phrase’s instrument only when you are not planning to add it in a part.

§§ As opposed to adding notes to a phrase, which always dictates their playback order.

¶¶ If a file does not work, it is probably not a standard MIDI file. Try opening it with a MIDI editing program (there are several freeware programs available), and resave it as a type 0 or type 1 MIDI file.

*** Be careful to end the MIDI filename with the extension “.mid”. In the above example, if you accidentally use the extension “.py” you would delete the file that contains your program! This brings up another piece of advice. Back up your work often (e.g., in a different folder or backup drive). At some point, you will be happy you did.

††† To play a MIDI file, simply double click on it. Most computers know how to play MIDI files.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset