220a project:
Randomness in Music
Ben D'Angelo
Autumn Quarter, 2001

My project started out with an interest in drum sounds, and I was planning on playing around with creating drum sounds with FM and also seeing how randomness figured into drum sounds (through noise, or on a larger scale of rhythm).  However, apparently my project had a mind of it's own, because I soon found myself dabbling much more in the random side of things, so to speak.

What especially intrigued me about the idea of randomness is that it can pervade so many elements of music.  Nearly every dimension of music can integrate randomness somehow.   Note choice, timbre, rhythm, dynamics, they all have the flexibility needed to allow a random factor to help guide them.  So I decided to try experimenting with as many of these as possible and seeing what kinds of things developed.  What follows are the products of these experiments.


First of all, you can find all the algorithms and things I wrote (plus the fm instrument I use for the drum sound but did not write) in random.scm.  In particular, all the helper functions to the main algorithms are there (I won't be including all the details here).  A compilation of the various examples and tests of the algorithms can be found in random-examples.scm.


Notes!

Random notes in a frequency range
Now, in going about this, I pretty much started from the bottom up.  One of the simplest ways randomness can factor into music is choosing the frequencies of the notes.  So first I made random-notes-in-freq-range.

(define random-notes-in-freq-range
  (lambda* (basefreq topfreq numnotes lengthnotes #&optional (amp (make-random-list '() .5 .5 numnotes)))
    (with-sound (:channels 1 :srate 44100  :output "/zap/test.wav")
         (do ((i 0 (1+ i))) ((= i numnotes))
            (fm-violin (* i lengthnotes) lengthnotes (+ (random (- topfreq basefreq)) basefreq) (ith amp i))
         )
    )
  )
)

Given a range of frequencies, how many notes to play, and how long to play each note, it creates a series of notes with random frequencies within the range.  Fairly straightforward.  Although here I also make it possible for the caller of the procedure to provide a list of amplitudes, one for each note.  If no amplitude list is provided, a list is created with all values .5 (make-random-list picks values between the two numbers given, so by making them both the same you can force all values to be that number; that is what is done here).

Note: for most of these algorithms, where I'm just having it create sequences of notes at various times, I use the fm-violin to play them since it is nice and reliable.

Ignoring the amplitude list for now, there are not too many possibilities here.  We get a sequence of notes of fixed length at a mishmash of unrelated pitches.  Here are some samples:

(random-notes-in-freq-range 100 500 5 .5)     sound file
(random-notes-in-freq-range 300 2000 13 .15)   sound file

Exciting,aren't they!  Moving on...

Scales
Of course, most of the time in music we don't want to simply pick notes which don't relate at all.  Usually we choose them to be a part of some scale.  So I wrote some functions which take a base frequency and return an octaves' worth of frequencies which are those notes in the particular scale (all equal-tempered :) ).  For example, I started with chromatic-scale.

(define chromatic-scale
  (lambda (basefreq)
    (sort (add-chromatic-notes-to-scale basefreq '() 12) #'<)
  )
)

(define add-chromatic-notes-to-scale
  (lambda (basefreq scale notenum)
    (if (< notenum 0) scale
        (cons (* (expt 2.0 (/ notenum 12.0)) basefreq) (add-chromatic-notes-to-scale basefreq scale (1- notenum)))
    )
  )
)

Given a single frequency, it returns a list (in ascending order) of the frequencies of a chromatic scale from that note.  The procedure play-line takes a list of frequencies and a duration and plays each note for that length, so we have:

(play-line (chromatic-scale 220) .2)       sound file

Then, it is relatively easy to create any other scale you feel like: you just create a chromatic scale and pick out the notes you want.  For example, major-scale looks like (ith picks out the ith element of a list--element indices go from 0 to one less than the length of the list):

(define major-scale
  (lambda (basefreq)
    (let ((scale (chromatic-scale basefreq)))
      (list (ith scale 0) (ith scale 2) (ith scale 4) (ith scale 5) (ith scale 7) (ith scale 9)
            (ith scale 11) (ith scale 12)
      )
    )
  )
)

In a similar way I got minor-scale, whole-tone-scale, and blues-scale.  Now we have the tools necessary to introduce randomness within the scope of a scale.

Random notes in scales
Simply, instead of playing random notes in a frequency range, random-notes-in-scale plays random notes from a scale (really just a sequence of frequencies).  Here it is:

(define random-notes-in-scale
  (lambda* (scale numnotes lengthnotes #&optional (amp (make-random-list '() .5 .5 numnotes)))
    (with-sound (:channels 1 :srate 44100  :output "/zap/test.wav")
         (do ((i 0 (1+ i))) ((= i numnotes))
            (fm-violin (* i lengthnotes) lengthnotes (random-note-from-scale scale) (ith amp i))
         )
    )
  )
)

With this we can do something like...

(random-notes-in-scale (major-scale 100) 12 .25)          sound file
(random-notes-in-scale (cons 0 (minor-scale 200)) 16 .2)  sound file

Notice in the second example I added a 0 to the list of frequencies, essentially making a rest a possibility equal with the rest of the notes.  And with that, it is high time to move on to randomness in rhythm!


Rhythm!

Random beats in a tempo range
In the rhythmic domain, we can mirror what can happen in the frequency domain.  First off we can allow beats to occur at a variable time interval, where the interval can be anything over some range.  For this I wrote beats, which just takes a number of notes, and optionally a list of amplitudes and a list of time intervals.

(define beats
  (lambda* (numnotes #&optional (amp (make-random-list '() .5 .5 numnotes)) (beatlengths
            (make-random-list '() .2 .2 numnotes)))
    (with-sound (:channels 1 :srate 44100  :output "/zap/test.wav")
         (let ((time 0.0))
            (do ((i 0 (1+ i))) ((= i numnotes))
              (fm-dill time .1 (ith amp i) 200 1.4 :car-env '(0 1 10 1 50 .2 100 0)
                :mod-env '(0 1 50 .2 100 0) :mod-index1 2 :mod-index2 1
              )
              (set! time (+ time (ith beatlengths i)))
            )
         )
    )
  )
)

Note: The instrument here is fm-dill.  It is Stephen Dill's fm instrument (see bottom of page for website reference), and I'm using it because he gives parameters for a snare sound, which is nice for a beat.
Things get interesting when we see what happens allowing the length of time between beats to change ever so slightly...

(beats 16)                                                                        sound file
(beats 16 (make-random-list '() .5 .5 16) (make-random-list '() .19 .21 16))       sound file
(beats 16 (make-random-list '() .5 .5 16) (make-random-list '() .18 .22 16))       sound file
(beats 16 (make-random-list '() .5 .5 16) (make-random-list '() .17 .23 16))       sound file

These allow the time between beats to vary by up to a maximum of 0, 10, 20, and 30% of the average time between beats, respectively.  Notice how the first one sounds too perfect, the next two maybe a little more like how a human would play it, and the last one simply off the beat.

Random beats in tempo
Now, just as we constrained ourselves from random notes in a frequency range to random notes in a scale, we can go from random beats in a tempo range to random beats in some particular tempo (I'll forgive you if you have to read that twice :) ).  For this I wrote random-accents, which generates a list of amplitudes where a certain percentage of them have the higher value and the rest of the lower value.

(define random-accents
  (lambda (list normalamp accentamp numtoadd percentToAccent)
    (if (= numtoadd 0) list
        (cons (if (> percentToAccent (random 100.0)) accentamp normalamp)
           (random-accents list normalamp accentamp percentToAccent (1- numtoadd))
        )
    )
  )
)

With this, we pass 0 as the normalamp to get beats a certain percentage of the time, or we can just make the normal and accent amplitudes different enough to hear accents a certain percentage of the time.  Such as...

(beats 32 (random-accents '() 0 .8 32 50))             sound file
(beats 32 (random-accents '() .08 .8 32 30))           sound file

I also wrote two other "accent-list makers," if you will.  First is even-accents, which makes a list with accents every so many beats.  Pretty straightforward.  Second, however, is a much more interesting one: mostly-even-accents.  This makes a list with accents every so many beats, but then the number of beats per "cycle" can change by one a certain percentage of the time.  (The code is rather messy so I'm not putting it here.)

(beats 32 (mostly-even-accents '() .08 .8 3 0 32 20))  sound file

Here the beats start out in a "3/4" meter, but this changes immediately.


Notes!  Rhythm!  Together!

Random notes in a scale with varying accents
Now things can get pretty interesting.  Using everything we already have, we can mix and match to get all sorts of results.  All of the "note playing" functions I have shown already can take amplitude lists, so we can use any of the accent functions to act on notes being generated, and we can use any of the scales to draw notes from!  It's rather fun to just play around and see what you can make, but here are some examples.

(random-notes-in-scale (major-scale 100) 12 .25 (make-random-list '() .2 .8 12))             sound file
(random-notes-in-scale (minor-scale 300) 24 .2 (random-accents '() .08 .8 24 50))            sound file
(random-notes-in-scale (whole-tone-scale 200) 24 .2 (even-accents '() .1 .9 4 0 24))         sound file
(random-notes-in-scale (minor-scale 200) 48 .2 (mostly-even-accents '() 0 .9 2 0 48 40))      sound file

There are many other algorithms one could write to accomodate other various combinations, which I didn't include.  For example, one could have the notes play in the slightly off tempo beats with some interesting accents.  Or one could add the snare beats along with the notes and have the two instruments have totally disjoint rhythmic lines.

Evolving line
Another fascinating idea is to have a musical line of notes that evolves over time.  I wrote a function to accomplish this called evolving-line, and it goes a little something like this:

(define evolving-line
  (lambda* (line scale numlines lengthnotes #&optional (amp (make-random-list '() .5 .5 (length line))))
    (with-sound (:channels 1 :srate 44100  :output "/zap/test.wav")
         (do ((i 0 (1+ i))) ((= i numlines))
            (do ((j 0 (1+ j))) ((= j (length line)))
              (fm-violin (+ (* i (length line) lengthnotes) (* j lengthnotes))
                  lengthnotes
                  (ith line j)
                  (ith amp j)
              )
            )
            (set! line (change-one-elem line scale))
         )
    )
  )
)

It takes a sequence of notes (the line), and plays it a number of times.  After each run through, it randomly picks one of the notes from the line and changes it to a random note from the scale it is given (just another list of notes).  There's a similar version called evolving-line-with-beats that also plays a repeating snare beat underneath the line.

To test this function out, I made a little bass line (I know, it's corny :-P):

(define bassriff
  (let ((line (blues-scale 60)))
    (list (ith line 0) (ith line 0) (ith line 6) (ith line 6) (ith line 5) (ith line 2)
       (ith line 3) (ith line 4) 0 (ith line 4) (ith line 4) (ith line 4) (ith line 2)
       (ith line 4) (ith line 2) (ith line 1)
    )
  )
)
(play-line bassriff .2 (cons 1 (make-random-list '() .3 .3 15)))      sound file

Then I can evolve it, replacing notes by ones from the blues scale (or a rest):

(evolving-line bassriff (cons 0 (blues-scale 60)) 8 .2 (cons 1 (make-random-list '() .3 .3 15)))   sound file

To make things even more interesting, instead of replacing notes by ones from the blues scale we can replace with notes from some other scale!  Here is a long example of such a case:

(evolving-line-with-beats bassriff (cons 0 (major-scale 60)) 40 .2 4 (cons .6 (make-random-list '() .2 .2 15)))
                                                                                                  sound file
Pretty crazy, eh?


Harmonic Content!

On a level lower than the one we have been looking at so far lies another place randomness can be found: the waveform of a sound itself.  This is a very complex area, and I myself did not do any algorithmic work to explore it, but I did read a lot about it.

Deterministic vs. Stochastic
Pretty much all sounds found in the world, musical or not, can be broken down into two elements: a deterministic part and a stochastic part.  The deterministic part is, like the name implies, determinable in that it is completely reproducible via a sum of (possibly changing) sine waves.  The stochastic part on the other hand is essentially made up of random noise (described by its power spectral density over time).

Often times, this makes sense for a particular instrument in considering the way sound is produced.  For example, the sound a flute makes consists of the periodic oscillations created by the wave reflecting between the ends of the flute and also of random noise from the player's breath sustaining the note.  For a string instrument, the deterministic component comes from the periodic vibrations of the string and the stochastic component comes from the complex bow/string interactions.  For the human voice, regular vibrations of the vocal folds create deterministic sound while the breath and irregularities from imperfect muscle control add a stochastic sound.

Thus, this is a relatively easy concept to understand.  However, it is a wee bit trickier to put into practice in analyzing/synthesizing sounds.  I will give a very brief outline of the steps one might undergo to do so.

First, a spectral analysis is taken of the sound.  A peak detection and peak tracking algorithm is used over the sound to determine the "path" that the periodic components of the sound take over time.  These deterministic components of the sound are subtracted from the original sound to leave a residual sound which (hopefully) does not contain any harmonic content, just random noise.  Analysis is done of the residual sound to determine its changing spectral energy over time.  The parameters of the changing sinusoidal components and the parameters describing the power spectral density of the rest of the sound now fully describe the deterministic and stochastic parts of the original sound.  A synthesis sounding like the original sound can be created by creating sinusoids with the same trajectories as the deterministic parts and adding them together, and adding this to random noise which is generated and filtered to have the same power spectral density as the stochastic part.  Whew!

For sound examples of analysis and synthesis done in this way (and lots of other fun stuff, too) see http://www.iua.upf.es/sms/examples/smsAnal/.

One interesting application of this I read about was a group trying to synthesize automotive sounds using the stochastic/deterministic model (they weren't doing this because they were totally crazy, it was a part of research for the Ford Motor Company's Sound Quality Lab :) ).  See website below for more info.


The End!

So, here we are at the bottom of the page.

All in all, I really enjoyed this project.  I found it fascinating to explore how randomness affects different areas of music.  One thing I find so exciting is that the results you get from randomness are different every time!  Each of the sample sound files above is simply one of innumerable possibilities.  Every time you run the code you can get something new.  I am also fascinated by the practical role randomness plays in music that people make.  Like I mentioned above, for all instruments, including the human voice, random sound is an integral element to the overall sound produced, even when we don't realize it.  Further, every time a musician performs some piece of music it is going to be different, simply because we are human, and so randomness figures into the nuances of each performance.  Even at the compositional level, it is virtually never the case that a piece of music is written based entirely on fixed rules, without an element of subjectivity and choice.  Thus, it is interesting to ask how randomness affects what chords, musical phrases, etc. that a composer happens to discover and like.

Clearly, randomness can be related to all parts of music.  For me, this is precisely what makes it such an intriguing topic.


References:

http://www.academy.qut.edu.au/music/newmedia/jMusic/jmtutorial/t1.html
A very helpful guide to programming music in java.  Unfortunately, I don't know how to program in java :-P.  But it gave me ideas for things to try in scm.

http://depts.washington.edu/smccweb/clmman/fm.html
An excellent resource on fm (I don't know if I ended up using anything from here, but it had a lot of interesting info).

http://www.iua.upf.es/~sms/
Information on stochastic/deterministic sound.  Xavier Serra is a prominent figure in the field (mentioned in our book for his leading contribution to it when he worked here at CCRMA).

http://www-ccrma.stanford.edu/~sdill/220A-project/drums.html
Stephen Dill's project website.

http://www.acoustics.org/press/131st/lay01.html
The stochastic/deterministic model of sound being used for automotive sound synthesis.

Pierce, John R. The Science of Musical Sound, 2nd edition. W. H. Freeman Company: May  1992.
Our class book.