Trying to make a float matrix and demultiplexers to control it

My first topic, just registered to this forum!

I’m trying to make and control a 3D array of float values for my plugin’s purposes. I call it matrix.

  • I want to change the matrix’s size on the run, so I can’t use APVTS for it. I don’t plan on making its values automatable by the host, so it should be fine.
    Topic refered: Adding Parameters to VST3 while plugin is running?
  • And also to make controlling the matrix easy, I plan to make a few knobs to control one row of the matrix at a time (the row’s length is constant), and 2 “demultiplexers” to specify the row. (maybe 2 sliders with 0-1 range will do it?) I read that Attachments can be swapped, so I thought this should be possible.
    Topic refered: Slider - change attached parameter dynamically

What is a good approach to achieve these? How can I implement the matrix so it can be accessed by both the PluginEditor and PluginProcessor, and be stored in the plugin’s state just like APVTS? Also I would like to know how I can interface the matrix and the demultiplexers.

Actually I’m a non-English speaker who has just begun learning c++ so I might have asked, and might be going to ask mind-numbingly dumb things… Please bear with me. Thank you in advance.

You should take a look at std::mdspan. This will help you create a light abstraction over a single vector and call into it as a multi-dimensional vector, rather than create a vector of vectors of vectors. Unfortunately it’s a C++23 feature which might not be available in all compilers, but if you’re just getting started, why not use the latest tools?

1 Like

Thanks for the info! I’ve never heard of it before since I’ve only used “array of arrays” technique to achieve matrices in other languages.

I’ll try to implement it as my plugin’s state now (if that’s ever possible)… might ask more in the future.

It looks like std::mdspan won’t help you much with resizing the matrix, since it’s non-owning. But it still might be preferable, since the vector of vectors option has less efficient lookup and is difficult in its own way.

You also have to consider threading issues, since you want your matrix accessible on the message thread and the audio thread, but locking the audio thread is a no-no.

One idea that springs to mind is that instead of resizing the matrix, you could create a new one with the new dimensions and then swap it in. This goes part of the way to solving both problems. Here’s roughly what I mean:

using MatrixData = std::vector<std::atomic_float>;
using Coord = std::array<int, 3>;

std::shared_ptr<const Matrix> currentMatrixData;

std::atomic_int matrixX, matrixY, matrixZ;

void copyMatrixData(MatrixData& from, MatrixData& to, const Coord& oldDimensions, const Coord& newDimensions)
{
  // use std::mdspan here.
}

void resizeMatrix(int x, y, z)
{
  auto newMatrixData = std::make_shared<MatrixData>();
  newMatrixData->resize(x * y * z);
  
  copyMatrixData(*newMatrixData, *currentMatrixData, {matrixX, matrixY, matrixZ}, {x, y, z});

  currentMatrixData.exchange(newMatrixData)
}

float getValue(const Coord& c)
{
    return std::mdspan(*currentMatrixData)[c[0], c[1], c[2]];
}

I just typed this code without compiling it and there will be errors, but you should get the idea. You’ll also have to think through and test the threading safety, as I’m not sure that .exchange() is thread safe*. But the point is that doing it this way is probably better than locking the whole thing while you resize every single sub-vector.

  • EDIT: You’ll also need to figure out of way of ensuring that the shared_ptr doesn’t deallocate on the audio thread.

woah. threading issues? it’s kinda overwhelming my mind. I have to learn those problems carefully… And as I guessed, appending to arrays is an expensive operation. I’ll go with swapping the matrix.
Thank you for guiding me. :relieved:

After a bit of thinking, I decided to make a custom class to hold internal data like the matrix, and pass it to the pluginEditor’s initializer to share them, because APVTS doesn’t support arrays.
Is that a good approach? because I’m not sure how to use sliders without using APVTS’s attachment…
Might have to make another topic about interfacing.

(But I’ll still use it to save the matrix in the plugin’s state. I found a post about saving a matrix in a valuetree, which is exactly what I want. I think I can merge the APVTS’s copy and my custom ValueTree right before saving, and separate them after loading, right? Is there any better method?
Topic refered: Multidimensional array of vars? )

I used to have similar approach in my own project, which was mentioned in above posts: creating new data and swapping it with the audio processing whenever required. It created a surprising amount of complexity to simple tasks, so I replaced it with the below approach and I’m happy now.


If the matrix size won’t be tweaked in realtime (or automated) like synth plugins filter knobs would, it means that it might be OK if there would be some small audio glitch when you change the matrix size? If it is OK to have some potential audio glitch when it happens, you could simply do the following:

  1. Use mutex to protect the whole audio thread (renderNextBlock()).
  2. renderNextBlock() should try locking the mutex. If it fails, it should just skip processing that audio block.
  3. Use that exact same mutex to protect the matrix size change operation, but so that it will always lock the mutex, instead of just trying to lock it.

This way you’ll have sometimes a tiny audio glitch when you change the matrix size, but you can change the matrix however you want, doing memory management etc. And the plugin won’t crash.

1 Like