blob: 4c10de08fcfc39e4c4c169001e229d8981d4eb08 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
(ns midi
(:require [missionary.core :as m])
(:import [javax.sound.midi MidiSystem Receiver ShortMessage MidiDevice$Info MidiDevice Transmitter]))
(def >midi-devices
(m/stream
(m/ap
(loop []
(m/amb= (MidiSystem/getMidiDeviceInfo)
(do
(m/? (m/sleep 5000))
(recur)))))))
;; NOTE: Seems that there is a JVM bug that prevents device rescanning
;;
(def >midi-device-info
"A flow of maps of device name -> device properties"
(m/signal
(m/eduction (dedupe)
(m/ap (let [devices (m/?< >midi-devices)]
(into {} (map (fn [^MidiDevice$Info d]
[(.getName d)
{:description (.getDescription d)
:vendor (.getVendor d)
:version (.getVersion d)
:device-info d}]) devices)))))))
(defn >device-info
[device-name]
(m/ap (let [device-info (m/?< >midi-device-info)]
(get-in device-info [device-name :device-info]))))
(defn >midi-messages
"A flow of java midi messages"
[^MidiDevice device]
(m/stream
(m/ap
(let [^Transmitter transmitter (m/? (m/via m/blk (.getTransmitter device)))
transmit (atom nil)
>transmit (m/eduction (filter some?) (m/watch transmit))
receiver (reify Receiver
(send [_this midi-message _timestamp]
(println "HI")
(reset! transmit midi-message))
;; TODO: Close
(close [this]))]
(try
(println "Connecting to transmitter")
(m/? (m/via m/blk (.setReceiver transmitter receiver)))
(println "Connected to transmitter")
(m/?< >transmit)
(finally
(println "Disconnecting from transmitter")
(m/? (m/compel (m/via m/blk (.close receiver))))
(println "Disconnected from transmitter")))))))
(defn >device
"Returns a device for given device name. Returns nil if device not found."
[device-name with-device]
(m/ap
(let [device-info
(m/?< (>device-info device-name))
^MidiDevice device (MidiSystem/getMidiDevice ^MidiDevice$Info device-info)]
;; Essential problem: Combining taking element from flow to put in
;; conditional, and cleaning up in catch.
;; NOTE: You need the MidiDevice type hint when you call open and close!
(try
(println "Opening device")
(m/? (m/via m/blk (.open device)))
(println "Device opened")
(m/?< (with-device device))
(finally
(println "Closing device")
(m/compel (m/? (m/via m/blk (.close device))))
(println "Device closed")
)))))
(defn new-device
"Generate a new midi device.
Currently, this is a map of channel-num to [atom signal]."
[]
(into {} (map (fn [i] [i (let [v (atom #{})] [v (m/signal (m/watch v))])]) (range 0 127))))
(defn >midi-messages->ch-stream
[>midi-messages]
(m/signal
(m/ap
(let [device (new-device)]
(m/amb= device
(do
(let [v (m/?< >midi-messages)]
(cond (instance? ShortMessage v)
(let [channel (.getChannel ^ShortMessage v)
command (.getCommand ^ShortMessage v)
data-1 (.getData1 ^ShortMessage v)]
(cond (= command ShortMessage/NOTE_ON)
(swap! (first (get device channel)) conj data-1)
(= command ShortMessage/NOTE_OFF)
(swap! (first (get device channel)) disj data-1)))
:else :other))
(m/amb)))))))
(defn >ch-stream [>device ch]
(m/cp (m/?< (second (get >device ch)))))
(def bus-sel (atom nil))
(def >bus-sel (m/eduction (dedupe) (m/watch bus-sel)))
(reset! bus-sel "Bus 1")
#_(reset! bus-sel "Bus 2")
#_(reset! bus-sel nil)
(def run
(m/ap
(prn (m/?< (>device (m/?< >bus-sel)
>midi-messages)))
))
(def close ((m/reduce prn {} run) {} {}))
#_
(close)
;; OH! You hreally have to think about the supervision tree at all times
;; It informs your function composition
;;
;;It helps to write in a continuation style - see `with-messages`
;;>device is my most mature fn
|