summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main.clj182
-rw-r--r--src/unheard/midi_util.clj4
-rw-r--r--src/unheard/theory.clj25
-rw-r--r--src/unheard/time_object.clj3
4 files changed, 1 insertions, 213 deletions
diff --git a/src/main.clj b/src/main.clj
deleted file mode 100644
index 57eedad..0000000
--- a/src/main.clj
+++ /dev/null
@@ -1,182 +0,0 @@
-(ns main
- (:require [missionary.core :as m]
- [clojure.set :refer [difference union]]
- [portal :refer [>portal-main rec show-portal hide-portal +cap -cap cap]]
- [notation :as notation]
- [example-song :refer [melody]]))
-
-;; How many times per second are output continuous values sampled and turned
-;; into events?
-(def sample-rate (atom 30))
-
-(defn set-sample-rate
- "Change the output sample rate."
- [v]
- (reset! sample-rate v))
-
-(def notes-on (atom #{}))
-(def >notes-on (rec :notes-set (m/signal (m/watch notes-on))))
-
-(def midi-enabled? (atom true))
-(def >midi-enabled? (rec :midi-enabled?
- (m/signal (m/watch midi-enabled?))))
-
-(defn enable-midi
- "Enable midi out."
- []
- (reset! midi-enabled? true))
-
-(defn disable-midi
- "Stop all active notes and disable midi out."
- []
- (reset! midi-enabled? false))
-
-(defn play-notes
- "Play notes."
- [& v]
- (when @midi-enabled?
- (swap! notes-on union (into #{} v))))
-
-(defn stop-notes
- "Stop notes."
- [& v]
- (when @midi-enabled?
- (swap! notes-on difference (into #{} v))))
-
-(def clock
- (m/ap
- (loop []
- (m/amb
- ;; TODO: This seems to be emitting twice per cycle
- ;; TODO: Currently, there will be latency when changing the sample rate
- ;; due to having to wait for the most recent sleep to complete
- ;; Update clock to be reactive on sample-rate, too
- (do (m/?
- (m/sleep (/ 1000 @sample-rate)))
- (m/amb))
- :tick
- (recur)))))
-
-(defn output
- "Convert the continuous time >notes-on flow to a series of discrete midi note
- on and off events."
- [composition]
- (m/eduction
- (comp (remove #(= (select-keys % [:note-on :note-off]) {:note-on #{} :note-off #{}}))
- (dedupe))
- (m/reductions (fn [{:keys [active note-on note-off]} [curr _]]
- {:note-on (difference (difference curr active) note-on)
- :note-off (difference (difference active curr) note-off)
- :active curr})
- {:note-on #{}
- :note-off #{}
- :active #{}}
- ;; This actually does a bunch of unnecessary work
- ;; What we really want is to sample at _at most_ sample rate,
- ;; but not at all if nothing has changed
- (m/sample
- vector
- composition
- clock))))
-
-(defn process-midi-toggle-events
- "Listen for changes on midi-enabled?
- When playback is disabled, send a note-off event for each active note
- and then zero out notes-on."
- [composition]
- (m/ap
- (let [local-midi-enabled? (atom nil)
- local-active (atom #{})
- [tag value] (m/amb=
- [:midi-enabled? (m/?< >midi-enabled?)]
- [:note-event (m/?< (output composition))])]
- (case tag
- :midi-enabled?
- (let [midi-enabled? value
- active @local-active]
- (reset! local-midi-enabled? midi-enabled?)
- (if (not midi-enabled?)
- (do
- (reset! notes-on #{})
- (reset! local-active #{})
- {:note-on #{}
- :note-off active
- :active #{}})
- (m/amb)))
- :note-event
- (let [{:keys [active]
- :as note-event} value]
- (reset! local-active active)
- (if @local-midi-enabled?
- note-event
- (m/amb)))))))
-
-(defonce engine (atom nil))
-
-(defn set->midi-events
- "Convert set representation of notes to midi events"
- [composition]
- (m/ap
- (let [{:keys [note-on note-off]} (m/?< (process-midi-toggle-events composition))]
- (m/amb=
- (loop [notes note-on]
- (if (first notes)
- (m/amb [:note-on (first notes)]
- (recur (rest notes)))
- (m/amb)))
- (loop [notes note-off]
- (if (first notes)
- (m/amb [:note-off (first notes)]
- (recur (rest notes)))
- (m/amb)))))))
-
-(defn main
- [composition]
- (m/ap (m/amb= (m/?< (rec :root (set->midi-events composition)))
- (m/?< >portal-main))))
-
-(defn start-engine
- "Start playback engine."
- [composition]
- (when (not @engine)
- (reset! engine
- (do (enable-midi)
- ((m/reduce {} {} (main composition)) {} {})))))
-
-(defn stop-engine
- "Stop playback engine."
- []
- (disable-midi)
- (when @engine
- (@engine)
- (reset! engine nil)))
-
-(comment
- (let [[>clock set-clock] (notation/clock)]
- (def >clock >clock)
- (def set-clock set-clock))
- (+cap :root :notes-set)
- (start-engine (melody >clock))
-
- (set-clock 0)
- (show-portal)
-
- (cap)
-
- (play-notes 1 2 3 4 5)
- (stop-notes 4 5)
-
- (play-notes 1 2 3 4)
-
- (-cap :root)
-
- (disable-midi)
- (play-notes 1 2 3 4)
- (hide-portal)
-
- (enable-midi)
- (play-notes 1 2 3 4)
- (play-notes 6 7 8)
- (play-notes 100)
-
- (stop-engine))
diff --git a/src/unheard/midi_util.clj b/src/unheard/midi_util.clj
deleted file mode 100644
index 382fe87..0000000
--- a/src/unheard/midi_util.clj
+++ /dev/null
@@ -1,4 +0,0 @@
-(ns unheard.midi-util)
-
-(defn control-message? [msg-type]
- (= :control msg-type))
diff --git a/src/unheard/theory.clj b/src/unheard/theory.clj
index e7ed9eb..5314ea7 100644
--- a/src/unheard/theory.clj
+++ b/src/unheard/theory.clj
@@ -2,7 +2,6 @@
(:require [missionary.core :as m]
[unheard.time-object :refer
[time-object lift phrase timeline point-query]]
- [clojure.set :refer [union]]
[unheard.util.missionary :refer [reconcile-merge]]))
(defn note
@@ -42,27 +41,3 @@
(swap! c inc)
(swap! v inc)
(swap! v dec))
-
-(defn poly
- [& notes]
- (m/signal (m/cp (apply union (m/?< (apply m/latest vector notes))))))
-
-;; TODO: Group could actually wrap note, rather than using explicitly
-;; WIll introduce a lot of GC churn, though
-(defn group
- [clock start end content]
- (m/cp (let [content (m/signal content)]
- (if (m/?< (m/latest #(<= start % end) clock))
- (m/?< content)
- (m/amb #{})))))
-
-;; TODO:
-;; - Note literals turn into numbers
-;; - Represent keyboard as byte array of shorts
-;; - play a note increments, stop a note decrements
-;; - Multiple instruments
-;; - Mapping inputs to vars
-;; - Inputs get declared at the top of a track
-;; - Devices get mapped to declared inputs
-;; - Notion of scenes that change mapping of inputs to vars
-;; - Loops
diff --git a/src/unheard/time_object.clj b/src/unheard/time_object.clj
index 89070f6..62e84b5 100644
--- a/src/unheard/time_object.clj
+++ b/src/unheard/time_object.clj
@@ -1,7 +1,6 @@
(ns unheard.time-object
(:require [missionary.core :as m]
- [unheard.interval :as i]
- [helins.interval.map :as imap]))
+ [unheard.interval :as i]))
;; TODO: Update description
(defn time-object