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
127
128
129
130
131
132
|
import Value, Op, Input, match from require 'core.base'
import apply_range, bit from require 'lib.midi.core'
import bor, lshift from bit
unpack or= table.unpack
color = (r, g) -> bit.bor 12, r, (bit.lshift g, 4)
cc_seq = Value.meta
meta:
name: 'cc-seq'
summary: "MIDI CC-Sequencer."
examples: { '(launchctl/cc-seq port i start chan [steps [range]])' }
description: "
returns the value for the i-th step steps (buttons starting from start).
steps defaults to 8.
range can be one of:
- 'raw' [ 0 - 128[
- 'uni' [ 0 - 1[ (default)
- 'bip' [-1 - 1[
- 'rad' [ 0 - tau[
- 'deg' [ 0 - 360[
- (num) [ 0 - num["
value: class extends Op
new: => super 'num'
setup: (inputs) =>
{ port, i, start,
chan, steps, range } = match 'midi/port num num num num? any?', inputs
super
port: Input.io port
i: Input.value i
start: Input.value start
chan: Input.value chan
steps: Input.value steps or Value.num 8
range: Input.value range or Value.str 'uni'
if not @out\unwrap!
@out\set apply_range @inputs.range, 0
tick: =>
{ :port, :i, :start, :chan, :steps, :range } = @inputs
if steps\dirty!
while steps! > #@state
table.insert @state, 0
while steps! < #@state
table.remove @state
curr_i = i! % #@state
if port\dirty!
changed = false
for msg in port!\receive!
if msg.status == 'control-change' and msg.chan == chan!
rel_i = msg.a - start!
if rel_i >= 0 and rel_i < #@state
@state[rel_i+1] = msg.b
changed = rel_i == curr_i
@out\set apply_range range, @state[curr_i+1] if changed
else
@out\set apply_range range, @state[curr_i+1]
gate_seq = Value.meta
meta:
name: 'gate-seq'
summary: "MIDI Gate-Sequencer."
examples: { '(launchctl/gate-seq port i start chan [steps])' }
description: "
returns `true` or `false` for the `i`-th note-button (MIDI-notes starting from
`start`). `steps` defaults to 8."
value: class extends Op
new: =>
super 'bool', false
@state = {}
setup: (inputs) =>
{ port, i, start, chan, steps } = match 'midi/port num num num num?', inputs
super
port: Input.io port
i: Input.value i
start: Input.value start
chan: Input.value chan
steps: Input.value steps or Value.num 8
light = (set, active) ->
set = if set then 'S' else ' '
active = if active then 'A' else ' '
color switch set .. active
when ' ' then 0, 0
when ' A' then 1, 1
when 'S ' then 1, 0
when 'SA' then 3, 1
display: (i, active) =>
{ :port, :start, :chan } = @unwrap_all!
port\send 'note-on', chan, (start + i), light @state[i+1], active
tick: =>
{ :port, :i, :start, :chan, :steps } = @inputs
if steps\dirty!
while steps! > #@state
table.insert @state, false
while steps! < #@state
table.remove @state
curr_i = i! % #@state
if port\dirty!
for msg in port!\receive!
if msg.status == 'note-on' and msg.chan == chan!
rel_i = msg.a - start!
if rel_i >= 0 and rel_i < #@state
@state[rel_i+1] = not @state[rel_i+1]
@display rel_i, rel_i == curr_i
if i\dirty!
prev_i = (curr_i - 1) % #@state
@display curr_i, true
@display prev_i, false
@out\set @state[curr_i+1]
{
'gate-seq': gate_seq
'cc-seq': cc_seq
}
|