Ch. 5 – Iteration and Lists

Topics:   Iteration, Python for loop, arpeggiators, constants, list operations, range(), frange(), FX-35 Octoplus, DNA music.

Most processes in nature and in human culture involve iteration or repetition at different levels of scale.  This chapter introduces Python constructs for iteration. More information is provided in the reference textbook.

Here is code from this chapter:


Play an arpeggio pattern using absolute pitches

This code sample (Ch. 5, p. 128) demonstrates how to play an arpeggio pattern using absolute pitches.  In other words, the arpeggio is fixed in a particular musical key (in this case C major).

# arpeggiator1.py
#
# A basic arpeggiator using absolute pitches.
#

from music import *

arpeggioPattern = [C4, E4, G4, C5, G4, E4]   # arpeggiate the C chord
duration = TN                                # duration for each note

repetitions = input("How many times to repeat arpeggio: ")

arpeggioPhrase = Phrase(0.0)        # phrase to store the arpeggio

# create arpeggiated sequence of notes
for pitch in arpeggioPattern:
   n = Note(pitch, duration)    # create note with next pitch
   arpeggioPhrase.addNote(n)    # and add it to phrase

# now, the arpeggiation pattern has been created.

# repeat it as many times requested
Mod.repeat(arpeggioPhrase, repetitions)

# add final note to complete arpeggio
lastPitch = arpeggioPattern[0]      # use first pitch as last pitch
n = Note(lastPitch, duration * 2)   # using longer duration
arpeggioPhrase.addNote(n)           # add it

Play.midi(arpeggioPhrase)

It plays this sound (if you type 7 as input):


 


Play an arpeggio pattern using relative pitches

