               [cljs.js :as cljs]))
 
 ;; -------------------------
-;; Model
+;; State
+
 (defonce state
   (atom {:music nil
          :text ""}))
 
 ;; -------------------------
-;; Behaviour
+;; Sound
+
+(defonce context (js/window.AudioContext.))
+(defn beep! [freq start dur]
+  (let [start (+ start (.-currentTime context))
+        stop (+ start dur)]
+    (doto (.createOscillator context)
+      (.connect (.-destination context))
+      (-> .-frequency .-value (set! freq))
+      (-> .-type (set! "square"))
+      (.start start)
+      (.stop stop))))
+
+;; -------------------------
+;; Evaluation
+
 (defn identify
   "Hack to make literal values still evaluate."
   [expr-str]

      {:eval cljs/js-eval}
      #(:value %)))

(defonce context (js/window.AudioContext.))
(defn beep! [freq start dur]
  (let [start (+ start (.-currentTime context))
        stop (+ start dur)]
    (doto (.createOscillator context)
      (.connect (.-destination context))
      (-> .-frequency .-value (set! freq))
      (-> .-type (set! "square"))
      (.start start)
      (.stop stop))))

(defn handle [expr-str]
  (swap! state assoc-in [:text] expr-str)
  (when-let [value (evaluate expr-str)]
    (swap! state assoc-in [:music] value)))

(defn note [t p] {:time t :pitch p})
(defn play []
  (doseq [{seconds :time hertz :pitch} (map note (range) (:music @state))]
    (beep! hertz seconds 1)))

;; -------------------------
;; Views

(defn home-page []
  [:div [:h1 "Welcome to Leipzig Live!"]
   [:div [:input {:type "text"
                  :value (:text @state)
                  :on-change #(-> % .-target .-value handle)}]
    [:button {:on-click play} "Start/stop"]]
   [:div
    (:music @state)]])

(defn current-page []
  [:div [(session/get :current-page)]])

;; -------------------------
;; Routes

(defn app []
  (home-page @state))

(secretary/defroute "/" []
  (session/put! :current-page #'app))

;; -------------------------
;; Initialize app
