Slow response on setvalue of slider


#1

I am working on an application where I am responding to a midi controller then trying to reflect the value on a filmstrip slider. The application is mixer with several windows open showing different views that I place on different monitors. The maincomponent window contains channel faders (variable number) and a second window called masterdlg(simple dialog window)contains faders for master/aux and busses.

When a midi controller is received, the controller is mapped to a window and fader control and uses setValue to move the fader. On the maincomponent window, the faders move very quickly with almost no delay. But on the masterdlg window they move very slowly.

If I move one fader quickly from 0 to 100% then back to 0, the fader takes about 10 second to reach 100% and another 10 to go back to 0. (running compiled in debug but in release mode its a bit faster but still painfully slow).

If I move another fader while the first is moving, it will not move until all the moves on the first are complete. I have tried using callFunctionOnMessageThread and there is no noticable difference.

The code between the maincomponent window and the masterdlg are almost identical.

Does anyone have any idea why this would be? Something different in the way the maincomponent works over a dialog window?


#2

So the updating is running from a separate thread? It sounds like that may be related, but without examples of what the code is doing it’s hard to know what the actual issue is


#3

I thought if I described the problem someone may recognize the problem. I will try to put the important code. There is a midi handler class called midimix. Its declaration is this:

class MidiMix    : public MidiInputCallback
{
public:
    MidiMix();
    ~MidiMix();
...
   void handleIncomingMidiMessage(MidiInput *sourced, const MidiMessage & message);
...
}

The constructor is:
MidiMix::MidiMix() 
	: backgroundThread ("Midi Engine"),
	sampleRate (0)
{
backgroundThread.startThread();
.. opens midi devices and sends initial midi data to controller
}

maincomponent class creates a single object:
MidiEngine = new MidiMix;

Channel sliders are all created in the maincomponent construction:
for (int x=0; x<
ChannelCount ; x++){
...
volume[x] = new FilmstripSlider ("Volume");
		volume[x]->setTextBoxStyle (Slider::NoTextBox, 0, 0, 0);
		volume[x]->setSliderStyle(Slider::LinearVertical);
		
		volume[x]->setImage (simage, Cont_Frames[MIX_Volume], false);
		volume[x]->addListener(this);
		addAndMakeVisible (volume[x]);
		volume[x]->setColour(Slider::textBoxBackgroundColourId ,Background);
		volume[x]->setRange(0,1024,1);
		volume[x]->setValue(Mixers[CurrentMix]->m_levelvalue[x]);
...  (Other controls are created also)
}

The constructor also opens the masterdlg:
        MasterD = new MasterDlgWindow();
	MasterD->setColour(ResizableWindow::backgroundColourId, Background);
	MasterD->setName("Master");
	MasterD->setVisible(true);
	MasterD->setResizable(true,true);

	int x=atoi(Properties->getValue("Master-x", "1").getCharPointer());
	int y=atoi(Properties->getValue("Master-y", "1").getCharPointer());
	int w=atoi(Properties->getValue("Master-width", "625").getCharPointer());
	int h=atoi(Properties->getValue("Master-height", "450").getCharPointer());
	
	MasterD->setTopLeftPosition(x,y);
	MasterD->setSize(w,h);

#4

The message posted before I was done.

The midimix handleIncomingMidiMessage parses the midi data then updates the control.

If the midi controller is for a master fader movein the masterdlg the code is:

    Mixers[ch]->m_masterVolumeValue = (float)value * 8;
   Mixers[ch]->m_masterVolume = again(value * 8, 1024);//pow((double)10.0, ((double)value*8-(double)767)/(double)256);
   if (Mixers[ch]->m_masterVolume < 0.0011) Mixers[ch]->m_masterVolume = 0.0f;
   if (MasterDlgOpen)
   {
	const MessageManagerLock Lockit;
	MasterThis->Volume[ch]->setValue(Mixers[ch]->m_masterVolumeValue, dontSendNotification);
}

If the midi controller is for the channel fader the code is:

    const MessageManagerLock Lockit;
    saveThis->leftlabel[1]->setText(" ", dontSendNotification);
   float change = Mixers[m_CurrentMix]->m_levelvalue[chmap] - (float)value;
   float gain = again(value, 1024);//(float)pow((double)10.0, ((double)value-(double)767)/(double)256);
  Mixers[m_CurrentMix]->m_level[chmap] = gain;
  Mixers[m_CurrentMix]->m_levelvalue[chmap] = (float)value;
  saveThis->volume[chmap]->setValue(value);

The code path in the midimix object is identical except for an if to determine which of the 2 controllers and all the midimessages are recieived on the same thread. So I think this has to be something to do with how the message gets routed by JUCE to the control based on whether the control is in the maincomponent object or the masterdlg object and what threads handle the change to. The midimix object does block until the control has been updated.

