Introduction

Why can't my music grow old with me?

Why does a recording sound exactly the same every time I listen to it? That makes sense when recordings are frozen in time on wax cylinders or vinyl or compact discs. But most of the music I listen to these days comes from a cloud-based streaming music service, and those digital 1s and 0s are streamed pretty much the same way every time.

In this world of infinitely malleable, movable bits, why must the music always stay the same? From day to day and year to year, I change. I bring new perspectives and experiences to the music I hear. Can my music change with me?

This streaming EP is my attempt to answer these questions. Once a day, a simple computer program recreates each track. From one day to the next, the changes in each track are usually quite subtle, and you may not even notice a difference. But over longer periods of time -- weeks, months, or years -- the changes become more substantial. So when you return to this music after a hiatus, then it, like you, will have changed.

I wrote this music for many reasons. My students at Georgia Tech are always at the forefront of my mind, and this music is, in one sense, for them. I often teach algorithmic composition, and these pieces serve a practical pedagogical purpose for my students. On this site, I'm sharing both the audio tracks and the source code that I wrote to create them. Each track is deliberately simple, following through one or two core ideas and a straightforward algorithm to (what seems to me) its logical conclusion. Some of the techniques here might even seem a bit cliche, at least to the computer music geeks I count among my friends and colleagues. I hope, though, that my students see these as examples of how complexity is not always necessary, and of how simple ideas can lead in interesting directions. Or perhaps, I instead hope they find this music to be so offensive in its simplicity that it provokes them to create its antithesis, but that in so doing, they embark on a creative experiment they might not otherwise have tried.

I also wrote this music for my family. Three of the tracks are inspired by members of my family; I'll explain more in the texts that accompany those tracks. I've always intended to write music for, or about, my family, but every composition project I undertake (except for this one) turns out to be a poor fit. This project at last presented me a perfect opportunity to do so.

Finally, this work is very much intertwined with Brad Garton, whom I see extremely infrequently these days but think about extremely often. The programming environment I'm using to create this music is RTcmix, which Brad developed (along with some other amazing folks) and first introduced to me 15 years ago. Some of his work, and especially his music books, were a direct influence on my thinking about this project. Brad has also always been an incredible role model as a professor, composer, computer musician, husband, and father. I don't know if I really appreciated all of this as a 21-year-old graduate student of his, but with each passing year I understand and admire him more and more.

Grow Old is a 2013 commission of New Radio and Performing Arts, Inc., for its Turbulence.org website. It was made possible with funding from the National Endowment for the Arts. My deepest thanks to Helen and Jo for their passion for experimental networked and sound art and for their support.

This work, including the web site, the source code, and the music, is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License. If you are interested in using it in a manner not permitted by that license, please contact me. The complete source code is available on GitHub.

If you want to easily keep up to date on this music and how it evolves, click the podcast links in the menu to automatically receive updated versions of each track within your favorite podcast app.

Jason Freeman

Atlanta, Georgia

May 2014

Saturn's Moons

Listen. (Generated on 05-23-2019)
for Alec

My older son Alec (currently 4) is astutely observant of the world around him, asks a lot of questions about what he sees, and conducts a lot of experiments to help answer those questions. He's extremely interested in how things work.

His fascination with the world naturally extends beyond our planet. He always keeps an eye out for the moon and lets me know what phase it is in, and he often looks for other interesting things in the night sky.

So I started to think about music based on the moon and its phases. I also thought about my own longtime fascination with phasing processes in composition (like in Steve Reich's classic works) where one musical line -- just like an orbiting moon -- spins around in repetition a little faster than another.

Since Earth only has one moon, that didn't make a good foundation for this phasing. But Saturn (Alec's favorite planet) has many, many moons, which proved more fruitful. I found data on seven of Saturn's largest moons -- Mimas, Enceladus, Tethys, Dione, Rhea, Titan, and Iapetus -- and looked up their orbit period, orbit radius, moon mass, and moon diameter. I used this data to algorithmically drive a repeated, synthesized plucked-string note for each moon. The pitch of the note is based on the orbit radius, and the timbre is based on the moon's mass and diameter. As the moons orbit Saturn at different rates, they fade in and out of prominence, change the speed at which their notes repeat, and change their panning position in the stereo spread. A very low, pure waveform represents Saturn itself, and it changes volume based on Saturn's rotation. Each day, the algorithm generates the music anew, condensing the entire days' movement into a few minutes of music. It takes more than a hundred years before the music repeats exactly the same way.

I don't know what career Alec will eventually pursue: a scientist, a chef, a landscape architect, or something else entirely. I do hope, though, that he always keeps the curiosity of a scientist, the creativity to ask interesting questions and devise experiments to answer them, a sense of wonderment at our small place within the universe, and a passion for sustaining the world around us.

/* do the settings for RTcmix */

set_option("clobber=1")
set_option("play=0")
set_option("audio=0")
rtsetparams(44100, 2)
rtoutput("alec.aif")

load("STRUM")
load("FREEVERB")
load("WAVETABLE")
bus_config("START", "aux 0-1 out")
bus_config("FREEVERB", "aux 0-1 in", "out 0-1")

/* grab the current unix timestamp from the command line argument */

curTime = f_arg(0)
printf(" %f is curTime\n", curTime)

/* basic data */

// higher pitches in cluster == smaller radius for moon
melody = { 10.09, 10.08, 10.05, 10.03, 10.02, 10, 9.10 }
// moon names are just for reference
moons = {"Mimas", "Enceladus", "Tethys", "Dione", "Rhea", "Titan", "Iapetus"}
// diameter of each moon
diameter = {0.12, 0.14, 0.3, 0.32, 0.44, 1.48, 0.42}
// size rankings of moon diameters
diameterRank = {6, 5, 4, 3, 1, 0, 2 }
// orbital period in days of each moon
periodInDays = {0.9, 1.4, 1.9, 2.7, 4.5, 16, 79}
// radius of each moon's orbit
radius = {0.5, 0.6, 0.8, 1, 1.4, 3.2, 9.3}
// mass of each moon
mass = {0.0005, 0.002, 0.008, 0.015, 0.03, 1.8, 0.025}
// mass rankings of moons
massRank = {6, 5, 4, 3, 1, 0, 2 }

// init these to dummy values, set for real later
// these are arbitrary just for spacing for initial values so everything doesn't start off at zero phase
newMoonTime = {0, 86400000/7, 86400000*2/7, 86400000*3/7, 86400000*4/7, 86400000*5/7, 86400000*6/7}
// will be set later, starting phase for each moon for this execution
startMoonPhase = {0, 0, 0, 0, 0, 0, 0}
// will be set later, amount of orbits for each moon for this execution (which represents 1 day of real time)
numPhaseCycles = {1, 1, 1, 1, 1, 1, 1}

msInADay = 86400000.0
totalDur = 240 // duration of piece in seconds
maxNoteVal = 2.5 // max duration of an individual note in seconds
minNoteVal = 0.1 // min duration of an individual note in seconds
minDb = 40 // max dB of an individual note
maxDb = 90 // min dB of an individual note
durSineWaveIntro = 8

