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!