🎼Functionally programming The King of Pop
Where were we?
In the previous post, we looked at the Haskell library Euterpea to create simple music using (functional) programming techniques. In this part, we’ll take some of these ideas further, and try to create something a little more like real (pop) music. As it’s concentrating more on the audio side than the coding side, I assume a little music / audio knowledge.
The King of Pop
It seems appropriate in the current light of EPA-destruction and climate treaty withdrawals that the mid-90s ode to the salvation of the planet by the late King of Pop himself, Michael Jackson.
Of course, it is completely inappropriate to choose this song, as:
- It’s almost five minutes long
- Has an epic, non-conventional song structure
- That key change mid-song…
- It’s heavily produced – layered synths, R&B-like sections, rock guitars, live drums, strings sections, sound effects, gospel choirs, a harp (!), brass sections
- Lots of funk elements (which rely on years of player experience), i.e. hidden complexity. This is a problem with code too.
But then, it wouldn’t be fun without a few challenges! We’ll concentrate on the earlier sections more than the epic ending, and lots of bits will be simplified (sorry!).
Making some music
- Reference chords
- In the right key
Luckily, the drum pattern here is relatively simple. The subtleties and performance are another matter, but we’re in the business of approximating here.
I find that the list format of
line is useful for drum patterns, and it’s best if possible to keep these even measures, or you go mad. Eighth notes are good here, so let’s adopt that.
Just like clean coding, I find well named music variables can help understand the code. Here I’ve used vaguely onomatopoeiac names for drums, and later extracted these to allow duration variations; yes, I think I’ve just coined higher-ordered percussion. WAT.
-- Helpers of type Dur -> Music (Pitch, Volume) -- ...will depend a lot on your synth and SoundFont / patchset boomOf = addVolume 115 . perc BassDrum1 smakOf = addVolume 70 . perc AcousticSnare tssOf = addVolume 40 . perc ClosedHiHat -- Basic sounds boom = boomOf en smak = smakOf en tss = tssOf en --Rest notes aliases da = rest en d_ = rest sn
Once we have that vocabulary defined, it’s easy to talk about simple drum patterns!
-- A basic down, up rock beat rockBeat = forever $ line [boom, da, smak, da] shaker = forever $ line [addVolume 10 $ perc Tambourine sn] -- Re-use existing aliases then double the speed fill = tempo 2 $ line [boom, boom, smak, boom :=: tss] -- An 8-bar beat with a fast fill at the very end beat = cut 8 $ (cut 7.75 rockBeat :+: fill)
Note how we did the fill by re-using existing eighth-note definitions and using the
tempo modifier to double the speed of the whole phrase. DRY in action! (the fill itself needs some work though, sorry, purists).
cut function is very useful – note how it can take non-integer durations too - so reusing drum patterns is easy, as you can cut off the end (adding a fill instead) rather than duplicating a large part of the content. More DRY, essentially.
Try it in your REPL:
playDevS channel $ rest qn :+: beat
- we add a small rest to help with the hiccups with some MIDI synths when starting playback
playDevSis the strict variant of
playDev, which does timing better (see the interesting arguments around this behaviour from the creators).
Layering sounds in MIDI
Note: this is not even vaguely a good idea. Your results will definitely differ. Rendering the output is the only guarantee, and even that’s not so much fun. So that said we’ll continue.
I found the soundfont I was using (Timidity plus FreePat, I think) was nice in places but lacked some thickness in the drums, so creating that huge 90s drum sounds was challenging.
- Thicker bass drums by layering a low tom (!), more like an 808 kick TBH, on to the bassdrum at a lower volume when we wanted the full weight of the drums (chorus, outro, etc)
- Layering the electric snare on top of the acoustic one made a nice sound
- The clap sound for the (second) bridge / pre-chorus I eventually got closer to (oh – the actual clap was totally broken for me) by using a little rimshot (too thin / short by itself), a high wood block at low volume to give the reverby strong percussive sound, with a bit of Cabasa (a latin shaker-like instrument) for the missing noise in the upper part of the spectrum. Phew.
- Also for the second bridge, the drummer uses a gentle but very tight closed hihat. The sound I had was far too open (splashy / sizzly) even when closed. For the very tight sound I ended up overlaying a mute triangle (sharp decay, high tone) with a very quiet closed hihat. Not ideal, but better.
Phatten the bass
Subharmonics (octaves below the actual note) are nice when used well. This is a very blunt way of doing this. Remember we haven’t got any signal processing capabilities here – we’re committed to 100% General MIDI!
Spread the pads
Now I will confess a complete lack of knowledge around arranging pads and strings, so most of this feels very wrong. Perhaps someone out there can advise better practices.
The idea was to take the simple three-part harmonies and replicate across instruments, layering and mixing to get nearer to the sound. Several parts were transposed an octave up (
transpose 12) to occupy more of the spectrum. Some parts had tension notes (e.g.
sus9), which generally were only in the upper registers, sounded a bit too muddy otherwise. All in all, I had to keep turning the volumes down, it’s easy to drown the mix in midrange, when the actual song is very full.
What about us?
Percussion is tricky, and fills are definitely hard to learn or notate. As always it helps to be able to hear the parts individually (this drumming video is useful).
Eventually it comes down to the amount of time you can devote to obsessing over the exact fills – whether it’s getting them exactly right, or even bothering with the variations at all. Session musicians are often very, very good at what they do (without grabbing the spotlight)!
A useful tip was to run the whole thing at double speed using
tempo 2, meaning that the eighth-note shortcuts defined earlier are re-usable for sixteenth notes.
As an former / occasional dabbler in bass guitar and various bass-driven genres (Drum n Bass especially), I’ve long been aware the subtleties of “simple” basslines. As usual though, this one caught me out completely: the main four notes (A♭, B, D♭… and E♭) are simple… but the excellent rendition on the track has complexity in the form of timing nuances (funk!), dynamics, ornaments and variations / fills that make this sequencing very hard. I’m not going to lie: a huge amount of trial and error, and referencing bass tabs was necessary.
Repetition and sections
In the previous post I explained how coding could be used to avoid repetition in the notation of the music (at the expense of some complexity, of course). With the amount of variation that crept in, the usefulness of this actually decreased quite a lot, but never mind…
Simply assigning variables usefully, and using combinators like
times, and also
tempo 2(doubling the speed)
It’s worth mentioning the
phrase function. This allows us to add all sorts of performance-type changes including dynamics like Crescendos, staccato (I used a
phrase [Art $ Staccato 0.9] to dampen bass strings – though this is a real fudge as any even beginner bass players know not to let notes ring, plus the common slide or glissando used here isn’t possible AFAICT).
Euterpea / GHC -> MID -> Timidity -> OGG -> HTML 5
earthSong :: Music (Pitch, Volume) earthSong = bpm 69.15 $ line [ rest qn -- ♬ (intro) ♬ , cut 4 $ phrase [Dyn $ Crescendo 0.9] (addVolume 14 $ instrument TremoloStrings verseChords) :=: instrument AcousticGrandPiano intro -- ♬ "What about sunrise?" ♬ , voicesFor verseChords -- ♬ "Did you ever stop to notice.." ♬ , thinBassOf bridgeBassLine :=: (phrase [Dyn $ Loudness 01] . phrase [Dyn $ Crescendo 0.2]) (stringsFor bridgeChords) :=: voicesFor bridgeChords -- TODO: harp arpeggios -- ♬ oohh ooh oooooohayahh ♬ , stringsFor chorusChords :=: voicesFor chorusChords :=: cut 7.75 shaker -- ♬ "What have we done to the world? Look what we've done..." ♬ , stringsFor verseChords :=: voicesFor verseChords -- TODO: funk guitar chord licks -- ♬ "Did you ever stop to notice..." ♬ , thinBassOf bridgeBassLine :=: stringsFor bridgeChords :=: voicesFor bridgeChords :=: (rest 3.75 :+: fastFill) -- ♬ oohh ooh oooooohayahh ♬ , phatBeat :=: chorusMusic -- TODO: funk guitar licks #2 -- ♬ "I used to dream, used to glance beyond the stars..." ♬ , thinBassOf funkBridgeBassLine :=: thinBeat :=: stringsFor bridgeChords :=: voicesFor bridgeChords -- ♬ oohh ooh oooooohayahh ♬ , phatBeat :=: chorusMusic -- ♬ ....heyeyyeeyeaa! ♬ , (cut 7 phatBeat :+: longFill) :=: chorusMusicUp -- TODO: horn stabs sections! -- ♬ "What about yesterday? (what about us?)" ♬ , times 5 (phatBeat :=: chorusMusicUp :=: powerGuitarOf (transpose 2 powerChords)) ]