/* compute starting and ending phases for each moon; 0 or 1 is new and 0.5 is full */

for (i=0; i < len(moons); i=i+1) {
        startMoonPhase[i] = (curTime - newMoonTime[i]) / (periodInDays[i] * msInADay)
        startMoonPhase[i] = startMoonPhase[i] - trunc(startMoonPhase[i])
        numPhaseCycles[i] = 1.0 / periodInDays[i]
        printf("moon %f start %f cycles %f\n\n", i, startMoonPhase[i], numPhaseCycles[i])
}

/* now make some sound */

for (i=0; i < len(moons); i=i+1) {
        // we generate phase envelopes from a sound file of an abs value of a 1 Hz
        // sine wave, setting duration and inskip to match the moon
        sfInskip = startMoonPhase[i] * 44100.0 / 2 * -1.0
        sfDur = numPhaseCycles[i] * 44100.0 / 2 * -1.0
        phaseenv = maketable("soundfile", 1000, "1Hzsine.aif", sfDur, sfInskip, 1)
        
        // note duration goes with the phase envelope
        // (but we must do manually because STRUM does not support maketable)
        noteDur = minNoteVal + (maxNoteVal - minNoteVal) * samptable(phaseenv, "interp", 0)
        for (st=0;st < totalDur; st = st + noteDur) {
                // amp goes with the phase envelope too, also manually
                amp = ampdb(minDb + (maxDb - minDb) * samptable(phaseenv, "interp", (st / totalDur) * tablelen(phaseenv)))
                // tiny random variation in start time
                actualSt = st + rand() * 0.005
                // tiny random variation in duration
                actualNoteDur = noteDur + rand() * 0.01 * noteDur
                // nyquist decay goes with mass ranking
                decay = actualNoteDur * 0.5 + actualNoteDur * 0.5 * (massRank[i] + 1) / len(moons)
                // map larger diameter moons to harder plectrums
                squish = diameterRank[i] / 2 + 2
                // pan goes with phase envelope, but at half speed so center pan = full moon
                pan = samptable(phaseenv, "interp", (st * 0.5 / totalDur) * tablelen(phaseenv))
                if (actualSt >= 0) {
                        // play the note
                        START(actualSt + durSineWaveIntro / 2, actualNoteDur, melody[i], actualNoteDur, decay, amp, squish, pan)
                }
                // update noteDur for next time
                noteDur = maxNoteVal - (maxNoteVal - minNoteVal) * samptable(phaseenv, "interp", (st / totalDur) * tablelen(phaseenv))
        }
}

// add some spacey reverb
FREEVERB(0, 0, totalDur + durSineWaveIntro, 1.25, 0.9, 0.001, 4.0, 71, 40, 60, 25)

/* now use the rotation period of Saturn to control a siney drone's changing amplitude */
periodInDays = 0.44375
newMoonTime = 0
startMoonPhase = (curTime - newMoonTime) / (periodInDays * msInADay)
startMoonPhase = startMoonPhase - trunc(startMoonPhase)
numPhaseCycles = 1.0 / periodInDays
printf("saturn rotation start %f cycles %f\n\n", startMoonPhase, numPhaseCycles)
sfInskip = startMoonPhase * 44100.0 / 2 * -1.0
sfDur = numPhaseCycles * 44100.0 / 2 * -1.0
ampenv = maketable("soundfile", 1000, "1Hzsine.aif", sfDur, sfInskip, 1)
fadeinout = maketable("line", 1000, 0,0, 1,1, 190,1, 200,0)
wave = maketable("wave", 1000, 1, 0.25, 0.01, 0.01)
ampenv = mul(ampenv, fadeinout)
WAVETABLE(0, totalDur + durSineWaveIntro, 17000 * ampenv, 5.01, 0.5, wave)

Toy Piano

Listen. (Generated on 05-23-2019)
for Jonathan

Jonathan (currently 16 months old) was so fascinated with a Schoenhut toy piano he discovered at a friend's house that we decided to get one of our own. He likes playing it, along with all the drums and maracas and other instruments we have around the house. He also really, really, really likes to dance.

Whenever a baby plays a piano, someone tells me it sounds like avant-garde music. I suppose they mean that it sounds completely random. I tend to disagree, arguing that neither the baby's playing nor avant-garde piano music is usually particularly random. Even in music created by chance techniques, each of us as listeners can usually discover something acutely personal and meaningful if we take just a moment to listen carefully.

This piece is my way of delving further into this idea. I wrote a simple piece for toy piano, a lullaby of sorts, built around an extremely repetitive Db that gradually builds into thick chords before returning to the simple Db.

My code makes a deliberately crude attempt to perform this music as if by a baby, randomly leaving out notes, adding extra notes, changing notes, playing notes at the wrong time, and so on. Each day, the music is performed anew by an ever-so-slightly older child. When Jonathan is young, nearly every note is wrong and the music sounds more like Jonathan's actual performances at the toy piano than the piece I wrote, though I do take some poetic license to mold the wrong notes in the shape of the piece's harmonic structure. As he gets older, there are fewer and fewer errors. The performance becomes virtually flawless when he turns 20. (Jonathan: I'm assuming, for sake of argument, that you will someday learn the piano well enough to be able to play this music. It's a convenient assumption for the sake of this piece. I promise I will not be disappointed if you never study piano.)

I'd be lying if I said I didn't care if my kids made music an important part of their lives. I don't really want, or expect, them to make a career out of music, but I hope they are engaged with music enough for it to continue to bring them joy. I also hope they appreciate the fundamental randomness of both art and life, and look beyond the surface of things to try to find a personal meaning in seeming chaos.

This piece is also inspired by a formative experience in my education as a composer. When I was in my early 20s, I studied composition at a seminar for young composers led by Brian Ferneyhough. The intervening years have likely distorted my memory, but I distinctly remember a story he told about his childhood. He would meticulously construct model airplanes and then gloriously crash them. He was interested not so much in the structure (the airplane) as in the remnants of the structure (the crashed plane) after it had been disturbed and transformed by chaos. I think that's very much what I'm trying to do in this piece too.

The toy piano sounds in this piece are recordings of our own toy piano. A score and recording of the original lullaby are below.

(Bonus Track: Live Performance of Lullaby for Growing Old by Tim Whitehead)
/* do the settings for RTcmix */

set_option("clobber=1")
set_option("play=0")
set_option("audio=0")
rtsetparams(44100, 2)
rtoutput("jonathan.aif")

load("STEREO")

/* grab the current unix timestamp from the command line argument */

curTime = f_arg(0)
printf(" %f is curTime\n", curTime)

/* the timestamp and midi note and sample data for the score */

