Plugin crash when trying to reference AudioBuffer

Working on some optimization on my synthesizer, and came across a problem trying to reference an AudioBuffer declared in PluginProcessor.

In PluginProcessor.h I got;

public:
AudioBuffer<float> outputPlotBuffer;
int test = 1964;
...

I define the AudioBuffer in PluginProcessor.cpp, in prepareToPlay

outputPlotBuffer.setSize (2, 512, false, true, false);
outputPlotBuffer.clear ();

In my component header file I got;

#include "../JuceLibraryCode/JuceHeader.h"
#include "PluginProcessor.h"

and down in the component class I got;

private:
MutineerAudioProcessor mup;

And finally in my component cpp file just a a test I got;

void WaveformOutput::paint (Graphics& g)
{
    // This shows fine
    g.drawSingleLineText (String(mup.test), 0, 16, 1);

    // This crashes the plugin
    g.drawSingleLineText (String(mup.outputPlotBuffer.getSample (0, 0)), 0, 16, 1);
}

Any help is greatly appreciated, thanks!

Is WaveformOutput::paint being called before prepareToPlay() has been called? If so, then you haven’t allocated the buffer yet. A Debug build should assert (which will likely crash), while a Release build will likely crash (but could do anything, really).

You can either allocate the buffer in your Processor’s constructor, or insert a check that it’s initialized before using it.

1 Like

Thank you very much! Even though I did not call paint before prepareToPlay, whenever I hit a note it crashed.

Now after having moved the definition to my processor’s constructor, at least it does not crash, however now the plot data, which is copied from the processBlock buffer to the outputPlotBuffer,
contains no data once accessed over in my WaveformOutput component. I am now trying to figure out why.

That is not the same processor instance that is used by the plugin to process the audio data, it is a separate instance that isn’t used by the plugin. To get access to the actual plugin’s processor, you need to use a reference or pointer.

2 Likes

Thank you very much for pointing that out, but here is where my knowledge of C++ really sucks, it makes absolutely zero sense to me.

So I logically assumed that I could do something similar as to what was done in the declaration of my MutineerAudioProcessorEditor class in the header file, which has this;

