Storing a vector of Editor component's data in the audioProcessor to avoid it being lost when the Editor is destroyed, best practice (non-apvts)?

I have a vector of components in the Editor, for example like the ones below.

Each node is a custom component. When the Editor is closed, the components are automatically destroyed, and obviously I need their parameters to act upon in the Processor.

I thought of creating this vector in the audioProcessor so that its not destroyed when the Editor is closed. Or another way I heard was to write out an xml tree of all the components data when the Editor is destroyed.

Whats the way you would approach it? I think I have to make as many params as possible atomics for thread safety.
image

What is the actual data that you want to have persist? If the components are the owner of that data, that is probably the problem you are trying to solve. Assuming the components represent some sort of data that is used by the processor, the processor should own the data and when the editor is created the components are then created from that data. Try to approach your architecture with the idea that the editor is just a view into the processor. Also, if I have misunderstood the problem, I apologize.

2 Likes

Most of my components like sliders and buttons are created in the Editor with reference to existing data gleaned from the Processor (and apvts).

The difference here is that these nodes (custom components) can be dynamically created by the user in the Editor at any time. So the nodes are a vector of custom components.

These nodes contain information like node position in the x,y, interpolation type, what LFO they are attached to, draggable/locked, selected/unselected etc.

If your plugin has dynamic data, you need to design a way to create that dynamic data in the processor like @cpr2323 mentioned. Then you need a way to create these components to reflect the dynamic state of the data, and not own it.

1 Like

I’ll create all the envelope node components in a vector in the audioProcessor.

I’m probably going to forbid the dynamic creation of new node components (and user alteration of their values) while the playhead isPlaying to avoid any thread issues, and probably make a lot of the variables atomic.
The Processor will mainly just be reading values from the vector of components.

I don’t need to get into mutex do I, or does this approach above seem pretty thread safe ?

It would not be thread safe. Anything to do with a dynamic array/vector/etc can’t be thread safe because the array could change (say by the UI) while the processor is reading it, invalidating the iterators and causing horrible UB.

Protecting all operations with a mutex would solve the threading problem, but can also cause performance issues on the processor side. I’d suggest looking at lock free Fifo to communicate the full data structure to the processor whenever an update from the UI has happened.

You can search the forum for many resources about how to do that.

3 Likes

If there is a reasonable maximum number of components and the values don’t have to be updated at the same time, you could use several array of atomics to store values and an atomic to store actual number of components.

1 Like

You can’t really do that. Imagine if you’re interpolating an array while changing one of the points… you might get horrible results.

Atomics would stop thread sanitizer from shouting, but the data race is always there when modifying a single value in an array that has to be iterated, like points in an envelope/lfo.

Atomics can only be used when the values are logically atomic/separate from other values.

1 Like

AFAIK, there is no data race if you use array of atomics, e.g., std::array<std::atomic<int>, 10>. There is a chance that some values are updated while some are outdated. If you do want to rule out that possibility, you could tryLock a spinLock on the audio process and lock the spinlock on the message thread.

However, for envelope FIFO might be a better fit.

1 Like

There’s no data race if you just access the individual values and read from them.

But if the values are connected, for example if you interpolate an LFO from a list of points, then you will get bad values during a read loop while a new value is being written.

1 Like

Yes, you are correct. Cause we need to treat the whole array as one object, just like the coeffs of filters.

1 Like