Action Listener


#1

Hi everybody, I am trying to build a plugin that there is a RmsDetector class inside the AudioProcessor that works as a broadcaster and a RmsMeter class inside the Editor that is a Listener.

Processor.h
std::unique_ptr<RmsDetector> MasterMeter;

Processor.cpp
MasterMeter.reset(new RmsDetector());

and call it in processblock()

//calculate buffer RMS and broadcast it to listener
MasterMeter->tick(buffer); 

Editor.h
std::unique_ptr<RmsMeter> meter;

Editor.cpp

//create meter and link with detector 
meter.reset(new RmsMeter(processor.MasterMeter.get()));

RmsDetector.h
std::unique_ptr<ActionBroadcaster> Broadcaster;

RmsDetector.cpp
Broadcaster.reset(new ActionBroadcaster());

and at some point it Broadcasts a message
Broadcaster->sendActionMessage("blabla");

RmsMeter.h

class RmsMeter: public ActionListener
{
public:
RmsDetectro* Detector;

RmsMeter(RmsDetector* InputDetector)
: Detector(InputDetector)
{
  Detector->Broadcaster->addActionListener(this);
}
  
void actionListenerCallback(const String &message) override {
 		            DoSomething();
 	             }
 }

When I run/debug the standalone plugin everything works fine but when i run the vst I get the following exception after Broadcaster->sendActionMessage(“blabla”) and actionListernCallback() have been already called several times:


#2

That error says that this->listener is an invalid value, which would lead me to one of two conclusions: either this->listener has not been set for this instance, or this itself is invalid. I’d first check that listener is always set to point to a valid object before it could ever be used. Then I’d debug and step up the hierarchy at the point of the crash and see where this came from and if it is valid or not, and work back from there if not.

(Not sure if it’s safe in this case to add this as a listener while still in the constructor or not. I generally let the creator of an object add the created object to my own list(s) after creating it, just to be sure I’m not relying on polymorphic behavior inside a constructor. May not have any bearing on this, though.)

One other way a member can be corrupted is if memory has been overwritten, but this feels more like an uninitialized pointer to me, possibly caused by assumptions on when certain things are created in stand-alone vs. plugin scenarios.


#3

I generally let the creator of an object add the created object to my own list(s) after creating it, just to be sure I’m not relying on polymorphic behavior inside a constructor.

I did try to remove the Broadcaster->addListener from the Listener constructor and I did it in the Listener creator, after having created the Listener but I do have the same behaviour.


#4

Take a look at how the AudioVisualiserComponent works. if you look at the recording demo, AudioBuffers are passed to that component in the AudioThread via AudioVisualiserComponent::pushBuffer, yet the rendering happens on the GUI thread. Obviously with an RMS meter, you’re trying to pass some value from the audio thread to the GUI thread for rendering.

The good thing about the AudioBuffer class is that it has member functions to give you the RMS of that particular buffer.

So, study that design of that class (the rendering is timer based) and try to apply it here, instead of using ActionListener.

an example of what you could also do is your own listener in your processor class:

struct MyAudioProcessor : public AudioProcessor
{
    struct BufferListener
    {
        virtual ~BufferListener() { }
        virtual void bufferReady(const AudioBuffer<float>& buffer) = 0;
    };
    void addBufferListener(BufferListener* l);
    void removeBufferListener(BufferListener* l);
private:
    ListenerList<BufferListener> bufferListeners;

and at the end of your processBlock:

void MyAudioProcessor::processBlock((AudioBuffer<float>& buffer, MidiBuffer& midi)
{
    bufferListeners.call([&](BufferListener& l){ l.bufferReady(buffer); } );
}

then in your GUI:

class MyAudioProcessorEditor  : public AudioProcessorEditor,
public Timer,
public MyAudioProcessor::BufferListener
{
    MyAudioProcessorEditor(MyAudioProcessor& p) : processor(p) { processor.addListener(this); }
    ~MyAudioProcessorEditor() { processor.removeListener(this); }
    void bufferReady(const AudioBuffer<float>& buffer) override;
    MyAudioProcessor& processor;
    AudioVisualiserComponent scope;
};

void MyAudioProcessorEditor::bufferReady(const AudioBuffer<float> &buffer)
{
    scope.pushBuffer(buffer); //called from the Audio Thread
}