Jump to content

A Script for intelligent legato


Recommended Posts

This is my first experiment with the LX Scripter API. I like the way it turned out, so I thought I'd share it.

 

I've never liked the way sustain pedals work, even on real pianos, so here's an alternative you might find useful from time to time. It reads the sustain pedal (CC #64) state and behaves as follows:

 

* While the pedal is down, each note is held until the next note is played. This is done by queuing NoteOff events until the next NoteOn event.

 

* When the pedal goes up, any remaining NoteOff events in the queue are sent immediately. This prevents stuck notes and allows you to move freely between legato and staccato execution.

 

* The pedal event is always swallowed to keep from triggering the normal sustain behavior in your AU.

 

You can use this script to make it easier to play melodies (and chord sequences) perfectly legato, with each note connected smoothly to its successor. You can also hold a chord down in one hand while playing a melody with the other. The chord will sound as long as you hold the notes down and the melody will sound one note at a time, still perfectly legato.

 

To use it, just copy the code below and paste it into a Script Editor window. Be sure to delete the window's previous contents to prevent duplicate function names. If you like what it does, Save it under some convenient name ( I called mine Legato) and it will be available to all your projects.

 

Enjoy!

 

/*
Intelligent Legato script for Logic Pro X
Version 1.0
Author: Michael Ellis
Copyright 2015 Ellis & Grant, Inc.
License: GPL
   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/


var Legato=false;    // True while sustain (damper) pedal is Down
var Pedalnum = 64;   // Standard midi number for the pedal
var Threshold = 64;  // interpretation of pedal down/up value
var Q = []           // the queue for deferred Note Off events

function HandleMIDI(event)
{
if (event instanceof ControlChange && event.number==Pedalnum) {	
    /* Set the value of Legato based on the pedal state */    
    Legato = event.value >= Threshold;
    if (Legato == false) { 
        /* Clear any NoteOff events still in the queue */
        Release()
        }
    }
else if (event instanceof NoteOff ) {
    /* If the pedal is down, 
       save the off event until the 
       next NoteOn or Pedal up event.
    */
    Legato == true ? Hold(event) : event.send();
    }
else if (event instanceof NoteOn) {
     /* After sending the event, release any NoteOff events
        in the queue.
     */
     event.send();
     Release();
     }      
else {
    /* Send anything else with no other action */    
	  event.send();
	  }
}

function Hold(eoff) 
{
   /* Push a NoteOff event onto the queue */
   Q.push(eoff);
}
   
   
function Release() 
{
   /* Send all accumulated NoteOff events and clear the queue */
   Q.forEach( function (e) {
   e.send();
   });
   Q = [];
}
/* End of Script */

  • Like 1
Link to comment
Share on other sites

  • 3 years later...
Thanks for your script Michael. It's pretty cool on a normal keyboard but with my mallet controller (Xylosynth by Wernick), the keys/bars just get strokes so notes length is constant (actually defined by a knob). With your script, I can't hold a chord and play above in that case. The way it is done on a real vibraphone is : usual sustain pedal staying down for the chord to ring, we mute a note played above by a touch on the ringing key/bar while we play the following note. I emulate that "mallet dampening" using velocities bellow 10 and I tried to remove the "legato" part of your script because holding "notes off" gives me an access to release samples ! Not successful til that day ... Actually, I need a script transforming on the fly pedaling in "push note off messages in a queue" until "pedal is up or a velocity bellow 10 has been sent". It's close to your script, so would you make an alternate version for mallets instruments players ? Or someone reading this ? I offer my self sampled EXS electric vibes sound (a good Musser M55 with Ayotte piezos and TC Electronics Stereo Chorus). Info : I use a continuous sustain pedal (Roland DP10) that does not always send 0 when up ... It can be 1 or 2. This problem is known but Roland does not seem to care.
Link to comment
Share on other sites

I need a javascript that retains the "note off" messages until, either I release the sustain pedal, either I send a second "note on" message at velocity lower than 10 for a note I want to kill before pedal is up. Without that script, my mallet controller sends "note off" messages that trig "release samples" at wrong times. I Think I need also the script to manage my continuous pedal Roland DP10 sending values 0 or 1 or 2 instead of always 0 when it is up. Not that easy to explain for a frenchy ... Thanks
Link to comment
Share on other sites

I don't have a good way to test this, so let me know what happens

 

var sustain = false;
var pitches = new Array(128);
for(var i=0;i<128;i++) {
   pitches[i]= false;
}
var note = new NoteOff;
note.velocity = 0;


function allOff(event) {
   for(var pitch=1;pitch < 128;pitch++) {
       if(pitches[pitch] == true) {
           note.channel = event.channel;
           note.pitch = pitch;
           note.send();
           pitches[pitch] = false;
       }
   }
}

ControlChange.prototype.handle = function() {

   if( this.number != 64 ) {
       this.send();
       return;
   }
   
   if( this.value >= 64 ) {
       sustain = true;
   }
   else {
       sustain = false;
       // send saved NoteOff's
       allOff(this);
   }
   
   // Do you want sustain message sent through?
   this.send();
};


NoteOn.prototype.handle = function() {
   

   // if the velocity is less then 10 and sustain pedal is down
   // then treat as a mute event
   if(this.velocity < 10 && sustain==true) {
       this.velocity = 0;
       this.send();
       pitches[this.pitch] = false;
   }
   
   // Otherwise play the note
   else {
       pitches[this.pitch] = true;
       this.send();
   }
};

NoteOff.prototype.handle = function() {
   // only send the NoteOff immediately if sustain is up
   if ( sustain == false ) {
       this.send();
   }
   pitches[event.pitch] = false;
};


// all other event types
Event.prototype.handle = function() {
   this.send();
};


function HandleMIDI(event) {
   event.handle();
}

// TODO - do you want the sustain message sent to the instrument?  
//
// TODO, what if a note is still held when the sustain pedal is released?

Link to comment
Share on other sites

Cool ! Thanks a lot Dewdman ... Release samples are correctly triggered but when I play several notes pedal up, the first following note played with pedal down has a much louder release sample (when pedal back to up position).

About the "note on vel<10", they trigger correctly release samples but they don't kill the note. Only a new move of pedal down and up reset a normal behavior of that particular note. Otherwise, it sounds sustained while pedal is up.

"Do you want sustain message sent through?" Yes we never know ...

"TODO - do you want the sustain message sent to the instrument?" Yes it can't generate issues no ?

"TODO, what if a note is still held when the sustain pedal is released?" Release of pedal should kill every notes

Well, sorry you made a brand new script ! I thought it was possible to modifie the script of Michael in head of that page ... Anyway thank you

Link to comment
Share on other sites

Reading again first post by Michael, I see :

* The pedal event is always swallowed to keep from triggering the normal sustain behavior in your AU.

So I think I'd better answer No to questions : "Do you want sustain message sent through?" & "TODO - do you want the sustain message sent to the instrument?"

Link to comment
Share on other sites

Here is a version with the sustain CC message being blocked from reaching the instrument. Might help. The loud notes could be various reasons, I'm not entirely sure right now. Try this one first. If that doesn't work I have a more complicated idea to keep track of the mute notes better, etc..but I need you to send me a LPX project that has a short region recorded with midi the way you would expect to play it. Perhaps if you include the EXS instrument you're trying to use I can fine tune the script until it works right for you. Its going to be close...but its a bit tricky to keep track of the normal notes vs the mute notes...and note off's happening at the right time. I suspect the loud ones you hear are duplicate notes in some way, or perhaps related to the sustain pedal. Anyway try this quick fix first...if not, send me the LPX project and I'll try to get it working. Its an interesting challenge.

 

var sustain = false;
var pitches = new Array(128);
for(var i=0;i<128;i++) {
   pitches[i]= false;
}
var note = new NoteOff;
note.velocity = 0;


function allOff(event) {
   for(var pitch=1;pitch < 128;pitch++) {
       if(pitches[pitch] == true) {
           note.channel = event.channel;
           note.pitch = pitch;
           note.send();
           pitches[pitch] = false;
       }
   }
}

ControlChange.prototype.handle = function() {

   if( this.number != 64 ) {
       this.send();
       return;
   }
   
   if( this.value >= 64 ) {
       sustain = true;
   }
   else {
       sustain = false;
       // send saved NoteOff's
       allOff(this);
   }
};


NoteOn.prototype.handle = function() {
   

   // if the velocity is less then 10 and sustain pedal is down
   // then treat as a mute event
   if(this.velocity < 10 && sustain==true) {
       this.velocity = 0;
       this.send();
       pitches[this.pitch] = false;
   }
   
   // Otherwise play the note
   else {
       pitches[this.pitch] = true;
       this.send();
   }
};

NoteOff.prototype.handle = function() {
   // only send the NoteOff immediately if sustain is up
   if ( sustain == false ) {
       this.send();
   }
   pitches[event.pitch] = false;
};


// all other event types
Event.prototype.handle = function() {
   this.send();
};


function HandleMIDI(event) {
   event.handle();
}
Link to comment
Share on other sites