class MutineerAudioProcessorEditor : public AudioProcessorEditor,
	                                 public Button::Listener,
	                                 public ComboBox::Listener,
	                                 public Slider::Listener,
	                                 public Timer, public Value::Listener
{
public:
    MutineerAudioProcessorEditor (MutineerAudioProcessor&);
    ~MutineerAudioProcessorEditor();

    void paint (Graphics&) override;
    void resized() override;

private:
    MutineerAudioProcessor& processor;
    ...

So I changed my component class in its header to do the same;

class WaveformOutput : public Component, public Timer {
public:
	WaveformOutput (MutineerAudioProcessor&);
	~WaveformOutput ();

	void timerCallback () override;
	void paint (Graphics&) override;
	void resized () override;

	ImageButton waveformCreatorButton, zoomDetailButton;
	ImageButton zoomOutputButton, zoomCloseButton;

private:
	MutineerAudioProcessor& mup;
        ...

Then in the plugineditor’s constructor it had this;

MutineerAudioProcessorEditor::MutineerAudioProcessorEditor (MutineerAudioProcessor& p)
    : AudioProcessorEditor (&p), processor (p)
{

So again I logically assumed I could do the same in my component’s constructor like this;

WaveformOutput::WaveformOutput (MutineerAudioProcessor& p) : mup (p)
{

But now that would have been too easy because now even though it shows no error, on the actual constructor, the next class I have in the same file, shows an error at its constructor “{”, saying “no default constructor exists for class WaveformOutput”. So either I made a typo I could not find during the last hour, or I am missing something else.

If it weren’t that I value the little hair I have left on my head, I would pull some out.

I also tried this;

WaveformOutput::WaveformOutput (MutineerAudioProcessor& p) : 
	Component (&p), mup (p)
{
...

That shouldn’t compile. You’re taking the address of the processor there with “(&p)”. Is that just a mistake in posting here?

We don’t see where you’re instantiating any instances of your WaveformOutput class, so we can’t tell why it’s complaining about not having a default constructor. Usually that means you are defining an instance inside another class or function (rather than a pointer or reference), or creating an array of that object (which only allows using the default constructor).

And I guess you made a(nother) typo when posting here, because when describing the latest error you referred to “WavefomrOutput”, when you obviously meant “WaveformOutput”, right?

Yeah thanks that “WavefomrOutput” was a typo which has now been fixed.

Back last year when I via Projucer created that class, that is what it did, been working fine

MutineerAudioProcessorEditor::MutineerAudioProcessorEditor (MutineerAudioProcessor& p)
    : AudioProcessorEditor (&p), processor (p)
{
   ...

I just created a brand new test project with latest version of Projucer/Juce;

class NewProjectAudioProcessorEditor  : public juce::AudioProcessorEditor
{
public:
    NewProjectAudioProcessorEditor (NewProjectAudioProcessor&);
    ~NewProjectAudioProcessorEditor() override;

    //==============================================================================
    void paint (juce::Graphics&) override;
    void resized() override;

private:
    // This reference is provided as a quick way for your editor to
    // access the processor object that created it.
    NewProjectAudioProcessor& audioProcessor;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NewProjectAudioProcessorEditor)
};
NewProjectAudioProcessorEditor::NewProjectAudioProcessorEditor (NewProjectAudioProcessor& p)
    : AudioProcessorEditor (&p), audioProcessor (p)
{
    // Make sure that before the constructor has finished, you've set the
    // editor's size to whatever you need it to be.
    setSize (400, 300);
}

NewProjectAudioProcessorEditor::~NewProjectAudioProcessorEditor()
{
}

//==============================================================================
void NewProjectAudioProcessorEditor::paint (juce::Graphics& g)
{
    ....
}

void NewProjectAudioProcessorEditor::resized()
{
    ....
}

And as you can see that is how JUCE does it.

I instatiate my WaveformOutput class in my plugineditor’s header file in the private section;

WaveformOutput waveformOutput;

So perhaps I am missing the reference there?

If taking the address works, then you must have assigned it to a pointer. You can’t assign an address to a reference.

That’s what I meant, yes. You need a reference or pointer there, to which you assign a reference or create an instance for, depending on who is creating the object and how.

I really appreciate all your time helping out, however you are much more knowledgeable at C++ than me, and I have almost no idea what you are talking about, address, reference, pointer. So I am giving up until a day in the far future where I have the time to properly learn C++, and for now get back to the fun stuff of making interesting ways of creating sound.

Ah, I see that class takes either a reference or a pointer. There are two constructors for AudioProcessorEditor, and yours uses the pointer, whereas mine use the references. Who knew? :slight_smile:

I do it very simply now by just having a global buffer in separate files only containing this;

OutputPlutBuffer.h

#pragma once

#include "../JuceLibraryCode/JuceHeader.h"

extern AudioBuffer<float> outputPlotBuffer;

and

OutputPlotBuffer.cpp

#include "OutputPlotBuffer.h"

AudioBuffer<float> outputPlotBuffer;

Then I simply #include the header file in my PluginProcessor and WaveformOutput component. I knew it was an improper (ugly) way to do it, but that’s all I knew, and it worked, just wanted to see if by doing it properly I could squeeze out a few CPU ticks.

That is a horrible way to do it. Static and global variables should (almost) never be used in plugins. It’s not an efficiency but code correctness issue why they should be avoided.

Agreed. It’s sometimes fine to use them for objects that will remain constant across all instances of a plugin (such as an array of names for the different notes), but plugins are quite likely to share their static/global memory, so multiple instances would be stomping on each other’s data if used like that.

I think the OP does indeed need to go learn some C++ basics here, as he said.

Which is why I wrote " I knew it was an improper (ugly) way to do it, but that’s all I knew, and it worked"!!!

So in other words, with my limited C++ knowledge, is is either my ugly way or nothing! I was trying to change it to a better way, and had hoped it would be fairly easy, but no, and I do not have freaking time to spend on pointers and references.

don’t be too hard on yourself. the statics and globals issue is kind of a c++ issue, but it is specific to dynamically loaded libraries (which plugins are), so the knowledge is a little specialized. I really encourage you to develop more comfort with pointers and references, as they are important tools in c++, and with the complexity of your project, you will want the most maintainable code for the long run. :slight_smile:

2 Likes

Thanks. The thing for me is that is makes no logical sense. See below how I tried another way, that also to me unexplainable does not work.

In my WaveformOutput.h I got;

class WaveformOutput : public Component, public Timer {
public:
	WaveformOutput ();
	~WaveformOutput ();

	void putBufferData (AudioBuffer<float>& buffer);
	void timerCallback () override;
	void paint (Graphics&) override;
	void resized () override;

	ImageButton waveformCreatorButton, zoomDetailButton;
	ImageButton zoomOutputButton, zoomCloseButton;

	AudioBuffer<float> outputPlotBuffer;

In PluginProcessor.h I got;

#include "WaveformOutput.h"

And then;

private:
WaveformOutput wfo;

Then in PluginProcessor’s processBlock, right at the end, I check if WaveformOutput has done drawing last plot data, and if so;

wfo.putBufferData (buffer);

Finally in WaveformOutput.cpp I got;

void WaveformOutput::putBufferData (AudioBuffer<float>& buffer)
{
	testValue[2] = buffer.getMagnitude (0, buffer.getNumSamples());
	outputPlotBuffer = buffer;

	testValue[3] = outputPlotBuffer.getMagnitude (0, outputPlotBuffer.getNumSamples ());

	outputPlotBuffer.applyGain (plotHalfHeight);	
	testValue[4] = outputPlotBuffer.getMagnitude (0, outputPlotBuffer.getNumSamples ());

	testValue[5] = outputPlotBuffer.getSample (0, 0);

	readSampleData = 5;
}

Now the above testValue variables are drawn in my plot window, just to show that indeed upon entering putBufferData it contains data, and after I “copy” it to my local buffer that also contains data, and then I do a final check after I scale the buffer to my components height in pixels. So all those three Magnitude checks confirms there is data, plus I even confirm first sample contains data. So with the “readSampleData = 5” I signal my Component to start it’s timer and there do a repaint(), but once there the outputPlotBuffer contains nothing!?! See that absolutely does not make any sense whatsoever to me. The outputPlotBuffer is the same size as the PluginProcessor’s size, and is initialized in the WaveformOutput’s constructor.