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
- Play an arpeggio pattern using relative pitches
- Build a Scale Tutor
- Generate a piano roll interactively
- Reverse the notes in a phrase
- Create a music effect based on the DOD FX-35 guitar pedal
- Create DNA music
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. 131) demonstrates 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: