A small question about a data race

I truly can’t believe that this is an always working and save solution. What happens in the following:

  • Audio thread sets value to false (knowing something updated)
  • Starts reading the data half way
  • UI thread pushes new changes and sets the boolean back to true
  • Audio thread continues reading and reads potential garbage.

@dariop please read this thread, especially my post. The creator of the library I references also has a talk on JUCEs YouTube channel. Please watch that for a deeper explanation of what the code does.

Now to your exact use-case. There is this number one rule in concurrent programming: don’t implement the algorithms yourself. Use what is out there. It is 1) for sure better tested, 2) for sure better maintained and 3) developed by someone who has more experience on that topic than you do.

With that being said, here is what I would do:

  1. First of all, make sure your sampler voices use an own self-sustaining pointer to you sample. Which means, don’t keep a reference to the sample living in the actual sampler, but give them their own “shared” (if you so will) pointer. That ensures that, what ever happens, you are not screwing with the voice already playing.
  2. The backend structure (might be the so called UI code in your case) has somewhere the SnapshotSource<AudioBuffer<float>> currentSample object. When the user changes the sample, the UI loads the file and flushed the data into currentSample. Ever time a new voice is started, the sampler pushes Snapshot<AudioBuffer> into the voice to be played.
  3. Start a time thread, that regularly deletes the outdated objects from the currentSample. (regular juce::Timer is enough, probably ever second is fine). EDIT: actually, I think you could cue up the timer thread when the user changed something as this is the only way you could built up stale objects. But that sounds a bit like premature optimisation to me, but might be a fun task to think about.

Thats it, you are done. It is actually a very easy task to accomplish if you know that there is a certain type of concurrent memory management that should be going on (which you did, since you asked the question) and you know the library that has the abilities to solve the problem. You should work with that as passing around std::shared_ptrs but the library assures you, you are doing it in a thread safe way.

This is of course a very basic and simple implementation. For example you always have the sample completely loaded in RAM. If you are building something small, this is not a problem. If you have a bigger sampler, you should overthink this part.