Background
In a previous post, I outlined a system for composing electronic music.
It was based on recording synthesis output from cl-collider (A Common Lisp library that acts as a client to the SuperCollider sound server) code into Ableton Live audio channels, and then manually sequencing the audio.
This system worked well, but I soon started to want to program the composition itself, and not just the synthesised audio.
Therefore, I started developing my Common Lisp music system in this direction.
Core Idea: Synth event generators
The system mainly revolves around stateful functions that return values that evolve over time.
This is achieved via the “let-over-lambda” pattern in Common Lisp, where you define functions that:
- bind values via LET
- return a lambda function that closes over the bound values
These then act as “generator” functions that can return numeric values used as synth parameters, or entire synth events; where synth events are property lists that describe a synth and its parameters to schedule on the SuperCollider server.
Here’s an example of one that cycles through a list of values:
(defun cycle (list)
(let* ((vec (coerce list 'vector))
(len (length vec))
(i -1))
(lambda ()
(setf i (mod (1+ i) len))
(aref vec i))))
And a synth event generator that uses it:
(defun ascending-bells ()
(let ((pitch (cycle '(71 76 81))))
(lambda ()
(list :bell
:out bus0
:amp 1
:pitch (funcall pitch)
:decay 8
:timbre 0.5
:morph 1.0
:harm 0.5))))
When the return value of this function is bound to a variable, it can then be called repeatedly, which returns a new synth event each time:
(defparameter *tmp-synth* (ascending-bells))
(funcall *tmp-synth*) ; event with pitch = 71
(funcall *tmp-synth*) ; event with pitch = 76
(funcall *tmp-synth*) ; event with pitch = 81
; ...
Further reading
There’s an entire book about Common Lisp, which is named after this pattern.
https://letoverlambda.com/textmode.cl/guest/chap2.html#sec_5
I still consider myself more of an intermediate Lisp programmer, so there’s much content there that I still need to internalise, however it has been a useful resource for deepening my understanding of using closures in Lisp.
Higher-level compositional structure
These synth event generators are powerful, but we can’t program music without having a way of scheduling their invocation over time.
Markov chains
One effective way of introducing higher-level structure into generative music systems is through Markov chains.
A Markov chain, in essence, is a system that probabilistically transitions between states.
This can work very well for musical structure, because:
- There’s an innate unpredictability that’s lacking in deterministic systems, which lends itself particularly well to electronic music that evolves slowly over time.
- The composer still has control over which state transitions are likely to occur, by setting transition weights.
A Markov chain sequencer
I created a simple Markov chain sequencer in Common Lisp, where:
- A Markov chain is defined as an adjacency-list graph, where edges are probabilities
- Each synth event generator is a node in the Markov chain graph.
- The states of the Markov chain are transitioned at random intervals.
Multiple copies of this sequencer can be active throughout the lifetime of the composition, with different synth event generators bound to their nodes.
Final musical output
Over Christmas and the new year, I worked on a short piece of music using this system, which can be listened to below.
I also kept to a constraint of only using a single synth instrument; a bell-like synth using the MiPlaits UGen from the Mutable Instruments SuperCollider UGens (which is included among cl-collider’s UGens).
This allowed me to focus more on what I was trying to achieve with the compositional aspects. I also decided to render the audio and consider the piece “finished” relatively early, rather than spend too long working on it. I think overall, I’ll be able to progress faster in what I’m trying to achieve with this system if I don’t spend too long on one composition at a time, and work towards longer, more complex pieces.