Topics: Randomness and creativity, Mozart, indeterminism, serialism, Python random functions, stochastic music, Iannis Xenakis, probabilities, wind chimes, melody generator, selection, Python if statement, flipping a coin, Russian roulette, throwing dice, realistic drums, relational and logical operators, generative music.
Computers offer us a source of untamed possibilities in the form of a random number generator. This chapter focuses on ways to tame this source of possibilities to serve our aesthetic purposes. More information is provided in the reference textbook.
Here is code from this chapter:
- Creating Mozart’s “Musikalisches Würfelspiel”
- Creating Pierre Cage’s “Structures pour deux Chances”
- Creating Iannis Xenakis’ stochastic piece,“Concret PH”
- Harnessing (or sieving) randomness – wind chimes
- Music from Brownian motion
- Throwing dice
- Let the drums come alive
- Creating generative music
Creating Mozart’s “Musikalisches Würfelspiel”
In 1787, Wolfgang Amadeus Mozart wrote “Musikalisches Würfelspiel”, a musical process for generating a 16-measure waltz through randomness – he rolled dice. In this process, each measure is selected from a set of 11 precomposed chunks of music.
This code sample (Ch. 6, p. 157) demonstrates how to implement a simplified version of Mozart’s musical game.
# Mozart.MusikalischesWurfelspiel.py # # This program generates an excerpt of Mozart's "Musikalisches # Wurfelspiel" (aka Mozart's Dice Game). It demonstrates how # randomness may be sieved (harnessed) to produce aesthetic results. # # See Schwanauer, S, and D Levitt. 1993. Appendix, in Machine Models # of Music. Cambridge, MA: MIT Press, pp. 533-538. # # The original has 16 measures with 11 choices per measure. # This excerpt is a simplified form. In this excerpt, # musical material is selected from this matrix: # # I II III IV # 96 6 141 30 # 32 17 158 5 # 40 # # Columns represent alternatives for a measure. The composer throws # dice to select an alternative (choice) from first column. # Then, connects it with the choice from second column, and so on. # from music import * from random import * # musical data structure walzerteil = Part() # contains a four-measure motif generated # randomly from the matrix above # measure 1 - create alternatives # choice 96 pitches96 = [[C3, E5], C5, G4] durations96 = [EN, EN, EN] choice96 = Phrase() choice96.addNoteList(pitches96, durations96) # choice 32 pitches32 = [[C3, E3, G4], C5, E5] durations32 = [EN, EN, EN] choice32 = Phrase() choice32.addNoteList(pitches32, durations32) # choice 40 pitches40 = [[C3, E3, C5], B4, C5, E5, G4, C5] durations40 = [SN, SN, SN, SN, SN, SN] choice40 = Phrase() choice40.addNoteList(pitches40, durations40) # measure 2 - create alternatives # choice 6 (same as choice 32) choice6 = Phrase() choice6.addNoteList(pitches32, durations32) # choice 17 pitches17 = [[E3, G3, C5], G4, C5, E5, G4, C5] durations17 = [SN, SN, SN, SN, SN, SN] choice17 = Phrase() choice17.addNoteList(pitches17, durations17) # measure 3 - create alternatives # choice 141 pitches141 = [[B2, G3, D5], E5, F5, D5, [G2, C5], B4] durations141 = [SN, SN, SN, SN, SN, SN] choice141 = Phrase() choice141.addNoteList(pitches141, durations141) # choice 158 pitches158 = [[G2, B4], D5, B4, A4, G4] durations158 = [EN, SN, SN, SN, SN] choice158 = Phrase() choice158.addNoteList(pitches158, durations158) # measure 4 - create alternatives # choice 30 pitches30 = [[C5, G4, E4, C4, C2]] durations30 = [DQN] choice30 = Phrase() choice30.addNoteList(pitches30, durations30) # choice 5 pitches5 = [[C2, C5, G4, E4, C4], [G2, B4], [C2, E4, C5]] durations5 = [SN, SN, QN] choice5 = Phrase() choice5.addNoteList(pitches5, durations5) # roll the dice!!! measure1 = choice([choice96, choice32, choice40]) measure2 = choice([choice6, choice17]) measure3 = choice([choice141, choice158]) measure4 = choice([choice30, choice5]) # connect the random measures into a waltz excerpt walzerteil.addPhrase(measure1) walzerteil.addPhrase(measure2) walzerteil.addPhrase(measure3) walzerteil.addPhrase(measure4) # view and play randomly generated waltz excerpt View.sketch(walzerteil) Play.midi(walzerteil)
Since randomness is involved, every time it runs, it will generate different outputs. Here are three examples:
Creating Pierre Cage’s “Structures pour deux Chances”
An interesting way of applying randomness in music is in a style referred to as chance music. Chance music, also known as aleatoric music, is a compositional technique that introduces elements of randomness into the compositional process. John Cage, among other composers, is well known for his aleatoric compositions. On the other hand, serialism involves using deterministic rules to control choices within the compositional process. Pierre Boulez is well known for his serial compositions.
Aleatoric and serial techniques are compositional opposites of each other. Surprisingly, though, the musical outcome can appear to be very similar. This can be observed in these two pieces — the first aleatoric, the second serialist:
- John Cage, “Music of Changes, Book I” (1951), and
- Pierre Boulez, “Structures I for two pianos” (1951-2).
This code sample (Ch. 6, p. 161) capitalizes on this similarity to create a program where is impossible to determine, simply by listening to it, if the compositional approach was aleatoric or a serialist. This piece is attributed to Pierre Cage. Pierre Cage is a fictitious composer (a remix of the names, Pierre Boulez and John Cage).
# PierreCage.StructuresPourDeuxChances.py # # This program (re)creates pieces similar to: # # Pierre Boulez, "Structures I for two pianos", and # John Cage, "Music of Changes, Book I". # # The piece generated consists of two parallel phrases containing # notes with random pitch and duration. # from music import * from random import * # import random number generator numberOfNotes = 100 # how many notes in each parallel phrase ##### define the data structure part = Part() # create an empty part melody1 = Phrase(0.0) # create phrase (at beginning of piece) melody2 = Phrase(0.0) # create phrase (at beginning of piece) ##### create musical data # create random notes for first melody for i in range(numberOfNotes): pitch = randint(C1, C7) # get random pitch between C1 and C6 duration = random() * 1.0 # get random duration (0.0 to 2.0) dynamic = randint(PP, FFF) # get random dynamic between P and FF note = Note(pitch, duration, dynamic) # create note melody1.addNote(note) # and add it to the phrase # now, melody1 has been created # create random notes for second melody for i in range(numberOfNotes): pitch = randint(C1, C7) # get random pitch between C1 and C6 duration = random() * 1.0 # get random duration (0.0 to 2.0) dynamic = randint(PP, FFF) # get random dynamic between P and FF note = Note(pitch, duration, dynamic) # create note melody2.addNote(note) # and add it to the phrase # now, melody2 has been created ##### combine musical material part.addPhrase(melody1) part.addPhrase(melody2) ##### play and write part to a MIDI file Play.midi(part) Write.midi(part, "Pierre Cage.Structures pour deux chances.mid")
Since randomness is involved, it will generate output similar (but not identical) to this:
Creating Iannis Xenakis’ stochastic piece,“Concret PH”
Stochastic music is a compositional method employed by Iannis Xenakis, as a reaction to the abstractness and complexity of music from the Serialist movement. Xenakis proposed that the mathematics of probability could be the basis of a more general and manageable compositional technique (Xenakis 1971).
“Concret PH” is a very influential piece of stochastic music. It was created by Xenakis to be played inside the Philips Pavilion in the 1958 World’s Fair in Brussels. This building was designed by architect Le Corbusier, who employed Xenakis as an architect and mathematician at the time.
The following program (Ch. 6, p. 167) demonstrates how to generate a stochastic piece of music. In the original piece, Xenakis used spliced tape of sounds made by burning charcoal. Here, we mimic the sound using the MIDI instrument BREATHNOISE, which at short “bursts” (notes with short duration) sounds much like Xenakis’ original sound elements.
# ConcretPH_Xenakis.py # # A short example which generates a random cloud texture # inspired by Iannis Xenakis's 'Concret PH' composition # # see http://en.wikipedia.org/wiki/Concret_PH from music import * from random import * # constants for controlling musical parameters cloudWidth = 64 # length of piece (in quarter notes) cloudDensity = 23.44 # how dense the cloud may be particleDuration = 0.2 # how long each sound particle may be numParticles = int(cloudDensity * cloudWidth) # how many particles part = Part(BREATHNOISE) # make particles (notes) and add them to cloud (part) for i in range(numParticles): # create note with random attributes pitch = randint(0, 127) # pick from 0 to 127 duration = random() * particleDuration # 0 to particleDuration dynamic = randint(0, 127) # pick from silent to loud panning = random() # pick from left to right note = Note(pitch, duration, dynamic, panning) # create note # now, place it somewhere in the cloud (time continuum) startTime = random() * cloudWidth # pick from 0 to end of piece phrase = Phrase(startTime) # create phrase with this start time phrase.addNote(note) # add the above note part.addPhrase(phrase) # and add both to the part # now, all notes have been created # add some elegance to the end Mod.fadeOut(part, 20) View.show(part) Play.midi(part) Write.midi(part, "ConcretPh.mid")
Since randomness is involved, it will generate output similar (but not identical) to this:
Harnessing (or sieving) randomness – wind chimes
As mentioned above, a way to generate artifacts that are aesthetically pleasing, starting with pure randomness, is to filter a random process through a sieve. For example, wind chimes capture random movements of air and force them onto a narrow set of aesthetic possibilities. The following program (Ch. 6, p. 169) demonstrates how to create wind chimes out of randomness.
# windChimes.py # # Simulates a 4-tube wind chime. # # Demonstrates how we may sieve (harness) randomness to generate # aesthetically pleasing musical outcomes. from music import * from random import * # program parameters cycles = 24 # how many times striker hits all four tubes duration = 8.0 # tubes sounds last from 0 to this time units minVol = 80 # low and high limit for random volume maxVol = 100 # tube tuning (D7 chord) tube1 = C5 tube2 = F5 tube3 = G4 tube4 = D6 # wind chime part windChimePart = Part(BELLS) # wind chime consists of four tubes tube1Phrase = Phrase(0.0) # first tube starts at 0.0 time tube2Phrase = Phrase(1.0) # second tube starts at 1.0 time, ... tube3Phrase = Phrase(3.0) # ... and so on. tube4Phrase = Phrase(5.0) # generate wind chime notes and add them to these phrases for i in range(cycles): # create random tube strikes (notes) note1 = Note(tube1, random() * duration, randint(minVol, maxVol) ) note2 = Note(tube2, random() * duration, randint(minVol, maxVol) ) note3 = Note(tube3, random() * duration, randint(minVol, maxVol) ) note4 = Note(tube4, random() * duration, randint(minVol, maxVol) ) # accumulate notes in parallel sequences tube1Phrase.addNote( note1 ) tube2Phrase.addNote( note2 ) tube3Phrase.addNote( note3 ) tube4Phrase.addNote( note4 ) # now, all notes have been created # add note sequences to wind chime part windChimePart.addPhrase( tube1Phrase ) windChimePart.addPhrase( tube2Phrase ) windChimePart.addPhrase( tube3Phrase ) windChimePart.addPhrase( tube4Phrase ) # view and play wind chimes View.sketch(windChimePart) Play.midi(windChimePart)
Since randomness is involved, it will generate output similar (but not identical) to this:
Creating a pentatonic melody
The following program (Ch. 6, p. 170) demonstrates how to harness randomness to create a melodic line within a particular scale.
# pentatonicMelody.py # Generate a random pentatonic melody. It begins and ends # with the root note. from music import * # import music library from random import * # import random library pentatonicScale = [C4, D4, E4, G4, A4] # which notes to use durations = [QN, DEN, EN, SN] # which durations to use # pick a random number of notes to create (between 12 and 18) numNotes = randint(12, 18) # number of notes to create phrase = Phrase() # create an empty phrase # first note should be root note = Note(C4, QN) # create root note phrase.addNote(note) # add note to phrase # generate enough random notes (minus starting and ending note) for i in range(numNotes - 2): pitch = choice(pentatonicScale) # select next pitch duration = choice(durations) # select next duration dynamic = randint(80, 120) # randomly vary the volume panning = random() # and place in stereo field note = Note(pitch, duration, dynamic, panning) # create note phrase.addNote(note) # add it to phrase # last note should be root also (a half note, to signify end) note = Note(C4, HN) # create root note phrase.addNote(note) # add note to phrase Play.midi(phrase) # play the melody
Since randomness is involved, it will generate output similar (but not identical) to this:
Music from Brownian motion
Brownian motion is a very correlated, yet unpredictable (random) process observed commonly in nature. The following Python program demonstrates how we can we harness randomness to generate music that is more correlated, “natural” sounding.
# brownianMelody.py # # Demonstrates how to create more correlated music from chaos # (i.e., randomness). This process simulates the random "walks" of # particles within water, etc., i.e., unpredictable, but not chaotic. # It models the flip of a coin - if heads, next note in the melody # goes up one scale degree; if tails, next note is down one scale # degree. from music import * from random import * numberOfNotes = 29 ##### define the data structure brownianMelodyScore = Score("Brownian melody", 130) brownianMelodyPart = Part("Brownian melody", TUBULAR_BELLS, 0) brownianMelodyPhrase = Phrase() ##### create musical data note = Note(C4, EN) # create first note brownianMelodyPhrase.addNote(note) # add note to phrase for i in range(numberOfNotes): # create enough notes # now, let's get next note according to brownian motion note = note.copy() # create a new copy # flip a coin heads = random() < 0.5 # a 50-50 chance to be True if heads: # if we got heads, Mod.transpose(note, 1, MAJOR_SCALE, C4) # up a scale degree else: # otherwise Mod.transpose(note, -1, MAJOR_SCALE, C4) # down a scale degree brownianMelodyPhrase.addNote(note) # add note to phrase # now, all notes have been generated ##### combine musical material brownianMelodyPart.addPhrase(brownianMelodyPhrase) brownianMelodyScore.addPart(brownianMelodyPart) ##### view score and play it View.sketch(brownianMelodyScore) Play.midi(brownianMelodyScore)
Since randomness is involved, it will generate output similar (but not identical) to this:
Throwing dice
The following program (Ch. 6, p. 177) demonstrates how to simulate the throwing of dice – how to divide randomness across many alternatives (in this case, 6).
# throwingDice.py # # Demonstrates the division of randomness to several choices. from music import * from random import * numNotes = 14 # how many random notes to play phrase = Phrase() # create an empty phrase for i in range(numNotes): dice = randint(1, 6) # throw dice (1 and 6 inclusive) # determine which dice face came up if dice == 1: note = Note(C4, QN) # C4 note elif dice == 2: note = Note(D4, QN) # D4 note elif dice == 3: note = Note(E4, QN) # E4 note elif dice == 4: note = Note(F4, QN) # F4 note elif dice == 5: note = Note(G4, QN) # G4 note elif dice == 6: note = Note(A4, QN) # A4 note else: print "Something unexpected happened... dice =", dice phrase.addNote(note) # add this random note to phrase # now, all random notes have been created # so, play them Play.midi(phrase)
Since randomness is involved, it will generate output similar (but not identical) to this:
Let the drums come alive
In the following program (Ch. 6, p. 179), every now and then (randomly) we interject an open hi-hat sound to the sequence of closed hi-hat sounds. It also randomly varies the loudness (dynamic level) of the notes.
# drumsComeAlive.py # # Demonstrates how to uses randomness to make a drum pattern come # "alive", i.e., to sound more natural, more human-like. # In this example, every now and then (randomly, 35% of the time), # we play an open hi-hat sound (as opposed to a closed one). # from music import * from random import * ##### musical parameters # 35% of the time we try something different comeAlive = 0.35 # how many measures to play measures = 8 ##### define the data structure score = Score("Drums Come Alive", 125.0) # tempo is 125 bpm drumsPart = Part("Drums", 0, 9) # using MIDI channel 9 (percussion) bassDrumPhrase = Phrase(0.0) # create phrase for each drum sound snareDrumPhrase = Phrase(0.0) hiHatPhrase = Phrase(0.0) ##### create musical data # kick # bass drum pattern (one bass 1/2 note) x 2 = 1 measure # (we repeat this x 'measures') for i in range(2 * measures): dynamics = randint(80, 110) # add some variation in dynamics n = Note(ACOUSTIC_BASS_DRUM, HN, dynamics) bassDrumPhrase.addNote(n) # snare # snare drum pattern (one rest + one snare 1/4 notes) x 2 = 1 measure # (we repeat this x 'measures') for i in range(2 * measures): r = Note(REST, QN) snareDrumPhrase.addNote(r) dynamics = randint(80, 110) # add some variation in dynamics n = Note(SNARE, QN, dynamics) snareDrumPhrase.addNote(n) # hats # a hi-hat pattern (one hi-hat + one rest 1/16 note) x 8 = 1 measure # (we repeat this x 'measures') for i in range(8 * measures): # if the modulo of i divided by 2 is 1, we are at an odd hit # (if it is 0, we are at an even hit) oddHit = i%2 == 1 # time to come alive? doItNow = random() < comeAlive # let's give some life to the hi-hats if oddHit and doItNow: # on odd hits, if it's time to do it, pitch = OPEN_HI_HAT # let's open the hit-hat else: # otherwise, pitch = CLOSED_HI_HAT # keep it closed # also add some variation in dynamics dynamics = randint(80, 110) # create hi-hat note n = Note(pitch, SN, dynamics) hiHatPhrase.addNote(n) # now, create rest r = Note(REST, SN) hiHatPhrase.addNote(r) ##### combine musical material drumsPart.addPhrase(bassDrumPhrase) drumsPart.addPhrase(snareDrumPhrase) drumsPart.addPhrase(hiHatPhrase) score.addPart(drumsPart) ##### view and play View.sketch(score) Play.midi(score)
Since randomness is involved, it will generate output similar (but not identical) to this:
Creating generative music
Thee following program (Ch. 6, p. 187) demonstrates how to develop more intricate algorithmic processes for setting up probabilities of musical events (e.g., pitches and durations) and mapping them into interesting musical artifacts.
# generativeMusic.py # # Demonstrates how to create music with weighted probabilities. # from music import * from random import * numNotes = 32 # how many random notes to play # pitches and their chances to appear in output (the higher the # chance, the more likely the pitch is to appear) pitches = [C4, D4, E4, F4, G4, A4, B4, C5] durations = [QN, EN, QN, EN, QN, EN, SN, QN] chances = [5, 1, 3, 2, 4, 3, 1, 5] #### # Create weighted lists of pitches and durations, where the number of # times a pitch appears depends on the corresponding chance value. # For example, if pitches[0] is C4, and chances[0] is 5, the weighted # pitches list will get 5 instances of C4 added. weightedPitches = [] weightedDurs = [] for i in range( len(chances) ): weightedPitches = weightedPitches + [ pitches[i] ] * chances[i] weightedDurs = weightedDurs + [ durations[i] ] * chances[i] # now, len(weightedPitches) equals sum(chances) # same applies to weightedDurs # debug lines: print "weightedPitches = ", weightedPitches print "weightedDurations = ", weightedDurs phrase = Phrase() # create an empty phrase # now create all the notes for i in range(numNotes): event = randint(0, len(weightedPitches)-1) note = Note(weightedPitches[event], weightedDurs[event]) # the note has been found; now add this note phrase.addNote(note) # now, all notes have been generated # so, play them Play.midi(phrase)
Since randomness is involved, it will generate output similar (but not identical) to this: