Wishful Coding

Didn't you ever wish your
computer understood you?

Generative Music

It’s been a long time since I played with Overtone. What I’ve always wanted to do is generate music automatically. Today I ran into jFugue, which makes this really easy, listen to this:

</param> </param> </embed>

Markov generated music by pepijndevos

I just took some music from wikifonia, constructed a Markov-chain and did a random note-walk.

This could work just as well with Overtone, but I used jFugue because it comes with a parser for MusicXML files.

To run the code below, you need to have jFugue on the classpath. Then just pipe a couple of notes from generate into play.

(ns ponger.core
  (:require clojure.string))

(defn inflate-mxl [file]
  (let [z (java.util.zip.ZipFile. file)]
    (slurp
      (.getInputStream z
        (.nextElement (.entries z))))))

(defn parse-musicxml [file]
  (let [parser   (org.jfugue.parsers.MusicXmlParser.)
        renderer (org.jfugue.MusicStringRenderer.)]
    (.addParserListener parser renderer)
    (.parse parser (inflate-mxl file))
    (.getPattern renderer)))

(defn notes [tokens]
  (filter #(re-matches #"[A-GR].*" %) tokens))

(defn voices [tokens]
  (let [[f [_ & r]] (split-with (complement #(.startsWith % "V")) tokens)]
    (when (seq f)
      (cons f (voices r)))))

(defn make-chain [tokens]
  (into {}
    (for [[k slice] (group-by first (partition 2 1 tokens))]
      [k (map last slice)])))

(defn walk-chain [chain start]
  (let [nxt (rand-nth
              (get chain start
                   (apply concat (vals chain))))]
    (cons
      nxt
      (lazy-seq
        (walk-chain chain nxt)))))

(defn generate [path]
  (-> path
    parse-musicxml
    (.getTokens)
    notes
    make-chain
    (walk-chain nil)))

(defn play [token-seq]
  (let [player (org.jfugue.StreamingPlayer.)]
    (doseq [token token-seq]
      (.stream player token))))

(defn save [token-seq path]
  (.saveMidi (org.jfugue.Player.)
         (clojure.string/join " " token-seq)
         (java.io.File. path)))