Jump to content

SCRIPT: CloneEvent


Recommended Posts

CloneEvent Script

 

UPDATE: This script is superseded by a much better script found here: viewtopic.php?f=45&t=143326

 

The old one is still here for anyone that wants it.

 

This script will clone CC, PitchBend and ChannelPressure events to other channels in a highly configurable way. It can also handle channelizing of Note events by articulationID, which is on by default in Relative mode. See below for more info.

 

In this fashion its possible to have one single source track, whereby articulationID can drive channelizing of the notes to other channels where articulations are found; and this script will make sure to forward CC, PitchBend and Aftertouch to those channels. Channelizing can also be disabled if you prefer to channelize from the ArticulationSet itself, but there is still as of this writing half-working functionality in that area of LPX 10.4.6

 

In order to install the script, unzip the following file and save it to ~/Music/Audio Music Apps/Plug-In Settings/Scripter/. The script will then appear in Scripter's preset list under the name of CloneEvents.

 

 

[*]Channelize Note events by articulationID if desired, on by default in Relative mode.

[*]Selectively clone CC's, Pitchbend and aftertouch

[*]Can specify which CC#'s are to be cloned to which channels.

[*]Is able to handle more than one source channel at a time, so that it can be used in multi-timbral mode, such as to VEP AU3 plugin.

[*]When using an AU3 plugin, can clone these events across ports

 

Usage

 

At this time, no Scripter GUI is provided. The script itself is capable of wide flexibility that would be difficult to implement as a Scripter GUI.

 

In order to configure the script for your specific use, there are a few lines well marked in the code to edit, don't worry its not difficult. There is some commented code with a few examples, and one starter default configuration. You will notice the default looks like this:

 

CloneParameters.push({
   srcChan:    1,
   destChans:  [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16],
   cc:         [1,7,11],
   pb:         true
});

 

The above adds an entry to the CloneParameters array, with some specific parameters that should look pretty sensible. In this case the script will take all CC1, CC7, CC11 and pitchbend events sent to channel 1 and clone them to channels 1-16.

 

Note all of the punctuation used, it will be relevant when modifying the above for your own purposes. Retain the same structure.

 

Parameters

 

There are other possible entries as follows:

 

srcChan:

 

The source channel that is to be cloned from. Allowable values are 1-16. Default=1.

 

srcPort:

 

The source port that is to be cloned from. Default=1

 

destChans:

 

This will be a list of values separated by comma, and enclosed in [ ] (see above). You put as many or few destination channels to clone events too. Possible values are 1-16. Default = [1]

 

destPort:

 

if provided will change the destination port where to send cloned events. Default=same as srcPort

 

cc:

 

This parameter will contain a list of cc numbers, separated by comma and surrounded by [ ] (see above example). Allowed values are 1-127. Default=[] (ie, none).

 

pb:

 

This parameter should have a value of either true or false. This will indicate whether pitchbend events will be cloned. Default=false.

 

cp:

 

This parameter should have a value of either true or false. This will indicate whether ChannelPressure aftertouch messages will be cloned. Default=false

 

Multiple Configurations

 

It is also possible to provide more than one CloneParameters entry for more sophisticated scenarios. For example, here is an example that will send CC1 and CC7 to channel 2, and CC11 to channel 3:

 

CloneParameters.push({
   srcChan:    1,
   destChans:  [2],
   cc:         [1,7]
});
CloneParameters.push({
   srcChan:    1,
   destChans:  [3],
   cc:         [11]
});

 

You can create as many CloneParameter entries as you want, each one will be handled separately. In this way you can handle more than one source track through this same script and clone its events however you wish. Its possible to create duplicate cloned events by this method, so be careful not to have overlapping cloning assignments, unless that is what you want.

 

here is another example that handles two source tracks:

 

CloneParameters.push({
   srcChan:    1,
   destChans:  [1,2,3,4],
   cc:         [1,7,11],
   pb:        true
});
CloneParameters.push({
   srcChan:    5,
   destChans:  [5,6,7,8],
   cc:         [1,7,11],
   pb:        true
});

 

The above configuration will forward CC1, CC7 and CC11 and PitchBend from source channel 1 to channels 1-4, and the same events from source channel 5 to dest channels 5-8.

 

Note Channelize By ID[/u]

 

A new feature in v1.3 is that Note events can be channelized by articulationID automatically. This generally works better then the LPX ArticulationSet functionality which has a few serious limitations, especially with regards to multi-instruments. This feature is on by default in Relative mode. There are three possible modes

 

  • 0 Disabled
    This will turn off any channelizing by articulationID of note events. CC, PB and AT events will continue to be forwarded according to the configuration mentioned above.
  • 1 Relative (default)
    Notes events are channelized relative to the source channel. For example, Notes arriving on channel 3 with articulationID's 1-3, will end up channelized to dest channels 3-5
  • 2 Absolute
    Notes are channelized directly from the articulationID. For example, Notes arriving on channel 3 with articulationID's 1-3 will end up channelized to dest channels 1-3.

 

Note that in all cases, articulationID values higher then 16 will result in multi-port channelizing. For example articulationID 17 (absolute mode) will result in channelizing to port2, channel 1, etc.. Max channel allowed is port 8, channel 15.

 

In order to configure channelizing different then the default, uncomment the following line in the script and configure the value as desired.

 

// var CHANNELIZE_MODE = 1;

 

Disabled=0, Relative=1, Absolute=2

 

Please ask questions about use on this thread

 

Future

 

  • Combine with functionality of ChannelFollower script to only send CC, PB and AT events to channels with note activity currently happening.
  • Seperate GUI app that can be used to configure the CloneParameter definitions with a nice GUI. This could be fairly easy for almost anyone to write that has some background in this sort of thing, using all kinds of tools that are out there. Feel free to jump in because I may never get to it.
  • C++ AU/VST version. This would have a great GUI but would probably not be free.

 

Finally here is the current script for any online discussion about the scripting.

 

var CloneParameters = [];  // DO NOT REMOVE

/********************************************************************************
*
* CloneEvents.js, for LPX Scripter - Multiport
*
* Version 1.351
*
* Copyright 2019 Steve Schow
* steve@bstage.com
*
* This script will clone events as spec'd in the CloneParameters array
* Multiple CloneEvent[] entries can be specified, and all will be performed
* even if resulting in duplicate cloned events.
*
* CC, PitchBend and ChannelPressure events can all be cloned.
*
* More complicated scenarios can be setup for multi-timbral situations with
* more then one srcChan that needs to be cloned through the same script.
* Just add more entries to the CloneParameters array below.  This includes
* cloning across AU3 ports.
*
* Sorry no GUI, its too complicated to make a good one in scripter that is 
* truly flexible.  Easier to just edit the CloneParameters array shown below.
*
* Usage explained here: https://www.logicprohelp.com/viewtopic.php?f=45&t=143299
*
* TODO Combine with behavior of ChannelFollower, to only clone events when 
*      Notes are sustaining.  Remove need to specify CC#'s
*
********************************************************************************/




CloneParameters.push({
   srcChan:    1,
   srcPort:    1,
   destChans:  [1,2],
   destPort:   1,
   cc:         [1,7,11],
   pb:         true
});



/******************************************
* Optional Configure Channelize mode
*
*   0 = disabled
*   1 = Relative  (default)
*   2 = Absolute
******************************************/

// var CHANNELIZE_MODE = 1;


/************************************************
************************************************
* DO NOT EDIT BELOW HERE
************************************************
************************************************/

var MAXPORTS = 8;
var MAXRANGE = 127;

/***************************************************************
*   channelizeEvent if there is an articulationID
**/
Event.prototype.channelize = function() {

   // if channelizing disabled, get out
   if (chanmode == 0) {
       return;
   }
   
   // if no articulation ID, then don't channelize
   if (this.articulationID == undefined || this.articulationID < 1) {
       return;
   }

   // check for articulationID range
   if (this.articulationID > MAXRANGE) {
       Trace("Event.ArticulationID 1-" + MAXRANGE + " allowed");
       this.trace();
       return;
   }

   /**
    * If Relative mode, then add articulation ID to source midi
    * channel to arrive at output channel.  If absolute mode, then
    * simply convert articulation id to channel
    **/
   
   var destFlatChan;
   
   // if relative mode
   if(chanmode == 1) {
       destFlatChan = ((this.port-1)*16+this.channel) + this.articulationID - 1;
   }
   else {
       destFlatChan = this.articulationID;
   }
   
   // convert flat chan back to port/chan
   this.port = Math.trunc(destFlatChan / 16) + 1;
   this.channel = destFlatChan % 16;
}

/*******************************************
* HandleMIDI
*******************************************/

function HandleMIDI(event) {
  
  event.channelize();
  
  for(var i in CloneParameters) {
     if(CloneParameters[i].srcChan == event.channel) {
         if(CloneParameters[i].srcPort == event.port) {
             handleClones(event, CloneParameters[i]);
         }
     }
     else {
         event.send();
     }
  } 
}

/*******************************************
* handleClones
*******************************************/


function handleClones(event, entry) {
  
  if(event instanceof PitchBend 
          && entry.pb == true) {
      sendToChannels(event, entry);    
  }

  else if(event instanceof ChannelPressure 
          && entry.cp == true) {
      sendToChannels(event, entry);
  }

  else if(event instanceof ControlChange 
          && entry.cc.length > 0) {
          
      for(var ccnum in entry.cc) {
          if (event.number == entry.cc[ccnum]) {
              sendToChannels(event, entry);
          }
      }
  }
  else {
      event.send();
  }

}

/*******************************************
* sendToChannels
*******************************************/

function sendToChannels(event, entry) {

  if(event.channel != entry.srcChan) {
      return;
  }

  for(var chan in entry.destChans) {
      var src = event.channel;
      var srcPort = event.port;
      event.channel = entry.destChans[chan];
      event.port = entry.destPort;
      event.send();
      event.channel = src;
      event.port = srcPort;
  }
}

/**************************************
* sanity check the JSON - set defaults
**************************************/


for(var e in CloneParameters) {
   if(CloneParameters[e].srcChan == undefined) {
       CloneParameters[e].srcChan = 1;
   }
   if(CloneParameters[e].srcPort == undefined) {
       CloneParameters[e].srcPort = 1;
   }       
   if(CloneParameters[e].destChans == undefined) {
       CloneParameters[e].destChans = [CloneParameters[e].srcChan];
   }
   for(var chan in CloneParameters[e].destChans) {
       if (CloneParameters[e].destChans[chan] < 1 
               || CloneParameters[e].destChans[chan] > 16) {
           Trace("ERROR: midi channel "
           +CloneParameters[e].destChans[chan]
           +" not allowed; must be 1-16");
       }
   }       
   if(CloneParameters[e].destPort == undefined) {
       CloneParameters[e].destPort = CloneParameters[e].srcPort;
   }       
   if(CloneParameters[e].cc == undefined) {
       CloneParameters[e].cc = [];
   }
   for(var ccnum in CloneParameters[e].cc) {
       if (CloneParameters[e].cc[ccnum] < 1 
               || CloneParameters[e].cc[ccnum] > 127) {
           Trace("ERROR: CC#"
           +CloneParameters[e].cc[ccnum]
           +" not allowed, must be 1-127");
       }
   }     
   if(CloneParameters[e].pb == undefined) {
       CloneParameters[e].pb = false;
   }       
   if(CloneParameters[e].cp == undefined) {
       CloneParameters[e].cp = false;
   }       
}


// provide a way to hardcode default into top of script
var chanmode=1;
var CHANNELIZE_MODE;
if(CHANNELIZE_MODE != undefined) {
  chanmode=CHANNELIZE_MODE;
}

var chanmodeNames = ["Disabled","Relative","Absolute"];
Trace("-");
Trace("Channelizing Mode = "+chanmodeNames[chanmode]);
Trace("-");
Edited by Dewdman42
Link to comment
Share on other sites

Update 1.351

 

Includes the ability to channelize incoming notes in addition to cloning CC's, PB's and AT's. There are two modes of channelizing, Relative and Absolute. Relative is the default. (see top post for more info)

 

Note - will be combining this script with the ChannelFollower script in upcoming update...

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...