midiNoteNumbers = {61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 54, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 54, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 53, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 54, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 63, 61, 61, 61, 61, 53, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 54, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 63, 61, 61, 61, 61, 53, 61, 61, 61, 61, 61, 61, 61, 61, 61, 65, 61, 61, 61, 61, 61, 54, 61, 61, 61, 61, 61, 54, 61, 61, 61, 61, 63, 61, 61, 61, 61, 53, 61, 61, 53, 61, 61, 61, 61, 65, 61, 61, 63, 61, 61, 61, 54, 61, 54, 61, 54, 61, 54, 61, 54, 61, 54, 63, 54, 61, 54, 61, 53, 61, 61, 53, 61, 61, 61, 61, 65, 61, 61, 63, 61, 61, 61, 61, 54, 61, 54, 61, 54, 61, 54, 61, 54, 61, 54, 63, 54, 61, 61, 61, 53, 61, 53, 61, 53, 61, 65, 63, 61, 53, 61, 63, 61, 61, 54, 61, 61, 54, 61, 54, 61, 54, 61, 54, 68, 54, 61, 54, 63, 54, 61, 54, 61, 53, 61, 53, 61, 53, 61, 65, 63, 61, 53, 61, 63, 61, 61, 61, 54, 61, 61, 54, 61, 54, 61, 54, 61, 54, 68, 54, 61, 54, 63, 54, 61, 54, 53, 61, 61, 53, 61, 53, 61, 53, 61, 63, 70, 61, 53, 61, 68, 61, 63, 65, 54, 61, 61, 61, 54, 61, 61, 61, 54, 61, 61, 54, 61, 68, 61, 54, 61, 61, 54, 61, 63, 61, 54, 61, 65, 61, 53, 61, 61, 53, 61, 53, 61, 53, 61, 63, 61, 70, 61, 63, 68, 53, 65, 63, 54, 61, 61, 54, 61, 61, 61, 54, 61, 61, 61, 54, 61, 54, 61, 68, 61, 54, 61, 61, 54, 61, 63, 68, 54, 61, 54, 61, 63, 53, 61, 61, 53, 61, 53, 61, 61, 53, 61, 63, 61, 61, 53, 61, 70, 61, 61, 53, 61, 68, 61, 61, 53, 61, 65, 53, 61, 63, 49, 54, 61, 68, 61, 49, 54, 61, 63, 65, 68, 61, 61, 49, 54, 61, 63, 65, 68, 70, 61, 61, 49, 54, 61, 49, 54, 61, 63, 65, 68, 70, 61, 49, 54, 61, 63, 65, 61, 49, 54, 61, 63, 65, 68, 49, 54, 61, 49, 54, 61, 63, 53, 61, 61, 53, 61, 53, 61, 61, 53, 61, 63, 61, 61, 53, 61, 63, 70, 61, 61, 53, 61, 63, 65, 68, 61, 61, 53, 61, 63, 65, 53, 61, 63, 49, 54, 61, 61, 49, 54, 61, 63, 65, 68, 61, 61, 49, 54, 61, 63, 65, 68, 70, 61, 49, 54, 61, 63, 65, 68, 70, 49, 54, 61, 49, 54, 61, 63, 65, 68, 70, 61, 49, 54, 61, 63, 65, 61, 49, 54, 61, 63, 65, 68, 49, 54, 61, 63, 65, 49, 54, 61, 63, 48, 49, 53, 61, 61, 49, 53, 61, 63, 65, 70, 49, 53, 61, 63, 65, 61, 48, 49, 53, 61, 63, 65, 68, 61, 61, 49, 53, 61, 63, 65, 70, 61, 61, 48, 49, 53, 61, 63, 65, 68, 48, 49, 53, 61, 63, 65, 68, 48, 49, 53, 61, 63, 65, 68, 48, 49, 53, 61, 63, 65, 68, 70, 51, 56, 61, 63, 65, 68, 70, 72, 51, 56, 60, 61, 63, 49, 51, 54, 61, 63, 65, 68, 54, 61, 63, 65, 68, 70, 51, 56, 61, 63, 65, 68, 70, 72, 51, 56, 60, 61, 63, 49, 51, 54, 61, 63, 65, 68, 70, 54, 61, 63, 65, 68, 51, 56, 61, 63, 65, 68, 70, 72, 51, 56, 60, 61, 63, 49, 51, 54, 61, 63, 65, 68, 54, 61, 63, 65, 68, 70, 51, 56, 61, 63, 65, 68, 70, 72, 51, 56, 60, 61, 63, 49, 51, 54, 61, 63, 65, 68, 70, 72, 49, 54, 61, 65, 53, 63, 68, 61, 54, 70, 63, 49, 54, 60, 61, 63, 65, 54, 66, 53, 65, 54, 63, 61, 60, 58, 48, 70, 49, 72, 54, 65, 56, 63, 49, 66, 53, 70, 68, 54, 60, 61, 63, 53, 66, 54, 65, 63, 65, 70, 68, 66, 49, 58, 60, 68, 53, 66, 54, 65, 54, 63, 54, 61, 54, 60, 56, 61, 54, 61, 54, 61, 54, 61, 54, 70, 54, 61, 54, 63, 54, 61, 61, 61, 53, 61, 53, 61, 53, 61, 65, 63, 61, 53, 61, 63, 61, 68, 61, 61, 61, 54, 61, 54, 61, 54, 61, 54, 61, 54, 61, 54, 63, 54, 61, 54, 61, 53, 61, 61, 53, 61, 61, 61, 61, 65, 61, 61, 63, 61, 61, 61, 61, 61, 54, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 53, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 54, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61}
midiTimeStamps = {10, 297, 546, 795, 1054, 1312, 1559, 1807, 2069, 2332, 2579, 2827, 3084, 3341, 3589, 3837, 4100, 4393, 4642, 4891, 5150, 5408, 5655, 5903, 6165, 6428, 6675, 6923, 7180, 7437, 7685, 7933, 8196, 8489, 8738, 8987, 9246, 9504, 9751, 9999, 10261, 10524, 10771, 11019, 11276, 11533, 11781, 12029, 12292, 12585, 12834, 13083, 13342, 13600, 13847, 14095, 14357, 14620, 14867, 15115, 15372, 15629, 15877, 16125, 16388, 16681, 16930, 17179, 17438, 17696, 17943, 18191, 18453, 18716, 18963, 19211, 19468, 19725, 19973, 20221, 20484, 20777, 21026, 21275, 21534, 21792, 22039, 22287, 22549, 22812, 23059, 23307, 23564, 23821, 24069, 24317, 24580, 24873, 25122, 25371, 25630, 25888, 26135, 26383, 26645, 26908, 27155, 27403, 27660, 27917, 28165, 28413, 28676, 28969, 29218, 29467, 29726, 29984, 30231, 30479, 30741, 31004, 31251, 31499, 31756, 32013, 32261, 32509, 32772, 33065, 33314, 33563, 33822, 34080, 34327, 34575, 34837, 35100, 35347, 35595, 35852, 36109, 36357, 36605, 36868, 37161, 37410, 37659, 37918, 38176, 38423, 38671, 38933, 39196, 39443, 39691, 39948, 40205, 40453, 40701, 40964, 41257, 41506, 41755, 42014, 42272, 42519, 42767, 43029, 43292, 43539, 43787, 44044, 44301, 44549, 44797, 45060, 45353, 45602, 45851, 46110, 46368, 46615, 46863, 47125, 47388, 47635, 47883, 48140, 48397, 48645, 49156, 49449, 49698, 49947, 50206, 50464, 50711, 50959, 51221, 51484, 51731, 51979, 52236, 52493, 52741, 52989, 53252, 53545, 53794, 54043, 54302, 54560, 54807, 55055, 55317, 55580, 55827, 56075, 56332, 56589, 56837, 57085, 57348, 57641, 57890, 58139, 58398, 58656, 58903, 59151, 59413, 59676, 59923, 60171, 60428, 60685, 60933, 61181, 61444, 61737, 61986, 62235, 62494, 62752, 62999, 63247, 63509, 63772, 64019, 64267, 64524, 64781, 65029, 65540, 65540, 65833, 66082, 66082, 66331, 66590, 66848, 67095, 67343, 67605, 67868, 68115, 68363, 68620, 68877, 69125, 69373, 69636, 69929, 70178, 70427, 70686, 70944, 71191, 71439, 71701, 71964, 72211, 72459, 72716, 72973, 73221, 73469, 73732, 73732, 74025, 74274, 74274, 74523, 74782, 75040, 75287, 75535, 75797, 76060, 76307, 76555, 76812, 77069, 77317, 77565, 77828, 77828, 78121, 78370, 78370, 78619, 78878, 79136, 79383, 79631, 79893, 80156, 80403, 80651, 80908, 81165, 81413, 81661, 81924, 81924, 82217, 82466, 82715, 82715, 82974, 83232, 83479, 83479, 83727, 83989, 83989, 83989, 84252, 84499, 84499, 84747, 85004, 85004, 85004, 85261, 85509, 85509, 85509, 85757, 86020, 86020, 86313, 86562, 86562, 86811, 87070, 87328, 87575, 87823, 88085, 88348, 88595, 88843, 89100, 89357, 89605, 89853, 90116, 90116, 90409, 90658, 90658, 90907, 91166, 91424, 91424, 91671, 91919, 92181, 92181, 92444, 92444, 92444, 92691, 92939, 92939, 93196, 93453, 93453, 93453, 93453, 93701, 93701, 93949, 93949, 93949, 94212, 94212, 94505, 94754, 94754, 95003, 95003, 95262, 95520, 95520, 95520, 95767, 96015, 96277, 96277, 96277, 96540, 96787, 97035, 97035, 97035, 97292, 97549, 97797, 97797, 97797, 98045, 98045, 98045, 98308, 98308, 98308, 98308, 98601, 98850, 98850, 98850, 98850, 98850, 98850, 99099, 99358, 99616, 99616, 99616, 99616, 99616, 99616, 99616, 99863, 100111, 100373, 100373, 100373, 100636, 100636, 100636, 100636, 100636, 100636, 100636, 100883, 101131, 101131, 101131, 101131, 101131, 101388, 101645, 101645, 101645, 101645, 101645, 101645, 101893, 101893, 101893, 102141, 102141, 102141, 102141, 102404, 102404, 102697, 102946, 102946, 103195, 103195, 103454, 103712, 103712, 103712, 103959, 104207, 104469, 104469, 104469, 104469, 104732, 104979, 105227, 105227, 105227, 105227, 105227, 105484, 105741, 105989, 105989, 105989, 105989, 106237, 106237, 106237, 106500, 106500, 106500, 106793, 107042, 107042, 107042, 107042, 107042, 107042, 107291, 107550, 107808, 107808, 107808, 107808, 107808, 107808, 107808, 108055, 108303, 108303, 108303, 108303, 108303, 108303, 108303, 108565, 108565, 108565, 108828, 108828, 108828, 108828, 108828, 108828, 108828, 109075, 109323, 109323, 109323, 109323, 109323, 109580, 109837, 109837, 109837, 109837, 109837, 109837, 110085, 110085, 110085, 110085, 110085, 110333, 110333, 110333, 110333, 110596, 110596, 110596, 110596, 110889, 111138, 111138, 111138, 111138, 111138, 111138, 111387, 111387, 111387, 111387, 111387, 111646, 111904, 111904, 111904, 111904, 111904, 111904, 111904, 112151, 112399, 112661, 112661, 112661, 112661, 112661, 112661, 112924, 113171, 113419, 113419, 113419, 113419, 113419, 113419, 113419, 113676, 113676, 113676, 113676, 113676, 113676, 113676, 113933, 113933, 113933, 113933, 113933, 113933, 113933, 114181, 114181, 114181, 114181, 114181, 114181, 114181, 114181, 114692, 114692, 114692, 114692, 114692, 114692, 114692, 114692, 116000, 116000, 116000, 116000, 116000, 117267, 117267, 117267, 117267, 117267, 117267, 117267, 118029, 118029, 118029, 118029, 118029, 118029, 118788, 118788, 118788, 118788, 118788, 118788, 118788, 118788, 119838, 119838, 119838, 119838, 119838, 121363, 121363, 121363, 121363, 121363, 121363, 121363, 121363, 121868, 121868, 121868, 121868, 121868, 122884, 122884, 122884, 122884, 122884, 122884, 122884, 122884, 124439, 124439, 124439, 124439, 124439, 125212, 125212, 125212, 125212, 125212, 125212, 125212, 126469, 126469, 126469, 126469, 126469, 126469, 126980, 126980, 126980, 126980, 126980, 126980, 126980, 126980, 128535, 128535, 128535, 128535, 128535, 129042, 129042, 129042, 129042, 129042, 129042, 129042, 129042, 130052, 130345, 130594, 130843, 131102, 131360, 131607, 131855, 132117, 132380, 132627, 132875, 133132, 133340, 133539, 133735, 133935, 134148, 134441, 134690, 134939, 135198, 135406, 135605, 135802, 136001, 136213, 136476, 136723, 136971, 137228, 137436, 137635, 137831, 138031, 138244, 138487, 138687, 138885, 139085, 139294, 139552, 139799, 140047, 140309, 140572, 140819, 141067, 141324, 141581, 141829, 142077, 142340, 142633, 142882, 143131, 143390, 143648, 143895, 144143, 144405, 144668, 144915, 145163, 145420, 145677, 145925, 146173, 146436, 146729, 146978, 147227, 147486, 147744, 147991, 148239, 148501, 148764, 149011, 149259, 149516, 149773, 150021, 150269, 150532, 150825, 151074, 151323, 151582, 151840, 152087, 152335, 152597, 152860, 153107, 153355, 153612, 153820, 154019, 154215, 154415, 154628, 154921, 155170, 155419, 155678, 155936, 156183, 156431, 156693, 156956, 157203, 157451, 157708, 157965, 158213, 158461, 158724, 159017, 159266, 159515, 159774, 159982, 160181, 160378, 160577, 160789, 161052, 161299, 161547, 161804, 162061, 162309, 162557, 162820, 163113, 163362, 163611, 163870, 164128, 164375, 164623, 164885, 165148, 165395, 165643, 165900, 166157, 166405, 166653, 166916, 167209, 167458, 167707, 167966, 168224, 168471, 168719, 168981, 169244, 169491, 169739, 169996, 170253, 170501, 170749, 171012, 171305, 171554, 171803, 172062, 172320, 172567, 172815, 173077, 173340, 173587, 173835, 174092, 174349, 174597, 174845, 175108, 175401, 175650, 175899, 176158, 176416, 176663, 176911, 177173, 177436, 177683, 177931, 178188, 178445, 178693, 178941}
midiVelocities = {61, 22, 22, 22, 22, 22, 21, 21, 29, 21, 21, 21, 21, 21, 21, 21, 39, 21, 20, 20, 20, 20, 20, 20, 28, 20, 20, 20, 29, 19, 28, 19, 56, 64, 60, 49, 42, 23, 24, 25, 26, 26, 27, 28, 28, 29, 30, 30, 59, 28, 16, 18, 20, 23, 25, 27, 47, 28, 28, 28, 40, 28, 43, 52, 52, 64, 64, 45, 46, 46, 45, 43, 37, 22, 22, 22, 36, 21, 46, 49, 64, 53, 50, 47, 43, 39, 36, 32, 28, 25, 21, 26, 36, 43, 49, 55, 62, 57, 53, 49, 46, 42, 38, 34, 31, 31, 33, 33, 33, 33, 33, 33, 31, 34, 35, 36, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 65, 52, 60, 47, 55, 59, 73, 76, 66, 54, 48, 46, 44, 42, 41, 46, 53, 71, 57, 48, 49, 51, 53, 55, 57, 59, 61, 62, 64, 66, 68, 70, 53, 53, 73, 53, 53, 53, 60, 60, 63, 60, 60, 64, 80, 73, 59, 51, 64, 68, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 85, 33, 33, 33, 33, 33, 33, 33, 33, 39, 42, 42, 42, 42, 42, 42, 47, 47, 47, 47, 47, 47, 46, 45, 44, 40, 39, 39, 39, 39, 39, 39, 39, 39, 40, 40, 44, 47, 47, 47, 47, 47, 47, 47, 47, 43, 46, 39, 32, 30, 30, 30, 30, 30, 29, 38, 44, 51, 54, 54, 54, 54, 53, 67, 70, 70, 71, 73, 73, 74, 74, 74, 74, 74, 99, 85, 73, 64, 64, 64, 57, 57, 57, 47, 47, 47, 47, 34, 30, 30, 30, 31, 37, 44, 47, 54, 56, 64, 64, 64, 64, 67, 67, 80, 80, 80, 85, 95, 104, 99, 87, 84, 84, 84, 84, 84, 84, 84, 77, 74, 74, 74, 74, 74, 75, 87, 87, 89, 93, 97, 97, 97, 98, 98, 98, 98, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 102, 102, 102, 102, 102, 92, 80, 71, 72, 75, 75, 75, 75, 84, 93, 100, 108, 108, 108, 108, 108, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, 109, 109, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 115, 118, 118, 118, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 122, 122, 122, 122, 122, 117, 117, 112, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 104, 104, 104, 104, 116, 116, 116, 125, 125, 125, 125, 127, 127, 127, 127, 127, 127, 120, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 53, 52, 57, 59, 71, 78, 78, 78, 71, 60, 60, 60, 60, 59, 63, 73, 84, 84, 82, 72, 64, 64, 64, 64, 60, 70, 69, 71, 71, 71, 71, 71, 71, 71, 72, 77, 82, 82, 80, 73, 64, 52, 44, 48, 51, 55, 58, 62, 66, 69, 73, 76, 81, 84, 88, 97, 97, 97, 100, 103, 106, 109, 112, 115, 118, 120, 123, 126, 64, 64, 64, 64, 64, 64, 64, 94, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 78, 59, 59, 59, 74, 60, 60, 60, 61, 60, 66, 69, 73, 77, 82, 87, 91, 96, 60, 60, 60, 73, 60, 60, 60, 88, 60, 60, 60, 65, 60, 60, 60, 59, 54, 49, 46, 42, 45, 46, 50, 45, 54, 64, 80, 92, 97, 35, 35, 35, 35, 33, 35, 35, 35, 35, 35, 36, 36, 36, 36, 36, 29, 29, 29, 29, 29, 29, 29, 29, 29, 21, 28, 28, 33, 39, 46, 53, 64, 64, 64, 16, 16, 19, 19, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22}
midiTicksPerSecond = 1024
samples48 = { "C3-1.wav", "C3-2.wav", "C3-3.wav", "C3-4.wav", "C3-5.wav", "C3-6.wav" }
samples49 = { "C#3-1.wav", "C#3-2.wav", "C#3-3.wav", "C#3-4.wav", "C#3-5.wav", "C#3-6.wav", "C#3-7.wav", "C#3-8.wav" }
samples50 = { "D3-1.wav", "D3-2.wav", "D3-3.wav", "D3-4.wav", "D3-5.wav", "D3-6.wav", "D3-7.wav", "D3-8.wav", "D3-9.wav"}
samples51 = { "D#3-1.wav", "D#3-2.wav","D#3-3.wav","D#3-4.wav","D#3-5.wav","D#3-6.wav","D#3-7.wav","D#3-8.wav" }
samples52 = { "E3-1.wav", "E3-2.wav", "E3-3.wav", "E3-4.wav", "E3-5.wav", "E3-6.wav", "E3-7.wav"}
samples53 = { "F3-1.wav", "F3-2.wav", "F3-3.wav", "F3-4.wav", "F3-5.wav", "F3-6.wav", "F3-7.wav", "F3-8.wav", "F3-9.wav"}
samples54 = { "F#3-1.wav", "F#3-2.wav", "F#3-3.wav", "F#3-4.wav", "F#3-5.wav", "F#3-6.wav", "F#3-7.wav", "F#3-8.wav"}
samples55 = { "G3-1.wav", "G3-2.wav", "G3-3.wav", "G3-4.wav", "G3-5.wav", "G3-6.wav", "G3-7.wav", "G3-8.wav"}
samples56 = { "G#3-1.wav", "G#3-2.wav", "G#3-3.wav", "G#3-4.wav", "G#3-5.wav", "G#3-6.wav", "G#3-7.wav", "G#3-8.wav"}
samples57 = { "A3-1.wav", "A3-2.wav", "A3-3.wav", "A3-4.wav", "A3-5.wav", "A3-6.wav", "A3-7.wav"}
samples58 = { "A#3-1.wav", "A#3-2.wav", "A#3-3.wav", "A#3-4.wav", "A#3-5.wav", "A#3-6.wav", "A#3-7.wav", "A#3-8.wav"}
samples59 = { "B3-1.wav", "B3-2.wav", "B3-3.wav", "B3-4.wav", "B3-5.wav", "B3-6.wav", "B3-7.wav", "B3-8.wav"}
samples60 = { "C4-1.wav", "C4-2.wav", "C4-3.wav", "C4-4.wav", "C4-5.wav", "C4-6.wav", "C4-7.wav", "C4-8.wav"}
samples61 = { "C#4-1.wav", "C#4-2.wav", "C#4-3.wav", "C#4-4.wav", "C#4-5.wav", "C#4-6.wav", "C#4-7.wav", "C#4-8.wav", "C#4-9.wav" }
samples62 = { "D4-1.wav", "D4-2.wav", "D4-3.wav", "D4-4.wav", "D4-5.wav", "D4-6.wav", "D4-7.wav", "D4-8.wav"}
samples63 = { "D#4-1.wav", "D#4-2.wav","D#4-3.wav","D#4-4.wav","D#4-5.wav","D#4-6.wav","D#4-7.wav"}
samples64 = { "E4-1.wav", "E4-2.wav", "E4-3.wav", "E4-4.wav", "E4-5.wav", "E4-6.wav"}
samples65 = { "F4-1.wav", "F4-2.wav", "F4-3.wav", "F4-4.wav", "F4-5.wav", "F4-6.wav", "F4-7.wav", "F4-8.wav"}
samples66 = { "F#4-1.wav", "F#4-2.wav", "F#4-3.wav", "F#4-4.wav", "F#4-5.wav", "F#4-6.wav", "F#4-7.wav"}
samples67 = { "G4-1.wav", "G4-2.wav", "G4-3.wav", "G4-4.wav", "G4-5.wav", "G4-6.wav", "G4-7.wav"}
samples68 = { "G#4-1.wav", "G#4-2.wav", "G#4-3.wav", "G#4-4.wav", "G#4-5.wav", "G#4-6.wav", "G#4-7.wav"}
samples69 = { "A4-1.wav", "A4-2.wav", "A4-3.wav", "A4-4.wav", "A4-5.wav", "A4-6.wav", "A4-7.wav"}
samples70 = { "A#4-1.wav", "A#4-2.wav", "A#4-3.wav", "A#4-4.wav", "A#4-5.wav", "A#4-6.wav", "A#4-7.wav", "A#4-8.wav"}
samples71 = { "B4-1.wav", "B4-2.wav", "B4-3.wav", "B4-4.wav", "B4-5.wav", "B4-6.wav", "B4-7.wav"}
samples72 = { "C5-1.wav", "C5-2.wav", "C5-3.wav", "C5-4.wav", "C5-5.wav", "C5-6.wav", "C5-7.wav", "C5-8.wav"}
samples = {samples48,samples49,samples50,samples51,samples52,samples53,samples54,samples55,samples56,samples57,samples58,samples59,samples60,samples61,samples62,samples63,samples64,samples65,samples66,samples67,samples68,samples69,samples70,samples71,samples72}
sampleDir = "toy-piano-samples/"

