diff options
Diffstat (limited to 'site/udo/midimap.udo')
-rwxr-xr-x | site/udo/midimap.udo | 247 |
1 files changed, 247 insertions, 0 deletions
diff --git a/site/udo/midimap.udo b/site/udo/midimap.udo new file mode 100755 index 0000000..63be6cf --- /dev/null +++ b/site/udo/midimap.udo @@ -0,0 +1,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
|