Sending signal/events from audio to GUI thread?


#1

Hello,

I am a newbie on JUCE, thus, please forgive me my stupid question:
I am writing an audio app, my MainComponent is derived from AudioAppComponent. I did overwrite getNextAudioBlock() for my audio processing. That works fine. Now I need to update some visual representation in my GUI during the audio processing. Calling repaint() does not work. Well that is logical, because of the different threads.
So far the only solution I found in the turorials is to wait for a timer event. Well, this is not so nice.
So I am wondering, is there any other way to send a signal from the audio thread to the GUI thread that triggers a repaint of the GUI?

Raphael


#2

JUCE has some other classes like the AsyncUpdater which allow for this kind of cross-thread updating, however they aren’t always real-time safe.

Timers on the GUI thread are a good way to handle this sort of thing, this post has some additional info:


#3

What’s wrong with a timer? Presumably, your audio thread will be active most of the time, and it will be providing new data to update the GUI most of the time, so using a timer makes total sense.


#4

The timer is almost always the best way to go with this.

If you need a true “signal” type message, just do this:

class MyAudioProcessorClass : public AudioProcessor, etc...
{

    Atomic<bool> someSortOfSignal;
    Atomic<double> someAudioParameter;

    void processBlock (AudioBuffer<float> &buffer, MidiBuffer &midiMessages) override
    {
        // dsp code here
        const double level = buffer.getRMSLevel (0, 0, bufer.getNumSamples());
       
        // Use juce::Atomic or std::atomic to share audio parameters between threads
        someAudioParameter.set (level);

        // Send a signal if the RMS of the buffer goes above 0.5
        if (level > 0.5) {
            someSortOfSignal.set (true);
        }
    }

}

MyPluginGuiClass : private Timer, public Component, etc...
{
    ...

    void paint (Graphics& g) override
    {
        // Color changes when the volume goes above 0.5
        g.fillAll (Colours::purple.withRotatedHue (rmsLevel);
    }
    
    void timerCallback() override
    {
        // Get your audio parameter
       rmsLevel = processor.someAudioParameter.get();

        // If Signal is activated
        if (processor.someSortOfSignal.get() == true) {
           
            // Deactivate the signal when you read it so you can send it again later.
            processor.someSortOfSignal.set (false);
            
            // Respond to the signal
            // If the RMS Level is > 0.5, repaint some level meter or something
            repaint();
        }
    }

    double rmsLevel = 0;
    ...
}

Always use juce::Atomic or std::atomic when sharing values between different threads! If you don’t, your app will crash at random times.

This method is probably the simplest and safest way to go and can help you make you UI performant.