/* destructo parameters */
birthdate = 1356548422
twenty = 1987700422
percent = (curTime - birthdate) / (twenty - birthdate)

timingErrorEnv = maketable("line", "nonorm", 1000, 0,1, 3,1, 8,0.2, 12,0.01, 20,0.001)
timingErrorProb = samptable(timingErrorEnv, "interp", 1000*percent)
timingErrorMagEnv = maketable("line", "nonorm", 1000, 0,0.25, 3,0.25, 8,0.0625, 12,0.01, 20,0.005)
timingErrorMag = samptable(timingErrorMagEnv, "interp", 1000*percent)

playNoteProbEnv = maketable("line", "nonorm", 1000, 0,0.2, 3,0.3, 6,0.4, 9,0.8, 12,0.95, 20,0.999)
playNoteProb = samptable(playNoteProbEnv, "interp", 1000*percent)

pitchErrorEnv = maketable("line", "nonorm", 1000, 0,0.95, 3,0.8, 8,0.5, 12,0.1, 14,0.01, 20,0.001)
pitchErrorProb = samptable(pitchErrorEnv, "interp", 1000*percent)
pitchErrorMag = 3

extraNoteEnv = maketable("line", "nonorm", 1000, 0,0.5, 5,0.5, 8,0.25, 11,0.05, 15,0.01, 20,0.0001)
extraNoteProb = samptable(extraNoteEnv, "interp", 1000*percent)
extraNoteMag = 2

