|
|
|
05-08-2018, 11:12 AM
|
#1
|
Human being with feelings
Join Date: Dec 2016
Posts: 62
|
Simplest script to filter specific midi data (ON SPECIFIC CHANNEL)
Hi guys i never programmed anything in any language, but i would like to start a little because the Reaper programability would be of much help to me. I would start with one issue iam currently dealing with and if someone could show me the simplest script that i can copy&paste into script editor to do the trick... i dont need any sliders, buttons, nothing just the bare couple lines of script to do the job.
I have 2 sustain pedals (CC64) that can each transmit on unique channel. So my synth track recieves this data when i press the sustain pedals:
B0 40 00 [CC64 Hold Pedal] chan 1 val 0
B0 40 7F [CC64 Hold Pedal] chan 1 val 127
// Sustain Pedal #1 - sending data on Channel 1 (B0)
B1 40 00 [CC64 Hold Pedal] chan 2 val 0
B1 40 7F [CC64 Hold Pedal] chan 2 val 127
// Sustain Pedal #2 - sending data on Channel 2 (B1)
what is the simplest script to filter pedal #2 and only pass data from pedal #1...
iam asking for command to filter specific midi data ON SPECIFIC CHANNEL in this case i want to discard 2 specific midi messages that are coming to the track and leave everything else intact
B1 40 00
B1 40 7F
i know there is plenty of exisiting filter plugins but id like to learn the script so i can make my own combinations in future... if you can keep the script as simple as possible so i can reverse learn from it
Thanks guys
|
|
|
05-08-2018, 11:16 AM
|
#2
|
Human being with feelings
Join Date: Oct 2013
Location: Moscow, Russia
Posts: 3,960
|
Remove MIDI CC64 from channel 2 (it is lua script based on Julian Sader examples)
Code:
channel = 2
-------------------------------------------------------------------------
for key in pairs(reaper) do _G[key]=reaper[key] end
-------------------------------------------------------------------------
function FilterMIDIData(take)
local tableEvents = {}
local t = 0 -- Table key
local gotAllOK, MIDIstring = MIDI_GetAllEvts(take, "")
local MIDIlen = MIDIstring:len()
local stringPos = 1 -- Position inside MIDIstring while parsing
local offset, flags, msg
while stringPos < MIDIlen-12 do -- -12 to exclude final All-Notes-Off message
offset, flags, msg, stringPos = string.unpack("i4Bs4", MIDIstring, stringPos)
if msg:len() > 1 then
if msg:byte(1)>>4 == 0xB and msg:byte(2) == 64 and msg:byte(1)&0xF == channel -1 then
msg = "" -- (MPL: leave as an dummy event for to not brake offsets )
end
end
t = t + 1
tableEvents[t] = string.pack("i4Bs4", offset, flags, msg)
end
MIDI_SetAllEvts(take, table.concat(tableEvents) .. MIDIstring:sub(-12))
MIDI_Sort(take)
end
-------------------------------------------------------------------------
function main()
local midieditor = MIDIEditor_GetActive()
if not midieditor then return end
local take = MIDIEditor_GetTake( midieditor )
if not take then return end
Undo_BeginBlock()
FilterMIDIData(take)
Undo_EndBlock(scr_title, 1)
end
-------------------------------------------------------------------------
main()
Last edited by mpl; 05-08-2018 at 12:11 PM.
|
|
|
05-08-2018, 01:33 PM
|
#3
|
Human being with feelings
Join Date: Nov 2009
Location: mostly inside my own head
Posts: 346
|
This can also be implemented as an effect (JSFX):
Code:
desc:MIDI pedal filter
in_pin:none
out_pin:none
slider1:channel_from_one=1<1,16,1>Filtered Channel
@block
// Humans count channels from 1-16, but computers count from 0-15
filtered_channel = channel_from_one - 1;
while (midirecv(offset, msg1, msg2, msg3)) (
// msg1 is two values packed together - this untangles them
channel = msg1&15;
type = (msg1&240)>>4;
(channel != channel_from_zero) || (type != 11) || (msg2 != 64) ? (
// It's not the right channel/type/controller, so let it through
midisend(offset, msg1, msg2, msg3);
)
);
This will display with JSFX's built-in slider, which lets you select which channel gets removed.
Last edited by geraintluff; 05-08-2018 at 02:12 PM.
|
|
|
05-12-2018, 12:48 PM
|
#4
|
Human being with feelings
Join Date: Dec 2016
Posts: 62
|
Quote:
Originally Posted by geraintluff
This can also be implemented as an effect (JSFX)
|
mpl> thanks for the super quick response but the script is sooo complicated and probably a little overkill for such a simple operation and my skill level... i dont want to sound ungratefull but i was hoping to learn a little from the examples you guys throw at me but this is like giving me a loaded bionic gun to kill a mosquito...
geraintluff> this is something i could wrap my untrained head around and actually use to learn very simple "midi filter" operation in JSFX. Could i be so insolent to ask you to make an edit to remove the slider code so i can see and understand only the actual bare filter code... i would prefer to understand the filter code and chnage the channel manually via code editor in Reaper Also at this moment it seems like the code actually can filter channel 1 but moving the slider to 2,3,4 doesnt do anything the code seems to be filtering CHANNEL 1 irrelevant of the slider maybe a bug ?
EDIT:
i managed to do it myself - the code is simple enough for me to understand (more or less ) i created 2 instances of the effect:
MIDI pedal filter CH01
MIDI pedal filter CH02
its not as elegant as using slider but it works and most importantly i can see how it works
THANKS mpl and geraintluff - this community ROCKS - Reaper Forever
Code:
desc:MIDI pedal filter CH01
in_pin:none
out_pin:none
@block
while (midirecv(offset, msg1, msg2, msg3)) (
// msg1 is two values packed together - this untangles them
channel = msg1&15;
type = (msg1&240)>>4;
(channel != 0) || (type != 11) || (msg2 != 64) ? (
// It's not the right channel/type/controller, so let it through
midisend(offset, msg1, msg2, msg3);
)
);
Last edited by rvrv; 05-12-2018 at 01:55 PM.
|
|
|
05-13-2018, 03:43 AM
|
#5
|
Human being with feelings
Join Date: Nov 2009
Location: mostly inside my own head
Posts: 346
|
Quote:
Originally Posted by rvrv
Also at this moment it seems like the code actually can filter channel 1 but moving the slider to 2,3,4 doesnt do anything the code seems to be filtering CHANNEL 1 irrelevant of the slider
|
Oops - yeah, I used the wrong variable name. "channel_from_zero" should have been "filtered_channel".
I'm glad you got it working, though! Congratulations on your first effect.
|
|
|
08-05-2019, 12:52 PM
|
#6
|
Human being with feelings
Join Date: Apr 2010
Posts: 34
|
Hi Geraintluff,
Can I modify the code so that ONLY CCs from the selected channel gets passed where CCs from all other channels are blocked?
I'm guessing it could use a new definition of Filtered Channel with a math function that results in "all values filtered except slider1" (first example below)?
It would be even better if I can single out cc1, cc64 etc. to also get passed on all incoming MIDI channels.
If not, should I use other methods such as if-then functions:
if MIDI channel is slider1 then pass to send, block others?
For example, I had been trying to cut down and modify the code from "MIDI Keyboard Splitter, with Xackley Zero Tricks", but so far I still have trouble understanding what details I should modify.
I want to figure out how to modify this code so it will only process CC, instead of "note & CC" (see below in 2nd example)
Thanks for being so helpful around here over the years!
Pat
Code:
desc:PT MIDI Channel filter
in_pin:none
out_pin:none
slider1:channel_from_one=1<1,16,1>Passed Channel
@init
//all-except-slider1
Filtered_Channel=slider1 !== 2;
@block
// Humans count channels from 1-16, but computers count from 0-15
filtered_channel = channel_from_one - 1;
while (midirecv(offset, msg1, msg2, msg3)) (
// msg1 is two values packed together - this untangles them
channel = msg1&15;
type = (msg1&240)>>4;
(channel != Filtered_Channel) || (type != 11) ? (
// It's not the right channel/type/controller, so let it through
midisend(offset, msg1, msg2, msg3);
)
);
Code:
desc:All Channel Block, applied only to CC
slider1:0<0,15,1{0 All,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}>MIDI Channel In
slider2:1<0,15,1{Block Input Channel - Just Notes to New Channels,Pass Input Channel - Just Notes to New Channels,Pass Input - Send Notes & CC to New Channels,Block Input - Send Notes & CC to New Channels}>Original
slider3:2<0,15,1{0 Set Range,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}>Ouput MIDI Channel A
slider4:1 <1,127,1>Minimum Note A
slider5:127<1,127,1>Maximum Note A
slider6:0<-36,36,1>Transpose A
//slider2:0<-1000,+1000,.00000001>test
////////////////////////////////////////////////////////////////////
@slider
chanA= slider3 -1;
slidermove = 1 ;
PassC = slider2==2 || slider2==3;
PassN = slider2==1 || slider2==2;
/////////////////////////////////////////////////////////////////////
@init
ext_noinit = 1 ;
chanA= slider3 -1;
////////////////////////////////////////////////////////////////////
@sample
while ( midirecv(ts,msg1,msg23) ? (
m = msg1 & 240;
channel = msg1 & 15;
vel = (msg23/256)|0;
passN ? midisend(ts,msg1,msg23);
slidermove ? (
slidermove = 0;
// all sounds off
midisend(ts,176 + 0,120);
midisend(ts,176 + 1,120);
midisend(ts,176 + 2,120);
midisend(ts,176 + 3,120);
midisend(ts,176 + 4,120);
midisend(ts,176 + 5,120);
midisend(ts,176 + 6,120);
midisend(ts,176 + 7,120);
midisend(ts,176 + 8,120);
midisend(ts,176 + 9,120);
midisend(ts,176 + 10,120);
midisend(ts,176 + 11,120);
midisend(ts,176 + 12,120);
midisend(ts,176 + 13,120);
midisend(ts,176 + 14,120);
midisend(ts,176 + 15,120);
);
OKchannel = channel==(slider1-1) || slider1==0;
slider3 && OKChannel ? ( //1
m == 144 || m == 128 ? ( //2
note >= slider4 && note <= slider5 ? ( //3
noteA = note + slider6;
noteA|=0;
noteA > 1 && noteA < 128 ? ( //4
midisend(ts,m + chanA,noteA|(vel*256));
); //4
); //3
):( //or on 2
passC ? midisend(ts,m + chanA,msg23);
x=1;
); //2
); //1
///////////////////////////////////////////////////////
slider3 == 0 && m == 144 ? (
slider4 = min(slider4,note);
slider5 = max(slider5,note);
);
);
);
Last edited by tksense; 08-05-2019 at 01:17 PM.
|
|
|
08-08-2019, 04:24 AM
|
#7
|
Human being with feelings
Join Date: Nov 2009
Location: mostly inside my own head
Posts: 346
|
Quote:
Originally Posted by tksense
Can I modify the code so that ONLY CCs from the selected channel gets passed where CCs from all other channels are blocked?
|
Sure. Previously, we passed through events if either: (1) the channel doesn't match our filtered channel, or (2) it was not a CC event. This was expressed like this:
Code:
(channel != filtered_channel) || (type != 11)
(So, two uses of "!=" for "are these values different", joined by a "||" for "is either side true".)
Your new conditions are: (1) the channel does match our filtered channel, or (2) it was not a CC event. To do this, we can simply change the first "!=" to "==":
Code:
(channel == filtered_channel) || (type != 11) ? (
midisend(offset, msg1, msg2, msg3);
)
The brackets are not strictly necessary (there are rules kind of like PEDMAS/BODMAS, but for code), but they make the groupings really explicit.
Quote:
Originally Posted by tksense
It would be even better if I can single out cc1, cc64 etc. to also get passed on all incoming MIDI channels.
|
For CC events, "msg2" is the CC index (e.g. 64 for pedals), and "msg3" is the value.
So, if you want to pass along all events for CC-64, regardless of channel, then you can add a third condition on, using "||":
Code:
(channel == filtered_channel) || (type != 11) || (msg2 == 64) ? (
midisend(offset, msg1, msg2, msg3);
)
As you start to need more complicated conditions, it might be easier to read if you split off parts of that condition into separate variables, then use those variables later:
Code:
channel_matches = (channel == filtered_channel);
is_not_cc = (type != 11);
is_cc64 = (msg2 == 64);
channel_matches || is_not_cc || is_cc64 ? (
midisend(offset, msg1, msg2, msg3);
)
Modifying "MIDI Keyboard Splitter"
The code as you've posted it has a couple of bugs in it. (E.g. I think the "@sample" section would be better as "@block", and it also uses the variable "note" but never gives it a value.)
Modifying our code as above is probably quicker, and will lead to shorter, cleaner code. As well as the bugs, "MIDI Keyboard Splitter" has a bunch of note-specific stuff (like ranges, or transposition), which aren't useful for our specific case.
So, I think there are probably better examples to look at, but let's have a go anyway:
The first thing to notice is that in this code, "m" is kind of like "type" in our code. The difference is that it doesn't have the ">>4". If you're curious you can dig into what this actually does, but the end result is: the value of "m" is 16x what "type" would be.
So, if we wanted to filter for note-on (type=8) and note-off (type=9) events, in our code we'd write:
Code:
(type == 8) || (type == 9)
The values of "m" are 16x larger, so their code checks for 128 and 144 instead:
Code:
m == 144 || m == 128 ? ( //2
So, this is the bit that's making it apply to notes, instead of CCs! If we want to modify their code to apply to CCs, we want to check for type 11 - which in their code will be 11*16 = 176:
The block inside that is the one that uses the "note" variable, so I don't think it's quite right. Also, if you make the above change so it starts looking at CC events, then this block of code (which used to deal with note-ranges and transposition) will start doing filtering/changes to CC-index, which probably isn't helpful. In general, I find building up a shorter example is easier than cutting down a longer one.
Anyway, I hope that helps a little bit.
Geraint
Last edited by geraintluff; 08-08-2019 at 04:47 AM.
|
|
|
Thread Tools |
|
Display Modes |
Linear Mode
|
Posting Rules
|
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts
HTML code is Off
|
|
|
All times are GMT -7. The time now is 01:01 AM.
|