Smack My Lazer - live coding

In this post I’ll introduce my live coding music environment built on top on Overtone and Leipzig.

TL;DR

This is an example session we’ll be discussing here:

I’m using lots of samples from Major Lazer & DJ Snake - Lean On (feat. MØ) and a beat from Prodigy - Smack My B**ch Up.

Main theme

First things first. To recreate the Lean On main theme we’ll use Leipzig as in previous post. The notes are here:

notes

And can be expressed as:

(def leanon-chords
  [[-9 -2 0 2 4]
   [-8 -1 -3 1 3]
   [-7 0 2 4]
   [-5 -1 2 4 5 6]
   [-5 -1 2 3 4]])

(def leanon
  (let [[ch1 ch2 ch3 ch4 ch5] leanon-chords]
    (->> (phrase (concat (take 9 (cycle [1/2 1/4]))
                         [1/2]
                         (take 9 (cycle [1/2 1/4]))
                         [1/2])
                 [ch1 nil ch1 nil ch1 nil ch2 nil ch2 nil ch3 nil ch3 nil ch3 nil ch4 nil ch4 ch5])
         (wherever :pitch, :pitch (comp scale/low scale/G scale/minor))
         (all :part :plucky)
         (all :amp 1))))

And I also created a simple “plucky” sound based on square wave with some reverb:

(definst plucky [freq 440 dur 1 amp 1 cutoff 1500 fil-dur 0.1]
  (let [env (env-gen (asr 0 1 1) (line:kr 1.0 0.0 dur) :action FREE)
        level (+ (* 0.85 freq) (env-gen (perc 0 fil-dur) :level-scale cutoff))]
    (-> (pulse freq)
        (lpf level)
        (free-verb :room 1 :wet 0.45)
        (* env amp))))

And to wire it up for Leipzig we need to implement live/play for :plucky

(def controls (atom {:plucky {:amp 1.0 :cutoff 900}}))

(defmethod live/play-note :plucky [{hertz :pitch seconds :duration amp :amp}]
  (when hertz
    (let [params {:freq hertz :dur seconds :volume (or amp 1)}]
      (apply i/plucky (to-args (merge (:plucky @controls) params))))))

This controls atom lets us modify live the instrument amp and cutoff frequency.

So it sounds like this:

Sampler

What I’m doing a lot in this video is using a homemade sampler. I stolen some ideas from Sam Aaron’s Sonic Pi sample packs concept ;)

Basically, I have a directory when I put all the samples and use special meaning of samples file name to detect some properties of the sample. So for example, I have 120_8_smack_beat.wav which tells my sampler that this file is in 120 BPM, is 8 beats long and is named :smack-beat. Because Lean On is ~98 BPM I can compute a correct ratio to match Smack My B**ch Up beat with Lean On melody.

Original beat (120 BPM):

Scaled to 100 BPM with melody:

I can also play only first few beats of some sample by using it like this [0 :lean-chorus 2], which means that on time 0 I only want first 2 beats of :lean-chorus sample. The beat lengths are of course computed from current BPM.

Song state

I found it really easy to manipulate current state of the song represented as a map of independent tracks:

{:beat   [{:time 0 :drum :kick :amp 1} ...]
 :plucky [{:pitch 39, :time 0, :duration 1/2, :part :plucky, :amp 1} ...]}

This allows me to modify the current state by using this update-track function. It just takes a key with track name and a new value of this track. So I can have my plucky instrument playing while I’m modifying the beat and so on:

(update-track :beat (times 2 l/lean-beat))

It’s also very easy to remove some of the notes as I’m doing just before the chorus to remove last frame from beat and theme:

(def last-frame (fn [n] (>= (:time n) 12)))

(update-track :beat (->> (times 2 l/lean-beat)
                         (remove last-frame))
              :plucky (->> (times 2 l/leanon)
                           (remove last-frame))
              :sampler (t/sampler [[0 :lean-verse-2]
                                   [0 :smack-beat 8 1.5]
                                   [8 :smack-beat 4 1.5]]))

In Leipzig I can just use live/jam to loop current state of the song. When I modify something it’ll we activated on next loop (same as :live-loop in Sonic Pi).

I you want to play around with this stuff, check out my disclojure repo on GitHub.