What happens on (second) UIClose

Hi, I ran into a problem concerning my plugin based on IPlugEffect example. It’s rather difficult for me to track the problem down, because I just witnessed it after it may be present since a while.

The problem: I debug with reaper and just found out that it crashes exactly after the second UIClose. I had multiple different errors, but after disabling some elements which seem to be just follow up problems I got this:

File: minkernel\crts\ucrt\src\appcrt\heap\debug_heap.cpp
Line: 908

Expression: is_block_type_valid(header->_block_use)

It seems to have something to do with dynamically allocated heap memory. The plugin uses some Buffers using sample** and a float* to store some values for a frequency response meter. Until the crash the plugin works as intended. I’d provide code, but since I don’t know when this problem occured I don’t know where to start, and I don’t want to bother you with my 2000 lines plugin code. I just might not have closed the UI two times since starting the project.

So my question is: what happens, if the user closes the GUI, where can I start to search for problems?

When I enable the follow up problems again (send meter values OnIdle()), it returns

File: D:\DSP\SRPlugins\iPlug2\IGraphics\IGrap...orDelegate.cpp
Line: 90

Expression: pControl

because the data seems to be nullptr.

Environment: Windows 10, VSCE 2022, c++ v143

Ok, I tracked that issue. It was the custom control (frequency response), which held 3 float* variables, for example for x and y positions to draw. In ~Class I deleted them.

Since I learned now that in current IPlug2 there are no controls if the UI is closed, these values being deleted every time the UI closed, I guess. I replaces these *float with std::vector, now it works. I used this meter class in IPlug2 already about 1,5 years ago without problems.

The questions left over are:

  • Under which conditions I need to make this if (!g.NControls())-test in my layout function (in NanoVG/SKIA), and in which usual cases I have to test if (GetUI())?
  • How would I handle pointers in controls derived from IControl anyway?
  • Whats the best way to handle huge data (single/multichannel) anyway? I saw examples with T**, WDL_Typedbuf or WDL_Ptrlist, std::array, std::vector and IPlugQueue and can’t figure out good use cases for each. In my own buffer class I tried all of them, seeing std::vector causing double cpu consumption.

Thanks a lot!

It’s a bit difficult to advise without seeing some code. If you can make a simplified example of what you’re trying to do that might help

Sure, thanks.

One example might be this response meter graph, where I store each a fixed amount of floats, one for the Y-axis values itself, and one each for the respective x and y coordinates the path will go through. Its in fact the control screwed up:

SRGraphBase(IRECT bounds, int numValues, float* values, float baseline = .5f
, const IVStyle& style = DEFAULT_STYLE)
  : IControl(bounds, -1)
  , IVectorBase(style)
  , mBaseline(baseline)
  , mNumValues(numValues)
  , mValues(numValues, .0)
  , mX(numValues, .0)
  , mY(numValues, .0)

There follows Draw() and OnResize() function mapping values to IRECT-x-y-values. Finally values are transmitted via:

void Process(float* values) {
  for (int i = 0; i < mNumValues; i++)
    mValues.at(i) = Clip<float>(values[i], -1.f, 1.f);
  OnResize();
};

And has now the members:

int mNumValues;
float mBaseline; 
// Following were float* before
std::vector<float> mValues;
std::vector<float> mX;
std::vector<float> mY;

The other example would be the buffer class. This may usually be used for storing sample data of different channel count, for sidechains, or if I just want to manipulate something before sending it to a meter, which usually wants sample** data. Therefore I made that class and tried different approaches:

template
<typename T = double, int MAXNUMCHANNELS = 1, int MAXNUMFRAMES = DEFAULT_BLOCK_SIZE>
  class SRBuffer {
  public:
    SRBuffer(int nChannels = MAXNUMCHANNELS, int nFrames = MAXNUMFRAMES)
    : mNumChannels(nChannels)
    , mNumFrames(nFrames)
    , mBuffer(new T* [nChannels])
// [...]

With members:

  unsigned int mNumFrames;
  unsigned int mNumChannels;
#if BUFFERIMPL == 1 // vector
  std::vector<WDL_TypedBuf<T>> mBuffer;
#elif BUFFERIMPL == 2 // ptrlist
  WDL_TypedBuf<T> mBufferData;
  WDL_PtrList<T> mBuffer;
#elif BUFFERIMPL == 3 // T**
  T** mBuffer;
#endif

There I have things like this, or respective T** Getbuffer() for getting the whole thing:

T GetBuffer(int channel, int sample) {
#if BUFFERIMPL == 1 // vector
  return mBuffer[channel].Get()[sample];
#elif BUFFERIMPL == 2 // ptrlist
  return mBuffer.GetList()[channel][sample];
#elif BUFFERIMPL == 3 // T**
  return mBuffer[channel][sample];
#endif

So finally the question is, if there might be a good solution for any of these use cases, where I want to store or read data may it be single/multichannel, samples or gain values or whatever, fixed or resizable.

By the way, I dont mind about protecting code or something, it’s just a hobby I turn to every other year (I bothered you already with WDL-OL). Just didn’t want to blow up the forum with my beginner code. Here it is:
SRBuffer.h
SRGraphBase