summaryrefslogtreecommitdiff
path: root/src/unheard/midi.clj
diff options
context:
space:
mode:
authorJake Zerrer <him@jakezerrer.com>2025-11-06 15:25:16 -0500
committerJake Zerrer <him@jakezerrer.com>2025-11-07 13:14:16 -0500
commit275450c8c07a4f73e1f6e1da83578cc2ec2248b8 (patch)
tree5076790f343ab537d28ff748dea82d2d63a1ef5d /src/unheard/midi.clj
parentc62ed160a04c1ee5d08297d13a26630a590c5d6a (diff)
Vastly simplify controller setup
Diffstat (limited to 'src/unheard/midi.clj')
-rw-r--r--src/unheard/midi.clj107
1 files changed, 20 insertions, 87 deletions
diff --git a/src/unheard/midi.clj b/src/unheard/midi.clj
index e3e65d2..174fec2 100644
--- a/src/unheard/midi.clj
+++ b/src/unheard/midi.clj
@@ -188,90 +188,23 @@
(m/? rv)
(recur)))))))))))
-;; CoreMidiSource is TX Device
-;; CoreMidiDestination is RX Device
-
-(defn |short-messages
- "Filter down to midi short messages"
- [>messages]
- (m/eduction (filter #(instance? ShortMessage %)) >messages))
-
-(defn |group-by-channel
- [>messages]
- (m/group-by #(.getChannel ^ShortMessage %) >messages))
-
-(defn |group-by-data-1
- [>messages]
- (m/group-by #(.getData1 ^ShortMessage %) >messages))
-
-(defn |matching-commands
- [>messages commands]
- (m/eduction (filter (fn [^ShortMessage v]
- (contains? commands (.getCommand v)))) >messages))
-
-(def note-commands
- #{ShortMessage/NOTE_OFF
- ShortMessage/NOTE_ON
- ShortMessage/POLY_PRESSURE})
-
-(def control-commands
- #{ShortMessage/CONTROL_CHANGE})
-
-(defn keyboard
- [f]
- (m/relieve
- (m/group-by
- first
- (m/ap
- (let [[ch ch-messages]
- (m/?> 128 (|group-by-channel (|short-messages (m/stream f))))
- ch-messages (m/stream ch-messages)]
- (m/amb=
- (let [[note note-messages]
- (m/?> 128 (-> ch-messages
- (|matching-commands note-commands)
- (|group-by-data-1)))]
- ;; TODO: Where to relieve in here?
- [:key ch note
- (m/?<
- (m/reductions
- (fn [_prev curr]
- (when (some? curr)
- (let [cmd (.getCommand ^ShortMessage curr)]
- (cond
- (= cmd ShortMessage/NOTE_ON)
- (.getData2 ^ShortMessage curr)
- (= cmd ShortMessage/POLY_PRESSURE)
- (.getData2 ^ShortMessage curr)
- (= cmd ShortMessage/NOTE_OFF)
- nil)))) nil note-messages))])
-
- (let [[control-number control-messages]
- (m/?> 128 (-> ch-messages
- (|matching-commands control-commands)
- (|group-by-data-1)))]
- [:control ch control-number (.getData2 ^ShortMessage (m/?< control-messages))])))))))
-
-#_(defn >ch-stream [>device ch]
- (m/cp (m/?< (second (get >device ch)))))
-
-#_{ch {:notes {note aftertouch}
- :pitch v
- :control {controller value}
- :program program}}
-
-;; Goal:
-;; Create function called `(receive-from-bus bus-name)`.
-;; `bus-name` is the name of a midi bus on this machine. If a bus with that name
-;; exists, the signal returned by `bus` will contain a map of {ch val}, where ch
-;; is a midi channel number and val is a signal of sets representing active
-;; notes on that channel.
-;;
-;; Create another function, `(send-to-bus bus-name sigs)`. `bus-name` is the
-;; name of a midi bus on this machine. If a bus with that name exists, it will
-;; start reading note values from `sigs`.
-;;
-;; Here, `sigs` is a map of {ch val}, where `ch` is a midi channel number and
-;; `val` is a signal of sets representing active notes on that channel.
-;;
-
+(defn controller
+ ;; NOTE: The structure of `config` currently assumes a fairly specific
+ ;; structure. It might be better for `config` to be a simple `kv` structure,
+ ;; where `k` can be e.g. a tuple [:knob 1], a single value [:mod-wheel],
+ ;; etc.
+
+ "Given a flow `f` and a controller config `config`, return a map of
+ controller flows taking from `f`.
+
+ A `config` is a map of maps of type `group` -> `id` -> `flow-constructor`.
+ Here, `group` is the name of a control type, e.g. :knob; `id` is a unique
+ identifier for that control, e.g. `1`, and `flow-constructor` is a function
+ accepting a flow of ShortMessages as its sole argument, and returning
+ a flow of values associated with the control."
+ [f config]
+
+ (into {}
+ (map (fn [[group instance]]
+ {group (into {} (map (fn [[id flow]] {id (flow f)}) instance))})
+ config)))