diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/main.clj | 182 | ||||
| -rw-r--r-- | src/unheard/midi_util.clj | 4 | ||||
| -rw-r--r-- | src/unheard/theory.clj | 25 | ||||
| -rw-r--r-- | src/unheard/time_object.clj | 3 |
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 |
