Access a member function of the AudioProcessorEditor class via the AudioProcessor class


#1

How can I call a function that’s a member of the AudioProcessorEditor class if I’m in the scope of the AudioProcessor class?

void AnalogueOverdriveAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
{
     callAudioProcessorEditorMemberFunction(); // defined in PluginEditor.cpp
}


#2

dynamic_cast

http://en.cppreference.com/w/cpp/language/dynamic_cast

Rail


#3

Also: NEVER do that.

If your callback on the audio thread starts calling methods on GUI classes, you’ll hit race conditions and crash, or at best cause glitches.


#4

The good news: also, it’s unnecessary. What are you trying to achieve? Depending on the level of control required, you could use some kind of thread-safe flag or message queue that the editor/s* (if any) can access to know to do something when given a chance.

Generally, it’s worth bearing in mind that an editor can reference the processor, but the processor should know nothing about any editor/s.

*Sometimes there are no editors, or multiple editors, so that’s worth considering.


#5

What about then people are allowed to set the play pointer like in the DSP demo? Isn’t that the editor setting a processor variable directly?


#6

Accessing the GUI directly from the audio thread is by design not intended to work at all. The GUI thread accessing the audio processing objects does not have such a restriction. Those accesses should still be carefully done, though. Also I suppose the DSP demo is a demo, not production proofed code.


#7

We all take the demos to show how transport position is set, officially.
I presume setting int64 is contained as an atomic thing anyway.


#8

I want to create some kind of graphical indication whenever levels exceed a certain value. Is there a typical way to do this?


#9

As far as I can tell, the demos are essentially provided as a starting point to get you going on a particular topic rather than necessarily a fully-fledged best practice approach in all cases.

Atomic operations on int64_t will depend entirely on your architecture so are by no means guaranteed in general. I use a simple wrapper based around std::atomic for simple types whenever thread safety is required, as this communicates your multi-threaded usage scenario explicitly. If the loads, stores and other operations on built-in types are atomic they will typically be optimised to single instructions by a modern compiler somewhere around -O1, so you don’t lose any performance. Also, std::atomic gives you access to things like is_lock_free() and is_always_lock_free (C++17) so you can check these out if you’re curious.


#10

Oh yes, you can do that.

The basic approach is to have a variable of some kind in your processor that you set there when the level condition is met. Provide visible access to this variable to the editor through a suitable public interface (read and/or write as required, and make sure it’s thread safe) so it can see when this has been updated one way or the other. Then do your GUI work in the editor based on the value of this variable.

The trick is making sure that the processor itself “holds the cards”. Everything else stems naturally from that.

Hope this helps. :slight_smile:


#11

I see; Thanks for the help.


#12

You’re welcome and good luck.


#13

AudioTransportSource is part of the Juce API. Not in the demo itself.

Thanks for the info on std::atomic though, it’s interesting that only int types are supported:
https://msdn.microsoft.com/en-us/library/hh874894.aspx


#14

Also Also: your AudioProcessorEditor may not even be there when your AudioProcessor is running.

When you open a saved song in some DAWs, chances are that the GUI for most of the plug-ins are not even shown, which means that the AudioProcessorEditor may not have been created yet for your plug-in.


#15

Also^3: If one thinks “I’m smarter that the machine, I use a smart pointer (aka WeakReference)”, it is still possible that the editor is just being destroyed while you are executing code there, because these are different threads.
“Ok, but I can use a ReferenceCountedObject to make sure, it is only being deleted when I release my reference”, then you have to hack the AudioProcessor, as that one uses a normal ScopedPointer and will trigger the destruction regardless of your counted reference…
It all leads nowhere and is not worth it…

Back to the topic: there is a simple level meter for a specific purpose in the AudioDeviceSelectorComponent. It is not meant to be used outside the Component, but you can see how they did it:

Also setting a link here to the thread from sondzark, and see my answer there:


#16

I understand that calling GUI methods on the audio thread is not advisable. But is the following a correct way of handling parameter changes in a Synthesizer object?

For instance: setting the detune amount in one of the oscillators, in my main AudioProcessor, I call setOsc1DetuneAmount in a processingBlock, passing AudioParameters as arguments.

void MonosynthPluginAudioProcessor::setOsc1DetuneAmount(float fine, int coarse)
{
    
    return dynamic_cast<MonosynthVoice*>(synth.getVoice(0))->setOsc1DetuneAmount(fine, coarse);
    
}

The AudioParameters are changed using sliders in the editor via this simple class:

class MonosynthPluginAudioProcessorEditor::ParameterSlider   : public Slider,
                                                              private Timer
{
public:
    ParameterSlider (AudioProcessorParameter& p)
        : Slider (p.getName (256)), param (p)
    {
        setRange (0.0, 1.0, 0.0);
        startTimerHz (60);
        updateSliderPos();
    }

    void valueChanged() override
    {
        if (isMouseButtonDown())
            param.setValueNotifyingHost ((float) Slider::getValue());
        else
            param.setValue ((float) Slider::getValue());
    }

    void timerCallback() override       { updateSliderPos(); }

    void startedDragging() override     { param.beginChangeGesture();  }
    void stoppedDragging() override     { param.endChangeGesture();   }

	

    double getValueFromText (const String& text) override   { return param.getValueForText (text); }
    String getTextFromValue (double value) override         { return param.getText ((float) value, 1024); }

    void updateSliderPos()
    {
        const float newValue = param.getValue();

        if (newValue != (float) Slider::getValue() && ! isMouseButtonDown())
        {           
            Slider::setValue (newValue);
        }
        
    }

    AudioProcessorParameter& param;


    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ParameterSlider)
};

I don’t encounter glitches, but just want to be sure I’m doing the right thing.