A technical support community for Apple Logic Pro users.



 
EoinF
Topic Author
Posts: 7
Joined: Fri Nov 29, 2019 4:19 pm

Conditional Delays Using Scripter?

Fri Nov 29, 2019 4:47 pm

Hello from a first-time poster,

I'm wondering if the following is possible using Logic's Scripter? Hopefully some experienced users of Scripter would be able give some advice - of which I would be extremely grateful.

I'm looking for a script that can delay sending MIDI notes (note on, note release, and velocity) to an instrument based on the value of a CC:

For instance, if the value of CC11 is between 11 and 20, delay the note by 250ms, If the value of CC11 is between 21 and 30 delay the note by 100ms, and other similar conditions.

Another slightly more complex conditional message, would include an 'and' statement:

For instance, if CC11 is between 31 and 40, and note velocity is between 1 and 64, delay the note by 200ms, or if CC11 is between 31 and 40, and note velocity is between 65 and 127, delay the note by 100ms.

I'm hoping something like this could be possible, as it could potentially save me an awful lot of time on projects.

If anyone has done something like this before, or feels they know how to, I would be extremely grateful to hear from you, and please do let me know if any more information is required.

Many thanks,
Eoin
 
User avatar
Atlas007
Posts: 7855
Joined: Mon Dec 14, 2009 11:58 pm
Location: Montreal

Re: Conditional Delays Using Scripter?

Sat Nov 30, 2019 11:26 am

Naively, I'm pretty sure what you describe is feasible.
How is another story I could not ascertain myself, since I am quite green in the scripting domain. However, I'd be surprised that some aficionados here could not be up for that challenge...
LogicX 10.4.7 ( & 9.1.8),MainStage3.4.3
MBPro 17", Core2Duo, 8G, OSX 10.12.6
MacPro, Xeon 6Cores, 64GB, OSX 10.13.6
ULN8, MOTU MIDI TP-AV, C4, Eucon McControl, KorgNano, Novation SLMkII
AAS, NI, Celemony, Spectrasonics, Korg, MIDIQuest, etc...
PC, iPad3(V-Control & LogicRemote), AtariST(Notator SL), Several vintage gear
 
User avatar
fuzzfilth
Posts: 2734
Joined: Mon Aug 03, 2009 2:31 am
Location: Germany

Re: Conditional Delays Using Scripter?

Sat Nov 30, 2019 11:55 am

It's possible, both in Scripter and in the Environment, depending on the type of setup and conditions, although proper processing of NoteOffs is not entirely trivial.

However, it is unclear what Eoin actually wants.

Have you tried to implement it, but got stuck somewhere in the process and need advice to solve specific problems ? Then ask away, with specifics.

Or did you just dream this up in your armchair and want someone to code it for you ?
1 x MacPro 6core 2010 24Gb RAM
2 x MacBookPro i7 2012 16Gb RAM
OSX.13.6. Logic X.4.4
 
EoinF
Topic Author
Posts: 7
Joined: Fri Nov 29, 2019 4:19 pm

Re: Conditional Delays Using Scripter?

Sat Nov 30, 2019 1:39 pm

Atlas007,

Thanks for your reply. Similar to yourself, I feel it should be possible, although I don’t know enough about Scripter to say for sure. I do hope you’re right!

fuzzfilth,

Thank you for your reply. I have tried to research how to implement something like this, but haven’t been able to find much information relating to what I am trying to do, and my experience with Scripter is very limited, so it is not something I would be likely to work out on my own.

I would be very appreciative if someone could point me in the direction of somewhere I could learn about similar processes, or share a commonly available and easily repeatable string of code that I could try to implement myself.

However, if the process is not entirely trivial like you say, I would be very happy to pay someone to write a script for me - I definitely do not want someone to do a not-insubstantial amount of work for no reward.

Here is a more specific version of what I am trying to do (leaving out the velocity sensitive information for now):

I am using articulation sets with instruments that have different note onset times for each articulation (say 50ms for staccato, 100ms for legato, etc.). The articulation sets change articulations on each instrument using values of a CC. I want the value of this CC to also control the value of a specified delay for each note sent to the instrument, so that when I change articulations on notes in the piano roll, the timing of each note onset does not become a problem, and I do not have to manually nudge each note into time. Hopefully what I have just said makes sense.

I am not sure that the NoteOff data needs to be treated in the way I had mentioned in my original post here (that the NoteOff needs to be treated with the same delay as the NoteOn of same note, regardless of what CC change has been made between NoteOn and NoteOff) - I thought, perhaps naively, this option would have been the easier option to implement, but if it’s easier to let the note releases follow the delay time of any CC change that happens between NoteOn and NoteOff, this may well be a preferable option. I will consider/test this with situations that may arise.

Thanks again for your reply,
Eoin
 
User avatar
Atlas007
Posts: 7855
Joined: Mon Dec 14, 2009 11:58 pm
Location: Montreal

Re: Conditional Delays Using Scripter?

Sat Nov 30, 2019 2:55 pm

I think that ArtID, being an integrated part of the note event, have been implemented to switch instantaneously to the targetted articulation. In case of sampler, it actually triggers a different sample set...
LogicX 10.4.7 ( & 9.1.8),MainStage3.4.3
MBPro 17", Core2Duo, 8G, OSX 10.12.6
MacPro, Xeon 6Cores, 64GB, OSX 10.13.6
ULN8, MOTU MIDI TP-AV, C4, Eucon McControl, KorgNano, Novation SLMkII
AAS, NI, Celemony, Spectrasonics, Korg, MIDIQuest, etc...
PC, iPad3(V-Control & LogicRemote), AtariST(Notator SL), Several vintage gear
 
EoinF
Topic Author
Posts: 7
Joined: Fri Nov 29, 2019 4:19 pm

Re: Conditional Delays Using Scripter?

Sat Nov 30, 2019 4:36 pm

Atlas007, yes, I’m pretty sure it sends the message contained in the ArtID either at the same instant or just before sending the NoteOn. In my experience it is a lot quicker to use than key switches or drawing in CC data, and is more reliable/foolproof.
 
User avatar
Dewdman42
Posts: 1880
Joined: Tue Sep 09, 2014 3:01 pm
Location: Park City, UT

Re: Conditional Delays Using Scripter?

Sat Nov 30, 2019 6:29 pm

Try this for starters...

var ccnum = 11;
var lastDelay = 0;

function HandleMIDI(event) {
    if(event instanceof ControlChange && event.number == ccnum) {
        lastDelay = event.value;
        return;
    }
   
    event.sendAfterMilliseconds(lastDelay);
}
5,1 MacPro 3.46ghz x 12 128gb ram, OSX 10.14, Logic Pro 10.4.7, Mainstage3, Cubase10, StudioOne, Reaper, DP9, VEP, VSL, too many plugins to list
 
User avatar
Dewdman42
Posts: 1880
Joined: Tue Sep 09, 2014 3:01 pm
Location: Park City, UT

Re: Conditional Delays Using Scripter?

Sat Nov 30, 2019 6:40 pm

Regarding NoteOff's yes that complicates things and it will not be a trivial script to write, so you'll have to learn javascript I guess if you want to do something a little more sophisticated. Scripter doesn't know anything about duration of notes. It can only detect a NoteOn coming through and a NoteOff coming through. So you can try to match the NoteOff delay with the NoteOn... For example something simple maybe:

var ccnum = 11;
var lastDelay = 0;

var notes = new Array(128);
for(var i=0;i<128;i++) {
    notes[i] = 0;
}

function HandleMIDI(event) {
    if(event instanceof ControlChange && event.number == ccnum) {
        lastDelay = event.value;
        return;
    }

    if (event instanceof NoteOn) {
        notes[event.pitch] = lastDelay;
        event.sendAfterMilliseconds(lastDelay);
        return;
    }
    else if(event instanceof NoteOff || (event instanceof NoteOn && event.velocity == 0)) {
        event.sendAfterMilliseconds(notes[event.pitch]);
        notes[event.pitch] = 0;
        return;
    }
       
    event.send();
}
Last edited by Dewdman42 on Sun Dec 01, 2019 7:59 pm, edited 1 time in total.
5,1 MacPro 3.46ghz x 12 128gb ram, OSX 10.14, Logic Pro 10.4.7, Mainstage3, Cubase10, StudioOne, Reaper, DP9, VEP, VSL, too many plugins to list
 
User avatar
Atlas007
Posts: 7855
Joined: Mon Dec 14, 2009 11:58 pm
Location: Montreal

Re: Conditional Delays Using Scripter?

Sun Dec 01, 2019 7:53 pm

I wonder if NoteOff could be converted into NoteOn value 0 and then be treated seemingly with the algorthim?
LogicX 10.4.7 ( & 9.1.8),MainStage3.4.3
MBPro 17", Core2Duo, 8G, OSX 10.12.6
MacPro, Xeon 6Cores, 64GB, OSX 10.13.6
ULN8, MOTU MIDI TP-AV, C4, Eucon McControl, KorgNano, Novation SLMkII
AAS, NI, Celemony, Spectrasonics, Korg, MIDIQuest, etc...
PC, iPad3(V-Control & LogicRemote), AtariST(Notator SL), Several vintage gear
 
User avatar
Dewdman42
Posts: 1880
Joined: Tue Sep 09, 2014 3:01 pm
Location: Park City, UT

Re: Conditional Delays Using Scripter?

Sun Dec 01, 2019 7:59 pm

I don't understand your suggestion, sorry. Though you bring up a good point that the if statement should check for both conditions to detect a NoteOff. Corrected above.
5,1 MacPro 3.46ghz x 12 128gb ram, OSX 10.14, Logic Pro 10.4.7, Mainstage3, Cubase10, StudioOne, Reaper, DP9, VEP, VSL, too many plugins to list
 
User avatar
Atlas007
Posts: 7855
Joined: Mon Dec 14, 2009 11:58 pm
Location: Montreal

Re: Conditional Delays Using Scripter?

Sun Dec 01, 2019 8:27 pm

In order to circumvent the NoteOff issue:
Convert all NoteOff events into NoteOn events value "0".
Then let the algorithm treat those as the other NoteOn events.
LogicX 10.4.7 ( & 9.1.8),MainStage3.4.3
MBPro 17", Core2Duo, 8G, OSX 10.12.6
MacPro, Xeon 6Cores, 64GB, OSX 10.13.6
ULN8, MOTU MIDI TP-AV, C4, Eucon McControl, KorgNano, Novation SLMkII
AAS, NI, Celemony, Spectrasonics, Korg, MIDIQuest, etc...
PC, iPad3(V-Control & LogicRemote), AtariST(Notator SL), Several vintage gear
 
User avatar
Dewdman42
Posts: 1880
Joined: Tue Sep 09, 2014 3:01 pm
Location: Park City, UT

Re: Conditional Delays Using Scripter?

Sun Dec 01, 2019 8:36 pm

You still have to detect NoteOn and NoteOff separately in order to handle them slightly differently. Look closely at the code to see the difference. The script has to keep track of what the delay was for the NoteOn coming through and then use that same amount of delay for its matching NoteOff when it finally comes through.
5,1 MacPro 3.46ghz x 12 128gb ram, OSX 10.14, Logic Pro 10.4.7, Mainstage3, Cubase10, StudioOne, Reaper, DP9, VEP, VSL, too many plugins to list
 
User avatar
Atlas007
Posts: 7855
Joined: Mon Dec 14, 2009 11:58 pm
Location: Montreal

Re: Conditional Delays Using Scripter?

Sun Dec 01, 2019 8:41 pm

But if the NoteOff event gets converted into a NoteOn "0" event it gets treated (added the delay) as the other NoteOn events...
LogicX 10.4.7 ( & 9.1.8),MainStage3.4.3
MBPro 17", Core2Duo, 8G, OSX 10.12.6
MacPro, Xeon 6Cores, 64GB, OSX 10.13.6
ULN8, MOTU MIDI TP-AV, C4, Eucon McControl, KorgNano, Novation SLMkII
AAS, NI, Celemony, Spectrasonics, Korg, MIDIQuest, etc...
PC, iPad3(V-Control & LogicRemote), AtariST(Notator SL), Several vintage gear
 
User avatar
Dewdman42
Posts: 1880
Joined: Tue Sep 09, 2014 3:01 pm
Location: Park City, UT

Re: Conditional Delays Using Scripter?

Sun Dec 01, 2019 8:55 pm

show us your solution then, I'm not understanding what you're suggesting.
5,1 MacPro 3.46ghz x 12 128gb ram, OSX 10.14, Logic Pro 10.4.7, Mainstage3, Cubase10, StudioOne, Reaper, DP9, VEP, VSL, too many plugins to list
 
EoinF
Topic Author
Posts: 7
Joined: Fri Nov 29, 2019 4:19 pm

Re: Conditional Delays Using Scripter?

Sun Dec 01, 2019 10:08 pm

Hello Dewdman42,

Thank you so much for taking the time to help me with this - I really appreciate it.

I have tried both of your scripts and they are both working as expected (if my understanding of them is correct).

Am I right in saying each of these scripts will create a delay where the the delay time in milliseconds is equal to the event.value (between 0 and 127)? I tried adding a multiplier (event.value * 10) on line 6 of your first example and it seems this is the case.

I'm hoping that the event.value can change the delay time to specified values that are independent of the event.value itself (that is, not a multiple of the event.value in any way), as this event.value will also be used to change the articulation.

It will have to be set up as a table of specified numbers. For example:

event.value 0 to 9 = 200ms delay
event.value 10 to 19 = 100ms delay
event.value 20 to 29 = 250ms delay etc.

I've tried editing your original script to do something like this. I am very new to this language, so perhaps I am not doing everything correctly, or in the most efficient way, but hopefully what I've done here makes sense.

var ccnum = 11;
var lastDelay = 0;
delaytime1 = 1000;
delaytime2 = 2000;

function HandleMIDI(event) {
    if(event instanceof ControlChange && event.number == ccnum && event.value >= 0 && event.value <= 9) {
        lastDelay = delaytime1;
        return;
    }
    else if(event instanceof ControlChange && event.number == ccnum && event.value >= 10 && event.value <= 19) {
        lastDelay = delaytime2;
        return;
        }
       
    event.sendAfterMilliseconds(lastDelay);
}


