summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJake Zerrer <him@jakezerrer.com>2025-10-14 14:49:31 -0400
committerJake Zerrer <him@jakezerrer.com>2025-10-14 16:19:03 -0400
commit756b4e6ceb360e8d5e93a2bde712aa46cda61d85 (patch)
treed4d532f8efd196b25feacdba44d659120904bd5f
parenta6e52696756056ccaf3a517a96b01f6756837d2f (diff)
Capture flow to portal
-rw-r--r--.nrepl-port2
-rw-r--r--portal.clj84
-rw-r--r--src/.main.clj.swpbin16384 -> 0 bytes
-rw-r--r--src/main.clj74
4 files changed, 108 insertions, 52 deletions
diff --git a/.nrepl-port b/.nrepl-port
index 41bb84d..0b9fe47 100644
--- a/.nrepl-port
+++ b/.nrepl-port
@@ -1 +1 @@
-51213 \ No newline at end of file
+53291 \ No newline at end of file
diff --git a/portal.clj b/portal.clj
new file mode 100644
index 0000000..ce05eb8
--- /dev/null
+++ b/portal.clj
@@ -0,0 +1,84 @@
+(ns portal
+ (:require [missionary.core :as m]
+ [clojure.set :refer [union difference]]))
+
+(def focused-tags (atom #{}))
+(def >focused-tags (m/signal (m/watch focused-tags)))
+
+(def seen-tags (atom #{}))
+(def >seen-tags (m/signal (m/watch seen-tags)))
+
+(defn rec
+ "Record flow f, tagging with id."
+ [id f]
+ (m/ap
+ (let [capturing? (atom nil)
+ [tag value]
+ (m/amb= [:focused-tags (m/?< >focused-tags)]
+ [:event (m/?< f)])]
+ (case tag
+ :focused-tags
+ (do
+ (reset! capturing? (boolean (value id)))
+ (m/amb))
+
+ :event
+ (do
+ (swap! seen-tags conj id)
+ (when @capturing?
+ (m/? (m/via m/blk ((requiring-resolve 'portal.api/submit) [id value]))))
+ value)))))
+
+(defn ptags
+ "Print all available tags."
+ [] @seen-tags)
+
+(def show-portal? (atom false))
+(def >show-portal? (rec :show-portal? (m/signal (m/watch show-portal?))))
+
+(defn show-portal
+ "Show portal window."
+ []
+ (reset! show-portal? true))
+
+(defn hide-portal
+ "Hide portal window."
+ []
+ (reset! show-portal? false))
+
+(def >portal-ui
+ (m/ap
+ (when (m/?< >show-portal?)
+ (try
+ (m/?<
+ (m/observe
+ (fn [cb]
+ ((m/via m/blk ((requiring-resolve 'portal.api/open))) {} {})
+ (cb :open)
+ (fn []
+ ((m/via m/blk
+ ((requiring-resolve 'portal.api/close))
+ ((requiring-resolve 'portal.api/clear))) {} {})))))
+ (catch missionary.Cancelled _
+ (m/amb)))
+ (m/amb))))
+
+(defn cap
+ "Capture flow elements with specified ids to portal"
+ [& ids]
+ (reset! focused-tags (into #{} ids)))
+
+(defn +cap
+ "Add do portal capture set"
+ [& ids]
+ (swap! focused-tags union (into #{} ids)))
+
+(defn -cap
+ "Remove from portal capture set"
+ [& ids]
+ (swap! focused-tags difference (into #{} ids)))
+
+(def >portal-main
+ "Main entrypoint."
+ (m/ap (m/amb= (do (m/?< >portal-ui)
+ (m/amb)))))
diff --git a/src/.main.clj.swp b/src/.main.clj.swp
deleted file mode 100644
index b91b1b6..0000000
--- a/src/.main.clj.swp
+++ /dev/null
Binary files differ
diff --git a/src/main.clj b/src/main.clj
index 7674cc1..d3f7ce0 100644
--- a/src/main.clj
+++ b/src/main.clj
@@ -1,22 +1,7 @@
(ns main
(:require [missionary.core :as m]
- [clojure.set :refer [difference union]]))
-
-(def >portal
- (m/signal
- (m/ap
- (try
- (m/?<
- (m/observe
- (fn [cb]
- ((m/via m/blk ((requiring-resolve 'portal.api/open))) {} {})
- (cb :open)
- (fn []
- ((m/via m/blk
- ((requiring-resolve 'portal.api/close))
- ((requiring-resolve 'portal.api/clear))) {} {})))))
- (catch missionary.Cancelled _
- (m/amb))))))
+ [clojure.set :refer [difference union]]
+ [portal :refer [>portal-main rec show-portal hide-portal +cap -cap cap]]))
;; How many times per second are output continuous values sampled and turned
;; into events?
@@ -28,9 +13,11 @@
(reset! sample-rate v))
(def notes-on (atom #{}))
-(def >notes-on (m/signal (m/watch notes-on)))
+(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."
@@ -62,8 +49,9 @@
;; 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
- (m/?
- (m/sleep (/ 1000 @sample-rate)))
+ (do (m/?
+ (m/sleep (/ 1000 @sample-rate)))
+ (m/amb))
:tick
(recur)))))
@@ -96,7 +84,7 @@
(let [local-midi-enabled? (atom nil)
local-active (atom #{})
[tag value] (m/amb=
- [:midi-enabled? (m/?< (m/watch midi-enabled?))]
+ [:midi-enabled? (m/?< >midi-enabled?)]
[:note-event (m/?< output)])]
(case tag
:midi-enabled?
@@ -137,33 +125,9 @@
(recur (rest notes)))
(m/amb)))))))
-(def enable-portal-submission? (atom false))
-(def >enable-portal-submission? (m/watch enable-portal-submission?))
-
-(defn enable-portal-submission []
- (reset! enable-portal-submission? true))
-
-(defn disable-portal-submission []
- (reset! enable-portal-submission? false))
-
-(def tap-flow
- (m/ap
- (let [local-enable-portal-submission? (atom nil)
- [tag value]
- (m/amb= [:enable-portal-submission? (m/?< >enable-portal-submission?)]
- [:event (m/?< set->midi-events)])]
- (case tag
- :enable-portal-submission?
- (do
- (reset! local-enable-portal-submission? value)
- (when value
- (m/?< >portal))
- (m/amb))
- :event
- (do
- (when @local-enable-portal-submission?
- (m/? (m/via m/blk ((requiring-resolve 'portal.api/submit) value))))
- value)))))
+(def main
+ (m/ap (m/amb= (m/?< (rec :root set->midi-events))
+ (m/?< >portal-main))))
(defn start-engine
"Start playback engine."
@@ -171,7 +135,7 @@
(when (not @engine)
(reset! engine
(do (enable-midi)
- ((m/reduce prn tap-flow) {} {})))))
+ ((m/reduce {} {} main) {} {})))))
(defn stop-engine
"Stop playback engine."
@@ -184,14 +148,22 @@
(comment
(start-engine)
+ (cap)
+
+ (+cap :root :notes-set)
+
+ (show-portal)
+
(play-notes 1 2 3 4 5)
(stop-notes 4 5)
- (enable-portal-submission)
+
(play-notes 1 2 3 4)
+ (-cap :root)
+
(disable-midi)
(play-notes 1 2 3 4)
- (disable-portal-submission)
+ (hide-portal)
(enable-midi)
(play-notes 1 2 3 4)