Exception thrown: read access violation. juce::HeapBlock<juce::AudioProcessorListener * __ptr64,0>::operator[]<int>(...) returned 0x3CA20000


#1

Hi!

This error is totally out of my league. I have a plugin with some sliders, and when I adjust a slider that in turn affects an AudioParameterFloat in the sliderValueChanged -method of the Plugin editor (GUI component), sometimes, just sometimes this happens and a crash occurs. No further information on the error what caused it. Any ideas? I use a recent dev build of JUCE from the git. Any slider that adjusts AudioParameter might cause this. I use AudioParameters in processBlock of the processor by first saving their values all into a temporary variables in the beginning of the method and then using those temporary variables in the processing algorithm.

Any help is much appreciated!


#2

Have you tried launching your host+plugin in debug mode? The debugger should then stop your program and show you the line of code where the error happens.


#3

Yes sorry for the lack of info… It stops in juce_Array.h line 272:

ElementType operator[] (const int index) const
{
    const ScopedLockType lock (getLock());

    if (isPositiveAndBelow (index, numUsed))
    {
        jassert (data.elements != nullptr);
        return data.elements[index]; <--------------------this one!
    }

    return ElementType();
}

I’m not using an Array-object in my own written code…


#4

It would be more interesting to see what called that function and what function called that function etc. (So if you can get the call stack, that’d be useful…)


#5

Ok, Let’s see. This is a call stack showing on VS2015. It seems to stop on 271 jassert now. I’d think it sometimes stop at 272 too.

>	EQProfiler.dll!juce::Array<juce::AudioProcessorListener * __ptr64,juce::DummyCriticalSection,0>::operator[](const int index) Line 271	C++
 	EQProfiler.dll!juce::AudioProcessorParameter::sendValueChangedMessageToListeners(float newValue) Line 1470	C++
 	EQProfiler.dll!juce::AudioProcessorParameter::setValueNotifyingHost(float newValue) Line 1397	C++
 	EQProfiler.dll!juce::AudioParameterFloat::operator=(float newValue) Line 87	C++
 	EQProfiler.dll!TestAmpSimAudioProcessorEditor::sliderValueChanged(juce::Slider * sliderThatWasMoved) Line 282	C++
 	EQProfiler.dll!juce::Slider::Pimpl::handleAsyncUpdate::__l2::<lambda>(juce::Slider::Listener & l) Line 324	C++
 	EQProfiler.dll!juce::ListenerList<juce::Slider::Listener,juce::Array<juce::Slider::Listener * __ptr64,juce::DummyCriticalSection,0> >::callChecked<void <lambda>(juce::Slider::Listener &),juce::Component::BailOutChecker>(const juce::Component::BailOutChecker & bailOutChecker, juce::Slider::Pimpl::handleAsyncUpdate::__l2::void <lambda>(juce::Slider::Listener &) && callback) Line 153	C++
 	EQProfiler.dll!juce::Slider::Pimpl::handleAsyncUpdate() Line 326	C++
 	EQProfiler.dll!juce::Slider::Pimpl::triggerChangeMessage(juce::NotificationType notification) Line 314	C++
 	EQProfiler.dll!juce::Slider::Pimpl::setValue(double newValue, juce::NotificationType notification) Line 194	C++
 	EQProfiler.dll!juce::Slider::Pimpl::mouseDrag(const juce::MouseEvent & e) Line 899	C++
 	EQProfiler.dll!juce::Slider::Pimpl::mouseDown(const juce::MouseEvent & e) Line 858	C++
 	EQProfiler.dll!juce::Slider::mouseDown(const juce::MouseEvent & e) Line 1615	C++
 	EQProfiler.dll!juce::Component::internalMouseDown(juce::MouseInputSource source, juce::Point<float> relativePos, juce::Time time, float pressure, float orientation, float rotation, float tiltX, float tiltY) Line 2358	C++
 	EQProfiler.dll!juce::MouseInputSourceInternal::sendMouseDown(juce::Component & comp, juce::Point<float> screenPos, juce::Time time) Line 143	C++
 	EQProfiler.dll!juce::MouseInputSourceInternal::setButtons(juce::Point<float> screenPos, juce::Time time, const juce::ModifierKeys newButtonState) Line 220	C++
 	EQProfiler.dll!juce::MouseInputSourceInternal::handleEvent(juce::ComponentPeer & newPeer, juce::Point<float> positionWithinPeer, juce::Time time, const juce::ModifierKeys newMods, float newPressure, float newOrientation, juce::PenDetails pen) Line 331	C++
 	EQProfiler.dll!juce::MouseInputSource::handleEvent(juce::ComponentPeer & peer, juce::Point<float> pos, __int64 time, juce::ModifierKeys mods, float pressure, float orientation, const juce::PenDetails & penDetails) Line 632	C++
 	EQProfiler.dll!juce::ComponentPeer::handleMouseEvent(juce::MouseInputSource::InputSourceType type, juce::Point<float> pos, juce::ModifierKeys newMods, float newPressure, float newOrientation, __int64 time, juce::PenDetails pen, int touchIndex) Line 88	C++
 	EQProfiler.dll!juce::HWNDComponentPeer::doMouseEvent(juce::Point<float> position, float pressure, float orientation, juce::ModifierKeys mods) Line 2112	C++
 	EQProfiler.dll!juce::HWNDComponentPeer::doMouseDown(juce::Point<float> position, const unsigned __int64 wParam) Line 2263	C++
 	EQProfiler.dll!juce::HWNDComponentPeer::peerWindowProc(HWND__ * h, unsigned int message, unsigned __int64 wParam, __int64 lParam) Line 3118	C++
 	EQProfiler.dll!juce::HWNDComponentPeer::windowProc(HWND__ * h, unsigned int message, unsigned __int64 wParam, __int64 lParam) Line 3027	C++
 	[External Code]	
 	reaper.exe!000000014036fec8()	Unknown
 	reaper.exe!00000001407ba791()	Unknown
 	[External Code]

