Thursday, June 7, 2012

Process Music with music21

Last July, I was watching this gorgeous video showing the waves created by a set of pendulums of different (but simple ratio) lengths after they're simultaneously released:


(courtesy Harvard Science Center demonstrations)

I thought about ways that such a demonstration could be made musically using simple processes.  After writing the music21 code to do so, I discovered that several other composers have done similar things, so I don't think that it's absolutely original, but I wanted to share the possibilities.  First the opening of the score and the recording:




Here's the music21 code used to make it (unhighlighted code in music21.composition.phasing):
  1. def pendulumMusic(show = True,
  2.                   loopLength = 160.0,
  3.                   totalLoops = 1,
  4.                   maxNotesPerLoop = 40,
  5.                   totalParts = 16,
  6.                   scaleStepSize = 3,
  7.                   scaleType = scale.OctatonicScale,
  8.                   startingPitch = 'C1'
  9.                   ):
  10.     from music21 import scale, pitch, stream, note, chord, clef, tempo, duration, metadata
  11.    
  12.     totalLoops = totalLoops * 1.01
  13.     jMax = loopLength * totalLoops
  14.    
  15.    
  16.     p = pitch.Pitch(startingPitch)
  17.     if isinstance(scaleType, scale.Scale):
  18.         octo = scaleType
  19.     else:
  20.         octo = scaleType(p)
  21.     s = stream.Score()
  22.     s.metadata = metadata.Metadata()
  23.     s.metadata.title = 'Pendulum Waves'
  24.     s.metadata.composer = 'inspired by http://www.youtube.com/watch?v=yVkdfJ9PkRQ'
  25.     parts = [stream.Part(), stream.Part(), stream.Part(), stream.Part()]
  26.     parts[0].insert(0, clef.Treble8vaClef())
  27.     parts[1].insert(0, clef.TrebleClef())
  28.     parts[2].insert(0, clef.BassClef())
  29.     parts[3].insert(0, clef.Bass8vbClef())
  30.     for i in range(totalParts):
  31.         j = 1.0
  32.         while j < (jMax+1.0):
  33.             ps = p.ps
  34.             if ps > 84:
  35.                 active = 0
  36.             elif ps >= 60:
  37.                 active = 1
  38.             elif ps >= 36:
  39.                 active = 2
  40.             elif ps < 36:
  41.                 active = 3
  42.            
  43.             jQuant = round(j*8)/8.0
  44.             establishedChords = parts[active].getElementsByOffset(jQuant)
  45.             if len(establishedChords) == 0:
  46.                 c = chord.Chord([p])
  47.                 c.duration.type = '32nd'
  48.                 parts[active].insert(jQuant, c)
  49.             else:
  50.                 c = establishedChords[0]
  51.                 pitches = c.pitches
  52.                 pitches.append(p)
  53.                 c.pitches = pitches
  54.             j += loopLength/(maxNotesPerLoop - totalParts + i)
  55.             #j += (8+(8-i))/8.0
  56.         p = octo.next(p, stepSize = scaleStepSize)
  57.            
  58.     parts[0].insert(0, tempo.MetronomeMark(number = 120, referent = duration.Duration(2.0)))
  59.     for i in range(4):
  60.         parts[i].insert(int((jMax + 4.0)/4)*4, note.Rest(quarterLength=4.0))
  61.         parts[i].makeRests(fillGaps=True, inPlace=True)
  62.         parts[i] = parts[i].makeNotation()
  63.         s.insert(0, parts[i])
  64.    
  65.     if show == True:
  66.         #s.show('text')
  67.         s.show('midi')
  68.         s.show()
One nice thing that you can do is call pendulumMusic with different attributes, such as:
        pendulumMusic(show = True, 
                  loopLength = 210.0, 
                  totalLoops = 1, 
                  maxNotesPerLoop = 70,
                  totalParts = 64,
                  scaleStepSize = 1,
                  scaleType = scale.ChromaticScale,
                  startingPitch = 'C1',
                  )


which gives a denser score that has parts that sound like Nancarrow. Or this version...(new tab):
        pendulumMusic(show = True, 
                  loopLength = 210.0, 
                  totalLoops = 1, 
                  maxNotesPerLoop = 70,
                  totalParts = 12,
                  scaleStepSize = 5,
                  scaleType = scale.ScalaScale('C3', '13-19.scl'),
                  startingPitch = 'C2',
                  )
which, if your soundcard is a standard one and your web browser is recent should produce a 19-tone version of the same piece.

Happy process music composing!

No comments:

Post a Comment