Dewdman42 Posted June 8, 2020 Share Posted June 8, 2020 (edited) Articulation Keyswitching with Scripter This post and thread will discuss some simple methods for sending articulation keyswitches using Scripter. The examples are being kept extremely simple in order to show the general concepts. More elaborate scenarios can also be handled. I will provide a few different approaches in different posts of this thread so you can explore them all and adapt to your own needs whatever works best. These are all small, simple and efficient scripts...any of which can overcome LogicPro's limitation of only 3 keyswitchs per articulationID in the articulationSet. Sending Keyswitch per Aritculation ID. Mainly the script needs to do the following in some manner: Look for articulationID in the incoming NoteOn object Based on the articulationID (if it exists), send one or more keyswitches Send the actual NoteOn Here is a simple to understand example script: https://gitlab.com/dewdman42/art2script/-/blob/main/keyswitcher.js A couple points about the above simple example: Note that each Keyswitch needs both a NoteOn and a NoteOff event. The easy way to send a matching NoteOff is to simply use the NoteOn object and set the velocity to zero, which in LogicPro is considered the same as a NoteOff. Also, note that for these kinds of scripts to work, the articulationSet must not have any entries in the OUTPUT section because when there are entries in the OUTPUT section, the articulationID will be stripped from the event Here is also a BASH script that can be used to generate a working Scripter Script by reading an articulationset file to get the names and keyswitches to use: https://gitlab.com/dewdman42/art2script/-/blob/main/art2script.sh Sending Keyswitches by Automation Another interesting way to send keyswitches from Scripter is to use automation points instead of articulationID. This is sometimes preferable because you can see a graph with labels on an automation lane showing the articulations in effect. Here is a simple example script doing something like that. This example is considerably more complicated then the above example, but it may get you started... https://gitlab.com/dewdman42/art2script/-/blob/main/lanekeyswitcher.js Any questions, comments or discussion will be welcomed here... Please see later posts in this thread with more example scripts using different approaches just for the sake of discussion. Edited October 18, 2021 by Dewdman42 1 Quote Link to comment Share on other sites More sharing options...
GtrJazz Posted June 9, 2020 Share Posted June 9, 2020 Is it possible to assign a key command to switch automation articulation switching via scripter? Id like to be able to toggle between CC1 and CC11 and this articulation script via key commands on iPad Quote Link to comment Share on other sites More sharing options...
Dewdman42 Posted June 9, 2020 Author Share Posted June 9, 2020 Scripter can't respond to keyboard commands, only midi and automation. You could use Keyboard Maestro to send midi commands from key commands, which could solve the problem for you, but that involves purchasing a third party product (Keyboard Maestro). Quote Link to comment Share on other sites More sharing options...
GtrJazz Posted June 9, 2020 Share Posted June 9, 2020 Thanks for the reply. Would this work with osculator by any chance? Quote Link to comment Share on other sites More sharing options...
ValliSoftware Posted June 9, 2020 Share Posted June 9, 2020 Id like to be able to toggle between CC1 and CC11 and this articulation script via key commands on iPad What program are you using on the iPad? Quote Link to comment Share on other sites More sharing options...
Dewdman42 Posted June 9, 2020 Author Share Posted June 9, 2020 I’m not sure about osculator but I don’t think so Quote Link to comment Share on other sites More sharing options...
GtrJazz Posted June 10, 2020 Share Posted June 10, 2020 Id like to be able to toggle between CC1 and CC11 and this articulation script via key commands on iPad What program are you using on the iPad? I’ve got lemur and metagrid Quote Link to comment Share on other sites More sharing options...
ValliSoftware Posted June 10, 2020 Share Posted June 10, 2020 I’ve got lemur and metagrid Did you setup Metagrid to work with Logic Pro X as in this video. Quote Link to comment Share on other sites More sharing options...
Dewdman42 Posted June 10, 2020 Author Share Posted June 10, 2020 (edited) Scripter can respond to midi and automation. You can setup smart controls to control the automation. Aside from that in order to switch via keyboard you would need to use Keyboard Maestro and have that send midi into LogicPro. If you use something like MetaGrid, then same thing..it will will need to send midi in response to you pressing an icon on the iPad. Either way, the midi has to be routed into the channel you are using to host Scripter...then you would be able to control Scripter that way, presuming you also update your scripts to listen for the incoming midi messages needed to change your CC#. Edited June 10, 2020 by Dewdman42 Quote Link to comment Share on other sites More sharing options...
ValliSoftware Posted June 10, 2020 Share Posted June 10, 2020 I’ve got lemur and metagrid In the Lemur manual, search for "Scripting Output Functions" and locate and read each of the following: ctlout noteout midiout Quote Link to comment Share on other sites More sharing options...
GtrJazz Posted June 10, 2020 Share Posted June 10, 2020 How do update the script to receive midi CC? I have no coding or scripting knowledge Quote Link to comment Share on other sites More sharing options...
Dewdman42 Posted June 10, 2020 Author Share Posted June 10, 2020 If you decide what midi messages you want to use to control the script figure that out first and then I will show you how to adapt the scripts above for that Quote Link to comment Share on other sites More sharing options...
GtrJazz Posted June 10, 2020 Share Posted June 10, 2020 If you decide what midi messages you want to use to control the script figure that out first and then I will show you how to adapt the scripts above for that Thanks. I’d like to use midi CC49 Quote Link to comment Share on other sites More sharing options...
Dewdman42 Posted June 10, 2020 Author Share Posted June 10, 2020 and have it change "what" based on which cc values? Quote Link to comment Share on other sites More sharing options...
GtrJazz Posted June 10, 2020 Share Posted June 10, 2020 and have it change "what" based on which cc values? Not sure what you mean. I’m just trying to be able to switch from the articulation view and expression views in the midi automation piano roll view. I’ll still be using my mouse to switch the articulations Quote Link to comment Share on other sites More sharing options...
Dewdman42 Posted June 10, 2020 Author Share Posted June 10, 2020 I'm sorry, but I'm not understanding your requirements so I can't recommend a solution yet. Quote Link to comment Share on other sites More sharing options...
GtrJazz Posted June 10, 2020 Share Posted June 10, 2020 I’m just trying to switch between midi draw modulation, expression etc and your articulation script Quote Link to comment Share on other sites More sharing options...
Dewdman42 Posted June 10, 2020 Author Share Posted June 10, 2020 This sounds like a new topic not directly related to the scripts I shared in the original post. Please create a new thread and explain exactly your requirements better(which I still don't understand well enough to offer a solution). Thanks. Quote Link to comment Share on other sites More sharing options...
Dewdman42 Posted May 1, 2021 Author Share Posted May 1, 2021 (edited) Here is an alternative script, just for sake of discussion. I still prefer the version in post#1, but this is here now for the sake of learning. function HandleMIDI(event) { if( ! (event instanceof NoteOn) ) { event.send(); return; } if(event.articulationID == undefined || event.articulationID < 1) { event.send(); return; } switch(event.articulationID) { case 1: Staccato(event); break; case 2: Legato(event); break; case 3: Spiccato(event); break; default: event.send(); } } function Staccato(event) { sendSwitch("C-2", event); sendSwitch("D0", event); sendSwitch("D1", event); sendSwitch("C-1", event); sendSwitch("D#0", event); sendSwitch("D#1", event); event.send(); } function Spiccato(event) { sendSwitch("C-2", event); sendSwitch("D0", event); sendSwitch("D1", event); sendSwitch("C-1", event); sendSwitch("D#0", event); sendSwitch("D#2", event); event.send(); } function Legato(event) { sendSwitch("C-2", event); sendSwitch("D0", event); sendSwitch("D1", event); sendSwitch("C-1", event); sendSwitch("D#0", event); sendSwitch("D#3", event); event.send(); } var ks = new NoteOn; function sendSwitch(ksName, ev) { ks.channel = ev.channel; ks.pitch = MIDI.noteNumber(ksName); ks.velocity = 100; ks.send(); ks.velocity = 0; ks.send(); } Edited June 21, 2021 by Dewdman42 Quote Link to comment Share on other sites More sharing options...
Dewdman42 Posted May 1, 2021 Author Share Posted May 1, 2021 (edited) - Edited May 2, 2021 by Dewdman42 Quote Link to comment Share on other sites More sharing options...
Dewdman42 Posted May 1, 2021 Author Share Posted May 1, 2021 here is an example script that is able to also send CC switches... function HandleMIDI(event) { if( ! (event instanceof NoteOn) ) { event.send(); return; } if(event.articulationID == undefined || event.articulationID < 1) { event.send(); return; } switch(event.articulationID) { case 1: Staccato(event); break; case 2: Legato(event); break; case 3: Spiccato(event); break; default: event.send(); } } function Staccato(event) { sendSwitch("C-2", event); sendSwitch("D0", event); sendCC(58, 13, event); sendSwitch("D#0", event); sendSwitch("D#1", event); event.send(); } function Spiccato(event) { sendSwitch("C-2", event); sendSwitch("D0", event); sendSwitch("D1", event); sendSwitch("C-1", event); sendSwitch("D#0", event); sendSwitch("D#2", event); event.send(); } function Legato(event) { sendSwitch("C-2", event); sendSwitch("D0", event); sendSwitch("D1", event); sendSwitch("C-1", event); sendSwitch("D#0", event); sendSwitch("D#3", event); event.send(); } var ks = new NoteOn; function sendSwitch(ksName, ev) { ks.channel = ev.channel; ks.pitch = MIDI.noteNumber(ksName); ks.velocity = 100; ks.send(); ks.velocity = 0; ks.send(); } var cc = new ControlChange; function sendCC(num, val, event) { cc.number = num; cc.value = val; cc.channel = event.channel; cc.send(); } That is accomplished by simply adding a sendCC function like this: var cc = new ControlChange; function sendCC(num, val, event) { cc.number = num; cc.value = val; cc.channel = event.channel; cc.send(); } Quote Link to comment Share on other sites More sharing options...
Dewdman42 Posted May 1, 2021 Author Share Posted May 1, 2021 here is an example Script where the keyswitches are only sent when the articulationID changes since the last note (on the same channel): var lastArtID = new Array(17); for (let chan=1; chan<=16; chan++) { lastArtID[chan] = 0; } function HandleMIDI(event) { if( ! (event instanceof NoteOn) ) { event.send(); return; } if(event.articulationID == undefined || event.articulationID < 1) { lastArtID[event.channel = 0]; event.send(); return; } if(event.articulationID == lastArtID[event.channel]) { event.send(); return; } lastArtID[event.channel] = event.articulationID; switch(event.articulationID) { case 1: Staccato(event); break; case 2: Legato(event); break; case 3: Spiccato(event); break; default: event.send(); } } function Staccato(event) { sendSwitch("C-2", event); sendSwitch("D0", event); sendCC(58, 13, event); sendSwitch("D#0", event); sendSwitch("D#1", event); event.send(); } function Spiccato(event) { sendSwitch("C-2", event); sendSwitch("D0", event); sendSwitch("D1", event); sendSwitch("C-1", event); sendSwitch("D#0", event); sendSwitch("D#2", event); event.send(); } function Legato(event) { sendSwitch("C-2", event); sendSwitch("D0", event); sendSwitch("D1", event); sendSwitch("C-1", event); sendSwitch("D#0", event); sendSwitch("D#3", event); event.send(); } var ks = new NoteOn; function sendSwitch(ksName, ev) { ks.channel = ev.channel; ks.pitch = MIDI.noteNumber(ksName); ks.velocity = 100; ks.send(); ks.velocity = 0; ks.send(); } var cc = new ControlChange; function sendCC(num, val, event) { cc.number = num; cc.value = val; cc.channel = event.channel; cc.send(); } One other comment to make is that LogicPro articulation set behavior for handling notes without articulationID is to play the keyswitches found (if found at all) in the first item of the articulationSet list. This hidden behavior is often not desired, in fact I usually make sure to program my Articulation Set so that the first item on the list is a dummy entry with no keyswitches to make sure. In any case, whatever you desired behavior is, you may have to modify any of these example scripts accordingly to handle that specific situation exactly as you desire. Quote Link to comment Share on other sites More sharing options...
Dewdman42 Posted May 1, 2021 Author Share Posted May 1, 2021 (edited) - Edited May 2, 2021 by Dewdman42 Quote Link to comment Share on other sites More sharing options...
Dewdman42 Posted May 2, 2021 Author Share Posted May 2, 2021 (edited) - Edited June 21, 2021 by Dewdman42 Quote Link to comment Share on other sites More sharing options...
Dewdman42 Posted May 2, 2021 Author Share Posted May 2, 2021 (edited) - Edited June 21, 2021 by Dewdman42 Quote Link to comment Share on other sites More sharing options...
D4rkKn1ght Posted May 28, 2021 Share Posted May 28, 2021 Hey Dewdman42 - I just want to really thank you for this. Not many replies on here, but this is SUPER useful and saved me. Basically I didn't think ahead before buying my first VSL instrument (Synchron Strings Pro) and then realised, "wait a minute, how can I build Articulation Sets for this when it has more than 3 required Keyswitches for some of the articulations?" So this semi tutorial really saved me. I didn't/don't want to (at least not yet) go the X-DAW route. I've only just done some preliminary tests modifying your first example and it seems to be working for me. I'll come back with any questions if I run into any problems and hopefully you can educate me further. But thanks again! P.S. If I did want to do some reading to understand the code you've used in your first example better - as in to actually understand more of what's going on there, can you point me somewhere I can do that? 1 Quote Link to comment Share on other sites More sharing options...
Dewdman42 Posted May 28, 2021 Author Share Posted May 28, 2021 There is not much information out there about scripter other then what is in the logicpro effects manual (from the help menu) start there. Feel free to ask questions here or start a thread with general questions about scripter on the forum, some others May chime in too. Glad it’s helpful! Quote Link to comment Share on other sites More sharing options...
ravez Posted May 31, 2021 Share Posted May 31, 2021 Sending Keyswitches by Automation Another interesting way to send keyswitches from Scripter is to use automation points instead of articulationID. This is sometimes preferable because you can see a graph with labels on an automation lane showing the articulations in effect. artlane.jpg Here is a simple example script doing something like that. This example is considerably more complicated then the above example, but it may get you started... var articulation = []; articulation[1] = {word:"Articulation 1", pitch:"C0"}; articulation[2] = {word:"Articulation 2", pitch:"C#0"}; articulation[3] = {word:"Articulation 3", pitch:"D0"}; articulation[4] = {word:"Articulation 4", pitch:"D#0"}; articulation[5] = {word:"Articulation 5", pitch:"E0"}; articulation[6] = {word:"Articulation 6", pitch:"F0"}; articulation[7] = {word:"Articulation 7", pitch:"F#0"}; articulation[8] = {word:"Articulation 8", pitch:"G0"}; articulation[9] = {word:"Articulation 9", pitch:"G#0"}; articulation[10] = {word:"Articulation 10", pitch:"A0"}; var VELOCITY=1; /*************************************************************** ***************** DO NOT EDIT BELOW HERE ********************** ***************************************************************/ function HandleMIDI(event) { event.ksSend(); } Event.prototype.ksSend = function() { this.send(); }; NoteOn.prototype.ksSend = function() { SwitchMap.send(this); }; var wordFlg=-1; /************************************************* * Setup SwitchMap object with pre-created * events based on the user provided articulation * array *************************************************/ var SwitchMap = {map:[]}; /************************************************* * send function that will be called for all * NoteOn events to potenetially send keyswitches ***/ SwitchMap.send = function (event) { if( wordFlg >= 0 ) { var node = this.map[wordFlg]; // If switches are defined, send them if(node != undefined) { for(var s=0;s<node.length;s++) { var e = node[s]; e.channel = event.channel; e.port = event.port; e.send(); } } wordFlg = -1; } // Send main note event event.send(); }; /*********************************************** * init function to load up the switches defined * in articulation[] array, preallocate events ***/ SwitchMap.init = function() { this.map = []; for (var a = 1; a < articulation.length; a++) { var userArt = articulation[a]; if (userArt != undefined) { this.configure(a, userArt); } } }; /********************************************** * use this function to configure each * user provided entry **/ SwitchMap.configure = function(artid, ksconfig) { // make sure wrapped by array if(this.map[artid] == undefined) this.map[artid] = []; var on = new NoteOn; if(typeof ksconfig.pitch == "string") { on.pitch = MIDI.noteNumber(ksconfig.pitch); } else { on.pitch = ksconfig.pitch; } if(ksconfig.velocity == undefined) { on.velocity = VELOCITY; } else { on.velocity = ksconfig.velocity; } this.map[artid].push(on); var off = new NoteOff; off.pitch = on.pitch; off.velocity = on.velocity; this.map[artid].push(off); }; /************************************************ * Callback called by LogicPro at the right time ***/ function Initialize() { SwitchMap.init(); } function ParameterChanged(id, val) { if(id==0) { wordFlg=val; } } // build words array for automation var words = ["Reset"]; for(var i=1;i<articulation.length;i++) { words.push(articulation[i].word); } var PluginParameters = []; PluginParameters.push({ type: "menu", name: "Your Articulation Lane", defaultValue: 0, hidden: true, disableAutomation: false, valueStrings: words }); hi, i tried this and it works, but couldn't get multiple keyswitches per articulation to work, like for example in Synchron player when you have different dimesions so you need to send a C0 and C#1 to activate a cell. What would be the correct syntax to get that to work? i tried: articulation[1] = {word:"Articulation 1", pitch:["C0","C#1"]}; but doesn't work thanks Quote Link to comment Share on other sites More sharing options...
Dewdman42 Posted May 31, 2021 Author Share Posted May 31, 2021 Guess I have to improve the script a little bit to support multiple keyswitches from automation....let me try that, hang on... Quote Link to comment Share on other sites More sharing options...
Dewdman42 Posted May 31, 2021 Author Share Posted May 31, 2021 Try this one, I made it more simple to understand as well, though technically speaking slightly less efficient but not enough to be worried about: /************************************************************** * Example Script that convert automation lane to keyswitches * * Note this example only works for a single midi channel, * Otherwise seperate automation lanes will need to be defined * for each midi channel in the articulation[] array **************************************************************/ var articulation = []; // Array of keyswiches per articulation articulation[1] = {word:"Articulation 1", ks:["C0","C1"]}; articulation[2] = {word:"Articulation 2", ks:["C#0",[58,5]]}; articulation[3] = {word:"Articulation 3", ks:["D0"]}; articulation[4] = {word:"Articulation 4", ks:["D#0"]}; articulation[5] = {word:"Articulation 5", ks:["E0"]}; articulation[6] = {word:"Articulation 6", ks:["F0"]}; articulation[7] = {word:"Articulation 7", ks:["F#0"]}; articulation[8] = {word:"Articulation 8", ks:["G0"]}; articulation[9] = {word:"Articulation 9", ks:["G#0"]}; articulation[10] = {word:"Articulation 10", ks:["A0"]}; /*************************************************************** ***************** DO NOT EDIT BELOW HERE ********************** ***************************************************************/ var lastArtID = -1; var currentArtID = -1; function HandleMIDI(event) { // Everything other then NoteOn if( !(event instanceof NoteOn)) { event.send(); return; } // If same artID as last note, skip keyswitching if(currentArtID == lastArtID || articulation[currentArtID] == undefined || articulation[currentArtID].ks == undefined) { event.send(); return; } // remember new artID lastArtID = currentArtID; // send keyswitches let ksList = articulation[currentArtID].ks; for(let k=0; k<ksList.length; k++) { if(Array.isArray(ksList[k])) { sendCC(ksList[k], event); } else { sendSwitch(ksList[k], event); } } // send actual note event.send(); } // Function to send a single keyswitch var ks = new NoteOn; function sendSwitch(pitchName, evt) { ks.pitch = MIDI.noteNumber(pitchName); ks.channel = evt.channel; ks.velocity = 100; ks.send(); ks.velocity = 0; ks.send(); } // Function to send a single CC switch var cc = new ControlChange; function sendCC(ccArray, evt) { cc.number = ccArray[0]; cc.value = ccArray[1]; cc.channel = evt.channel; cc.send(); } function ParameterChanged(id, val) { if(id==0) { currentArtID=val; } } // build words array for automation var words = ["Reset"]; for(var i=1;i<articulation.length;i++) { words.push(articulation[i].word); } var PluginParameters = []; PluginParameters.push({ type: "menu", name: "Your Articulation Lane", defaultValue: 0, hidden: true, disableAutomation: false, valueStrings: words }); Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.