Jump to content

MIDI Scripter: How to avoid loop when using ParameterChanged and SetParameter


zAoumZero

Recommended Posts

Hi,

many of you noticed the weird behaviour of the PluginParameters array and the ParameterChanged/SetParameter function that turn into endless loop.

After some tweaking, I discovered that ParameterChanged() have and undocumented property named "caller", that get a value of "null" if ParameterChanged() is called from a change on the UI or from SetParameter(), and get as value the function from where it's triggered if it's triggered from any function elsewhere in the code (excepted of course SetParameter() ).

So I've done a little POC that show how to use this hidden property, to be able to use UI controls without looping, as well as linking this UI controls to external CC, and not getting in trouble when an external CC update a value of PluginParameters array and so refresh the UI... without looping.

The POC contains many Trace() so you can check all this stuff. It is available here on Github: MIDI-SCRIPTER-PLAYGROUND: Avoid Looping

Just copy/paste the content of .js file into your MIDI Scripter.

If you have to deal with other undocumented functions or objects, as the documentation is really crappy, this JS snippet may help you:

var objs = Object.getOwnPropertyNames(TheObjectYouWantToTest);
for(var i in objs ){
  Trace(objs[i]);
}

or to know if a given property is a function:

var functionNames = [];
Object.getOwnPropertyNames(TheObjectYouWantToTest).forEach(function(property) {
  if(typeof obj[property] === 'function') {
    functionNames.push(property);
  }
});
for(var i in functionNames )
{ 
	Trace(functionNames[i]);
}

Have fun.

Edited by zAoumZero
  • Like 1
Link to comment
Share on other sites

that is great and useful info!!!  Thank you.  

your profile is new so I don't know how much Scripter experience you have, but you appear to have good experience with javascript!  So welcome to Scripter!

Some more background in case you are coming up to speed on Scripter....

Its not normal for a user code to actually ever call the ParameterChanged function directly.  That is a so called "callback" function.  Now a user can certainly do that if they want, but they should not ever need to. 

There is an API function called UpdatePluginParameters(), which is what users should normally call for specific times they need to have the UI explicitly updated, and which sometimes they must call from within ParameterChanged.  And that is where sometimes they hit endless looping.   The API function UpdatePluginParameters() may also be calling into ParameterChanged "caller" set to nil also, I don't have time to check any of this out any time soon...  

Note that UpdatePluginParameters may be doing other important work under the covers that we don't know about in order to handle updating the internal PluginParameters structures, before or after calling the ParameterChanged callback for user code.  So generally it would not be a good idea to call the ParameterChanged function directly in user code pretty much ever that I can think of.

 

 

 

Edited by Dewdman42
Link to comment
Share on other sites

thanks, I forget to test this UpdatePluginParameters() , you're right, ParameterChanged "caller" property is null with this one too...I'm not sure at this point to really understand the workflow of this 3 functions but as I understand, the UpdatePluginParameters() at least save you of writing many times SetParameter() ... I really don't understand the point to have a SetParameter() that at first seems to allow to update only one parameter of the UI but in fact trigger ParameterChanged() that then update everything! (but this behaviour is fine in the case of parameters depending of the value of other ones).

I think the "caller" prop. is used in the deepness of Logic, probably when changes come from Automation, precisely to avoid to trigger users functions. But being able to pass the appealing function as parameter to ParameterChanged() open a lot of possibilities ( or another kind of loops 🙃 ).

I've added another snippet to Github to test that:

MIDI_Scripter_passing_function_to_ParameterChanged

 

Link to comment
Share on other sites

SetParameter is used to set the value of one of the parameters of your script.

The PluginParameters array itself does not hold those values.  That array is used to "configure" the list of parameters..what they are....how some of them might be displayed on the UI, etc.  But when you start using it..the actual values are stored somewhere else internally.

So SetParameter is used to assign a value to one of the "configured" parameters.

UpdatePluginParameters ,on the other hand, reconfigures the internal structures and the UI after you may have updated or changed the PluginParameters configuration array.  For example if you have changed some parameters to hidden or unhidden.

ParameterChanged, is a user-provided callback function...which allows you to execute some user code whenever it is called by LogicPro.  It is typically called for sure whenever the user changes the UI by moving a slider or something like that.  Then the script can deal with the fact that a certain parameter was changed by the user through the UI.

It is also called at other times...like during startup of the script when the project is being loaded, and perhaps it ends up getting called indirectly from inside the UpdatePluginParameters execution.  I don't think it gets called from inside SetParameter, but I could be wrong about that right now its been a while since I used this stuff.

But anyway, sometimes inside the ParameterChanged callback, if your user code then calls another SetParameter call, or call UpdatePluginParameters...that's when you can end up in some endless call recursion loop.

 

 

 

 

Edited by Dewdman42
  • Like 1
Link to comment
Share on other sites

Yes, I was misusing ParameterChanged, thinking it was here to update actual parameters :-(

Quote

I don't think it gets called from inside SetParameter

it does..but as SetParameter pass to it param/value you can finally choose which callback to execute...and if a parameter change must trigger another param change, that might work breaking out of the condition after calling another outside function, where another SetParameter will be done...my first snippet is pointless so, unless you really need to use SetParameter inside ParameterChanged.

thanks for the explanation!

Link to comment
Share on other sites

I'm not quite following what you're saying...  But anyway, I would suggest you have a read through the Scripter user manual to get the low down on the basics of Scripter it will all make sense once you have done a few.  Its not really that complicated.  

But anyway consider a situation where if someone changes a value of one slider on the UI, and you want that to automically cause two other parameters to also be changed automatically in sync with it.  That's when someone might call SetParameter from inside ParameterChanged.

Another example would be if say you have a checkbox...and when the box is checked, another group of 13 parameters are hidden or unhidden from the UI.  Again..that would happen inside ParameterChanged, and you'd have to call UpdatePluginParameters to cause them to change whether they are shown or not.

In both of the above cases, a recursive call to ParameterChanged can occur.  And if not careful....endless looping and even just needlessly deep recursion.

Edited by Dewdman42
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...