Overtone and ClojureScript, Updated

Last night I was fooling around with ClojureScript, running through examples floating around on the web. I thought Chris Granger’s post on Overtone and ClojureScript looked neat, so I pulled the project from GitHub and tried to lein run it.

Alas, it failed, for several reasons. So I updated it to build correctly, and I’m posting what I did in case someone else finds it useful. My updated version of OvertoneCljs is on GitHub.

Getting Things Working

The first problem I hit when trying to lein run the project was Overtone failing to download piano samples. The old version of Overtone referenced in the project.clj file had a hardcoded URL to the samples that was no longer live. I updated project.clj to use a newer version of Overtone and solved that problem:

[overtone "0.8.1"]

but created another. The API of Overtone changed between 0.6.0 and 0.8.1. In particular, the pink-noise unit-generator no longer takes arguments, so the Clojure compiler choked:

Exception in thread "main" java.lang.IllegalArgumentException: Error in checker for ugen ==> pink-noise
You supplied too many arguments. 
Supplied args: ([1 1])
Expected arg keys: ()

I modified the relevant line (src/overtoneinterface/models/dubstep.clj:20) and removed the arguments to pink-noise to fix the problem. lein run then got the server running and working.

Getting Things Stable

When Chris first whipped up this project, many of its dependencies were in a state of flux. Stable builds depend upon repeatability. In Clojure projects, repeatability is typically handled by specifying libraries with semantic versions, and making sure these libraries are posted somewhere public, like the Maven Central Repository or Clojars. -SNAPSHOT versions break this rule and can result in build problems appearing over time.

Looking at project.clj, several library versions are snapshots:

[jayq "0.1.0-SNAPSHOT"] 
[crate "0.1.0-SNAPSHOT"]
[fetch "0.1.0-SNAPSHOT"]

I updated the first two to their latest stable versions:

[jayq "2.0.0"]
[crate "0.2.4"]

When I updated fetch, it pulled in an older version of noir, which was easy enough to exclude:

[fetch "0.1.0-alpha2" :exclusions [noir]]

You can use lein deps :tree to see recursive dependencies, and the lein-pedantic plugin to look for common pitfalls related to incompatible dependencies. The final version of my project.clj has a couple of other tweaks recommended by lein-pedantic.

Since jayq, fetch, and crate are ClojureScript libraries that are used in the ClojureScript source that Chris wrote, I needed to recompile that ClojureScript to make sure everything was OK. I added:

[lein-cljsbuild "0.3.0"]

to my plugin list (which you can do in project.clj or globally in ~/.lein/profiles.clj; I chose the former for repeatability reasons). Running lein cljsbuild once informed me the build configuration was out of date; I updated the :cljsbuild key in project.clj to use the new format:

:cljsbuild {:builds [{:source-paths ["src"],
                      :builds nil,
                      :compiler {:pretty-print true,
                                 :output-dir "resources/public/cljs/",
                                 :output-to "resources/public/cljs/bootstrap.js",
                                 :optimizations :simple}}]}

I also updated the required version of Clojure to 1.4.0 (as the current ClojureScript compiler requires it):

[org.clojure/clojure "1.4.0"]

lein cljsbuild once then started, but had problems when it entered non-ClojureScript directories. Since Chris wrote this example, the ClojureScript toolchain has evolved to (I think) expect Clojure and ClojureScript to be kept in separate hierarchies, so I moved main.js from src/overtoneinterface/client to src-cljs/overtoneinterface/client. I also changed :source-paths to the ClojureScript directory ["src-cljs"] as part of this move.

Then it was down to problems introduced by the newer versions of dependencies. crate’s crate.macro namespace is now crate.def-macros; updating src-cljs/overtoneinterface/client/main.cljs to reflect this change allowed compilation to succeed. Similarly, I needed to account for the rename of hiccup.page-helpers to hiccup.page in the newer version of hiccup pulled in by the new version of noir. Finally, I explicitly passed nil as the last parameter in the remote-callback to quiet a compiler warning; the remote being called triggers the audio playback, but it doesn’t care about any callback (hence the nil). I’m not sure if Chris modified the API at some point, or just didn’t worry about the warning when he wrote this code.

There are still some warnings being emitted by the ClojureScript compiler. They are related to clj->js being redefined. Since Chris wrote his example, an equivalent function was introduced into the ClojureScript runtime. Barring an update to fetch, there’s no way to fix this warning (jayq version 2.0.0 fixes this problem in that library), and no harm done by ignoring it.

TODO or not TODO

There’s lots more that could be done: noir is deprecated, so you could replace it with Compojure and lib-noir. fetch never left alpha, and there are almost certainly better alternatives available now (like shoreleave-remote). I’m not sure that the community has standardized on one yet, though. Feel free to fork!

Marc Liberatore
Marc Liberatore
Senior Teaching Faculty

My research interests include anonymity systems, file and network forensics, and computer science pedagogy.