summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJake Zerrer <him@jakezerrer.com>2025-10-16 13:36:16 -0400
committerJake Zerrer <him@jakezerrer.com>2025-10-16 13:54:03 -0400
commita36b60a8ee2a293d0c9783cbe59da2a8d9c1b195 (patch)
treed3d85aa044a3952aed9ab56c24f0c55935d4cc40
parent9b1b81e5097f05fe879bb67605fa839e2d00bfd1 (diff)
Simplify note; create group
-rw-r--r--.nrepl-port2
-rw-r--r--src/.notation.clj.swpbin0 -> 16384 bytes
-rw-r--r--src/main.clj5
-rw-r--r--src/notation.clj77
4 files changed, 41 insertions, 43 deletions
diff --git a/.nrepl-port b/.nrepl-port
index 67de041..4e321f3 100644
--- a/.nrepl-port
+++ b/.nrepl-port
@@ -1 +1 @@
-57858 \ No newline at end of file
+65174 \ No newline at end of file
diff --git a/src/.notation.clj.swp b/src/.notation.clj.swp
new file mode 100644
index 0000000..6bfd6d0
--- /dev/null
+++ b/src/.notation.clj.swp
Binary files differ
diff --git a/src/main.clj b/src/main.clj
index d3f7ce0..74982ed 100644
--- a/src/main.clj
+++ b/src/main.clj
@@ -1,7 +1,8 @@
(ns main
(:require [missionary.core :as m]
[clojure.set :refer [difference union]]
- [portal :refer [>portal-main rec show-portal hide-portal +cap -cap cap]]))
+ [portal :refer [>portal-main rec show-portal hide-portal +cap -cap cap]]
+ [notation :refer [melody]]))
;; How many times per second are output continuous values sampled and turned
;; into events?
@@ -73,7 +74,7 @@
;; but not at all if nothing has changed
(m/sample
vector
- >notes-on
+ melody
clock))))
(def process-midi-toggle-events
diff --git a/src/notation.clj b/src/notation.clj
index a4b9488..7cf4e86 100644
--- a/src/notation.clj
+++ b/src/notation.clj
@@ -73,50 +73,47 @@
;; - Loops
)
-(def ctr (atom nil))
-(def pulse (m/signal (m/watch ctr)))
-
-(defn note [value duration]
- (m/eduction (take-while #(not= ::done %))
- (m/ap
- (m/amb #{value}
- (let [c (atom 0)]
- (m/?< pulse)
- (swap! c inc)
- (if (> @c duration)
- (m/amb
- #{}
- ::done)
- (m/amb)))))))
-
-(defmacro chord
+(def clock (atom 0))
+(def >clock (m/signal (m/watch clock)))
+
+(defn note [clock start duration value]
+ (m/cp
+ (if (m/?< (m/latest #(<= start % (dec (+ start duration))) clock))
+ #{value}
+ #{})))
+
+(defmacro poly
[& notes]
(let [atoms (repeatedly (count notes) gensym)
let-bindings (vec (mapcat (fn [atom] [atom `(atom #{})]) atoms))
reset-forms (map (fn [atom note] `(m/amb (reset! ~atom (m/?< ~note)))) atoms notes)
union-form (cons `union (map (fn [atom] `(deref ~atom)) atoms))]
- `(m/ap
- (m/amb
- (let ~let-bindings
- (m/amb= ~@reset-forms)
- ~union-form)))))
-
-(defmacro line
- [& notes]
- `(m/ap (m/amb ~@(map (fn [note] `(m/?< ~note)) notes))))
+ `(m/relieve {}
+ (m/ap
+ (let ~let-bindings
+ (m/amb= ~@reset-forms)
+ ~union-form)))))
+
+;; 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 #{})))))
(def melody
- (line
- (chord (note 1 2)
- (note 3 2)
- (note 5 2))
- (chord (note 2 2)
- (note 4 2)
- (note 6 2))))
-
-(def cancel
- ((m/reduce prn #{} melody) {} {}))
-
-(cancel)
-
-(reset! ctr nil)
+ (m/signal
+ (poly (note >clock 0 4 1)
+ (note >clock 0 5 3)
+ (note >clock 0 3 5))))
+
+#_(def cancel
+ ((m/reduce prn #{} melody) {} {}))
+
+#_(cancel)
+#_(reset! clock 0)
+#_(swap! clock inc)
+#_(swap! clock dec)