This code sample (Ch. 5, p. 131demonstrates how to play an arpeggio pattern using relative pitches.  This makes the arpeggiator a little more flexible.  The relative pitches are added to a root note (e.g., C4) provided through user input.

# arpeggiator2.py
#
# A basic arpeggiator using relative pitches.
#

from music import *

arpeggioPattern = [0, 4, 7, 12, 7, 4]   # arpeggiate a major chord
duration = TN                           # duration for each note

rootPitch   = input("Enter root note (e.g., C4): ")
repetitions = input("How many times to repeat arpeggio: ")

arpeggioPhrase = Phrase(0.0)        # phrase to store the arpeggio

# create arpeggiated sequence of notes
for interval in arpeggioPattern:
   pitch = rootPitch + interval # calculate absolute pitch
   n = Note(pitch, duration)    # create note with next pitch
   arpeggioPhrase.addNote(n)    # and add it to phrase

# now, the arpeggiation pattern has been created.

# repeat it as many times requested
Mod.repeat(arpeggioPhrase, repetitions)

# add final note to complete arpeggio
lastPitch = rootPitch + arpeggioPattern[0]  # close with first pitch
n = Note(lastPitch, duration * 4)   # but with longer duration
arpeggioPhrase.addNote(n)           # add it

Play.midi(arpeggioPhrase)

It plays this sound (if you type G3 and 7 as inputs):


 


Build a Scale Tutor

The following program (Ch. 5, p. 136) demonstrates how to output the notes (pitches) in a particular scale.

# scaleTutor.py
#
# Outputs the pitches of a scale, starting at a given root.
#
# Also demonstrates the reverse look-up of MIDI constants using music
# library's MIDI_PITCHES list.

from music import *

print "This program outputs the pitches of a scale."

# get which scale they want
scale = input("Enter scale (e.g., MAJOR_SCALE): ")

# get root pitch
root = input("Enter root note (e.g., C4): ")

# output the pitches in this scale
print "The notes in this scale are",   # print prefix (no newline)

# iterate through every interval in the chosen scale
for interval in scale:
   pitch = root + interval     # add interval to root
   print MIDI_PITCHES[pitch],  # print pitch name (no newline)

# after the loop
print "."  # done, so output newline

Generate a piano roll interactively

This code sample (Ch. 5, p. 138) demonstrates how to construct an interactive piano roll generator.  The program allows us to specify which MIDI instrument to use.   As is, it accepts only one melodic line.  It may be extended to accept more lines.

# pianoRollGenerator.py
#
# Task:   An interactive pianoRoll generator.
#
# Input:  User selects MIDI instrument and enters pitches
#         one at a time.
#
# Output: Program generates a pianoRoll for the entered pitches,
#         and plays the corresponding notes.
#
# Limitation: Currently, all notes have QN durations.

from music import *

# ask user to select a MIDI instrument
instrument = input("Select MIDI instrument (0-127): ")

# output the name of the selected instrument
print "You picked", MIDI_INSTRUMENTS[instrument]

howMany = input("How many notes to play (0 or more): ") 

pitches = []   # to be populated via user input

for i in range(howMany):                 # loop this many times
   pitch = input("Enter note (e.g., C4): ") # get next pitch
   pitches.append( pitch )                  # append to pitch list

# now, all pitches have been entered by user and stored in pitches

# create notes
phrase = Phrase()                        # create empty phrase
phrase.setInstrument( instrument )       # use selected instrument

for pitch in pitches:                    # for each pitch in the list
   note = Note( pitch, QN )                 # create next note
   phrase.addNote( note )                   # and add it to phrase

# now, all notes have been created and stored in phrase

# generate pianoRoll and play notes
View.pianoRoll( phrase )
Play.midi( phrase )

Reverse the notes in a phrase

This code sample (Ch. 5, p. 144) demonstrates how to reverse the notes in a phrase. This is equivalent to the Mod.retrograde() function.

# retrograde.py
#
# Demonstrates one way to reverse the notes in a phrase.
#

from music import *

# create a phrase, add some notes to it, and save it (for comparison)
pitches = [C4, D4, E4, F4, G4, A4, B4,   C5]    # the C major scale
rhythms = [WN, HN, QN, EN, SN, TN, TN/2, TN/4]  # increasing tempo

phrase = Phrase()
phrase.addNoteList( pitches, rhythms )
Write.midi(phrase, "retrogradeBefore.mid") 

# now, create the retrograde phrase, and save it
pitches.reverse()  # reverse, using the reverse() list operation
rhythms.reverse() 

retrograde = Phrase()
retrograde.addNoteList( pitches, rhythms )
Write.midi(retrograde, "retrogradeAfter.mid")

To implement the functionality of Mod.retrograde() precisely, we would be working with an existing phrase. The following uses the existing phrase’s notes:

# now, a more general way...

# get the notes from the phrase
noteList = phrase.getNoteList()   # this gives us a list

pitches   = []  # create empty lists of pitches
durations = []  # ...and durations

# iterate through every note in the note list
for note in noteList:
   # for each note, get its pitch and duration value
   pitches.append( note.getPitch() )      # append this pitch
   durations.append( note.getDuration() ) # append this duration

# now, create the retrograde phrase, and save it
pitches.reverse()  # reverse, using the reverse() list operation
durations.reverse() 

retrograde = Phrase()
retrograde.addNoteList( pitches, durations )
Write.midi(retrograde, "retrogradeAfter.mid")

Yet another way would be to iterate through the note list in reverse order directly.  The following code demonstrates this:

# a more economical way...

# get the notes from the phrase
noteList = phrase.getNoteList()   # this gives us a list

pitches   = []  # create empty lists of pitches
durations = []  # ...and durations

# iterate *backwards* through every note in the note list
numNotes = len( noteList )   # get the number of notes in the list

# iterate through all the notes in reverse order
for i in range( numNotes ): 

   # calculate index from the other end of the list
   reverseIndex = numNotes - i - 1

   note = noteList[reverseIndex]   # get corresponding note

   pitches.append( note.getPitch() )      # append this pitch
   durations.append( note.getDuration() ) # append this duration

# now, create the retrograde phrase, and save it
retrograde = Phrase()
retrograde.addNoteList( pitches, durations )
Write.midi(retrograde, "retrogradeAfter.mid")

Create a music effect based on the DOD FX-35 guitar pedal

This code sample (Ch. 5, p. 147) shows how to implement a more advanced version of a classic guitar effect box, the DOD FX-35 Octoplus. The FX-35 Octoplus generates a note one octave below the original note, allowing guitarists to add “body” to their sound, or play bass lines on regular guitars. Here we generate one octave below plus a fifth below, creating a “power chord” effect from a single melodic line.

# octoplus.py
#
# A music effect based loosely on the DOD FX-35 guitar pedal.
# It takes a music phrase and generates another phrase containing
# the original notes plus an octave lower, and a fifth lower.  

from music import *

# program constants
instrument = STEEL_GUITAR
tempo = 110

# test melody - riff from Deep Purple's "Smoke on the Water"
pitches = [G2, AS2, C3,  G2, AS2, CS3, C3, G2, AS2, C3,  AS2, G2]
durs    = [QN, QN,  DQN, QN, QN,  EN,  HN, QN, QN,  DQN, QN,  DHN+EN]

#################
# create original melody
originalPhrase = Phrase()

# set parameters
originalPhrase.setInstrument( instrument )
originalPhrase.setTempo( tempo )  

originalPhrase.addNoteList(pitches, durs)

#################
# create effect melody (original + octave lower + fifth lower)
octoplusPhrase = Phrase() 

# set parameters
octoplusPhrase.setInstrument( instrument )
octoplusPhrase.setTempo( tempo ) 

# for every note in original, create effect notes
for note in originalPhrase.getNoteList():

   pitch = note.getPitch()        # get this note's pitch
   duration = note.getDuration()  # and duration

   # build list of effect pitches, for given note
   chordPitches = []                  # create list to store pitches
   chordPitches.append( pitch )       # add original pitch
   chordPitches.append( pitch - 12 )  # add octave below
   chordPitches.append( pitch - 5 )   # add fifth below
   # now, list of concurrent pitches if ready, so...

   # add effect pitches (a chord) and associated duration to phrase
   octoplusPhrase.addChord( chordPitches, duration )

# now, we have looped through all pitches, and effect phrase is built

#################
# save both versions (for comparison purposes)
Write.midi(originalPhrase, "octoplusOriginal.mid")
Write.midi(octoplusPhrase, "octoplusEffect.mid")

When saved, the two MIDI files sound like this (first the original, then the effect):



 


Create DNA music

Many researchers have explored ways to convert human proteins into music. This conversion (also known as sonification) allows people to better understand and study the structures and interdependencies in genetic material. In one study, Takahashi and Miller made music from the amino acid sequence in a human protein (Takahashi and Miller, 2007).

The code sample below (Ch. 5, p. 150) builds on their technique.

# proteinMusic.py
#
# Demonstrates how to utilize list operations to build an unfolding piece
# of music based on the first 13 amino acids in the human thymidylate
# synthase A (ThyA) protein.
#
# The piece starts with the 1st amino acid, continues with the 1st and 2nd
# amino acids, then with the 1st, 2nd, and 3rd amino acids, and so on,
# until all 13 amino acids have been included.
#
# See: Takahashi, R. and Miller, J.H. (2007), "Conversion of Amino-Acid
# Sequence in Proteins to Classical Music: Search for Auditory Patterns",
# Genome Biology, 8(5), p. 405. 

from music import *

# set of pitches/rhythms to use for building incremental piece
pitches = [[D3, F3, A3], [E3, G3, B3], [B3, D4, F4], [D4, F4, B4],
            [D4, F4, A4], [G4, B4, E5], [G4, B4, D5], [A4, C4, E4],
             [B3, G3, E3], [A4, C5, E5], [A4, C5, E5], [E3, G3, B3],
              [A3, C4, E4]]
rhythms = [HN,           QN,           HN,           QN,
            HN,           EN,           WN,           WN,
             EN,           QN,           QN,           QN,
              QN]

# we will store each incremental portion in a separate phrase
phraseList = []   # holds incremental phrases

# iterate through every index of the pitch/rhythm set
for i in range( len(pitches) ):

   # get next incremental slice of pitches/rhythms
   growingPitches = pitches[0:i+1]
   growingRhythms = rhythms[0:i+1]

   # build next incremental phrase (no start time - for sequential play)
   phrase = Phrase()            # create empty phrase
   phrase.addNoteList( growingPitches, growingRhythms)
   silenceGap = Note(REST, HN)  # add a separator at the end of the phrase...
   phrase.addNote( silenceGap ) # ...to distinguish from subsequent phrases  

   # remember this phrase
   phraseList.append( phrase )

# now, phraseList contains incremental phrases from set of pitches/rhythms

# add incremental phrases to a part
part = Part()
for phrase in phraseList:
   part.addPhrase( phrase )
# now, all phrases have been added to the part

# set the tempo
part.setTempo(220)

# view part and play
View.sketch( part )
Play.midi( part )

It plays this sound: