Jump to content

Multiple MIDI CC Remap Plugin?


michaelrasbury

Recommended Posts

Hello,

 

Every time a Kontakt library is updated by the creator, I loose all my custom CC assignments and have to start over. I'm looking for something similar to the "Modifier" in MIDI channel effects but instead of being able to control one CC, I need to control multiple CC. I want the plugin or script to allow me to reassign my CC to control the defaults in Kontakt. The "Modifier" does this but only allows one CC per instance of the plugin.

Link to comment
Share on other sites

There are example scripts that come with Logic and are self-documenting. Explore them and you should see how to modify events, there are similar enough examples that it should be quite straightforward.

 

I tried using the "15-Control Plug-ins" as an example. I was able to change the script so that the CC2 controlled CC1, but then I couldn't figure out how to paste more iterations of the code so that I could simultaneously transform the other nine MIDI CC's I need to alter. I kept getting syntax errors. I need to take incoming CC2 and send it to CC1, and also CC1 and send it to CC21, CC3 and send it to CC20, CC15 to CC26, CC42 to CC16, CC43 to CC17, CC46 to CC28, CC50 to CC29 and CC33 to CC52. I need this to happen all at the same time!

Link to comment
Share on other sites

Welcome to the wonderful world of coding! You've just had your first taste of how a programmer feels tackling software problems!

 

You can paste in your script here if you want to get some help with it...

 

This is about my tenth or so taste LOL after trying to get some KSP working. I also understand HTML pretty well. I also use to program computers in basic a LONG time ago. I'm not totally green. I can understand when it is already there and copy and paste items to make it work. Here's what I've got so far. I'm trying to get it to display more than one CC.

 

var PluginParameters = [
{
	name:"CC21 Target", 
	type:"target"
}];

var PluginParameters = [
{
	name:"CC2 Target", 
	type:"target"
}];

function HandleMIDI(incomingEvent)
{
if ((incomingEvent instanceof ControlChange) && (incomingEvent.number == 21))
{
	var newEvent = new TargetEvent();	
	newEvent.target = "CC21 Target";	
	newEvent.value = incomingEvent.value / 127; 
	newEvent.send();							
} else
{	
	incomingEvent.send();
};
};

function HandleMIDI(incomingEvent)
{
if ((incomingEvent instanceof ControlChange) && (incomingEvent.number == 2))
{
	var newEvent = new TargetEvent();	
	newEvent.target = "CC2 Target";	
	newEvent.value = incomingEvent.value / 127; 
	newEvent.send();							
} else
{	
	incomingEvent.send();
};
};

Link to comment
Share on other sites

Ok. :)

 

You can't just duplicate the HandleMIDI callback, there is only one - when a MIDI event comes in, it's passed to that function, so you need to do all the work in that one single function.

 

In short, you want to do something like:

 

if event = CC21, do this stuff

else if event = CC22 do this stuff

else if event = CC 76 do this stuff

 

Along these lines:

 

function HandleMIDI(incomingEvent)
{
  if ((incomingEvent instanceof ControlChange) && (incomingEvent.number == 21))
  {
     var newEvent = new TargetEvent();   
     newEvent.target = "CC21 Target";   
     newEvent.value = incomingEvent.value / 127; 
     newEvent.send();                     
  } else if ((incomingEvent instanceof ControlChange) && (incomingEvent.number == 22))
  {
     var newEvent = new TargetEvent();   
     newEvent.target = "CC22 Target";   
     newEvent.value = incomingEvent.value / 127; 
     newEvent.send();                     
  } else
  {   
     incomingEvent.send();
  };
};

 

That should get you on the right track...

Link to comment
Share on other sites

Ok. :)

 

You can't just duplicate the HandleMIDI callback, there is only one - when a MIDI event comes in, it's passed to that function, so you need to do all the work in that one single function.

 

In short, you want to do something like:

 

if event = CC21, do this stuff

