/
UNO Synth Pro - ENV scaling.moz
202 lines (175 loc) · 4.36 KB
/
UNO Synth Pro - ENV scaling.moz
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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
@Description
Sliders change AMP & FILTER ENV ADSR on UNO Synth Pro.
XY Pad applies scaling to their total length (inspired by Dreadbox Typhon).
Shift button resets XY Pad to no-effect position, or pulls envelopes from the synth via SysEx (works in AUM when synth's MIDI output routed to Mozaic) if already in no-effect position.
LOOP sliders are ON/OFF and not scaled.
@End
@OnLoad
MIDI_CH = 0
// AMP ENV knob index
AE_A = 0
AE_D = 1
AE_S = 2
AE_R = 3
// FILTER ENV knob index
FE_A = 4
FE_D = 5
FE_S = 6
FE_R = 7
AE_LOOP = 8
FE_LOOP = 9
// mapped by knob index
CC = [59, 60, 61, 62, 53, 54, 55, 56, 63, 57]
// https://stackoverflow.com/a/5732390
SCALE_FROM = -3 // means divided by 4
SCALE_TO = 3 // means multiplied by 4
SLOPE = (SCALE_TO - SCALE_FROM) / 127
ShowLayout 3
LabelKnobs {UNO Synth Pro - Envelopes Control & Length Scaling}
LabelKnob AE_A, {AE:A}
LabelKnob AE_D, {AE:D}
LabelKnob AE_S, {AE:S}
LabelKnob AE_R, {AE:R}
LabelKnob FE_A, {FE:A}
LabelKnob FE_D, {FE:D}
LabelKnob FE_S, {FE:S}
LabelKnob FE_R, {FE:R}
LabelKnob AE_LOOP, {AE:LOOP}
LabelKnob FE_LOOP, {FE:LOOP}
LabelXY {X – AMP ENV, Y – FILTER ENV}
@End
@OnKnobChange
knob = LastKnob
if knob = AE_LOOP or knob = FE_LOOP
value = Round(GetKnobValue knob)
Call @Control
else
Call @ScaleAndControl
endif
@End
@OnXYChange
for knob = 0 to 7
Call @ScaleAndControl
endfor
@End
@OnShiftUp
if GetXValue = 64 and GetYValue = 64
Call @RequestState
else
SetXYValues 64, 64
for knob = 0 to 7
Call @ScaleAndControl
endfor
endif
@End
// @param knob
@ScaleAndControl
value = GetKnobValue knob
if knob >= AE_A and knob <= AE_R
factor = GetXValue
elseif knob >= FE_A and knob <= FE_R
factor = GetYValue
endif
if factor = 64
// pad center, default position, no effect
factor = 0
else
factor = SCALE_FROM + (SLOPE * factor) // use TranslateScale instead?
endif
if factor < 0
factor = 1 / Abs(factor - 1)
else
factor = factor + 1
endif
scaled = Round(value * factor)
if scaled > 127
scaled = 127 // use Clip instead?
endif
// Log {Slider }, knob, {: }, value, { * }, factor, { = }, scaled
value = scaled
Call @Control
@End
// @param knob
// @param value
@Control
Log {CC }, CC[knob], {: }, value
SendMIDICC MIDI_CH, CC[knob], value
@End
// let's get crazy and reverse-engineer SysEx
@RequestState
request = [0x00,0x21,0x1A,0x02,0x03,0x37,0x00,0x00]
SendSysex request, 8
@End
@OnSysex
ReceiveSysex response
//length = SysexSize
//for i = 0 to length
// if prev[i] <> response[i]
// Log {[}, i, {] changed from }, prev[i], { to }, response[i]
// endif
//endfor
//CopyArray response, prev
header = [0x00,0x21,0x1A,0x02,0x03,0x00,0x37,0x00]
found = YES
for i = 0 to 7
if response[i] <> header[i]
found = NO
endif
endfor
if found
Call @ParseState
endif
@End
// @param response
@ParseState
// response is long, only intrested in few parts
offsets = [70, 65] // AMP ENV, FILTER ENV
for part = 0 to 1
// values are not aligned to bytes
if part = 0
// AMP ADSR
sizes = [8, 8, 8, 8] // in bits
norm = [2, 2, 1, 2] // to 0..127 for MIDI CC
else
// FILTER ADSR
sizes = [11, 8, 7, 10]
norm = [16, 2, 1, 4]
endif
bits = [] // here we go...
range = 7 // last bit not in use, maybe reserved for +/-
part_size = 5 // in bytes
for i = 0 to part_size - 1
byte = response[i + offsets[part]]
for j = 0 to range - 1
bits[i * range + j] = byte % 2
byte = Div byte, 2
endfor
endfor
offset = 0
for v = 0 to 3
value = 0
for n = 0 to sizes[v] - 1
value = value + bits[offset + n] * (Pow 2, n)
endfor
value = Div value, norm[v]
offset = offset + sizes[v]
if part = 0 and v = 0
SetKnobValue AE_A, value
elseif part = 0 and v = 1
SetKnobValue AE_D, value
elseif part = 0 and v = 2
SetKnobValue AE_S, value
elseif part = 0 and v = 3
SetKnobValue AE_R, value
elseif part = 1 and v = 0
SetKnobValue FE_A, value
elseif part = 1 and v = 1
SetKnobValue FE_D, value
elseif part = 1 and v = 2
SetKnobValue FE_S, value
elseif part = 1 and v = 3
SetKnobValue FE_R, value
endif
endfor
endfor
@End