I'm there ! I was just trying to send you something that help as well as possible. I hope there is no problem for me to give you a wetransfer link on that forum ... Here it is : https://we.tl/t-CWPLjg6P6i (link valid during 1 week)

So I took time to test your second version of the script. In the Logic file I send you, first regions are what I prepared before I see your second version. First track is my vibraphone EXS sound played with usual On/Off sust ped. "The loud notes" are still there. Sorry ... Second track make it easy to ear.Third track shows how my continuous sustain pedal mess around value 0. I tried a little script but it does not work ... My improvisation ending first track was my test of second version of your script. After bar 35 the notes sustained and nothing kill them. Neither soft strokes or ped moves as you will see in Logic's piano roll. If managing at same time mute notes and sust ped transformed in note off messages, maybe we can leave the mute notes part because I found an internal fonction in my mallet controller that is supposed to do that. Maybe there will be no conflict with the script ?

Link to comment
Share on other sites

why do you need to adjust the CC64 from value 8 down to zero? I think sustain pedal is off for everything less than 64?

 

I'm a little confused about what each track and region are in the project you sent me, can you please clarify that? I just need to know what is the midi you WANT to be able to play and and explanation of what you WANT it to sound like, I don't need to see the problems recordings, I'm not sure which track or region in that project is that one that will show me what I need to fix the script.

Link to comment
Share on other sites

Looking at your EXS instrument a little bit, I see that its depending on CC64 for sustain in some way. I do not think the script can do what you want with the MUTE functionality as long as the instrument depends on CC64 for holding notes. In order to mute the notes while the sustain is held, the script has to manually handle all sustaining so that it can choose to mute notes as you play the low velocity mute notes.

 

I will need more clarification from you about what the instrument is expecting to receive and what you need the midi to do in order to work for you.

 

Right now my understanding was to attempt to block CC64 from reaching the instrument, which may or may not be ok, since I see in the instrument that CC64 is configured for "hold"

 

Secondly I would need to know what is supposed to happen with notes that are played while the sustain is held down?

 

I *think* what you are saying you want is that when sustain pedal is down you want all notes to continue ringing until the sustain pedal is released. If a mute event happens (low velocity), then end those notes eary before releasing the sustain pedal.

 

I'm not sure what should happen if notes are played when the sustain pedal is not down.

 

when the sustain pedal is released you want ALL notes to end, yes?

Link to comment
Share on other sites

If your instrument is dependent on the value of the sustain pedal having some meaning other than on or off, then I don't think we can do what you want. So I'm not sure why you are worried about some of the sustain values not being zero. Generally anything under value=64 is off. So it should not matter I don't think unless your instrument is programmed in some way to consider the actual CC64 value for half pedaling, etc..
Link to comment
Share on other sites

Try this:

 

// Version 3.0
var NeedsTimingInfo=true;

var sustain = false;
var pitches = new Array(128);
for(var i=0;i<128;i++) {
   pitches[i]= 0;
}
var off = new NoteOff;
off.velocity = 64;

function allOff(channel) {
   for(var pitch=0;pitch < 128;pitch++) {
        while(pitches[pitch] > 0) {
           off.channel = channel;
           off.pitch = pitch;
           off.send();
           pitches[pitch]--;
        }
   }
}

ControlChange.prototype.handle = function() {
   if( this.number != 64 ) {
       this.send();
       return;
   }
   
   if( this.value >= 64 ) {
       sustain = true;
   }
   else {
       sustain = false;
       
       // send saved NoteOff's
       allOff(this.channel);
   }
};

NoteOn.prototype.handle = function() {
   // if the velocity is less then 10 and sustain pedal is down
   // then treat as a mute event
   if(this.velocity < 10 && sustain==true) {
       this.velocity = 0;
       this.send();
       pitches[this.pitch]++;  // still expecting noteOff??
   }
   
   // Otherwise play the note
   else {
       pitches[this.pitch]++;
       this.send();
   }
};

NoteOff.prototype.handle = function() {
   // only send the NoteOff immediately if sustain is up
   if ( sustain == false ) {
       pitches[this.pitch]--
       this.send();
   }
   // Otherwise, when sustain pedal released, the notes will be released.
};

// all other event types
Event.prototype.handle = function() {
   this.send();
};

function HandleMIDI(event) {
   event.handle();
}

//==========================================
// detect stopped playback to stop hung notes
//
var stopped=false;
function ProcessMIDI() {
   var ctx = GetTimingInfo;
   
   if(ctx.playing) {
       stopped = false;
   }
   else {
       if(!stopped) {
           allNotesOff();
           stopped = true;
       }
   }    
}

