Skip to content

Commit ce70663

Browse files
authored
Release MIDI hold notes range v1.0.0 (#433)
1 parent 98e2fe4 commit ce70663

1 file changed

Lines changed: 101 additions & 0 deletions

File tree

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
desc: MIDI hold notes range
2+
author: tomaszpio
3+
version: 1.0.0
4+
about:
5+
This JSFX plugin implements a "note hold" function, active only within
6+
a specified MIDI note range. It is intended for live playing or MIDI
7+
processing where one key at a time is held, regardless of Note Off messages.
8+
9+
HOW IT WORKS:
10+
- The plugin reacts only to Note On / Note Off messages.
11+
- It works on:
12+
• a selected MIDI channel (1–16), or
13+
• all channels if Channel = 0 (omni mode).
14+
- It is active only for notes within the [Low note .. High note] range.
15+
- For any Note On with velocity > 0 in that range:
16+
• The previously held note is turned off (Note Off is sent).
17+
• The new note is turned on (Note On is sent) and stored as the
18+
currently held note.
19+
- Note Off messages and Note On with velocity = 0 in that range are ignored,
20+
so they do NOT release the held note.
21+
- All other MIDI messages (outside the note range, or on other channels,
22+
or non-note messages) pass through unchanged.
23+
24+
SLIDERS:
25+
- Channel (0=omni, 1–16 = specific MIDI channel)
26+
• 0 => process all channels
27+
• 1–16 => only process that MIDI channel
28+
- Low note – lower bound of the active note range (0–127)
29+
- High note – upper bound of the active note range (0–127)
30+
If Low note > High note, the plugin automatically swaps them.
31+
32+
TYPICAL USE CASES:
33+
- Use a low octave on your keyboard as a "hold" controller area,
34+
while higher notes play normally.
35+
- Restrict hold behavior to a specific region (e.g., bass notes or keyswitches)
36+
without affecting the rest of the keyboard.
37+
38+
slider1:0<0,16,1>Channel (0=omni)
39+
slider2:36<0,127,1>Low note
40+
slider3:84<0,127,1>High note
41+
42+
in_pin:none
43+
out_pin:none
44+
45+
@init
46+
held_note = -1;
47+
held_chan = -1;
48+
49+
@slider
50+
// Channel mapping:
51+
// slider1 = 0 -> omni (chan = -1, so all channels match)
52+
// slider1 = 1–16 -> MIDI channels 0–15
53+
s1 = slider1|0;
54+
chan = s1 ? (s1 - 1) : -1;
55+
56+
low = slider2|0;
57+
high = slider3|0;
58+
59+
// If user sets Low > High, swap them
60+
low > high ? (
61+
tmp = low;
62+
low = high;
63+
high = tmp;
64+
);
65+
66+
@block
67+
while (
68+
midirecv(ts, msg, msg23) ? (
69+
status = msg & $xf0; // 0x80 = Note Off, 0x90 = Note On
70+
ch = msg & $x0f; // MIDI channel 0–15
71+
note = msg23 & $xff; // note number
72+
vel = (msg23/256) & $xff; // velocity
73+
74+
is_note = (status == $x80) || (status == $x90);
75+
in_chan = (chan < 0) || (ch == chan);
76+
in_range = (note >= low) && (note <= high);
77+
78+
is_note && in_chan && in_range ? (
79+
// Hold logic only for Note On with vel > 0
80+
status == $x90 && vel ? (
81+
// Turn off previously held note (if any)
82+
held_note >= 0 ? (
83+
midisend(ts, $x80 + held_chan, held_note);
84+
);
85+
86+
// Store new held note
87+
held_note = note;
88+
held_chan = ch;
89+
90+
// Send Note On for new note
91+
midisend(ts, $x90 + ch, note | (vel << 8));
92+
);
93+
// Note Off and Note On with vel=0 in the active range are ignored
94+
) : (
95+
// Pass everything else unchanged
96+
midisend(ts, msg, msg23);
97+
);
98+
99+
1;
100+
);
101+
);

0 commit comments

Comments
 (0)