running out of time
My plan was to do the audio analysis myself which is what I started off doing. Unfortunately time was coming to an end I was still a long way from having it working. As such I decided to use Echonest's excellent audio analysis api to quickly grab some data about my 3 example songs. I'm a bit disappointed but I wanted to get something visual to show at the end of week demos. It's something I will need to continue working on.
playing the songs
Playing the music is really simple. Just place the songs in the resources folder and use (sound)
. I keep track the resulting audio object in an atom so that
We can then stop the playing with
altering the world
I've saved the echonest data in a .info file inside of the resources folder. Loading json into a clojure map is really simple, typically at MixRadio we use the wonderful cheshire library to parse JSON. For this I tried out pjson as I've been looking at it for performance. No big problems switching over although I miss the lack of automatic conversion of strings to keywords. Anyway reading the json is as simple as:
Calling :info
on the @current-track simply retrieves the filename. That gives me a nice Clojure map to work with.
We pass this map into the (blocks)
function in the world namespace to provide variation for our generation.
The (blocks)
function now takes the danceability attribute as seed for the simplex noise (just to provide variation) and makes use of the energy attribute (which is between 0 and 1) to scale the height of the world. We also pass the energy value into the (block)
function to vary the block textures that are associated with the type of song.
(block)
really just passes the energy onto (random-material)
which uses it to assign the material. I want to cluster the block types towards a type of world, e.g. grass for a calm song and fire for a heavy song. To do this I'm generating the random number using a normal distribution weighted by the energy. This allows most blocks to be clustered around a point but with some deviation.
Next I use Clojure's (min-key)
function to find the closest block (in terms of energy) to the random number I generated. This is shown above in the (nearest-material)
function.
The calm world from an ambient song:
The dangerous world from a metal song:
adding a quick menu
Finally I wanted to add a quick menu screen to load the songs.
This is just another (screen)
but with some code to create and highlight menu items, when they are rotated by a key event. The key event handler just calls rotate-menu with the list of entities and either (inc)
or (dec)
. I don't love using the (atom)
here but it's difficult to pass state around in play-clj and doing without needlessly complicates the code. It's the pragmatic choice I suppose.
where's the code?
Clone the repo and run:
git checkout tags/music
To see the code as it is right now
what's next
Well I'm out of time for this hack project but there are a few things I'd like to continue with. Firstly I want to get my own audio analysis working to replace the echonest data. Secondly I'd like to rewrite the rendering so that it's significantly more efficient and continue with the world generation as I think it's an interesting example of using clojure. Finally I'd like to write up my thoughts on how well libgdx and play-clj worked in this project.