else if event = CC22 do this stuff

else if event = CC 76 do this stuff

 

Along these lines:

 

function HandleMIDI(incomingEvent)
{
  if ((incomingEvent instanceof ControlChange) && (incomingEvent.number == 21))
  {
     var newEvent = new TargetEvent();   
     newEvent.target = "CC21 Target";   
     newEvent.value = incomingEvent.value / 127; 
     newEvent.send();                     
  } else if ((incomingEvent instanceof ControlChange) && (incomingEvent.number == 22))
  {
     var newEvent = new TargetEvent();   
     newEvent.target = "CC22 Target";   
     newEvent.value = incomingEvent.value / 127; 
     newEvent.send();                     
  } else
  {   
     incomingEvent.send();
  };
};

 

That should get you on the right track...

That's of great help but something I don't understand... I need the script to take incoming CC 21 Data and instead send it on as CC1. Incoming CC 20 data needs to output as CC3 Data. In the attached code, I see it looking for CC21 but then I don't see it changing it into CC1. Am I not understanding something?

Link to comment
Share on other sites

I wasn't planning on writing your code for you (just as you're not offering to write mine for me! ;) ), just helping you along the way with some basics.

 

At the moment, you seem to be setting the new event as a string ("CC21 Target") which I'm not sure is correct, but I'm problem solving my own Logic code related stuff in other languages and can't dive into this right now of refresh my memory of Logic's JS controller MIDI functions - there should be example code there though that will give you clues.

Link to comment
Share on other sites

Here, try this modified and simplified version of Des99's code:

 

function HandleMIDI(incomingEvent)
{
  if ((incomingEvent instanceof ControlChange) && (incomingEvent.number == 21))
  {
	incomingEvent.number = 1;
       	incomingEvent.send();
} else if ((incomingEvent instanceof ControlChange) && (incomingEvent.number == 20))
  {
	incomingEvent.number = 3;
       	incomingEvent.send();
  } else
  {   
     incomingEvent.send();
  };
};

Link to comment
Share on other sites

Here, try this modified and simplified version of Des99's code:

 

function HandleMIDI(incomingEvent)
{
  if ((incomingEvent instanceof ControlChange) && (incomingEvent.number == 21))
  {
	incomingEvent.number = 1;
       	incomingEvent.send();
} else if ((incomingEvent instanceof ControlChange) && (incomingEvent.number == 20))
  {
	incomingEvent.number = 3;
       	incomingEvent.send();
  } else
  {   
     incomingEvent.send();
  };
};

 

That is exactly what I needed! I didn't quite understand the syntax. Here's what I did with your VERY HELPFUL example and it works!

function HandleMIDI(incomingEvent)
{
  if ((incomingEvent instanceof ControlChange) && (incomingEvent.number == 2))
  {
     incomingEvent.number = 1;
          incomingEvent.send();
} else if ((incomingEvent instanceof ControlChange) && (incomingEvent.number == 1))
  {
     incomingEvent.number = 21;
          incomingEvent.send();
} else if ((incomingEvent instanceof ControlChange) && (incomingEvent.number == 3))
  {
     incomingEvent.number = 20;
          incomingEvent.send();
} else if ((incomingEvent instanceof ControlChange) && (incomingEvent.number == 15))
  {
     incomingEvent.number = 26;
          incomingEvent.send();           
} else if ((incomingEvent instanceof ControlChange) && (incomingEvent.number == 42))
  {
     incomingEvent.number = 16;
          incomingEvent.send();
} else if ((incomingEvent instanceof ControlChange) && (incomingEvent.number == 43))
  {
     incomingEvent.number = 17;
          incomingEvent.send();
} else if ((incomingEvent instanceof ControlChange) && (incomingEvent.number == 46))
  {
     incomingEvent.number = 28;
          incomingEvent.send();
} else if ((incomingEvent instanceof ControlChange) && (incomingEvent.number == 50))
  {
     incomingEvent.number = 29;
          incomingEvent.send();
} else if ((incomingEvent instanceof ControlChange) && (incomingEvent.number == 52))
  {
     incomingEvent.number = 33;
          incomingEvent.send();
  } else
  {   
     incomingEvent.send();
  };
};

