 #!/usr/bin/env boot
 
+;; TODO:
+;;   displacement syntax < >
+;;   MPE support
+;;   :transpose
+;;   :time-signature
+
 (ns semitone.core)
 
-(import [javax.sound.midi 
-         MidiSystem MidiMessage MetaMessage MetaEventListener 
+(use '[clojure.pprint :only [pprint]])
+
+(import [javax.sound.midi
+         MidiSystem MidiMessage MetaMessage MetaEventListener
          SysexMessage ShortMessage Sequencer Sequence MidiEvent Track]
         [java.nio ByteBuffer])
 

    state
    (let [token (first notes)
          token (if (or (= (type token) Long) (= (type token) Double)) (str token) (name token))]
      (if (re-matches #"((!|\?|&|)(>+|<+|)(-?(?:[0-9]+|[0-9]*\.[0-9]+|[0-9]+\.[0-9]*|)(?:[eE]-?[0-9]+)?)?)+" token)
        (merge
         (apply merge state
                (map #(let [param (get {"!" :attack  "?" :release "&" :key-pressure} (get % 1) (:param state))
                            max-value (if (= param :pitch-bend) 16384 128)
                            value (if (re-find #"[.eE]" (get % 3))
                                    (if (= param :pitch-bend)
                                      (max 0 (min max-value (int (* (/ (+ 1.0 (Double. (get % 3))) 2.0) max-value))))
                                      (max 0 (min max-value (int (* (Double. (get % 3)) max-value)))))
                                    (mod (Integer. (get % 3)) max-value))
                            value (cond
                                    (= \> (get (get % 2) 0)) (mod (+ (param state) (or value 0) (- (count (get % 2)) 1)) max-value)
                                    (= \< (get (get % 2) 0)) (mod (- (param state) (or value 0) (- (count (get % 2)) 1)) max-value)
                                    :else value)]
                        {param value})
                     (re-seq #"\G(!|\?|&|@|)(>+|<+|)(-?(?:[0-9]+|[0-9]*\.[0-9]+|[0-9]+\.[0-9]*|)(?:[eE]-?[0-9]+)?)?" token)))
         {:notes (rest notes)})
        state))))


                            :param nil
                            :notes (rest notes)})
              ;; channel pressure, pitch bend, note on/off / key pressure by key number
              (if-let [m (re-matches #"(&&|\^|&k|k|-k)(>+|<+|)(-?(?:[0-9]+|[0-9]*\.[0-9]+|[0-9]+\.[0-9]*)(?:[eE]-?[0-9]+)?|)(-|)" note)]
                (let [max-value (if (= "^" (get m 1)) 16384 128)
                      value (if (re-find #"[.eE]" (get m 3))
                              (max 0 (min max-value (int (* (Double. (get m 3)) max-value))))
                              (mod (if (= "" (get m 3))
                                     (if (= "" (get m 2)) 0 1)
                                     (Integer. (get m 3))) max-value))
                      param ({"&&" :channel-pressure "^" :pitch-bend "&k" :key-pressure "k" :key} (get m 1))
                      old-value (get state param)
                      value (cond