#6

Can you please show the part of your code, where you create the parameters and how you add them?

Without that we can only guess, maybe something like the parameter was created and connected, but not properly added to the array inside the AudioParameterValueTreeState, so the lookup fails…?


#7

Actually I don’t use AudioParameterValueTreeState, but just addParameter() -method on the audioprocessor :thinking:

addParameter(LowTresholdAmplitude = new AudioParameterFloat("Low Treshold", "Low Treshold", 0.01f, 1.0f, 0.1f));
addParameter(Wetness = new AudioParameterFloat("Wet level", "Wet level", 0.0f, 1.0f, 1.0f));
addParameter(NormalizeTo = new AudioParameterFloat("Normalization level", "Normalization level", 0.0f, 1.0f, 0.5f));
addParameter(DetectorSpeedSeconds = new AudioParameterFloat("Attack seconds", "Attack seconds", 0.001f, 2.0f, 0.5f));
addParameter(Resolution = new AudioParameterInt("Resolution", "Resolution", 10, maxNodes, 50));
addParameter(StaticEQ = new AudioParameterBool("Static EQ", "Static EQ", false));

maxNodes is #define maxNodes 500


#8

How do you make the sliders change the parameter value? Maybe there’s some indexing issue. Or are you using a lambda that captures some local state by reference?


#9

It’s like this:

void TestAmpSimAudioProcessorEditor::sliderValueChanged (Slider* sliderThatWasMoved)
{
	//[UsersliderValueChanged_Pre]
	//[/UsersliderValueChanged_Pre]

	if (sliderThatWasMoved == attac.get())
	{
		//[UserSliderCode_attac] -- add your slider handling code here..
		OF->DetectorSpeedSeconds->operator=(attac->getValue());
		//[/UserSliderCode_attac]
	}
	else if (sliderThatWasMoved == normalizeto.get())
	{
		//[UserSliderCode_normalizeto] -- add your slider handling code here..
		OF->NormalizeTo->operator=(normalizeto->getValue());
		//[/UserSliderCode_normalizeto]
	}
	else if (sliderThatWasMoved == currentgainl.get())
	{
		//[UserSliderCode_currentgainl] -- add your slider handling code here..
		//[/UserSliderCode_currentgainl]
	}
	else if (sliderThatWasMoved == effectdrywet.get())
	{
		//[UserSliderCode_effectdrywet] -- add your slider handling code here..
		OF->Wetness->operator=(effectdrywet->getValue());
		//[/UserSliderCode_effectdrywet]
	}
	else if (sliderThatWasMoved == lowtreshold.get())
	{
		//[UserSliderCode_lowtreshold] -- add your slider handling code here..
		OF->LowTresholdAmplitude->operator=(lowtreshold->getValue());
		//[/UserSliderCode_lowtreshold]
	}

	//[UsersliderValueChanged_Post]
	//[/UsersliderValueChanged_Post]
}

Where OF is a pointer to the processor object.

Thanks for helping me out! :slight_smile:


#10

There is something smelly: introducing a pointer to the processor is a bit backwards, since the Editor is guaranteed not to outlive it’s processor, it is recommended to use the reference, that the JUCE boilerplate already adds as TestAmpSimAudioProcessor& processor;.

The array, that is out of bounds is the ListenerList of the AudioProcessor, and the only AudioProcessorListener is the wrapper. Never try to use the AudioProcessorListener yourself. IMHO it should be better hidden from the user.

The code you posted could be rewritten more readable:

if (sliderThatWasMoved == attac.get())
{
    //[UserSliderCode_attac] -- add your slider handling code here..
    processor.DetectorSpeedSeconds->operator=(attac->getValue());
    //[/UserSliderCode_attac]
}
// ...

or even better without calling the operator but using it:

if (sliderThatWasMoved == attac.get())
{
    //[UserSliderCode_attac] -- add your slider handling code here..
    &(processor.DetectorSpeedSeconds.get()) = attac->getValue();
    //[/UserSliderCode_attac]
}
// ...

the new unique_ptr makes it a bit harder, so you have to use the .get() and get dereference it afterwards…


#11

Hmm all I can find is just AudioProcessor& processor; Nothing about derived class I made myself; and I’d be surprised as there’s no “generator” in projucer for this? Do I static_cast that as derived processor or how should I use this?


#12

At least here Projucer generates a GUI editor for the plugin that has a reference member that is the same type as the derived AudioProcessor subclass. Your GUI code looks like it was not generated by the Projucer from the plugin project template? (I am guessing that from the comments in the slider handler code…)


#13

You could do that, but actually it is already present in the generated file. The AudioProcessorEditor.h of an empty Plugin project looks like this:

class TestAmpSimAudioProcessorEditor  : public AudioProcessorEditor
{
public:
    TestAmpSimAudioProcessorEditor (TestAmpSimAudioProcessor&);
    ~TestAmpSimAudioProcessorEditor();

    //==============================================================================
    void paint (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.
    TestAmpSimAudioProcessor& processor;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TestAmpSimAudioProcessorEditor)
};

Since you copied a generated Component from the GUI editor, you might have accidentally overwritten it.


#14

Ah. Now I figured. I just deleted the default PluginEditor files and create a GUI Component instead. Dummy me. :slight_smile: Ok, I’ll give this a spin. Altho I don’t understand why a pointer to the processor is bad but a reference would work better :open_mouth: ?


#15

The reference versus pointer doesn’t really matter but the reference makes it clearer the audioprocessor should always exists while the editor exists.


#16

Plus it is clear, that this is owned somewhere else, and you can’t be bothered to destroy it.


#17

Ok! :slight_smile: I tried this way, but unfortunately this didn’t fix the issue. Same callstack, same error. :confused:


#18

Yes, I thought so. It was just some thing to clean up for best practice.

Like I said before, what happened is, that the AudioProcessor has an AudioProcessorListener registered, that doesn’t exist any longer. It was deleted without calling processor.removeListener (listener) first. And like I said, AudioProcessorListener is actually not meant to be used by you.

The AudioProcessorValueTreeState provides a listener interface (state.addParameterListener (paramID, listener)), but the processor itself doesn’t. And that’s good this way, since it is not clear for all hosts, on which thread they send the parameter change messages. There are differences, that you don’t want to consider yourself.

Have a look here:


#19

I’ve not used AudioProcessorListener anywhere in my code… AFAIK :thinking: :grinning:

I have one hunch tho and I don’t have the knowledge to know if it matters. There’s a thread for timers, right? There’s also a thread for processor, and probably one for GUI components or so? What happens if an object tries to use an audioparameter in a timer thread at the same exact time than in some other thread? Can there be “malfunctions” like some conflict happening? as you can see I don’t really now my multi-threading stuff. :stuck_out_tongue:


#20

The normal Juce Timers run in the GUI thread so anything happening in those callbacks shouldn’t cause much problems. However the Juce HighResolutionTimer, which you should use only very sparingly, does use a different thread. You are not using that, are you?