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.
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.#frommusicimport*arpeggioPattern=[C4,E4,G4,C5,G4,E4]# arpeggiate the C chordduration=TN# duration for each noterepetitions=eval(input("How many times to repeat arpeggio: "))arpeggioPhrase=Phrase(0.0)# phrase to store the arpeggio# create arpeggiated sequence of notesforpitchinarpeggioPattern:n=Note(pitch,duration)# create note with next pitcharpeggioPhrase.addNote(n)# and add it to phrase# now, the arpeggiation pattern has been created.# repeat it as many times requestedMod.repeat(arpeggioPhrase,repetitions)# add final note to complete arpeggiolastPitch=arpeggioPattern[0]# use first pitch as last pitchn=Note(lastPitch,duration*2)# using longer durationarpeggioPhrase.addNote(n)# add itPlay.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.#frommusicimport*arpeggioPattern=[0,4,7,12,7,4]# arpeggiate a major chordduration=TN# duration for each noterootPitch=eval(input("Enter root note (e.g., C4): "))repetitions=eval(input("How many times to repeat arpeggio: "))arpeggioPhrase=Phrase(0.0)# phrase to store the arpeggio# create arpeggiated sequence of notesforintervalinarpeggioPattern:pitch=rootPitch+interval# calculate absolute pitchn=Note(pitch,duration)# create note with next pitcharpeggioPhrase.addNote(n)# and add it to phrase# now, the arpeggiation pattern has been created.# repeat it as many times requestedMod.repeat(arpeggioPhrase,repetitions)# add final note to complete arpeggiolastPitch=rootPitch+arpeggioPattern[0]# close with first pitchn=Note(lastPitch,duration*4)# but with longer durationarpeggioPhrase.addNote(n)# add itPlay.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.frommusicimport*print("This program outputs the pitches of a scale.")# get which scale they wantscale=eval(input("Enter scale (e.g., MAJOR_SCALE): "))# get root pitchroot=eval(input("Enter root note (e.g., C4): "))# output the pitches in this scaleprint("The notes in this scale are",)# print prefix (no newline)# iterate through every interval in the chosen scaleforintervalinscale:pitch=root+interval# add interval to rootprint(MIDI_PITCHES[pitch],)# print pitch name (no newline)# after the loopprint(".")# 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.frommusicimport*# ask user to select a MIDI instrumentinstrument=eval(input("Select MIDI instrument (0-127): "))# output the name of the selected instrumentprint("You picked",MIDI_INSTRUMENTS[instrument])howMany=eval(input("How many notes to play (0 or more): "))pitches=[]# to be populated via user inputforiinrange(howMany):# loop this many timespitch=eval(input("Enter note (e.g., C4): "))# get next pitchpitches.append(pitch)# append to pitch list# now, all pitches have been entered by user and stored in pitches# create notesphrase=Phrase()# create empty phrasephrase.setInstrument(instrument)# use selected instrumentforpitchinpitches:# for each pitch in the listnote=Note(pitch,QN)# create next notephrase.addNote(note)# and add it to phrase# now, all notes have been created and stored in phrase# generate pianoRoll and play notesView.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.
retrograde1.py
1 2 3 4 5 6 7 8 910111213141516171819202122
# retrograde.py## Demonstrates one way to reverse the notes in a phrase.#frommusicimport*# 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 scalerhythms=[WN,HN,QN,EN,SN,TN,TN/2,TN/4]# increasing tempophrase=Phrase()phrase.addNoteList(pitches,rhythms)Write.midi(phrase,"retrogradeBefore.mid")# now, create the retrograde phrase, and save itpitches.reverse()# reverse, using the reverse() list operationrhythms.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:
# retrograde.py## Demonstrates one way to reverse the notes in a phrase.#frommusicimport*# 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 scalerhythms=[WN,HN,QN,EN,SN,TN,TN/2,TN/4]# increasing tempophrase=Phrase()phrase.addNoteList(pitches,rhythms)Write.midi(phrase,"retrogradeBefore.mid")### now, a more general way...# get the notes from the phrasenoteList=phrase.getNoteList()# this gives us a listpitches=[]# create empty lists of pitchesdurations=[]# ...and durations# iterate through every note in the note listfornoteinnoteList:# for each note, get its pitch and duration valuepitches.append(note.getPitch())# append this pitchdurations.append(note.getDuration())# append this duration# now, create the retrograde phrase, and save itpitches.reverse()# reverse, using the reverse() list operationdurations.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:
# retrograde.py## Demonstrates one way to reverse the notes in a phrase.#frommusicimport*# 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 scalerhythms=[WN,HN,QN,EN,SN,TN,TN/2,TN/4]# increasing tempophrase=Phrase()phrase.addNoteList(pitches,rhythms)Write.midi(phrase,"retrogradeBefore.mid")### now, a more economical way...# get the notes from the phrasenoteList=phrase.getNoteList()# this gives us a listpitches=[]# create empty lists of pitchesdurations=[]# ...and durations# iterate *backwards* through every note in the note listnumNotes=len(noteList)# get the number of notes in the list# iterate through all the notes in reverse orderforiinrange(numNotes):# calculate index from the other end of the listreverseIndex=numNotes-i-1note=noteList[reverseIndex]# get corresponding notepitches.append(note.getPitch())# append this pitchdurations.append(note.getDuration())# append this duration# now, create the retrograde phrase, and save itretrograde=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.frommusicimport*# program constantsinstrument=STEEL_GUITARtempo=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 melodyoriginalPhrase=Phrase()# set parametersoriginalPhrase.setInstrument(instrument)originalPhrase.setTempo(tempo)originalPhrase.addNoteList(pitches,durs)################## create effect melody (original + octave lower + fifth lower)octoplusPhrase=Phrase()# set parametersoctoplusPhrase.setInstrument(instrument)octoplusPhrase.setTempo(tempo)# for every note in original, create effect notesfornoteinoriginalPhrase.getNoteList():pitch=note.getPitch()# get this note's pitchduration=note.getDuration()# and duration# build list of effect pitches, for given notechordPitches=[]# create list to store pitcheschordPitches.append(pitch)# add original pitchchordPitches.append(pitch-12)# add octave belowchordPitches.append(pitch-5)# add fifth below# now, list of concurrent pitches if ready, so...# add effect pitches (a chord) and associated duration to phraseoctoplusPhrase.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.frommusicimport*# set of pitches/rhythms to use for building incremental piecepitches=[[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 phrasephraseList=[]# holds incremental phrases# iterate through every index of the pitch/rhythm setforiinrange(len(pitches)):# get next incremental slice of pitches/rhythmsgrowingPitches=pitches[0:i+1]growingRhythms=rhythms[0:i+1]# build next incremental phrase (no start time - for sequential play)phrase=Phrase()# create empty phrasephrase.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 phrasephraseList.append(phrase)# now, phraseList contains incremental phrases from set of pitches/rhythms# add incremental phrases to a partpart=Part()forphraseinphraseList:part.addPhrase(phrase)# now, all phrases have been added to the part# set the tempopart.setTempo(220)# view part and playView.sketch(part)Play.midi(part)