Link to comment
Share on other sites

Yep, there wasn't much point in introducing new coding concepts when the OP was struggling with the basics, which is why I just illustrated the simplest and most understandable way of doing it. If it works, that's plenty good enough...

Yes it's certainly more fun to get started that way and learn the "proper way" when and if needed than it is to get started with an academic course on proper algorithm. I'm far from being good at this stuff, but I can kind of haphazardly hack my way through it when needed.

Link to comment
Share on other sites

The above works fine.. It could be done different ways... There is not necessarily any right or wrong way to do it. I would prefer to setup this kind of situation with an array map, because its easy to maintain and tweak.

 

var ccMap = [];
for(let i=0;i<128;i++) {
   ccMap[i] = i;
}

ccMap[2] =   1;
ccMap[1] =  21;
ccMap[3] =  20;
ccMap[15] = 26;
ccMap[42] = 16;
ccMap[43] = 17;
ccMap[46] = 28;
ccMap[50] = 29;
ccMap[52] = 33;

function HandleMIDI(event) {
   if(event instanceof ControlChange) {
       event.number = ccMap[event.number];
   }
   
   event.send();
};
Link to comment
Share on other sites

one more thing though, I notice the original script provided by michaelrasbury is actually using TargetEvent, which is slightly different/more then merely remapping the CC number.

 

That provides a way to tell Scripter to bypass the kontakt CC automation engine entirely and directly convert CC events directly into parameter automation driven from Scripter. However, the way its all designed this will be a similar problem insomuch that Scripter never knows ahead of time which Plugin parameter is which, which can change depending on the order of plugins in the channel strip, etc.. So Scripter has this method where you have to manually associate the Scripter GUI control to a specific plugin parameter, etc. Well that is the very thing the OP is trying to avoid I think. But anyway just wanted to point out, these final solutions went to CC number mapping and if that is good enough then great, but if we want to dig deeper into using TargetEvent, that would be a deeper topic for further exploration.

Link to comment
Share on other sites

You could also get tricky and make the script with a gui for assigning which CC's to remap... But in a lot of ways that involves a lot of extra scripter code and its not that hard to just tweak the above script directly.

 

Thanks to all for these lessons! I appreciate the "cleaner" updated code as it is easier for me to edit and make changes to visually. I can generally understand what the code does but still struggle generating it. To be honest, I probably would have paid for a script like you describe above. I started this post by using the script in "Tutorial Scripts" called "15-Control Plugins." It does what you describe above but for only the Modulation Wheel. I tried to duplicate the code to make more CC appear in the popup menus. This is why TargetEvent was left over. I also need to reverse the CC data for a couple of those CC's- meaning instead of my associated controller sending 0-128, I need it to send 128-0 which in turn makes my physical controller send data in the opposite direction. For now, I'm doing that in Kontakt's controller section but of course when I get instrument updates all those custom assignments vanish with the old patch. This is why I am so keen to figure a script!

Link to comment
Share on other sites

I also need to reverse the CC data for a couple of those CC's- meaning instead of my associated controller sending 0-128, I need it to send 128-0 which in turn makes my physical controller send data in the opposite direction.

 

So if you need to reverse the controller values, or any other stuff, then you may be better off going back to something like what Des or David suggested earlier...using if/then statements, so you can handle each controller# in a totally different way for each one. You reverse the value like this:

 

event.value = 127-event.value;
Link to comment
Share on other sites

so maybe like this, I put one example of reversing the order on CC15:

 

