Component repaint() continuous - problem


#1

Hello,
I try to make an audio analyser so I need display of my analyser to be refreshing continuously, endless, or alway there is some audio on input.
So I tried to put MyDisplayClass::repaint() function in my AudioProcessor::processBlock(AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
But I get error like that:

void Component::internalRepaintUnchecked (Rectangle area, bool isEntireComponent)
{
// if component methods are being called from threads other than the message
// thread, you’ll need to use a MessageManagerLock object to make sure it’s thread-safe.
ASSERT_MESSAGE_MANAGER_IS_LOCKED Built-in Output (14): EXC_BREAKPOINT (code=EXC_I386_BPT, subcode=0x0)

if (flags.visibleFlag)

How to deal with that? Could anyone help me?
Now I have some stupid solution. In the AudioProcessorEditor I created Slider::listener, and override

void sliderValueChanged(Slider * slider)

And inside of that I put MyDisplayClass::repaint()
And it woks, but to refreshing my display I need to moving my slider :slight_smile:
Of course I though about create AudioProcessorValueTreeState in AudioProcessor and attache it to my slider, and then manipulate it continously in the processBlock. But I am not sure if it would work, but I also think it’s crude way.
So please, could anyone help me?


#2

Use a Timer. (Inherit MyDisplayClass from Timer and implement the timerCallback method and do the repaint call there.)


#3

Hey great thanks for reply,
but actually it doesn’t work (for sure my implementation is wrong)
I inherited Timer, and override void timerCallback, like that:

void MyDisplayClass::timerCallback()
{
repaint();
}

And then I call it in the processBlock, and have exactly the same error.


#4

You are not supposed to call the timerCallback yourself, you need to start the timer somewhere suitable in MyDisplayClass, for example in the constructor, with startTimer().


#5

You should consider that you should never ever call anything from the audio thread, that doesn‘t return after a predictable – and in the best case short – time. So depending on how you process your data before displaying it, you might to consider a different design:

  • Your class iherits AsyncUpdater or has a timer
  • Your audio thread just pushes samples to a fifo or a simple buffer until a certain amount of samples is reached
  • A dedicated thread waits for this buffer to be filled up to the desired amount. It gets woken up by the audio thread throug a WaitableEvent and then does all the processing needed on that data (e.g. windowing, fft…). Maybe it already renders an Image from the processed data and stores it to a pre allocated image buffer inside your class. When it‘s done with that, it either calls triggerAsyncUpdate or sets some flag.
  • The repaint call goes into the handleAsyncUpdate or timerCallback, where the timer should check if you set the flag before calling repaint. Both approaches will invoke the painting from the message thread.

If your processing is not that heavy, you might want to skip the part involving the extra thread. But the pattern stays roughly the same


#6

Actually you can use AsyncUpdater.
This way u can update the GUI only required and not continuously using timer.
You can call triggerAsyncUpdate in procesBlock and you will get callback handleAsyncUpdate()
This you can implement and call repaint.


#7

AsyncUpdater is not really the best choice because it does allocate memory on some architectures (don’t remember if on macOS or Windows, but also certainly on Linux) and thus it is not strictly to be considered realtime-safe.

Given the fact that in this particular case a regular cadence is required anyway, I’d say that the Timer approach is a better fit.


#8

Good you mentioned that. I didn’t use a lot of asyncUpdaters until now so I wasn’t aware of this when writing the post above! But reading the doc would have helped :roll_eyes:
So yes, go for the timer! I implemented some rta that works flawlessly with a timer and a structure just as described above


#9

Can you elaborate little more? how much more memory does it allocates?


#10

Hello, great thanks for all your answer.
Now my analyser graph works great with timer. thanks.


#11

It’s not really a matter of how much memory it allocates: the simple fact that it performs allocation means that it must call into the OS, which in turn means that the return time is not predictable.

For example, that allocation, however small, could be the last drop that causes the memory manager to decide to perform some reorganization, possibly swapping some memory to disk, which is very time consuming.

For sure that is what may happen on macOS, as also mentioned in the following post:

The bottom line is that on macOS the message queue is nothing but a reference counted array, also protected by a CriticalSection, which makes things worse from the realtime audio POV: if the audio thread tries to trigger the AsyncUpdater while the GUI thread is also posting a message, that will result in a contention for the CriticalSection that will make the audio thread wait until the lock is released by the UI thread


#12

Thanks for the explanation.
I learnt something new today.