I don’t know how Juce handles this. I tried to step through this with the debugger and just got confused.

Not sure if I have given enough info yet? Not sure what else would be relevant.


#5

A quick side note:

I think it would be helpful for everyone that wants to help you if you would format your code properly as your code snippets is quite hard to read this way!

Did you know that the forum automatically formats your code if you put a line of ``` above and below your code block? If you look in other threads round here you’ll see that nearly everyone uses this feature to highlight code blocks.

Did you also know that you can edit your posts with that little pen icon right below your post? This way you can a) add the code formatting to your previous post and b) can simply append text to an accidentally posted reply that was not ready yet.

Go on and give your post some re-formatting to make it easier for others (me included) to help you :wink:


#6

Have you profiled the application? It could show you what part within setValue() is causing the issue.

You may want to check out juce::AsyncUpdater or a timer to handle changes, like juce::AudioProcessorValueTreeState::SliderAttachment does for syncing parameters to a slider


#7

Thanks, I was not aware of how to format, so I just added it. Hope that helps.

I have never used profiling before but had thought about learning to use it. I will try it but it will probably be a few days before I have time.

I had thought about pushing the updates to a queue, then throwing away intermediate updates and only taking the most recent, but until I understand why it works this way I am reluctant to try something so complex.

Hopefully, now that the code is formatted, someone will recognize the problem.


#8

I am afraid, the code is way too complex to understand for anyone without having the full project.
What I would do in your place is to remove bit by bit, to simplify, until the problem goes away, and then try to bring back the functionality you lost, with the insight that you learned.
I would start with the addListener calls. Something of that seems to go into a recursive update loop. At least that would be my guess…

Good luck!


#9

The number of channels busses and auxes are configurable. So I tried reducing the channel count from 48 to 1 and buss and aux counts to 0 leaving only the FOH mix. This reduced the delay so that the channel and master delays both appear to be 0.

I know that the setValue calls result in messages to the message thread. I know this because JUCE will throw and exception that says I must lock the message thread to make the call.

My suspicion is that the handling of the messages is time consuming. Each channel strip on the maincomponent has a pan, volume, 4 buttons by default. So when configured for 48 channels there are at minimum 288 controls. I don’t know how the message handling logic is handled but this could be why it is so slow. My guess it must decide that the control is not in the maincomponent the pass it on to the next dialog to find the control. My code uses array indexes to find the control number of channels busses or auxes has no bearing on path length to send the message. But I can imagine that it could take a lot of overhead for the message thread to find the control if searching is involved.

Can anyone confirm this is a possible explaination? If so, is there a way around this?


#10

I think I have figured out the problem. I am using graphical vu meters someone shared years ago. Each meter uses a timer to calculate decay an repaint. There was a bug in the code causing repaint when not called for. Correcting this has made a significant difference on the system I am currently testing on. I need to try on the slower system where the problem was much more pronounced.

Nope. Only a marginal improvement at best.

I have tried stepping through the setValue call on both controls and the paths are identical. I am going to attempt profiling now.


#11

I created a new simple project that does the following:
MainComponentWindows has sliders only. for fun I created 256 faders.
the slider listener captured the value of a move and moves all other sliders to match. This happens very fast.

I then created a new dialog window does the same thing with 16 sliders. again very fast.

Then I added midi input and routed one controller to the maincomponent window and moving one slider is very fast.

But if I route the controller value to the dialog window it is very slow. I dont understand the dynamics here.

Why does data received on the background midi thread perform very well when updating the maincomponent windows controls with setValue, but very slow when going to the dialog window using identical code???


Some progress. In my program I am using FilmstripSlider that was posted many years ago. So in my test program I removed the FilmstripSliders and used a standard Juce slider. The delay I see in my test program is reduced dramatically. To compare I use a midi controller and move the fader quickly from 0 to max, 10 times as quickly as I can.

With the filmstrip slider it will take ten seconds or more to make all the moves.
With the standard juce slider it will take about 2 seconds.

This explains part of my problem but it still does not explain why there is no noticeable delay at all, (even the filmstripslider) when the control is on the main content window. (I only see the delay in the nonmodal child dialog window).

I have tried profiling to figure this out but I got nowhere. I have come up with a workaround using a buffer to store the midi data and a timer in the dialog to update the faders from the buffer. This way I see no delay. For now I am using that. I will clean up my test program if there is any watching the thread who may be able to help.


#12

Hi, I don’t know if it make sense in your code, but the active sensing midi message can slow down code by a regular call.


#13

Thanks for the suggestion. But there are no other midi messages being sent other that the controllers I specified. I even setup midi monitoring to be sure.