changeTempoEnv = maketable("line", "nonorm", 1000, 0,1, 3,0.85, 6,0.5, 12,0.1, 20,0.01)
changeTempoProb = samptable(changeTempoEnv, "interp", 1000*percent)
changeTempoMagEnv = maketable("line", "nonorm", 1000, 0,100, 3,50, 12,10, 20,1)
changeTempoMag = samptable(changeTempoMagEnv, "interp", 1000*percent)

keepToScaleProb = 0.98

/* make a guassian table to draw random gaussian numbers from */
gaussTable = maketable("random", 20000, "gaussian", -1, 1)
gaussIndex = 0

/* now play back the score */

for (i=0; i < len(midiNoteNumbers); i = i + 1) {
        midiNote = midiNoteNumbers[i]
        if (random() < pitchErrorProb) { // random probablility of playing wrong note
                randNum = samptable(gaussTable, gaussIndex) // grab the next random gauss number
                guassIndex = gaussIndex + 1
                midiNote = midiNote + pitchErrorMag * randNum // change to new MIDI note
                 // i.e. if this note is not one in the original piece
                if ((index(midiNoteNumbers, trunc(midiNote)) == -1) && (random() < keepToScaleProb)) {
                        // increment until we get to a note from the original piece
                        while ((index(midiNoteNumbers, trunc(midiNote)) == -1) && (midiNote < 72)) {
                                midiNote = midiNote + 1
                        }
                }
                if (midiNote < 48) { midiNote = 48 }
                if (midiNote > 72) { midiNote = 72 }
        }
        sampleArray = samples[midiNote - 48] // get the right sample array for the MIDI note
        // pick a sample based on MIDI velocity of note
        sampleArrayIndex = trunc((1.0 - (midiVelocities[i] / 128.0)) * len(sampleArray))
        if (random() < 0.5) { // select neighbor sample instead to increase variety
                sampleArrayIndex = sampleArrayIndex + trand(-1, 2)
                if (sampleArrayIndex < 0) { sampleArrayIndex = 0 }
                if (sampleArrayIndex > (len(sampleArray)-1)) {
                        sampleArrayIndex = len(sampleArray)-1
                }
        }
        sample = sampleArray[sampleArrayIndex]
        // convert MIDI time to seconds for playback time
        st = midiTimeStamps[i] / midiTicksPerSecond
        if (random() < timingErrorProb) { // random probability of playing note at wrong time
                randNum = samptable(gaussTable, gaussIndex) // grab the next random gauss number
                guassIndex = gaussIndex + 1
                st = st + randNum * timingErrorMag
        }
        rtinput(sampleDir + sample) // load the correct sample
        pan = (midiNote - 48) / 24 // set panning to lower notes pan left, higher pan right
        // random chance note won't get played at all
        if ((random() < playNoteProb) && (st >= 0)) {
                STEREO(st, 0, DUR(), 0.25, pan, 1 - pan)
                // random chance extra notes will get played;
                // this loops until the random trigger is false so we might get several extra notes
                while (random() < extraNoteProb) { 
                        midiNote = midiNote + extraNoteMag * rand()
                        // i.e. if this note is not one in the original piece
                        if ((index(midiNoteNumbers, trunc(midiNote)) == -1) && (random() < keepToScaleProb)) {
                                // increment until we get to a note from the original piece
                                while ((index(midiNoteNumbers, trunc(midiNote)) == -1) && (midiNote < 72)) {
                                        midiNote = midiNote + 1
                                }
                        }
                        if (midiNote < 48) { midiNote = 48 }
                        if (midiNote > 72) { midiNote = 72 }
                        sampleArray = samples[midiNote - 48]
                        sampleArrayIndex = (1.0 - (midiVelocities[i] / 128.0)) * len(sampleArray)
                        if (random() < 0.5) { // select neighbor sample instead to increase variety
                               sampleArrayIndex = sampleArrayIndex + trand(-1, 2)
                               if (sampleArrayIndex < 0) { sampleArrayIndex = 0 }
                               if (sampleArrayIndex > (len(sampleArray)-1)) {
                                sampleArrayIndex = len(sampleArray)-1
                               }
                        }
                        sample = sampleArray[sampleArrayIndex]
                        rtinput(sampleDir + sample)
                        pan = (midiNote - 48) / 24
                        STEREO(st, 0, DUR(), 0.25, pan, 1 - pan)
                }
        }
        if (random() < changeTempoProb) { // random chance of introducing tempo change
                midiTicksPerSecond = midiTicksPerSecond + changeTempoMag * rand()
                if (midiTicksPerSecond < 750) { midiTicksPerSecond = 750 }
                if (midiTicksPerSecond > 1250) { midiTicksPerSecond = 1250 }
        }
}

