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
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
|
#ifndef UDO_MIDI
#define UDO_MIDI ##
/*
MIDI control to named channel mapper
Currently only handling one channel
This file is part of the SONICS UDO collection by Richard Knight 2022
License: GPL-2.0-or-later
http://1bpm.net
*/
#include "array_tools.udo"
#include "table_tools.udo"
gSmidimap_channels[] init 128 ; channel names with index correlating to MIDI CC number
gimidimap_values = ftgen(0, 0, -384, -2, 0) ; min and max values for scaling, and default/saved value, with index correlating to MIDI CC number
/*
Handle incoming MIDI messages and write to channel with scaling if defined in gSmidimap_channels and gkmidimap_values
*/
instr midimap_handler
kstatus, kchan, kdata1, kdata2 midiin
if (kstatus == 176) then
if (strcmpk(gSmidimap_channels[kdata1], "") != 0) then
kvalue = scale(kdata2, table:k(kdata1+128, gimidimap_values), table:k(kdata1, gimidimap_values), 127, 0)
chnset kvalue, gSmidimap_channels[kdata1]
;outvalue strcmpk(gSmidimap_channels[kdata1], "_retval"), kvalue
endif
#ifndef MIDI_NOTE_HANDLER_INSTRUMENT
endif
#else
elseif (kstatus == 144) then
schedulek("_midi_note_handler", 0, 1, 1, kchan, kdata1, kdata2)
elseif (kstatus == 128) then
schedulek("_midi_note_handler", 0, 1, 0, kchan, kdata1, kdata2)
endif
#end
endin
alwayson("midimap_handler")
instr _midi_note_handler
ionoff = p4
ichannel = p5
inote = p6
ivelocity = p7
instrnum = nstrnum("$MIDI_NOTE_HANDLER_INSTRUMENT") + (ichannel / 100) + (inote / 100000)
if (ionoff == 0) then
turnoff2 instrnum, 4, 1
else
schedule(instrnum, 0, -1, ichannel, inote, ivelocity)
endif
turnoff
endin
/*
Register a MIDI CC to channel mapping, replacing any current mapping
p4 CC number
p5 channel name
p6 minimum value to scale to
p7 maximum value to scale to
p8 default/save value
*/
instr midimap_register
icc = p4
Schannel = p5
imin = p6
imax = p7
ivalue = p8
gSmidimap_channels[icc] = Schannel
tablew imin, icc, gimidimap_values
tablew imax, icc+128, gimidimap_values
tablew ivalue, icc+256, gimidimap_values
turnoff
endin
/*
Register a MIDI CC to a channel mapping, replacing any current mapping, based on user input to specify the CC number.
Threshold (p7) can be supplied to account for noisy devices that may emit unwanted CCs
p4 channel name
p5 minimum value to scale to
p6 maximum value to scale to
p7 default/save value
p8 how many points the CC must be moved to count as registered (defaults to 1 if not supplied)
p9 optional instrument name or number to call when learning completed; called with p3 = -1
*/
instr midimap_learn
Schannel = p4
imin = p5
imax = p6
ivalue = p7
ithreshold = p8
if (qnan(p9) == 1) then ; if onComplete provided
ionComplete = nstrnum(strget(p9))
elseif (p9 > 0) then
ionComplete = p9
else
ionComplete = -1
endif
if (ithreshold < 1) then
ithreshold = 1
endif
kvals[] init 128
prints sprintf("Learn mode: move a controller to assign to channel '%s'\n", Schannel)
kstatus, kchan, kcc, kvalue midiin
if (kstatus == 176) then
if (ithreshold > 1 && kvals[kcc] == 0) then
kvals[kcc] = kvalue
elseif (ithreshold == 1 || abs:k(kvals[kcc] - kvalue) >= ithreshold) then
gSmidimap_channels[kcc] = Schannel
tablew imin, kcc, gimidimap_values
tablew imax, kcc+128, gimidimap_values
tablew ivalue, kcc+256, gimidimap_values
printf "Controller %d assigned to channel '%s'\n", 1, kcc, Schannel
if (ionComplete != -1) then
schedulek(ionComplete, 0, -1)
endif
turnoff
endif
endif
endin
/*
Abort learning mode; turnoff midimap_learn
*/
instr midimap_learn_abort
turnoff2 "midimap_learn", 0, 0
turnoff
endin
/*
Set CCs and channels to default/saved value
*/
opcode _midimap_setvalues, 0, 0
icc = 0
while (icc < lenarray(gSmidimap_channels)) do
if (strcmp(gSmidimap_channels[icc], "") != 0) then
imin table icc, gimidimap_values
imax table icc+128, gimidimap_values
ivalue table icc+256, gimidimap_values
chnset ivalue, gSmidimap_channels[icc]
outic 1, icc, ivalue, imin, imax
endif
icc += 1
od
endop
/*
Gather CC values and set default/saved value
*/
opcode _midimap_getvalues, 0, 0
icc = 0
while (icc < lenarray(gSmidimap_channels)) do
if (strcmp(gSmidimap_channels[icc], "") != 0) then
imin table icc, gimidimap_values
imax table icc+128, gimidimap_values
ivalue ctrl7 1, icc, imin, imax
tablew ivalue, icc+256, gimidimap_values
endif
icc += 1
od
endop
/*
Save map state to file
p4 file path
p5 1 = save current CC values; 0 = do not save values
*/
instr midimap_savestate_fs
Sfile = p4
isavevalues = p5
if (isavevalues == 1) then
_midimap_getvalues()
endif
Serial = sprintf("%s\n%s", arr_serialise(gSmidimap_channels), tab_serialise(gimidimap_values))
fprints Sfile, Serial
turnoff
endin
/*
Load map state from file
p4 file path
*/
instr midimap_loadstate_fs
Sfile = p4
read:
Sline, ilinenum readfi Sfile
if (ilinenum == 1) then
gSmidimap_channels arr_unserialise Sline
igoto read
elseif (ilinenum == 2) then
tab_unserialise Sline, gimidimap_values
endif
_midimap_setvalues()
turnoff
endin
#ifdef USING_DB
/*
Save map state to database
p4 state name
p5 1 = save current CC values; 0 = do not save values
*/
instr midimap_savestate_db
Sname = p4
isavevalues = p5
if (isavevalues == 1) then
_midimap_getvalues()
endif
pgdb_array_save strcat(Sname, ".channels"), "midimap", gSmidimap_channels
pgdb_table_save strcat(Sname, ".values"), "midimap", gimidimap_values
turnoff
endin
/*
Load map state from database
p4 state name
*/
instr midimap_loadstate_db
Sname = p4
gSmidimap_channels pgdb_array_get strcat(Sname, ".channels"), "midimap"
i_ pgdb_table_get strcat(Sname, ".values"), "midimap", gimidimap_values
_midimap_setvalues()
turnoff
endin
#end ; USING_DB
#end
|