🎼Music, Haskell... and Westeros
Music is a recurring theme on this blog. Quod Libet takes up a fair bit of spare time, but the more abstract intersection of music and programming is fun, and it turns out functional programming has a lot to offer in this space. I assume some basic knowledge of Haskell / Elm (or ML-like languages perhaps), but a minimal amount of music knowledge.
Euterpea1 is a great music DSL for Haskell, with a long lineage based at Yale and around the late Paul Hudak’s Haskell School Of Music. I won’t try to explain its wide remit and concepts, but definitely worth reading some introductions. We’ll be focusing on the composition (music) rather than synthesis (audio) side.
There are a few other projects around (even in Haskell alone). One that looks particularly interesting, for more electronic / sample / dance-based work is Tidal Cycles, but we’re going to concentrate on Euterpea here.
Rather than algorithmic generation of music, I became interested in how far this library could be used to create or recreate, say, Western popular music (for a start) by hand, but in a concise and readable way.
The ability to use General MIDI seemed of limited value, but having set up Timidity it became clear bog-standard software synths have come a long way since, err, the late 90s, and this is a quick, free solution for creating passable music without specialist hardware or software. Interesting…
The history of trackers
Talking of the late 90s, some readers will be too young to remember Trackers on the Amiga / ST / DOS from then, so a quick recap!
These were nearly all:
- non-notated (i.e. no traditional music score)
- sample-based (you could provide your own lo-fi instrument sounds)
- tabular based on discrete time-steps, like a spreadsheet of sorts with channels as columns and time steps (1/8th of a note) as … Playback would going down the screen
- declarative (not recorded, or err, generative maybe). Entries (notes) looked like
C 4 A0, or similar, meaning C in the 4th octave with a volume of
- embedded metadata (such as BPM, vibrato, dynamics) into individual notes
Some of this experience is simply outdated by newer GUIs and advanced MIDI Sequencers, though for dance music / drum machines, similar interface persist.
…and some drawbacks
The ability to replicate entire sections in trackers was usually available in some way, but without any variation. What was completely lacking was the ability to re-use individual channels or phrases, or apply transformations (transpose, make louder or quieter, change instrument) to pre-existing parts.
In fact, this problem is sounding a lot like bad codebases, especially ones that don’t employ DRY. As developers, we’ll usually try to refactor: extracting common code to methods, building abstractions (even just functions that operate on data) and using generic types to write algorithms that can be independent of the data structure.
In functional programming these are even more important, and generic datatypes, type classes, higher-order functions and lazy evaluation allow us to do take these principles further. Could these somehow be applied to music?
data Music a = Prim (Primitive a) -- primitive value | Music a :+: Music a -- sequential composition | Music a :=: Music a -- parallel composition | Modify Control (Music a) -- modifier deriving (Show, Eq, Ord)
Primitive datatype is defined:
data Primitive a = Note Dur a | Rest Dur deriving (Show, Eq, Ord)
Dur, the duration for notes / rests is a
Rationalalias, so you’re not restricted to any particular quantisation of notes. Yes, this means it’s easy to do all sorts of polyrhythms, but more on that later (or not – we’re aiming for simple timing here).
Controlis to apply transformations (dynamics, tempo adjustments) to other music sections, a powerful concept modelled very simply.
Primitiveare Functors, though this isn’t important right now.
Well, it’s no fun just talking about this. How about we make some noise?
Setting up the project
Most of the snippets below should be run interactively in GHCI (using
stack repl), but it’s best to have a Stack project set up already, so do that now (or use my euterpea-sandbox one). Here’s the interesting bits from my
stack.yaml to get you started:
extra-deps: - Euterpea-2.0.2 - PortMidi-0.1.5.2 - Stream-0.4.7.2 - arrows-0.4.4.1 - heap-0.6.0 - lazysmallcheck-0.6 - stm-2.4.2 resolver: lts-9.0
The Cabal file just needs
Euterpea > 2.0.0 && < 2.1.0. I’ve put the full source on Github if you’re feeling lazy.
Set up MIDI
The Euterpea guide to MIDI output is as good as any.
If you’re on Linux I strongly recommend Timidity unless you have hardware synths of course. The FreePats samples are a good start (but don’t cover all GM instruments). For detailed setup, the Arch Linux Timidity page is good too, especially if you want to set Timitidy to run by default.
For OS X users, SimpleSynth is recommended, but I haven’t tried it myself. Windows users shouldn’t have too many problems with the default setup I believe.
Run your MIDI synth
Make sure your synth (see above) is running. FYI, I use
timidity -iA -Os -f -A 210 --verbose=2 in a separate shell. Note what channel your MIDI synth now running on (mine is usually 4).
Make some noise!
If we load GHCI (with
stack repl), we can play around live:
λ> import Euterpea λ> devices Input devices: InputDeviceID 1 Midi Through Port-0 InputDeviceID 3 Scarlett 2i4 USB MIDI 1 Output devices: OutputDeviceID 0 Midi Through Port-0 OutputDeviceID 2 Scarlett 2i4 USB MIDI 1 OutputDeviceID 4 TiMidity port 0 OutputDeviceID 5 TiMidity port 1 OutputDeviceID 6 TiMidity port 2 OutputDeviceID 7 TiMidity port 3 λ> channel = 4 -- Or whatever works for you λ> playDev channel $ c 4 wn
Woah – some sound! More precisely, an acoustic grand piano playing middle C for a whole note (
wn)… in 4/4 at 120bpm, assuming the usual defaults. Remember to choose the right output
channel for your setup for that
The two fundamental operations for composing (in the FP sense) sounds are
:=:, which plays them in parallel, i.e. together.
:+:, which plays them in sequence, i.e. one thing after another.
:=: means lots of notes played together? Even the non-musicians will recognise this one – that’s a chord. And yes, there’s a list helper function for that:
chord, as lists are generally easier to type or manipulate. Try this in your REPL:
λ> cMinor = chord [c 3 qn, ef 3 qn, g 3 qn] λ> cMinor' = c 3 qn :=: ef 3 qn :=: g 3 qn λ> print cMinor Prim (Note (1 % 4) (C,3)) :=: (Prim (Note (1 % 4) (Ef,3)) :=: (Prim (Note (1 % 4) (G,3)) :=: Prim (Rest (0 % 1)))) λ> playDev channel cMinor
Cool! So we can see the internal representation of this, noting the zero-length rest at the end, and how it mirrors list construction where
x :  == [x]. This is why
cMinor ≠ cMinor' even though it probably should2.
Let’s try some more. The
line function is the equivalent helper for
:+: – it takes a list and composes the elements sequentially, like moving right in piece of notated music (or down in a tracker).
λ> playDev channel $ line [c 3 qn, e 3 qn, g 3 qn, bf 3 qn]
A nice C7 arpeggio – note that we’re using
qn for quarter notes (aka a crotchet in music theory).
Just to spice things up a bit, what if we make… an infinite piece of music? I mean, Haskell is a non-strict (≅“lazy”) language, right? So we can use the
line operator on infinite lists as well, and we’ll have some infinite
Music a. Let’s use the standard
cycle list function to repeat our arpeggio forever and see what we get.
For display we can avoid printing the infinite list by using Euterpea’s
cut function, which limits a piece of
Music to the specified number of beats.
λ> arpeggio = line $ cycle [c 3 qn, e 3 qn, g 3 qn, bf 3 qn] λ> print (cut 2 arpeggio) Prim (Note (1 % 4) (C,3)) :+: (Prim (Note (1 % 4) (E,3)) :+: (Prim (Note (1 % 4) (G,3)) :+: (Prim (Note (1 % 4) (Bf,3)) :+: (Prim (Note (1 % 4) (C,3)) :+: (Prim ( Note (1 % 4) (E,3)) :+: (Prim (Note (1 % 4) (G,3)) :+: (Prim (Note (1 % 4) (Bf,3) ) :+: Prim (Rest (0 % 1)))))))))
If you squint a bit (or are used to LISPs…) you can see this is a series of
Note Primitives, all of a 1/4 time, composed together with the
:+: operator. Again, the empty element3 is visible here.
Taking these basic operators, we can now easily create pieces of music. Now here’s one I made earlier (you’ll need to put this in your
.hs source file, getting too big for a REPL session)
-- It's in 3-time (3/4 or 6/8) -- so each bar lasts one dotted half note (@dhn@) melody = line [ g 3 dhn, c 3 hn, rest qn, ef 3 en, f 3 en, g 3 hn, c 3 hn, ef 3 en, f 3 en, d 3 (dwn + dhn), rest dhn, f 3 dhn, bf 2 hn, rest qn, d 3 en, ef 3 en, f 3 hn, bf 2 dhn, ef 3 en, d 3 en, c 3 dwn, rest dhn ] -- Convenient wrapper inst :: InstrumentName -> Volume -> Music Pitch -> Music (Pitch, Volume) inst i v = addVolume v . instrument i -- Repeat each element four times quadruplicate :: [a] -> [a] quadruplicate = concatMap (replicate 4) -- I always find this useful bpm = tempo . (/ 120.0)
The layout for
melody is slightly non-conventional, but I find spacing it like this, whilst not linear in time (like trackers), allows us to compare notes and phrases easier. The line spacing separates 4 bar sections (if you can read music, see this simplified score which helped with the above).
Try reloading your REPL and doing
playDev channel $ bpm 170 . inst Cello 70 $ melody!
Note also because
Dur is just a
Rational (i.e. a
Num), we can use normal arithmetic on note durations! So yes,
(dwn + dhn) is a thing, and it’s, erm, 9 quarter notes (or 3 bars here) I reckon. This allows for some nice maths / shorthand in your music (and in fact pitches have similar opportunity).
Add some harmony
Remember, harmony is just two or more different notes playing at once, so we already have the tools to do that, Euterpea’s
:=: operator. So we’ll just add a very simple sequence of bass notes changing every four bars as per the melody.
λ> chordSeq = [c 1, g 1, bf 1, f 1] -- Just the notes λ> contraBass = line $ map ($ 3) chordSeq -- 3 is 4 whole bars in 3/4 λ> song = inst Cello 70 melody :=: inst TremoloStrings 70 contraBass λ> playDev channel $ bpm 170 song
…and some rhythm
That bass is pretty boring though. Really, we want some percussion, but for a quick fix, let’s try adding a little rhythm to that bass. For each bar we can repeat the one note but in a particular rhythm.
λ> rhythmFor note = [note dqn, rest en, note en, note en] λ> bass2 = line $ concatMap rhythmFor $ quadruplicate chordSeq λ> song = inst Cello 60 melody :=: inst AcouticBass 80 bass2 λ> playDev channel $ bpm 170 song
Note we use
concatMap to flatten, as each note in the chord sequence is mapped to a list of
Music Pitch by our
Hopefully lots to think about and play with there for newcomers. You should be able to see the quick turnaround that using a REPL for (simple) music can bring, just as programmers often enjoy for code, as well as the potential advantages for quickly building music pieces from smaller parts… just like with functional programming itself. I’ve definitely become very taken by the library even at this basic level.
Next post we’ll see investigate creating drum patterns, pushing the use of MIDI further and allowing DRY to help us create more realistic sounds from even a basic synth setup.
Euterpea has also been used for music students (without necessarily any programming experience) as presented in this paper.