Infinite Snowflake

Listen. (Generated on 05-23-2019)
for Leah

This piece is a reminder that all the details we can sometimes get bogged down in -- kids waking up in the middle of the night, seemingly endless trips to the pediatrician, repairs to our 100-year-old house, a constant deluge of dirty dishes and laundry, and so on -- really don't matter very much. It all fades away when we focus on the love, devotion, and selflessness for our family that have always been at our core. We must always find frequent moments that remind us of the joy we bring each other and that our children bring to us.

I've long been fascinated with mathematically interesting infinite number sequences, and I've often used them as a structural foundation for my music. This music uses one of my favorite sequences to create a sense of these simple moments continually resurfacing. It starts like this:

0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,...

The music steps through each number in the sequence in order. As the numbers grow larger, more pitches are played simultaneously. These pitches are part of a harmonic series, and they repeat rhythmically at rates derived from those harmonic relationships, thus creating complex polyrhythmic, microtonal textures as the integers get higher and higher. (This approach was particularly inspired by Larry Polansky's Psaltery works and by Henry Cowell's Rhythmicon.)

As time passes, more and more numbers from the sequence are used to generate the piece, leading to increasingly complex and detailed textures. Yet the sequence, and therefore the music, always takes time to relish in the lower numbers and purer textures with which the piece began.