It seems to be doing what I want it to do (I'm going to forget about the separate treatment of NoteOffs for now). I'll spend some more time with this tomorrow and actually input what I hope to do with it and give it an initial test.

One question - does this script delay all MIDI messages (modulation, pitch bend, etc.) or just notes?

Thank you again for your help,
Eoin
 
User avatar
Dewdman42
Posts: 1880
Joined: Tue Sep 09, 2014 3:01 pm
Location: Park City, UT

Re: Conditional Delays Using Scripter?

Sun Dec 01, 2019 10:34 pm

what I would do if I were you is to use an array that maps the cc value to the desired delay. Then you can assign all 128 cc values to any delay you want.

So something like this:

var ccnum = 11;

var delayMap = [200,200,200,200,200,200,200,200,200,200,100,100,100,100,100,100,100,
                100,100,100,250,250,250,250,250,250,250,250,250,250,0,0,0,0,0,0,0,0,
                0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
                                               
var lastDelay = 0;
var notes = new Array(128);
for(var i=0;i<128;i++) {
    notes[i] = 0;
}

function HandleMIDI(event) {
    if(event instanceof ControlChange && event.number == ccnum) {
        lastDelay = delayMap[event.value]
        return;
    }

    if (event instanceof NoteOn) {
        notes[event.pitch] = lastDelay;
        event.sendAfterMilliseconds(lastDelay);
        return;
    }
    else if(event instanceof NoteOff
            || (event instanceof NoteOn && event.velocity == 0)) {
        event.sendAfterMilliseconds(notes[event.pitch]);
        notes[event.pitch] = 0;
        return;
    }
       
    event.send();
}


The first script is very simplistic, it delays all events for whatever the CC currently is and doesn't try to match the NoteOn to the NoteOff delay time. The second one makes sure the NoteOff is delayed by the same amount as NoteOn.

Anyway, hopefully that is enough info to get you started. Check out endless javascript resources on the net and tweak the script as needed!
Last edited by Dewdman42 on Mon Dec 02, 2019 1:35 am, edited 5 times in total.
5,1 MacPro 3.46ghz x 12 128gb ram, OSX 10.14, Logic Pro 10.4.7, Mainstage3, Cubase10, StudioOne, Reaper, DP9, VEP, VSL, too many plugins to list
 
EoinF
Topic Author
Posts: 7
Joined: Fri Nov 29, 2019 4:19 pm

Re: Conditional Delays Using Scripter?

Sun Dec 01, 2019 10:54 pm

Thank you so much for your help! You definitely have given me enough info to get started, and then some! I'll spend some more time learning the language and working on this script, and report back here when I have made some progress (or more likely when I come to a point where I haven't a clue what I am doing!).

Eoin
 
EoinF
Topic Author
Posts: 7
Joined: Fri Nov 29, 2019 4:19 pm

Re: Conditional Delays Using Scripter?

Tue Dec 03, 2019 5:55 pm

With thanks to Dewdman42 I'm making some good progress with this script. I decided to go with the original method he suggested (and not the array two posts above) as I feel it would be easier to make quick changes to the delay times on the script if I need to in the future. Here it is as it stands, including some script that is not working towards the end.

 /* Editable Delay Times */
LegatoStartSlow = 0;
LegatoStartMedium = 100;
LegatoStartFast = 200;
LegatoSlow = 0;
LegatoMedium = 100;
LegatoFast = 200;
Spiccato = 250;
Staccatissimo = 245;
Staccato = 240;
Sfz = 240;
Pizzicato = 250;
BartokSnap = 250;
ColLegno = 250;
Trills = 250;
Harmonics = 200;
Tremolo = 200;
MeasuredTremolo = 200;
Marcato = 250;
MarcatoOverlay = 250;

/* Keyswitch CC */
var ccnum = 58;
var lastDelay = 0;

function HandleMIDI(event) {
    if(event instanceof ControlChange && event.number == ccnum && event.value >= 11 && event.value <= 15) {
        lastDelay = Spiccato;
        return;
    }
    else if(event instanceof ControlChange && event.number == ccnum && event.value >= 16 && event.value <= 20) {
        lastDelay = Staccatissimo;
        return;
    }
    else if(event instanceof ControlChange && event.number == ccnum && event.value >= 21 && event.value <= 25) {
        lastDelay = Staccato;
        return;
    }
    else if(event instanceof ControlChange && event.number == ccnum && event.value >= 26 && event.value <= 30) {
        lastDelay = Sfz;
        return;
    }
    else if(event instanceof ControlChange && event.number == ccnum && event.value >= 31 && event.value <= 35) {
        lastDelay = Pizzicato;
        return;
    }
    else if(event instanceof ControlChange && event.number == ccnum && event.value >= 36 && event.value <= 40) {
        lastDelay = BartokSnap;
        return;
    }
    else if(event instanceof ControlChange && event.number == ccnum && event.value >= 41 && event.value <= 45) {
        lastDelay = ColLegno;
        return;
    }
    else if(event instanceof ControlChange && event.number == ccnum && event.value >= 46 && event.value <= 50) {
        lastDelay = Trills;
        return;
    }
    else if(event instanceof ControlChange && event.number == ccnum && event.value >= 51 && event.value <= 55) {
        lastDelay = Harmonics;
        return;
    }
    else if(event instanceof ControlChange && event.number == ccnum && event.value >= 56 && event.value <= 60) {
        lastDelay = Tremolo;
        return;
    }
    else if(event instanceof ControlChange && event.number == ccnum && event.value >= 61 && event.value <= 65) {
        lastDelay = MeasuredTremolo;
        return;
    }
    else if(event instanceof ControlChange && event.number == ccnum && event.value >= 66 && event.value <= 70) {
        lastDelay = Marcato;
        return;
    }
    else if(event instanceof ControlChange && event.number == ccnum && event.value >= 71 && event.value <= 75) {
        lastDelay = MarcatoOverlay;
        return;
    }
   
    /* Not Yet Working */
    else if(event instanceof NoteOn && event.velocity > 0 && event.velocity <= 64 && event instanceof ControlChange && event.number == ccnum && event.value >= 6 && event.value <= 8) {
        lastDelay = LegatoStartSlow;
        return;
        }
       
    event.sendAfterMilliseconds(lastDelay);
}


Everything receiving just a cc event.value to specify the delay time seems to be working as expected, but I have not yet figured out how to specify a delay time for articulations that require a cc event.value and NoteOn velocity. I have written an attempt at this towards the end of the script labelled "Not Yet Working".

In the case of the attempt above, the delay time for LegatoStartSlow should be used if (and only if) both the cc event.value is between 6 and 8, and the NoteOn velocity is between 1 and 64. There will eventually be more cases that will use the same event.value range, but with different velocities, and another few cases that will use the same NoteOn velocity range but with different event.value ranges. The NoteOn velocity should not affect the delay time of the articulations that require only cc event.value ranges (the working cases above). Hopefully all of this makes sense.

In my attempt, I have tested each part of it separately...

    if(event instanceof NoteOn && event.velocity > 0 && event.velocity <= 64) {
        lastDelay = LegatoStartSlow;
        return;
        }
       
    event.sendAfterMilliseconds(lastDelay);
}

