Go Back   Cockos Incorporated Forums > REAPER Forums > ReaScript, JSFX, REAPER Plug-in Extensions, Developer Forum

Reply
 
Thread Tools Display Modes
Old 05-08-2018, 11:12 AM   #1
rvrv
Human being with feelings
 
Join Date: Dec 2016
Posts: 62
Default 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
rvrv is offline   Reply With Quote
Old 05-08-2018, 11:16 AM   #2
mpl
Human being with feelings
 
mpl's Avatar
 
Join Date: Oct 2013
Location: Moscow, Russia
Posts: 3,960
Default

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.
mpl is offline   Reply With Quote
Old 05-08-2018, 01:33 PM   #3
geraintluff
Human being with feelings
 
geraintluff's Avatar
 
Join Date: Nov 2009
Location: mostly inside my own head
Posts: 346
Default

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.

Attached Images
File Type: png Screen Shot 2018-05-08 at 21.45.41.png (27.5 KB, 496 views)
__________________
JSFX set | Bandcamp/SoundCloud/Spotify

Last edited by geraintluff; 05-08-2018 at 02:12 PM.
geraintluff is offline   Reply With Quote
Old 05-12-2018, 12:48 PM   #4
rvrv
Human being with feelings
 
Join Date: Dec 2016
Posts: 62
Default

Quote:
Originally Posted by geraintluff View Post
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.
rvrv is offline   Reply With Quote
Old 05-13-2018, 03:43 AM   #5
geraintluff
Human being with feelings
 
geraintluff's Avatar
 
Join Date: Nov 2009
Location: mostly inside my own head
Posts: 346
Default

Quote:
Originally Posted by rvrv View Post
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.
__________________
JSFX set | Bandcamp/SoundCloud/Spotify
geraintluff is offline   Reply With Quote
Old 08-05-2019, 12:52 PM   #6
tksense
Human being with feelings
 
Join Date: Apr 2010
Posts: 34
Default

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.
tksense is offline   Reply With Quote
Old 08-08-2019, 04:24 AM   #7
geraintluff
Human being with feelings
 
geraintluff's Avatar
 
Join Date: Nov 2009
Location: mostly inside my own head
Posts: 346
Default

Quote:
Originally Posted by tksense View Post
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 View Post
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:

Code:
     m == 176 ?  (          //2
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
__________________
JSFX set | Bandcamp/SoundCloud/Spotify

Last edited by geraintluff; 08-08-2019 at 04:47 AM.
geraintluff is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -7. The time now is 01:01 AM.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.