This integer sequence is closely related to the Koch snowflake, a famous fractal pattern whose perimeter is of infinite length. I wasn't thinking of this connection while writing the piece, but in retrospect, I suppose I imagine sitting by a fire with my family, watching snowflakes fall outside, and remembering my infinite blessings.

/* do the settings for RTcmix */

set_option("clobber=1")
set_option("play=0")
set_option("audio=0")
rtsetparams(44100, 2)
rtoutput("leah.aif")

load("MBANDEDWG")
load("FREEVERB")

bus_config("MBANDEDWG", "aux 0-1 out")
bus_config("FREEVERB", "aux 0-1 in", "out 0-1")

/* grab the current unix timestamp from the command line argument */

curDate = f_arg(0)
printf(" %f is curDate\n", curDate)
startDate = 1136133673
endDate = 2998053673
percent = (curDate - startDate) / (endDate - startDate)
daysSinceBeginning = (curDate - startDate) / 86400

/* set up some basic constants */

baseFreq = 90
numPartials = 19
totalDurInSecs = 240
/* as time passes, use more and more elements of thue morse */
sequenceLength = percent * (512 - 32) + 32
sequenceStartIndex = 1
thuemorse = { }

/* compute the thue morse terms we want to use for this run */

for (i=0; i<sequenceLength; i=i+1) {
        n = sequenceStartIndex + i
        temp = n
        result = 0
        while (temp > 0) {
                result = result + temp %2
                temp = trunc(temp / 2)
        }
        thuemorse[i] = result
}

/* make the music one partial at a time */

for (partial=1; partial < numPartials; partial=partial+1) {
        /* higher partials make faster repeating rhythms */
        skip = (numPartials - partial) * 0.125
        for (st=0; st<totalDurInSecs; st=st+skip) {
                /* get current array index based on how far we are into the piece */
                positionIndex = trunc((st / totalDurInSecs) * len(thuemorse))
                /* layer one starts with highest partial at beginning and adds in descending */
                if ((thuemorse[positionIndex] >= (numPartials - partial))) {
                        /* amplitude of this layer decreases over time
                         * and lower partials are quieter */
                        amp = ampdb(50 + 15 - pow(15 * st / totalDurInSecs, 0.5)) * (0.65 + 0.35 * partial / numPartials)
                        /* small random variation in amplitude of each note */
                        amp = amp * irand(0.90, 1.1)
                        /* occasional accents added in */
                        if (random() < 0.25 * pow(((numPartials - partial) / numPartials), 0.5)) { amp = amp * 3 }
                        /* panning based on partial number */
                        pan = 0.25 + 0.5 - 0.5 * partial / numPartials
                        /* finally, play the note! */
                        MBANDEDWG(st, skip, amp, baseFreq*partial, 0, 0, 1, 0, 0, 0.99, 0, pan) 
                }
                /* layer two starts with lowest partial at beginning and adds in ascending */
                if (thuemorse[positionIndex] >= partial) {
                        /* amplitude of this layer increases over time
                         * and higher partials are quieter */
                        amp = ampdb(65 + 15 * st / totalDurInSecs) * (0.65 + 0.35 * (numPartials - partial) / numPartials)
                        /* small random variation in amplitude of each note */
                        amp = amp * irand(0.95, 1.05)
                        /* occasional accents added in */
                        if (random() < 0.25 * pow(((numPartials - partial) / numPartials), 0.5)) { amp = amp * 3 }
                        /* panning based on partial number */
                        pan = 0.25 + 0.5 - 0.5 * partial / numPartials
                        /* finally, play the note! */
                        MBANDEDWG(st, skip, amp, baseFreq*partial, 0, 0, 1, 0, 0, 0.99, 0, pan)
                }
        }
}

/* add in some reverb that increases as the piece progresses */
dry = maketable("line", "nonorm", 100, 0,95, totalDurInSecs,65)
wet = maketable("line", "nonorm", 100, 0,5, totalDurInSecs,35)
FREEVERB(0, 0, totalDurInSecs + 5, 1.25, 0.9, 0.001, 4.0, 71, dry, wet, 25)

Coda

Listen. (Generated on 05-23-2019)

It is natural for a composer to want his or her work to survive for a long time. We like to believe that others will value our music enough that they will want to perform and preserve it decades or even centuries into the future. This whole EP, in fact, is rather presumptuous in its assumption that anyone will care about it years into the future. It's also somewhat naive in assuming that the technologies it relies upon today will still be useable far into the future. (I have made conscious decisions designed to maximize the potential for technological longevity, such as posting the source code for each track on the site and relying on an open-source computer music language that has already been around for 30 years. But technological obsolescence moves in quick and unpredictable ways.)

