Data feedback loop problem using a Parameter Listener object.

Guillaume DUPONT's icon

Hi,
I'm currently designing a MIDI editor for a hardware synth under Max for Live (Max 8.1.5 | Ableton Live 10.1.18). My software architecture is based on a MVC pattern. I would like to use the new Parameter Listener object in order to simplify my dev, but I can't solve a basic problem and I would appreciate a bit of help.

When the user changes let's say a dial's value in the View, the callback function attached to that dial is called in my Controller js code, I can then update my Model : great. The problem occurs when the Model sends a new value to the Controller for that dial. The Controller updates the dial correctly with a call to the Parameter Listener setvalue() function. But the setvalue() function also makes the dial's callback function to be called again (because the dial has changed, I guess...) and the new dial's value is sent back... to the Model ! What a nice feedback loop.

Screenshot of my PL Test patcher.

Indeed, I think I would need a way to determine if the dial has changed because of a user action in the View, or because the Controller tries to update it. Before I started to develop with js, designing classic Max patchers, I used to manage this scenario with silent UI updates using "set $1" messages in front of all my UI objects. I've replicated this mecanism with success in js, using a Max object and a message("set", value) function call. The silent UI update works fine but the Parameter Listener callback function attached to the UI object is still called !

Ha ha ! What a nightmare !! What can I do ? Any idea ?

Here is an example of what I would like to achieve with a simple dial object and MVC simulation :

Max Patch
Copy patch and select New From Clipboard in Max.

JS code (file name : "plTest.js") :

//---------------------------------------------------------- js setup
autowatch = 1;

//-------------------------------------------------- global variables
var g_pl = null; // parameter listener object

//-------------------------------------------------------------------
function init() {
  if (!g_pl) {
    g_pl = new ParameterListener("myDial", onMyDialChanged);
  }
}

//-------------------------------------------------------------------
function msg_int(data) { 
  post("msg_int: " + data + "\n");
  updateView(data);
}

//-------------------------------------------------------------------
function updateView(data) {
  if (g_pl) {
    g_pl.setvalue(data);
  }
}

//-------------------------------------------------------------------
function onMyDialChanged(data) {
  post("onMyDialChanged: " + data.value + "\n");
    
  // update model
  outlet(0, data.value);
} onMyDialChanged.local = 1;

//--------------------------------------------------------------- eof
11OLSEN's icon

Hi,
I think you just need to block execution of onMyDialChanged() when you call updateView(), then remove the block with a deferlow call or delayed.

Guillaume DUPONT's icon

Hi,
It's interesting, can you develop a bit ? How can I block the execution of a callback function like onMyDialChanged() ? Or maybe I should block the code inside of it rather than the call ? Could you give me an example in js ? Thx !

11OLSEN's icon

I'm only using js if i have to. So I'm really only guessing here.

updateView(){
block=true
set dial
deferlow( releaseBlock() )
}

releaseBlock() {
block=false
}

MyDialChanged() {
if (!block){ update model }
}

keepsound's icon

Hi...I don't have any feedback....

Jeremy's icon

This works properly for me, too, whether setting from the model or the view.

Guillaume DUPONT's icon

Thank you 11olsen, I will try that.

Guillaume DUPONT's icon

Hi KeepSound & Jeremy,

Thank you for your time and your tests. This is strange... Have you tried to click on the "init" message in the controller simulation before testing my js code, just in case ? Do you see the red number changing in the model simulation while changing the black one ?

Let me clarify what I'm trying to achieve : in my MIDI editor design, the data stored in the model are indeed some parameters values I read from a hardware synthesizer via MIDI sysex messages, or from a file on the computer disk where they have been previously stored.

My model contains two components : a data manager and a sysex manager. Everytime a new set of values is loaded into the data manager (from the synth or from a file), these are immediately sent to the controller which update all the UI objects in the view. Everytime the controller sends a new value to the model after a value change in the view (user action), it is first stored at the right address in the data manager (for later operations like file saving, etc.), then it is sent to the hardware synth via a dedicated sysex message, in order to keep things perfectly in sync.

Let's now go back to my little simulation : the black number represents some data stored in the model (data manager component). When this value is changing in the model, it must be sent to the controller which will update the corresponding UI object in the view. This part of my design works correctly using the ParameterListener object, and I would like the processing to stop here. Now, here is my problem : while you change the black number value in the model, have a look to the red number value changing too. As you can see, the value change coming from the model is... sent back to it ! This is what I call a data feedback loop. In my case it is quite problematic and also not very clean, since the set of values which are potentially loaded from the synth itself into the model will be immediately bulk sent back to the machine via a sysex messages, a real nightmare !

