Tuesday, July 29, 2014

OSCON 2014 Theme Song - Andrew Sorensen's Live Coding Keynote

Andrew Sorensen live-coding at OSCON 2014
Keynote

Shortly after Andrew Sorensen began the performance segment of his keynote at OSCON 2014, the #oscon Twitter topic began erupting with posts about the live coding session. Comments, retweets, and additional links persisted for that day and the next. In short, Andrew was a hit :-)

My first encounter with Andrew's work was a few years ago when I was getting back into Lisp. I was playing with generative music with Overtone (and then, a bit later, experimenting with SuperCollider, Hy, and Twisted) and came across his piece A Study in Keith. You might want to take a break from reading this port and watch that now ...

When Andrew started up his presentation, I didn't immediately recognize him. In fact, when the code was displayed on the big screens, I assumed it was Clojure until I looked closely and saw he was using (define ...) and not (defun ...).  This seemed very familiar, and then I remembered Impromptu, which ultimately lead to my discovery of Extempore (see more links below) and the realization that this is what Andrew was using to live code.

At the end of the performance a bunch of us jumped up and gave a standing ovation. (In fact, you can hear me yell out "YEAH" at the end of his presentation when he says "And there we go."). It was quite a show. It seemed that OSCON 2014 had been given a theme song. The next step was getting the source code ...


Andrew's gist (Dark Github Theme)
Sharing the Code

Andrew gave a presentation on Extempore in the ballroom right after the keynote. This too was fantastic and resulted in much tweeting.

Afterwards a bunch of us went up front and chatted with him, enthusing about his work, the recent presentation, the keynote, and his previously published pieces.

I had Andrew's ear for a moment, and asked him if he was interested in sharing his keynote source -- there had been several requests for it on Twitter (that also got retweeted and/or favourited). Without hesitation, he gave an enthusiastic "yes" and we were off and running for the lounge where we could sit down to create a gist (and grab a cappuccino!). The availability of the source was announced immediately, to the delight of many.


Setting Up Extempore

Sublime Text 3 connected to Extempore
Later that night in my hotel room, I had time to download and run Extempore ... and discovered that I couldn't actually play the keynote code, since there was some implicit setup I was missing. However, after some digging around on the docs site and the mail list, music was pouring forth from my laptop -- to my great joy :-D

To ensure anyone else who is not familiar with Extempore can also have this pleasure, I've put together the all the prerequisites and setup necessary in a forked gist, in multiple parts. I will go through those in this blog post. Also: all of my testing and live coding was done using Ben Swift's Extempore Sublime Text plugin.

The first step is getting all the dependencies. You'll want to start the downloads right away, since they are large (the sample files are compressed .wavs). While that's going on, you can install Extempore using Homebrew (this worked for me on Mac OS X with no additional tweaking/configuration necessary):

$ git clone https://gist.github.com/8b4404e538e61c7996a5.git
$ cd 8b4404e538e61c7996a5
$ mkdir salamander && cd salamander
$ curl -L -O https://archive.org/download/SalamanderDrumkit/salamanderDrumkit.tar.bz2
$ curl -L -O https://github.com/johnsen/drumsandpercussion/blob/master/SalamanderKick/salamanderdrum-kick-r1.tar.gz
$ curl -L -O http://freepats.zenvoid.org/Piano/SalamanderGrandPianoV3_44.1khz16bit.tar.bz2
$ tar xvfz salamanderDrumkit.tar.bz2
$ tar xvfz salamanderdrum-kick-r1.tar.gz
$ mv Kick/kick* OH/
$ tar xvfz SalamanderGrandPianoV3_44.1khz16bit.tar.bz2
$ brew tap benswift/extempore
$ brew install extempore
$ cd /usr/local/Cellar/extempore/0.52
$ ./extempore
view raw 01-setup.sh hosted with ❤ by GitHub
With Extempore running, let's do some setup. We're going to need to:

  • load some libraries (this takes a while for them to compile),
  • define some samples, and then
  • define some musical note aliases for convenience (and visual clarity).
The easiest way to use the files below is to clone the gist repo and load them up in Sublime Text, executing blocks of text by hi-lighting them, and then pressing ^x^x.

Here is the file for the fist two bullets mentioned above:

;;;; Much of the content in this file is taken from various Extempore
;;;; blog posts by Ben Swift which are indexed here:
;;;; http://benswift.me/extempore-docs/index.html
;;;;
;;; Load the needed libraries -- this will take several minutes
;;;
(sys:load "libs/core/instruments.xtm")
(sys:load "libs/core/pc_ivl.xtm")
(sys:load "libs/external/instruments_ext.xtm")
;;; Set up the constants we're going to need
;;;
(define sample-path "/Users/oubiwann/Downloads/8b4404e538e61c7996a5/")
(define drum-path (string-append sample-path "salamander/OH/"))
(define piano-regex "^.*([ABCDEFG][#b]?[0-9])v([0-9]+)\.wav$")
;;; Add the samplers/instruments we're going to need to the dsp output
;;; callback
;;;
(define-sampler drums sampler_note_hermite_c sampler_fx)
(define-sampler piano sampler_note_hermite_c sampler_fx)
(bind-func dsp:DSP
(lambda (in time chan dat)
(cond ((< chan 2.0)
(+ (drums in time chan dat)
(piano in time chan dat)
(fmsynth in time chan dat)))
(else 0.0))))
(dsp:set! dsp)
;;; Set up drum samples
;;;
(define add-drum-sample
(lambda (file-name const-name)
(set-sampler-index drums
(string-append drum-path file-name)
const-name
0 0 0 1)))
(define drum-sample-data
(list
(list "kick_OH_F_9.wav" *gm-kick*)
(list "hihatClosed_OH_F_20.wav" *gm-closed-hi-hat*)
(list "hihatFoot_OH_MP_12.wav" *gm-pedal-hi-hat*)
(list "hihatOpen_OH_FF_6.wav" *gm-open-hi-hat*)))
(define add-drum-samples
(lambda (data)
(map (lambda (sample-pair)
(add-drum-sample (car sample-pair) (cadr sample-pair)))
data)))
(add-drum-samples drum-sample-data)
;;; Set up piano samples
;;;
(define parse-salamander-piano
(lambda (file-list)
(map (lambda (fname)
(let ((result (regex:matched fname piano-regex)))
(if (null? result)
#f
;; load 4th velocity layer only
(if (= (string->number (caddr result)) 4)
(list fname
(note-name-to-midi-number (cadr result))
0
0)
#f))))
file-list)))
(load-sampler
piano
;; Can't use a variable here; need the actual path string
"/Users/oubiwann/Downloads/8b4404e538e61c7996a5/salamander/SalamanderGrandPianoV3_44.1khz16bit/44.1khz16bit"
;; 'sound bank' index
0
parse-salamander-piano)
;; Now that everything is loaded, try out some notes ...
(play-note (now) drums *gm-kick* 180 44100)
(play-note (now) drums *gm-open-hi-hat* 140 44100)
(play-note (now) piano 24 180 44100)
(play-note (now) fmsynth (random 60 80) 100 (* 1.0 *second*))

You will need to edit this file to point to the locations where your samples were downloaded. Also,
at the very end there are some lines of code you can execute to make sure that your samples are working.

Now let's define the note aliases. You can just hi-light the entire contents of this file in Sublime Text and then ^x^x:
(define base-note 12)
(define get-note
(lambda (octave offset)
(+ (* base-note octave) offset)))
(define C1 (get-note 1 0))
(define C2 (get-note 2 0))
(define C3 (get-note 3 0))
(define C4 (get-note 4 0))
(define C5 (get-note 5 0))
(define C6 (get-note 6 0))
(define C7 (get-note 7 0))
(define D1 (get-note 1 2))
(define D2 (get-note 2 2))
(define D3 (get-note 3 2))
(define D4 (get-note 4 2))
(define D5 (get-note 5 2))
(define D6 (get-note 6 2))
(define D7 (get-note 7 2))
(define E1 (get-note 1 4))
(define E2 (get-note 2 4))
(define E3 (get-note 3 4))
(define E4 (get-note 4 4))
(define E5 (get-note 5 4))
(define E6 (get-note 6 4))
(define E7 (get-note 7 4))
(define G1 (get-note 1 7))
(define G2 (get-note 2 7))
(define G3 (get-note 3 7))
(define G4 (get-note 4 7))
(define G5 (get-note 5 7))
(define G6 (get-note 6 7))
(define G7 (get-note 7 7))
(define A1 (get-note 1 9))
(define A2 (get-note 2 9))
(define A3 (get-note 3 9))
(define A4 (get-note 4 9))
(define A5 (get-note 5 9))
(define A6 (get-note 6 9))
(define A7 (get-note 7 9))
(define B1 (get-note 1 11))
(define B2 (get-note 2 11))
(define B3 (get-note 3 11))
(define B4 (get-note 4 11))
(define B5 (get-note 5 11))
(define B6 (get-note 6 11))
(define B7 (get-note 7 11))
At this point, we're ready to play!


Playing the Music

To get started on the music, open up the fourth file from the clone of the gist and ^x^x the root, scale, and left-hand-notes-* constants.

Here is the evolution of the left hand part:
(define root E3)
(define scale (pc:scale 4 'aeolian))
(define left-hand-notes-1 (list C3 G3))
(define left-hand-notes-2 (list G4 G4 A4 B4))
;; start with this
(define left-hand
(lambda (beat ps ds)
(play piano (car ps) 170 (* 2.0 (car ds)))
(callback (*metro* (+ beat (* .95 (car ds)))) 'left-hand (+ beat (car ds))
(rotate ps -1)
(rotate ds -1))))
(left-hand (*metro* 'get-beat 4) left-hand-notes-2 (list 1))
;; after a bit, switch to this
(define left-hand
(lambda (beat ps ds)
(play piano (car ps) 170 (* 2.0 (car ds)))
(play 1/2 piano root 170 (* 2.0 (car ds)))
(callback (*metro* (+ beat (* .95 (car ds)))) 'left-hand (+ beat (car ds))
(rotate ps -1)
(rotate ds -1))))
;; don't wait too long to execute the next bit
(define left-hand
(lambda (beat ps ds)
(if (= 0 (modulo beat 8))
(set! root (random (remove root left-hand-notes-1))))
(play piano (car ps) 170 (* 2.0 (car ds)))
(play 1/2 piano root 170 (* 2.0 (car ds)))
(callback (*metro* (+ beat (* .95 (car ds)))) 'left-hand (+ beat (car ds))
(rotate ps -1)
(rotate ds -1))))
(define left-hand-notes-1 (list E3 D3 C3))
Go ahead and start that first one playing (^x^x the definition as well as the call). Wait for a bit, and then execute the next one, etc. Once you've started playing the final left hand form, you can switch to the wider range of notes defined/updated at the bottom.

Next, you'll want to bring in the right hand ... then bassline ... then the higher fmsynth sparkles for the right hand:
(define right-hand
(lambda (beat dur)
(play piano
(pc:quantize (cosr (+ root C2) (cosr 5 3 1/2) 7/3) scale)
(cosr 160 20 7/3)
(* 4.0 dur))
(callback (*metro* (+ beat (* .5 dur))) 'right-hand (+ beat dur) dur)))
(right-hand (*metro* 'get-beat 4) 1/4)
(define bassline
(lambda (beat ps ds)
(play fmsynth root 100 (* (car ps) (car ds)) 1.0 0.5)
(callback (*metro* (+ beat (* .95 (car ds)))) 'bassline (+ beat (car ds))
(rotate ps -1)
(rotate ds -1))))
(bassline (*metro* 'get-beat 4) (list .25 .25 .6) '(3/2 1 3/2))
(define right-hand
(lambda (beat dur)
(play piano
(pc:quantize (cosr (+ root C2) (cosr 5 3 1/2) 7/3) scale)
(cosr 160 20 7/3)
(* 4.0 dur))
(if (> (random) .6)
(play fmsynth
(pc:quantize (+ 7 (cosr (+ root 36) (cosr 5 3 1/2) 7/3)) scale)
(cosr 75 20 7/3)
(* .2 dur) 0.5 5.0))
(callback (*metro* (+ beat (* .5 dur))) 'right-hand (+ beat dur) dur)))

Then you'll increase the energy with the drum section:
(define kick
(lambda (beat dur)
(play drums 35 150 dur)
(callback (*metro* (+ beat (* .5 dur))) 'kick (+ beat dur) dur)))
(kick (*metro* 'get-beat 4) 1)
(define kick
(lambda (beat dur)
(play drums 35 150 dur)
(play -1/4 drums 35 130 (* .5 dur))
(callback (*metro* (+ beat (* .5 dur))) 'kick (+ beat dur) dur)))
(define hats
(lambda (beat dur)
(play drums (random '(44 42)) (cosr 80 60 (random '(7/3 5/2))) dur)
(callback (*metro* (+ beat (* .5 dur))) 'hats (+ beat dur) dur)))
(hats (*metro* 'get-beat 4) 1/4)

Finally, you'll bring it to the climax, and then start the gentle fade out:
(define left-hand-notes-1 (list E3 D3 C3 G3))
(define right-hand
(lambda (beat dur)
(play piano
(pc:quantize (cosr (+ root C2) (cosr 5 3 1/2) 7/3) scale)
(cosr 170 20 7/3)
(* 4.0 dur))
(if (> (random) 0)
(play fmsynth
(pc:quantize (+ 12 (cosr (+ root 36) (cosr 5 3 1/2) 7/3)) scale)
(cosr 60 20 7/3)
(* .2 dur) 0.5 5.0))
(callback (*metro* (+ beat (* .5 dur))) 'right-hand (+ beat dur) dur)))
;; bring volume of sparkles up to 70 then 80
(define left-hand-notes-1 (list C3 G3))
(define left-hand-notes-1 (list E3 D3 C3 G3))
(define right-hand
(lambda (beat dur)
(play piano
(pc:quantize (cosr (+ root C2) (cosr 5 3 1/2) 7/3) scale)
(cosr 170 20 7/3)
(* 4.0 dur))
(if (> (random) .6)
(play fmsynth
(pc:quantize (+ 7 (cosr (+ root 36) (cosr 5 3 1/2) 7/3)) scale)
(cosr 80 20 7/3)
(* .2 dur) 0.5 5.0))
(callback (*metro* (+ beat (* .5 dur))) 'right-hand (+ beat dur) dur)))
;; bring volume of sparkles down to 70 then 60
(define left-hand-notes-1 (list C3 G3))
;; now bring down the piano volume of 160 by increments to 10
(define right-hand)
(define kick)
(define bassline)
(define hats)
(define left-hand
(lambda (beat ps ds)
(if (= 0 (modulo beat 8))
(set! root (random (remove root left-hand-notes-1))))
(play piano (car ps) 170 24.0)
(play piano root 170 24.0)))

A slightly modified code listing for the final keynote form is here:

;;;; This is a slightly modiifed version of code for the OSCON Keynote
;;;; which Andrew Sorenson performed live in Portland Oregon in July, 2014.
;;;;
;;;; Some of the differences include:
;;;; * Free samples were used instead of the cuomst samples that Andrew uses
;;;; * Note definitions are used instead of numbers
;;;; * The sustain of the piano notes was increased
;;;; * Some of the relative volumes were changed.
;;;;
;;;; The original is preserved in the history of this gist, which was forked
;;;; from Andrew's gist.
;;;;
;;;; Final note: thank you so much, Andrew, for the inspiring performance
;;;; you gave to OSCON; it was an absolute delight to finally be able to
;;;; attend one of your live performances!
;;;;
(define root E3)
(define scale (pc:scale 4 'aeolian))
;; (define left-hand-notes-1 (list E2 D2 C2 G2))
;; (define left-hand-notes-2 (list G3 G3 A3 B3))
;; (define left-hand-notes-1 (list E3 D3 C3 G3))
(define left-hand-notes-1 (list E3 D3 C3))
;; (define left-hand-notes-1 (list E4 D4 C4))
(define left-hand-notes-2 (list G4 G4 A4 B4))
;; (define left-hand-notes-1 (list E4 D4 C4 G4))
;; (define left-hand-notes-2 (list G5 G5 A5 B5))
;; (define left-hand-notes-1 (list E5 D5 C5 G5))
;; (define left-hand-notes-2 (list G6 G6 A6 B6))
(define left-hand)
(define left-hand
(lambda (beat ps ds)
(if (= 0 (modulo beat 8))
(set! root (random (remove root left-hand-notes-1))))
(play piano (car ps) 170 (* 2.0 (car ds)))
(play 1/2 piano root 170 (* 2.0 (car ds)))
(callback (*metro* (+ beat (* .95 (car ds)))) 'left-hand (+ beat (car ds))
(rotate ps -1)
(rotate ds -1))))
(left-hand (*metro* 'get-beat 4) left-hand-notes-2 (list 1))
(define right-hand)
(define right-hand
(lambda (beat dur)
(play piano
(pc:quantize (cosr (+ root 24) (cosr 5 3 1/2) 7/3) scale)
(cosr 160 20 7/3)
(* 4.0 dur))
(if (> (random) .6)
(play fmsynth
(pc:quantize (+ 7 (cosr (+ root 36) (cosr 5 3 1/2) 7/3)) scale)
(cosr 60 20 7/3)
(* .2 dur) 0.5 5.0))
(callback (*metro* (+ beat (* .5 dur))) 'right-hand (+ beat dur) dur)))
(right-hand (*metro* 'get-beat 4) 1/4)
(define bassline
(lambda (beat ps ds)
(play fmsynth root 110 (* (car ps) (car ds)) 1.0 0.5)
(callback (*metro* (+ beat (* .95 (car ds)))) 'bassline (+ beat (car ds))
(rotate ps -1)
(rotate ds -1))))
(bassline (*metro* 'get-beat 4) (list .25 .25 .6) '(3/2 1 3/2))
(define kick
(lambda (beat dur)
(play drums 35 150 dur)
(play -1/4 drums 35 130 (* .5 dur))
(callback (*metro* (+ beat (* .5 dur))) 'kick (+ beat dur) dur)))
(kick (*metro* 'get-beat 4) 1)
(define hats
(lambda (beat dur)
(play drums (random '(44 42)) (cosr 80 60 (random '(7/3 5/2))) dur)
(callback (*metro* (+ beat (* .5 dur))) 'hats (+ beat dur) dur)))
(hats (*metro* 'get-beat 4) 1/4)

Variation on a Theme

I have recorded a variation of Andrew's keynote based on the code above, for your listening pleasure :-) You can listen to it in your browser or download it.

This version plays part of the left hand piano an octave lower. There's a tiny bit of clipping in places, and I accidentally jazzed it up (and for too long!) with a hi-hat change in the middle. There are also some awkward transitions and volume oddities. However, these should be inspiration for you to make your own variation of the OSCON 2014 Theme Song :-)

The "script" used for the recording can found here.


Links of Note

Some of these were mentioned above, some haven't been. All relate to Extempore :-)


6 comments:

joesh said...

Hi Duncan, nice article.

I've also been listening - on and off - to Andrew's music for the past few years. I did start looking at Extempore and Impromptu and then backed down as I realised that the learning curve was going to be too difficult without some help. And, I must say that forums are great, but a live class is better, as questions are answered immediately.

I'd be interested to read why you think/find Extempore better than Supercollider? I have to say that I can see the point that programs such as these are perfect for live coding, whereas PD and Max are great for building sound modules and working with sound design.

Lastly, I've always wondered why Andrew is doing his thing alone (with his university team) and not making it a little more user-friendly. It reminds me a little of the history of ChucK which hasn't taken off due to the developers at CalArts being unable to communicate with the rest of the world - programming wise. Extempore makes me think of Linux at the beginning, an excellent and very powerful musical tool, which, maybe in twenty years time, will be easily downloadable in a package with no need to compile libraries etc.

If you have some thoughts on the matter I'd be most interested to read them, either here or in a direct mail.

Big thanks for such an interesting article.

p.s. I don't know the Lisp language, which of course makes Extempore even harder.

joesh said...

By the way Duncan. Although I expect you know this already but if not, and you're interested, James Harkins web-page shows some of the possibilities of SC - http://www.dewdrop-world.net/audio/index.php

Very interesting, very musical - and often live.

All the best.

Anonymous said...

Hey Joe, thanks for your comments :-)

It's good to hear your perspective ... I've been immersed in so many different Lisps over the course of my career (even when I wasn't really using them), that I've completely lost the "beginner's mind" perspective. You are definitely right -- it's daunting at first. My only response is that, as with anything, practice generates familiarity, and from that one finds natural ease. You can do it!

I do agree too that classes would be great. Andrew has given workshops in the past, but, iirc, they seem to be a fairly rare occurrence :-/

I don't have a preference one way or another of Extempore vs. SuperCollider (Overtone, actually; I haven't used SuperCollider directly much at all). One thing that would help me evaluate would be to watch a live coding performance of "Study in Keith" with both Extempore and Overtone :-)

I'm not sure I believe that Andrew is doing it alone, per se. I think he's created a tool that he loves and he works with very well. I believe if more people use it, it will become more user-friendly for a larger audience. To be honest, I found it very easy to use ... a few blog posts, some threads on the mail list, and I had everything I needed.

Though I am a Lisper, I'm not a schemer, so I had to adjust my dialect a bit ... but I do have a passion for Lisps -- any opportunity to play with a new dialect, and I'm usually quite game :-) That would obviously be quite useful for picking up Extempore.

You have, though, got me thinking -- completely on a different tangent: what's the best way to transfer that delight to someone else (someone willing, of course!). There is a way to view the multitude of Lisps in a unified view ... in particular, I'm getting hints of a tiny project/tutorial (maybe a multi-dialect one) that could help those who are interested see the bigger picture, and "get it" ... I'll let you know if anything useful comes of these thoughts :-)

Glad you liked the article, though!

Anonymous said...

Very cool! I hadn't seen that -- thanks for sharing :-)

Unknown said...

Tnx a lot for sharing the detailed reconstruction of Andrew's awesome performance.
I really enjoyed listening to your recoding of the music.
I've done a transcript of his performance a while ago for my own study and use
and just now published that on Gist as well. This version uses the default sample assets from extempore,
Had done a blogpost on the extempore list
about that which you might be interested as well.
https://groups.google.com/forum/#!topic/extemporelang/WCOMNC9-w2M

Tnx again for sharing.

Anonymous said...

@Circu -- you're very welcome! Also, thanks for the link -- good stuff! Hadn't thought about publishing on Sound Cloud ... great idea :-)