-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathtenfour-midi_cc_curve
145 lines (120 loc) · 3.37 KB
/
tenfour-midi_cc_curve
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
desc: MIDI CC value curve (tenfour)
//tags: MIDI processing
slider1:2<0,127,1>Midi CC
slider2:0<0,127,1>Input range min
slider3:127<0,127,1>Input range max
slider4:0<0,127,1>Output range min
slider5:127<0,127,1>Output range max
slider6:0<-1,1,0.01>Curve
/*
1 = mod wheel
2 = breath control
7 = volume
10 = pan
11 = expression
...
*/
in_pin:none
out_pin:none
@init
gLastInput = 0;
gLastOutput = 0;
@slider
gTargetCC = slider1;
gInputRangeMin = slider2;
gInputRangeMax = slider3;
gOutputRangeMin = slider4;
gOutputRangeMax = slider5;
gCurve = slider6;
@block
function clamp(x, l, h)
(
min(h, max(l, x))
);
function lerp(v0, v1, t)
(
(1 - t) * v0 + t * v1;
);
function reverseLerp(a, b, value)
(
(a == b) ? (
0;
) : (
(value - a) / (b - a);
);
);
// valid for 0<k<1 and 0<x<1
function modCurve_x01_k01(x, k)
(
ret = 1 - pow(x, k);
pow(ret, 1.0 / k);
);
// extends range to support -1<x<0 and -1<k<0
// outputs -1 to 1
function modCurve_xN11_kN11(x, k)
(
CornerMargin = 0.6;
k *= CornerMargin;
k = clamp(k, -CornerMargin, CornerMargin);
(k >= 0) ?
(
(x > 0) ?
(
1.0 - modCurve_x01_k01(x, 1.0 - k);
) : (
modCurve_x01_k01(-x, 1.0 - k) - 1;
)
) : (x > 0) ?
(
modCurve_x01_k01(1.0 - x, 1.0 + k);
) :
(
-modCurve_x01_k01(x + 1, 1.0 + k);
)
);
/*
https://www.midi.org/specifications-old/item/table-2-expanded-messages-list-status-bytes
https://www.midi.org/specifications-old/item/table-3-control-change-messages-data-bytes-2
status hi:
map channel gets filtered?
8 = note off (low = channel) yes no
9 = note on(low = channel) yes no
10 = aftertouch(low = channel) yes no
11 = CC(low = channel) yes yes
msg2 = cc#
0= bank select msb
32=bank select lsb
...
msg3 = value
12 = program change(low = channel) yes yes
13 = channel pressure/aftertouch (low = channel) yes yes
14 = pitch wheel (low = channel) yes yes
15 = system messages where low nybble = no yes
*/
while (
midirecv(ts, status, msg23) ? (
statusHi = (status/16)|0; // type of msg
statusLo = status-(statusHi*16)|0; // channel usually
msg3 = (msg23/256)|0; // cc value
msg2 = msg23-(msg3*256)|0; // cc #
((statusHi == 11) && (msg2 == gTargetCC)) ? ( // CC for all channels
// modify value byte.
gLastInput = msg3;
msg3 = reverseLerp(gInputRangeMin, gInputRangeMax, msg3); // map to 0-1 based on inp range
msg3 = modCurve_xN11_kN11(msg3, gCurve); // apply curve
msg3 = lerp(gOutputRangeMin, gOutputRangeMax, msg3); // and map back to output range
gLastOutput = msg3;
);
status = (statusHi*16+statusLo)|0;// re-construct status from nybbles
msg23 = (msg3*256+msg2)|0; // re-construct msg23 value
midisend(ts,status,msg23);
1; // for the while loop to function
);
);
@gfx 512 64 // request horizontal/vertical heights
gfx_x=gfx_y=0;
gfx_r=gfx_g=gfx_b=.5;
gfx_rectto(gLastInput * 4,32);
gfx_x=0;gfx_y=32;
gfx_r=gfx_g=gfx_b=.5;
gfx_rectto(gLastOutput * 4,64);