function HandleMIDI(event) {

  if (event instanceof ControlChange) {
  
      if (event.number == 2) {
          event.number = 1;
      }
      
      else if (event.number == 1) {
          event.number = 21;
      }
      
      else if (event.number == 3) {
          event.number = 20;
      }
      
      else if (event.number == 15) {
          event.number = 26;
          event.value = 127-event.value; // reverse order
      }
      
      else if (event.number == 42) {
          event.number = 16;
      }
      
      else if (event.number == 43) {
          event.number = 17;
      }
      
      else if (event.number == 46) {
          event.number = 28;
      }
      
      else if (event.number == 50) {
          event.number = 29;
      }
      
      else if (event.number == 52) {
          event.number = 33;
      }
  }
  
  event.send();
}   

 

I cleaned up a few things to make it easier to read, and don't need to check for ControlChange instanceof so many times over and over.. But you get the point hopefully, do whatever you want with each CC#. In theory you could scale them each differently this way too, etc..

 

The earlier script I gave uses a so called data-driven approach, which generally is preferable to using complicated nested if/then statements. Its easier to read, easier to troubleshoot and often times is faster execution too, but for small scripts like this, it doesn't really matter, use whatever method that is easiest to understand for yourself.

 

But in case you were wondering, how might we use a data driven approach while handling both the job of remapping CC# and also sometimes occasionally reversing the value order?

 

Well it might be something like this:

 

var ccMap = [];
for(let i=0;i<128;i++) {
   ccMap[i] = {num: i, revorder: false};
}

ccMap[2].num = 1;
ccMap[1].num = 21;
ccMap[3].num = 20;

ccMap[15].num = 26;
ccMap[15].revorder = true;

ccMap[42].num = 16;
ccMap[43].num = 17;
ccMap[46].num = 28;
ccMap[50].num = 29;
ccMap[52].num = 33;


function HandleMIDI(event) {
  if (event instanceof ControlChange) {
      if (ccMap[event.number].revorder == true) {
          event.value = 127 - event.value;
      }
      event.number = ccMap[event.number].num;
  }
  
  event.send();
}   

 

As you can see the code is still compact and easy to change CC mappings later...but..its a bit more complicated to understand and possibly made a few heads explode already..but anyway..there you go...two ways to to do it.

Link to comment
Share on other sites

David also mentioned the switch statement as another alternative. Here is an example of that. Honestly there is not much inherent advantage to switch statements vs If/then statements...its really just a matter of personal style choice, what is easier for you to look at and understand.

 

one advantage of IF statements is that you can include multiple conditions in one IF, and you can easily nest IF statements... the switch statement is best reserved for very simple situations and some programming languages don't even include it. I tend to not use it much myself. There are a couple rare examples I won't get into now about when it's favorable. its nit picking though.

 

How you format your code can make all the difference also in how easy it is to understand when you look at it or need to update it later.

 

Anyway, here is a switch example of the above:

 

function HandleMIDI(event) {
  if (event instanceof ControlChange) {
  
      switch(event.number) {
      case 2:
          event.number = 1;
          break;

      case 1:
          event.number = 21;
          break;
          
      case 3:
          event.number = 20;
          break;
          
      case 15:
          event.number = 26;
          event.value = 127-event.value; // reverse order
          break;
          
      case 42:
          event.number = 16;
          break;
          
      case 43:
          event.number = 17;
          break;
          
      case 46:
          event.number = 28;
          break;
          
      case 50:
          event.number = 29;
          break;
          
       case 52:
          event.number = 33;
          break;          
      }
  }
  
  event.send();
}   
Link to comment
Share on other sites

I also need to reverse the CC data for a couple of those CC's- meaning instead of my associated controller sending 0-128, I need it to send 128-0 which in turn makes my physical controller send data in the opposite direction.

 

So if you need to reverse the controller values, or any other stuff, then you may be better off going back to something like what Des or David suggested earlier...using if/then statements, so you can handle each controller# in a totally different way for each one. You reverse the value like this:

 

event.value = 127-event.value;

 

Ok- thanks for all your help!

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...