diff options
Diffstat (limited to 'src/unheard/midi.clj')
| -rw-r--r-- | src/unheard/midi.clj | 107 |
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))) |
