JUCE vector fail

Hi, juce team @attila @fr810

I’m debugging on windows for my plugin and got an issue, the function I’m going for is reversing a waveform drew with vector. This function works fine under both iOS and Mac, works under Reaper and JuceAudioPluginHost, but fail debugging on windows JuceAudioPluginHost, works fine with release version. Not quite sure how to fix it. Any idea? Thanks in advance!

Here is the break point:

#if _CONTAINER_DEBUG_LEVEL > 0
        _STL_VERIFY(
            _Pos < static_cast<size_type>(_My_data._Mylast - _My_data._Myfirst), "vector subscript out of range");
#endif // _CONTAINER_DEBUG_LEVEL > 0

Here is the debug output:

Debug Assertion Failed!

Program: ...er\Builds\VisualStudio2022\x64\Debug\VST3\xxx.vst3
File: d:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.34.31933\include\vector
Line: 1949

Expression: vector subscript out of range

For information on how your program can cause an assertion
failure, see the Visual C++ documentation on asserts.

(Press Retry to debug the application)
'AudioPluginHost.exe' (Win32): Loaded 'C:\Windows\System32\TextShaping.dll'. 
The thread 0x4108 has exited with code 0 (0x0).
The thread 0x1c4 has exited with code 0 (0x0).
The thread 0x4f8 has exited with code 0 (0x0).
The thread 0x3748 has exited with code 0 (0x0).
The thread 0xa24 has exited with code 0 (0x0).

Btw I’m compiling with the latest juce develop commit f9b6e2ef69692527e637f741ebc5fe0d79d7ed35

It’s telling you right there: you have a vector subscript out of range. That means your vector size is, say, 50, and you’re doing, say, vec[50], or anything that’s not between 0 and 49. It won’t fail on release because the check won’t be performed, and you’ll read rubbish or, worse, write over something that’s not your vector. If you pause debugging there, the call stack should take you to the place where the error happens.

2 Likes

Thanks for this, any idea on why it won’t pause on iOS or Mac? They’re using the same function.

It depends if and how you have the debugger attached.

But the root cause is impossible to tell without the calling code.

Ok, here’s the related code:

header

std::vector<float> mAudioPoints;

cpp

void WaveThumbnail::paint (juce::Graphics& g)
{
    g.fillAll (juce::Colours::black);    
    auto waveform = audioProcessor.getWaveForm();
        
    if (waveform.getNumSamples() > 0)
    {
        
        juce::Path p;
        mAudioPoints.clear();
        
        auto ratio = waveform.getNumSamples() / getWidth();
        auto buffer = waveform.getReadPointer(0);

        for (int sample = 0; sample < waveform.getNumSamples(); sample+=ratio)
        {
            mAudioPoints.push_back(buffer[sample]);
        }

        p.startNewSubPath(0, getHeight() / 2);

        if(audioProcessor.mSampler.getReversePlayValue() == 0)
        {
            for (int sample = 0; sample < mAudioPoints.size(); ++sample)
            {
                auto point = juce::jmap<float>(mAudioPoints[sample], -1.0f, 1.0f, getHeight() * 0.95, 0);
                p.lineTo(sample, point);
            }
        } else
        {
            for (int sample = 0; sample < mAudioPoints.size(); ++sample)
            {
                auto point = juce::jmap<float>(mAudioPoints[mAudioPoints.size() - sample], -1.0f, 1.0f, getHeight() * 0.95, 0);
                p.lineTo(sample, point);
            }
        }
            
        g.setColour (juce::Colours::gainsboro);
        g.strokePath(p, juce::PathStrokeType (2));
        
    }
    else
    {
        g.setColour (juce::Colours::white);
        g.setFont (22.0f);
        g.drawFittedText ("Drop", getLocalBounds(), juce::Justification::centred, 1);
        
    }

   
}

then when I click the reverse button, processor will call this wavethumbnail to repaint, and this is where it stops debugging.

In the first pass sample is 0, so you are accessing mAudioPoints[mAudioPoints.size()], which is out of bounds…
You need to subtract 1 in addition to the counter

@kamedin hinted that already in his first post :wink:

1 Like

Damn, such a stupid fault, I still haven’t got used to start count from 0 lol.
Thanks for the help man!

Just to anticipate further issues, a couple of things.

You may not want to do this in paint(), which can be called at any moment by the system. You could store the path in the editor, update it when the relevant events happen (or on a timer if it’s continuously updating), then call repaint(), and just draw the stored path in paint().

I don’t know what getWaveform() returns, but if it’s an AudioBuffer or similar, you need synchronization. If the buffer is written in processBlock(), it may be written while it’s being read by the editor, with the worst case being a change in numSamples.

1 Like

Appreciated for this!

I used setBufferedToImage(true) in the editor for the WaveThumbnail I use, which should helps avoiding repaint rapidly.
And for now the waveform is static, no animation, and won’t change in real time, so I guess it’s ok to leave it unsynchronized.