and

    if(event instanceof ControlChange && event.number == ccnum && event.value >= 6 && event.value <= 8) {
        lastDelay = LegatoStartSlow;
        return;
        }
       
    event.sendAfterMilliseconds(lastDelay);
}


...and alone they work as expected, but I have not been able to figure out how to combine the two so that they work together. As it stands the code labelled "Not Yet Working" has no effect on the output (and yes, I have tested it giving a value other than zero for LegatoStartSlow!).

Any help with this problem would be greatly appreciated.

Thanks,
Eoin
 
User avatar
Dewdman42
Posts: 1880
Joined: Tue Sep 09, 2014 3:01 pm
Location: Park City, UT

Re: Conditional Delays Using Scripter?

Tue Dec 03, 2019 6:19 pm

Now that I see what you're trying to do, correct for articulation latency... You might want to consider using articulationID instead of a CC in front of the event. But if you're using an articulationSet to send Keyswitches, then no you can't do that because the articulationSet will strip off the articulationID when it sends the key switch.

As far as combining the two aspects, what you should do is rename the lastDelay variable to lastCC, then determine the delay to use later when the Note comes in..


 /* Editable Delay Times */
LegatoStartSlow = 0;
LegatoStartMedium = 100;
LegatoStartFast = 200;
LegatoSlow = 0;
LegatoMedium = 100;
LegatoFast = 200;
Spiccato = 250;
Staccatissimo = 245;
Staccato = 240;
Sfz = 240;
Pizzicato = 250;
BartokSnap = 250;
ColLegno = 250;
Trills = 250;
Harmonics = 200;
Tremolo = 200;
MeasuredTremolo = 200;
Marcato = 250;
MarcatoOverlay = 250;

/* Keyswitch CC */
var ccnum = 58;
var lastCC = 0;

function HandleMIDI(event) {
    if(event instanceof ControlChange && event.number == ccnum) {
        lastCC = event.value;
        return;
    }
   
    if(event instanceof NoteOn) {
        var delay = 0;
        if(lastCC >=11 && lastCC <=15) {
            if(event.velocity>=1 && event.velocity <=64) {
                delay = Spiccato;
            }
            else {
                delay = BiggerSpiccato;
            }
        }
        else if(lastCC >= 16 && lastCC <= 20) {
            if(event.velocity >=1 && event.velocity <=64) {
                delay = SomeOtherAmount;
            }
            else {
                delay = AnotherBiggerDelay;
            }
        }
        /*
           .
           .
           .  More conditions here
           .
           .
        */
        event.sendAfterMilliseconds(delay);
    }
}
5,1 MacPro 3.46ghz x 12 128gb ram, OSX 10.14, Logic Pro 10.4.7, Mainstage3, Cubase10, StudioOne, Reaper, DP9, VEP, VSL, too many plugins to list
 
User avatar
Dewdman42
Posts: 1880
Joined: Tue Sep 09, 2014 3:01 pm
Location: Park City, UT

Re: Conditional Delays Using Scripter?

Tue Dec 03, 2019 6:37 pm

another comment. it sounds like you are trying to do latency compensation for different articulations. So you will ultimately use a negative track delay to bring them all back forward again. That means you need to delay your NoteOff's by the longest-used delay amount, so that with the negative track delay they will be NoteOff when you intended them to be. Hope that makes sense.
5,1 MacPro 3.46ghz x 12 128gb ram, OSX 10.14, Logic Pro 10.4.7, Mainstage3, Cubase10, StudioOne, Reaper, DP9, VEP, VSL, too many plugins to list