var ccOff = new ControlChange;
ccOff.number = 123;
ccOff.value = 0;
function allNotesOff() {
   for(var chan=1;chan<=16;chan++) {
       ccOff.channel = chan;
       ccOff.send();
   }    
}
  • Like 1
Link to comment
Share on other sites

Sorry, my previous answer is missing. Don't know why ...

 

You say : "Looking at your EXS instrument ... I do not think the script can do what you want with the MUTE functionality as long as the instrument depends on CC64 for holding notes."

 

Yes, before I heard of the Midi Scripter, I've tried several things right in the EXS24. Actually, the mute part is done by a monophonic sample group per key with the sample corresponding to lowest velocities acting as a mute because it is a sample tail. I see that it would be better to use a more simple EXS instrument and let the scripter do the job.

 

I tried your new script with the default electric piano sound of Logic's "New Project". It works as I want but after a while, one note don't ever mute (pedal or low vel). Same when opening another new project un choose factory Steinway of Logic.

 

You say : I will need more clarification from you about what the instrument is expecting to receive and what you need the midi to do in order to work for you

Right now my understanding was to attempt to block CC64 from reaching the instrument, which may or may not be ok, since I see in the instrument that CC64 is configured for "hold"

 

You are right, in some of my tries, I left blank the box « Hold via » of EXS instrument. It seems we have to do that because I need CC64 as a source to modulate things like enveloppe and filter for half pedaling … So CC64 has to reach the instrument ...

 

You say : Secondly I would need to know what is supposed to happen with notes that are played while the sustain is held down

I *think* what you are saying you want is that when sustain pedal is down you want all notes to continue ringing until the sustain pedal is released. If a mute event happens (low velocity), then end those notes eary before releasing the sustain pedal.

 

Yes that’s it.

 

You say : I'm not sure what should happen if notes are played when the sustain pedal is not down.

 

When pedal is up , notes played are shorts. Actually, my mallet controller sends notes off after a time defined by a knob on the controller. Usually a natural length of notes « pedal up » on a vibraphone is around 400 milliseconds. This can be change on the fly while playing.

 

You say : when the sustain pedal is released you want ALL notes to end, yes?

 

Yes, all notes that are ringing before pedal is released. But as soon as pedal is up, some notes can be played. They just sound short.

 

You say : If your instrument is dependent on the value of the sustain pedal having some meaning other than on or off, then I don't think we can do what you want.

 

Maybe if we leave the mute part of the script ? Only retain note off messages until sust ped is up ?

 

You say : So I'm not sure why you are worried about some of the sustain values not being zero

 

I don’t know why but when I set a midi transformer in Logic environment to say CC64 messages below 8 has to be 0, my Roland DP10 pedal works fine. By doing it in the scripter, I try to get everything in the patch

Link to comment
Share on other sites

I re-post my missing answers to following questions (probably a session « time out » occured the first time…)

 

You say : I’m a little confused about what each track and region are in the project you sent me, can you please clarify that? … I'm not sure which track or region in that project is that one that will show me what I need to fix the script.

 

In the Logic project I sent you :

 

- First track (called « My EXS El Vibes ») is : my EXS instrument (the one that has mute programming and « Hold » by CC64)

regions with pedal moves and Low velocity notes for mutes. (some up to vel15 instead of vel10. Sorry)

 

- Second track (called « Simple sound rel spl ») is : a single sample vibraphone sound with a woodblock sample as release sample.

I made it to drag any region and clearly ear when exagerates release samples are triggered.

 

- Third track (called « Continuous sust ped ») is : my EXS instrument

region here (called « Continuous sust issues ») shows Roland DP10 continuous pedal not always sending value 0. So I tried to make a simple script to push all pedal’s value below 8 to 0. My script does not work.

 

 

If it can help, here are 3 links probably better than talks.

1 - Real vibraphone mallet mute technique :

2 - Xylosynth basic sustain demo :

In this second video, the first stroke of the guy is played with pedal down and later when he turns the knob all the way through, the maximum note length is 8 seconds. Not an infinite length as the guy seems to think.

3 - Real vibraphone half pedaling demo at 5’07 of this video :

Link to comment
Share on other sites

  • 9 months later...

Hi Michael Ellis - if you are still visiting this forum

Thanks for your script...

It saved me a huge amount of time and gave me a legato pedal control over ARIA player GPO instruments that are shipped without the ability to recognise CC#64 'Sustain'.

Just what I was looking for....

Great work.

Link to comment
Share on other sites

  • 3 years later...

Michael, does this still work in Logic Pro X (I'm on an M1)? I pasted the code into Logic's Scripter and didn't perceive any results. I'm trying to perform a simple guitar-type legato while a chord is sustaining. This seems like a great solution... Thanks.

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