git.s-ol.nu alive / f554b54
lib: move midi.core to _midi s-ol 1 year, 3 months ago
4 changed file(s) with 150 addition(s) and 150 deletion(s). Raw diff Collapse all Expand all
00 MODULES:=$(sort $(wildcard alv-lib/*.moon))
1 MODULES:=$(filter-out $(wildcard alv-lib/_*.moon), $(MODULES))
12 MODULES:=$(MODULES:alv-lib/%.moon=docs/reference/module/%.html)
23 REFERENCE=docs/reference/index.md $(sort $(wildcard docs/reference/[01]*.md)) docs/reference/builtins.html $(MODULES)
34 REFTOC=$(REFERENCE:%.md=%.html)
0 import Constant, T, Struct, Op, Input, T, Error, const from require 'alv.base'
1 import RtMidiIn, RtMidiOut, RtMidi from require 'luartmidi'
2
3 bit = if _VERSION == 'Lua 5.4'
4 {
5 band: (a, b) -> a & b
6 bor: (a, b) -> a | b
7 lshift: (a, b) -> a << b
8 rshift: (a, b) -> a >> b
9 }
10 else
11 ok, bit = pcall require, 'bit32'
12 if ok then bit else require 'bit'
13 import band, bor, lshift, rshift from bit
14
15 MIDI = {
16 [0x9]: 'note-on'
17 [0x8]: 'note-off'
18
19 [0xa]: 'after-key'
20 [0xd]: 'after-channel'
21
22 [0xb]: 'control-change'
23 [0xe]: 'pitch-bend'
24 [0xc]: 'program-change'
25 }
26
27 rMIDI = {v,k for k,v in pairs MIDI}
28
29 find_port = (Klass, name) ->
30 with Klass RtMidi.Api.UNIX_JACK
31 id = nil
32 for port=1, \getportcount!
33 if name == \getportname port
34 id = port
35 break
36
37 \openport id
38
39 class InPort
40 new: (@name) =>
41 @port = find_port RtMidiIn, @name
42 @msgs = {}
43
44 poll: =>
45 @msgs = while true
46 delta, bytes = @port\getmessage!
47 break unless delta
48 { status, a, b } = bytes
49 chan = band status, 0xf
50 status = MIDI[rshift status, 4]
51 { :status, :chan, :a, :b }
52
53 __tostring: => "[#{@name}]"
54 __tojson: => string.format '%q', tostring @
55
56 class OutPort
57 new: (@name) =>
58 @port = find_port RtMidiOut, @name
59
60 send: (status, chan, a, b) =>
61 if 'string' == type 'status'
62 status = bor (lshift rMIDI[status], 4), chan
63 @port\sendmessage status, a, b
64
65 __tostring: => "[#{@name}]"
66 __tojson: => string.format '%q', tostring @
67
68 class PortOp extends Op
69 setup: (inputs) =>
70 super inputs
71 { :inp, :out } = @unwrap_all!
72
73 if inp and out
74 type = Struct in: T['midi/in'], out: T['midi/out']
75 @out = type\mk_const { 'in': InPort(inp), out: OutPort(out) }
76 elseif inp
77 @out = T['midi/in']\mk_const InPort inp
78 elseif out
79 @out = T['midi/out']\mk_const OutPort out
80 else
81 error "no port opened"
82
83 input = Constant.meta
84 meta:
85 name: 'input'
86 summary: "Create a MIDI input port."
87 examples: { '(midi/input name)' }
88
89 value: class extends PortOp
90 setup: (inputs) =>
91 name = const.str\match inputs
92 super inp: Input.hot name
93
94 poll: =>
95 @.out!\poll!
96 false
97
98 output = Constant.meta
99 meta:
100 name: 'output'
101 summary: "Create a MIDI output port."
102 examples: { '(midi/output name)' }
103
104 value: class extends PortOp
105 setup: (inputs) =>
106 name = const.str\match inputs
107 super out: Input.hot name
108
109 port = Constant.meta
110 meta:
111 name: 'port'
112 summary: "Create a bidirectional MIDI port."
113 examples: { '(midi/port name)' }
114
115 value: class extends PortOp
116 setup: (inputs) =>
117 { inp, out } = (const.str + const.str)\match inputs
118 super
119 inp: Input.hot inp
120 out: Input.hot out
121
122 poll: =>
123 @.out!.in\poll!
124 false
125
126 apply_range = (range, val) ->
127 if range\type! == T.str
128 switch range!
129 when 'raw' then val
130 when 'uni' then val / 128
131 when 'bip' then val / 64 - 1
132 when 'rad' then val / 64 * math.pi
133 when 'deg' then val / 128 * 360
134 else
135 error Error 'argument', "unknown range '#{range!}'"
136 elseif range\type! == T.num
137 val / 128 * range!
138 else
139 error Error 'argument', "range has to be a string or number"
140
141 {
142 :input
143 :output
144 :port
145 :apply_range
146 :bit
147 }
+0
-148
alv-lib/midi/core.moon less more
0 import Constant, T, Struct, Op, Input, T, Error, const from require 'alv.base'
1 import RtMidiIn, RtMidiOut, RtMidi from require 'luartmidi'
2
3 bit = if _VERSION == 'Lua 5.4'
4 {
5 band: (a, b) -> a & b
6 bor: (a, b) -> a | b
7 lshift: (a, b) -> a << b
8 rshift: (a, b) -> a >> b
9 }
10 else
11 ok, bit = pcall require, 'bit32'
12 if ok then bit else require 'bit'
13 import band, bor, lshift, rshift from bit
14
15 MIDI = {
16 [0x9]: 'note-on'
17 [0x8]: 'note-off'
18
19 [0xa]: 'after-key'
20 [0xd]: 'after-channel'
21
22 [0xb]: 'control-change'
23 [0xe]: 'pitch-bend'
24 [0xc]: 'program-change'
25 }
26
27 rMIDI = {v,k for k,v in pairs MIDI}
28
29 find_port = (Klass, name) ->
30 with Klass RtMidi.Api.UNIX_JACK
31 id = nil
32 for port=1, \getportcount!
33 if name == \getportname port
34 id = port
35 break
36
37 \openport id
38
39 class InPort
40 new: (@name) =>
41 @port = find_port RtMidiIn, @name
42 @msgs = {}
43
44 poll: =>
45 @msgs = while true
46 delta, bytes = @port\getmessage!
47 break unless delta
48 { status, a, b } = bytes
49 chan = band status, 0xf
50 status = MIDI[rshift status, 4]
51 { :status, :chan, :a, :b }
52
53 __tostring: => "[#{@name}]"
54 __tojson: => string.format '%q', tostring @
55
56 class OutPort
57 new: (@name) =>
58 @port = find_port RtMidiOut, @name
59
60 send: (status, chan, a, b) =>
61 if 'string' == type 'status'
62 status = bor (lshift rMIDI[status], 4), chan
63 @port\sendmessage status, a, b
64
65 __tostring: => "[#{@name}]"
66 __tojson: => string.format '%q', tostring @
67
68 class PortOp extends Op
69 setup: (inputs) =>
70 super inputs
71 { :inp, :out } = @unwrap_all!
72
73 if inp and out
74 type = Struct in: T['midi/in'], out: T['midi/out']
75 @out = type\mk_const { 'in': InPort(inp), out: OutPort(out) }
76 elseif inp
77 @out = T['midi/in']\mk_const InPort inp
78 elseif out
79 @out = T['midi/out']\mk_const OutPort out
80 else
81 error "no port opened"
82
83 input = Constant.meta
84 meta:
85 name: 'input'
86 summary: "Create a MIDI input port."
87 examples: { '(midi/input name)' }
88
89 value: class extends PortOp
90 setup: (inputs) =>
91 name = const.str\match inputs
92 super inp: Input.hot name
93
94 poll: =>
95 @.out!\poll!
96 false
97
98 output = Constant.meta
99 meta:
100 name: 'output'
101 summary: "Create a MIDI output port."
102 examples: { '(midi/output name)' }
103
104 value: class extends PortOp
105 setup: (inputs) =>
106 name = const.str\match inputs
107 super out: Input.hot name
108
109 port = Constant.meta
110 meta:
111 name: 'port'
112 summary: "Create a bidirectional MIDI port."
113 examples: { '(midi/port name)' }
114
115 value: class extends PortOp
116 setup: (inputs) =>
117 { inp, out } = (const.str + const.str)\match inputs
118 super
119 inp: Input.hot inp
120 out: Input.hot out
121
122 poll: =>
123 @.out!.in\poll!
124 false
125
126 apply_range = (range, val) ->
127 if range\type! == T.str
128 switch range!
129 when 'raw' then val
130 when 'uni' then val / 128
131 when 'bip' then val / 64 - 1
132 when 'rad' then val / 64 * math.pi
133 when 'deg' then val / 128 * 360
134 else
135 error Error 'argument', "unknown range '#{range!}'"
136 elseif range\type! == T.num
137 val / 128 * range!
138 else
139 error Error 'argument', "range has to be a string or number"
140
141 {
142 :input
143 :output
144 :port
145 :apply_range
146 :bit
147 }
00 import Constant, Op, Input, T, Struct, sig, evt from require 'alv.base'
1 import input, output, port, apply_range from require 'alv-lib.midi.core'
1 import input, output, port, apply_range from require 'alv-lib._midi'
22 import monotime from require 'system'
33
44 gate = Constant.meta
139139 summary: "`send MIDI note events."
140140 examples: { '(midi/send-notes [port] [chan] note-events)' }
141141 description: "
142 `chan` can be
143142 `note-events` is a !-stream of structs with the following keys:
144143
145144 - `pitch`: MIDI pitch (num)