One of the more spectacular projects in Clojure is Overtone, a toolkit for programmable music written by Sam Aaron and others. Overtone is a front-end to the SuperCollider sound synthesis server giving you a Clojure library to use synthesis, sampling and event instrument construction.
This post is the beginning of a series, documenting my experiments with generating music with Overtone. To get started, in this post we discuss how to play a short melody.
The melody is the subject of “Die Kunst der Fuge” by J.S. Bach.
To follow along you need the Leiningen build tool. Create a project as a sandbox.
1 2 | |
Edit the project.clj file in that directory and add the following to the dependencies.
1
| |
When you start a REPL, leiningen automatically downloads all dependencies.
1
| |
When you get the user=> prompt load overtone and see if it’s working.
1 2 3 | |
You should hear a short and somewhat annoying sound. If it doesn’t work for you, check the Overtone wiki.
Now we need a proper instrument so that we get less annoying sounds. Let’s go with a piano.
1 2 3 | |
As the name implies this piano uses samples to make sounds. When it’s
used for the first time, it downloads the samples which can take some
time. When the samples are downloaded, you should hear a single
note. Without arguments, the sampled-piano function plays a C. You can
pass a MIDI note value to specify which note you want.
1
| |
Overtone provides the note function to resolve a symbolic name to the MIDI value.
1
| |
The next thing we need is timing, because we don’t want to make music by
typing lines like these really fast into a REPL. Overtone gives us at
and apply-at to schedule actions. at is a macro that takes a
timestamp and a body of Clojure forms, executing the body at the given
time.
1
| |
This plays a note one second from now (the now function returns the
current timestamp). apply-at lets us schedule a function application
which is handy for recursively performing a function at specific times.
1
| |
This does the same thing as the at form above. The last argument is a
sequence that gets applied to the scheduled function (sampled-piano in
this case) as arguments, like the apply function from
clojure.core. We don’t need this here, but it is necessary to have at
least an empty vector in the arguments.
We could start playing melodies with these functions, but dealing with millisecond timestamps is cumbersome. A more appropriate level of abstraction is to work with counts and bars, as you would when making music without a computer. Fortunately, Overtone provides us with metronomes that can be used to keep track of an ongoing beat and resolve the timestamp of specific beats.
The metronome function creates a metronome with a given number of
beats per minute that starts counting in the background immediately and
returns a function to access that metronome. Called without arguments,
the metronome access function returns the next beat number. When called
with a beat number it returns the timestamp when that beat will come up.
1 2 3 4 5 | |
(The return values depend on the timing when you execute them, so you will see different ones.)
Now that we have an instrument, and beat based scheduling, we can start to think about playing a melody. Starting from the outside, what should a playing function look like? The function should take a metronome, so that we can define the speed and use the same metronome across different sound making functions, the instrument to use, and the melody.
But how do we encode the melody? We only want to play a single voice for the moment, so a sequence of notes is enough. Each note can be encoded as a vector of the symbolic name of its pitch (which I also referred to as note above – music terminology is a bit ambiguous) and its duration in beats. So the subject of “Kunst der Fuge” shown above can be encoded as:
1 2 | |
Knowing what the data look like, we can implement the functions to
interpret them. The first step is a function that plays a single
note. In order to be able to make the note part of the melody we have to
schedule playing it using at.
1
| |
Here we schedule the note for the next beat. But just scheduling when to play the note and then the next note afterwards is not enough, as you can hear the sound dragging on for a few seconds. We also need a way to stop it after the duration we encoded in our note vector.
To stop a note, we need the handle returned by the playing function and
use the ctl function to set the gate to 0.
1 2 | |
Tying this together into a function we get:
1 2 3 4 5 6 7 | |
We take the metronome, the target beat, and the instrument as
arguments. In the final argument, we destructure the note vector into
pitch and duration. Then we calculate the beat when the note needs to
stop by adding the duration to the start beat. In order to encode rests
with a pitch of nil, we only go into the playing code, if the pitch is
not nil. After that we play the note and stop it as shown
above. Finally, we return to beat when this note ends, so that we know
the timing of the next note.
Finally, we can now define the play function that goes through the
melody, calls play-one for each note and uses apply-at to schedule a
recursive execution of itself at for the next note.
1 2 3 4 5 6 7 8 9 | |
The recursive call has to get the beat, on which it should play its first note, but for a convenient API, we make that argument optional and ask the metronome for the next beat, so that it starts playing at once, when called. We cannot use the next beat from the metronome at each recursive call, as we need to be able to handle fractional beats for eighth or sixteenth notes.
We have reached our goal and can play the subject of “Kunst der Fuge”:
1
| |