This track attempts to keep my timescale ambitions (and my composerly ego) in check. It gradually self-destructs by decaying slowly and (I hope) gracefully over the course of thirty years.

The music consists of three layers of sound: a single note, a two-note chord below it, and a bass note below that. For each layer, there are three different notes or chords that alternate slowly over the course of the piece. Each layer is also sometimes silent. All three layers are based on recordings of a piano (samples from the musical instrument sample database at the University of Iowa Electronic Music Studios). The lowest layer plays these recordings without modification. The upper two layers use a simple granular technique to transform the original recordings dramatically.

Over the course of the piece, the track erases itself. Over time, the upper two layers become silent more frequently, a filter gradually removes more and more of the sound from the piece (starting with the highest frequencies and moving downwards), and there is more and more reverb. By the end, all is silence.

The same process unfolds over the course of thirty years. As time passes, the beginning of the piece becomes more and more similar to the end, increasingly favoring silence and filtering out more sound. By 2044, the entire piece is silence.

/* do the settings for RTcmix */

set_option("clobber=1")
set_option("play=0")
set_option("audio=0")
rtsetparams(44100, 2)
rtoutput("coda.aif")

load("JCHOR")
load("BUTTER")
load("STEREO")
load("FREEVERB")

bus_config("JCHOR", "in 0-1", "aux 0-1 out")
bus_config("STEREO", "in 0-1", "aux 0-1 out")
bus_config("BUTTER", "aux 0-1 in", "aux 2-3 out")
bus_config("FREEVERB", "aux 2-3 in", "out 0-1")

/* grab the current unix timestamp from the command line argument */

curDate = f_arg(0)
printf(" %f is curDate\n", curDate)
startDate = 1399256115 /* May 2014 */
endDate = 2346027273 /* the year 2044 */
percent = (curDate - startDate) / (endDate - startDate)

/* the musical blocks */

lh1 = {"coda-sounds/Piano.ff.C3.aif", "coda-sounds/Piano.ff.E3.aif"}
lh2 = {"coda-sounds/Piano.ff.Bb2.aif", "coda-sounds/Piano.ff.F3.aif"}
lh3 = {"coda-sounds/Piano.ff.A2.aif", "coda-sounds/Piano.ff.F3.aif"}
lhb1 = { "coda-sounds/Piano.ff.C1.aif"}
lhb2 = { "coda-sounds/Piano.ff.Bb0.aif"}
lhb3 = { "coda-sounds/Piano.ff.A1.aif"}
rh1 = "coda-sounds/Piano.ff.G3.aif"
rh2 = "coda-sounds/Piano.ff.C4.aif"
rh3 = "coda-sounds/Piano.ff.D4.aif"
lh = {lh1, lh2, lh3}
lhb = {lhb1, lhb2, lhb3}
rh = {rh1, rh2, rh3}

/* constants */

enDur = 0.32608696 /* dur of eighth note */
mDur = 2.60869564 /* dur of full measure */
dur = 180 /* dur of whole piece */
grainenv = maketable("window", 1000, "hanning")

/* make the music, left hand */

st = 0  /* start time for loop */
lastIndex = -1
while (st < dur) {
        /* calculate a position metric based on current st time and our time up to 2044 */
        percentThroughSong = st / dur * (1.0-percent) + percent
        /* pick random dur btwn 1 and 6 eighth notes long */
        thisDur = enDur * trand(8, 48)
        /* random decision on whether this is sound or silence */
        if (random() > pow(percentThroughSong, 2.5)) {
                index = lastIndex
                /* pick a chord different from the last one */
                while (index == lastIndex) {
                        index = trand(0, len(lh))
                }
                chord = lh[index]
                /* iterate through each note in the chord */
                for (noteIndex = 0; noteIndex < len(chord); noteIndex = noteIndex + 1) {
                        note = chord[noteIndex]
                        rtinput(note) /* load the corresponding sound file */
                        ampenv = maketable("line", 1000, 0,0, 1,1, thisDur-1,1, thisDur,0)
                        /* play it with a granular-style technique, chorale-like */
                        JCHOR(st, 0, thisDur, 5, 1, 0, 5, 0.5, 2, 0.01, 0.5, 0, 0, 2 * ampenv, grainenv)
                        /* second JCHOR with shorter time values, more static-y results
                         * as we move through song (and time), use fewer grains and a smaller
                         * segment of the original sound file as material */
                        numgrains =  45 * pow((1-percentThroughSong),2) + 1
                        infileDur = 0.07 * pow((1-percentThroughSong),2) + 0.04
                        /* play it with a granular-style technique */
                        JCHOR(st, 0, thisDur, infileDur, 1, 0, numgrains, 0.5, 2, 0.01, 0.5, 0, 0, 0.2 * ampenv, grainenv)
                }
                /* randomly (and more as we move through the song) play an octave below too */
                if (random() < (percentThroughSong * 0.33)) {
                        chord = lhb[index]
                        note = chord[0]
                        pan = 0.5
                        rtinput(note)
                        /* play the original piano sound, no granular processing */
                        STEREO(st, 0, DUR(), 0.5, 0, 1)
                }
                lastIndex = index
        }
        st = st + thisDur
}

/* make the music, right hand */

st = 0 /* start time for loop */
lastIndex = -1
while (st < dur) {
        /* calculate a position metric based on current st time and our time up to 2044 */
        percentThroughSong = st / dur * (1.0-percent) + percent
        /* random decision on whether this is sound or silence */
        if (random() > pow(percentThroughSong, 3.5)) {
                index = lastIndex
                /* pick a note different from the last one */
                while (index == lastIndex) {
                        index = trand(0, len(rh))
                }
                note = rh[index]
                /* pick random dur btwn 1 and 16 measures long */
                thisDur = mDur * trand(1,16)
                rtinput(note) /* load the corresponding sound file */
                ampenv = maketable("line", 1000, 0,0, 1,1, thisDur-1,1, thisDur,0)
                /* as we move through song (and time), use fewer grains and a smaller
                 * segment of the original sound file as material */
                numgrains =  35 * pow((1-percentThroughSong),2) + 1
                infileDur = 0.06 * pow((1-percentThroughSong),2) + 0.02
                /* play it with a granular-style technique */
                JCHOR(st, 0, thisDur, infileDur, 1, 0, numgrains, 0.5, 2, 0.01, 0.5, 0, 0, 0.2 * ampenv, grainenv)
        }
        st = st + thisDur
}

/* add a lowpass filter that cuts out higher frequencies as we move through the song (and time) */
cutoff = maketable("curve", "nonorm", 1024, 0, (1.0-percent) * 9960 + 40, -2.5, dur + 20, 40)
BUTTER(0, 0, dur + 20, 1, "lowpass", 1, 1, 0, 0.5, 0, cutoff)

/* add in more reverb as we move through the song (and time) */
dry = maketable("line", "nonorm", 100, 0,45*(1.0-percent)+40, dur,40)
wet = maketable("line", "nonorm", 100, 0,55.0*percent+15, dur,60)
FREEVERB(0, 0, dur + 5, 2.5, 0.9, 0.001, 4.0, 71, dry, wet, 25)