ComboBox AsyncUpdate


#1

I ran into a problem, and not sure if it’s only ComboBox related, but maybe there is a workaround that I didn’t think of…

1, I set a lambda on ComboBox’s onChange member, to update a parameter in the processor.
2, I run a timer in the Editor class and iterate through a couple of Components, including the ComboBoxes, calling an update function on them, so they can update their actual selected item, based on the parameter changes.
3, But quite a lot of times the ComboBox won’t update to the user selected item, because the selection’s setSelectedId() method calls for an async update for my onChange lambda method and my timer will be called earlier than my onChange resetting the selectedId back to its previous value. So when the onChange is called, the actually selected id is already back to its previous value.

The same happens when I use a ComboBox::Listener with the comboBoxChanged method…

Is there a way to tell the ComboBox to call the onChange method synchronously?


#2

It’s hard to tell without the code and perhaps I’m getting it wrong, but could it be that you’re not using the right NotificationType ?

void timerCallback() override
{
    ...
    comboBox.setSelectedId (theId, dontSendNotification);
}

#3

Thanks for your suggestion, but that doesn’t matter (actually I use dontSendNotification), because this is what happens in order:

1, the user selects a new item from the list
2, ComboBox calls setSelectedId with aSync Notification, that will set the ID correctly but not calling the onChange method right then, but aSync. (This is the point where those callbacks should be called to keep this problem happening).
3, TimerCallback is called, I compare the parameterValue with the actual selectedID and as they are different (the ComboBox selection already set its selectedID correctly in the previous step) I update the selectedID with the parameter, so reverting the change.
4, The aSync onChange or Listener methods are called (not from step 3 as that is a “dontSendNotification” setting, but the aSync ones initiated in step 2) , but then the selectedID is already reverted in comboBox. So the user change is not happening.

This is the case about 1 out of 5 times with a 30FPS timer. The other 4 times the order will be right so the aSync onChange/Listener methods will be called before the TimerCallback checks and revert the changes. So it’s pretty much by chance if the aSync callbacks or the TimerCallback is called first. I am not sure why the Listeners and onChange is called aSync at all, as that will give a huge time gap between setting the new ID and calling the callbacks… but probably there is a reason for it.


#4

IMHO these two methods don’t mix well. For updating components I would stick to just using notifications and for pure paint updates (i.e. read only operations!) I would use a timer, if necessary at all.

I can’t comment on the reasoning to rely on the asynchronous default notification in ComboBox though, IMHO it wouldn’t hurt to change that, but there might be implications…


#5

I am not sure I understand what you mean here. I would appreciate if you could elaborate!


#6

What I meant is, your timer should only update painting. When a ComboBox is changing, rely on the listener mechanics to propagate the change. There is no reason IMHO to synchronise anything from a timer, since you have two independent mechanisms fighting each other.

The change of the combobox is synchronised to the parameter, if you an AudioProcessorValueTreeState::ComboboxAttachment for instance. Or if you implement the onChanged method like you did, it is also updating quite well, there is no need to do anything with the value in the timerCallback()…


#7

I am still not sure what you mean where I should update the comboBox’s value if the parameter is changed by the host. That’s the TimerCallback is for in this case.


#8

Like I said, if you use the AudioProcessorValueTreeState::ComboboxAttachment, the change from the parameter is propagated automatically.
Another option is to add the GUI as an AudioProcessorValueTreeState::Listener and listen to the parameterChanged() message.

Are you using the AudioProcessorValueTreeState at all?
If not I would have to check, how it is done without, I always used it because it is more convenient IMHO…


#9

Yeah thanks, I know there is a bit more complicated Async update mechanism in VTS Attachments, but I am still looking for a way for updating a comboBox value without VTS. I didn’t met this problem in other Components (maybe just didn’t notice), so it’s strange that ComboBox use an Async callback system.


#10

Regardless of what mechanics are being used to manage the parameter, VTS or not, what i do is create a base class which i can use to connect components to parameters in the audio line, when the parameter is changed by the host, i broadcast a parameter changed call.

On the UI side, i let the objects inherit from ASyncUpdater. If the parameter change call comes from the MessageManager thread, I update sync from the call, if not, I post a sync message to myself to update the UI state from the Message thread.

this is roughly designed around the way the attachment classes work, however i found it simpler to create unified base which i can inherit into any GUI components to manage a connection to a parameter in the audio line. Usually it involves inheriting from the class, and then filling out a single function which is called in a thread safe manner to update the UI, letting the base class handle all of that logic.

Let me know & i’m happy to share w/ you Kevin : )


#11

Cheers Jake, yeah that’s a good solution too, I just didn’t want to create several new classes while prototyping something, but sure as the project gets further the listeners/broadcasters are the way.


#12

Ahh I see now Kevin.

I’m not at my computer but you may find that the lamba callback is async while the actual inherited listener subclass is sync or something like that

Potentially inheriting ComboBox::Listener into your component and moving your code into the comboBoxChanged function will solve it instead