From my point of view, all this pain is due to the ParameterListener internal mechanism : the callback functions attached to the UI objects are called whether these UI objects values are changing due to a user action, or because they are set programmatically using the setvalue() function in the js code. There seem to be no way to determine what is the cause of the value changing (user or code), and it makes a great difference for the dev. It would have be very practical if a kind of setvaluesilently() function would have been provided in the ParameterListener object.

As I've explained in my initial post, at the time I was designing classic Max patchers without js, I used to manage this scenario quite smoothly and elegantly via silent UI updates using "set $1" messages in front of all my UI objects (see an Max patch example below). I would like to find an elegant solution with the new ParameterListener object which would produce the same result.

Max Patch
Copy patch and select New From Clipboard in Max.

I don't understand why such a mechanism has not been foreseen in the ParameterListener object, since a lot of UI Max object provide the possibility to be set silently thanks to a "set" message. Maybe I'm missing something ? Or maybe there is a good reason why it can't be implemented this way with js ?

Any idea / solution ? Thank you in advance guys.

Best,
Guillaume

Jeremy's icon

OK, if the red number box being updated is the feedback, then yes, I see the feedback loop. But I don't consider this to be a major problem. A few notes:

  • parameters have ranges, types, etc. and setting a parameter with an arbitrary value will not always result in the parameter getting the value you intended. The value returned by the callback is the actual value of the parameter.

  • parameters are different than the object, they are a value-managing component of the object. You are being notified when the value changes, and it changes whether you use set or not. Whether the owning object sends output into the patcher is unknown by the parameter. So your "set" strategy won't work as you intend (and it would be problematic if it did).

  • the notification that the value changed is not synchronous (as I suggested to you in email, that was incorrect information), so the setting flag I proposed (similar to the idea from @11olsen) won't work, either.

I think there are a couple of ways to consider improving this. We could introduce synchronous behavior to setvalue so that it's possible to filter it out if you need to (there are technical reasons why the asynchronicity was introduced, but it may be possible to solve those), or we could add a 'silent' property to the listener to do this automatically for all calls to setvalue(), or a setvalue_silently() method as you suggest. Or some combination.

As I was working on these objects, I dealt with the issue by simply not setting the model directly, always going via setvalue() and updating values in the model from the callback. So for instance, I read in a sysex file and it looks like:

parseSysex()
for each parameter:
setvalue() -> callback() -> updateModel()

This is the simplest way of handling the way that setvalue(), and the way I would recommend handling it until one of the solutions suggested above can be implemented. You can do this for the dynamically arriving values from the synth, as well, although I understand why that's not ideal. The synth won't freak out if it gets a parameter change back in response to sending one -- DAWs do this constantly.

Thanks for bringing this up, though. It shouldn't be hard to add esp. the latter two proposals above, but it might take a version of two of Max before those improvements (or something like them) arrive.

Guillaume DUPONT's icon

Hi Jeremy,
Thank you for these extra infos, I understand a bit more now the complexity behind all this stuff. It would be really great if some silent update mechanism could be added to the ParameterListener object in the near future, I will follow that with attention.
Generally speaking, what is the main goal of the silent "set" update mechanism provided by many UI Max objects ? Is it in relation with this kind of scenario, or are there any other situations where a silent update is usefull or even essential ? I'm asking the question because I'm quite new to Max & JS and I would like to adopt the best strategy as I can in my dev, as sometimes things get very time consuming... 😅
Best,
Guillaume

Jeremy's icon

So real quick: the Parameter paradigm is fairly modern compared to the object part of Max. The 'set' stuff is related to the behavior of objects in the patcher, where you need some way of controlling flow (e.g. does this box send a value from its outlet or not). The Parameter stuff is a more infrastructural system which manages value and doesn't really concern itself with how those values are communicated throughout the software (you can set them, get them and subscribe to changes).

And the ParameterListener stuff is very recent, just a version or two old. We've never had a user-facing mechanism for watching arbitrary Parameter values (although we have stuff in Max like 'pattr' which kind of does similar things).

Anyway, your feature request is totally legitimate -- if you were working with Parameter values in C/C++, you'd be able to determine whether you were the cause of the change notification (because it is synchronous). In the JS world, this isn't the case (although it arguably should be, still thinking about whether it's possible and safe to do so), so having an additional mechanism to set a value without being called back after doing so seems necessary. Not everyone will need it, but those who do will appreciate the addition.