Two instances of my plugin communicating with each other in a DAW project - what's a good approach?

I would like my plugin to check for other instances of itself in a DAW project, because I’d like the option for them to communicate.

I’d just like to communicate two atomic values (so hopefully thread safe), and update them when necessary.

What’s a good approach to having two instances communicate, and would the approach work on all DAWs ?

An important factor to consider is whether the DAW loads the plug-ins all in the same process or not.
If it does, your two atomics can simply be made static and their value will be accessible across all instances of your plug-in.
If the DAW loads plug-ins in separate processes, which seems more and more frequent as time goes, then you have to resort to more complex solutions to keep them updated between instances, search the JUCE docs for anything that has “Interprocess” in its name.
I don’t remember which DAW offer which option

2 Likes

You should be able to use JUCE MemoryMappedFile and InterProcessLock to share memory between processes. Technically you can’t use a std::atomic variable across processes via shared memory (C++ standard doesn’t guarantee atomicity across processes), but realistically if you use an InterProcessLock to reference an aligned ‘int’ pointer into shared memory you should be fine.

1 Like

Even though a few DAWs now load plugins in a separate process, most/all of them would load different instances of the same plugin in the same process at least by default, even in multi process mode.

IIRC Bitwig is the only that lets you choose a separate process for each plugin and even then it’s the most extreme option that the user can select - probably because it would break quite a lot of plugins and also add quite a large memory overhead to many other plugins.

(In the photo below from Bitwig’s settings, all options except for the right most one would still load multiple instances in the same process).

4 Likes

^^ what he said

We’ve used juce::SharedResourcePointer for this purpose pretty reliably. No need to do IPC or some other gymnastics. I’ve had no complaints of it not working yet…

6 Likes

@yfede @eyalamir @adamwilson @caustik
appreciate all the responses …
With the SharedResourcePointer I assume the code (below) is a good start?

I did get some information about DAWs. I don’t know how accurate it is.
FL Studio “Bridging” mode. Might run plugins in separate process.
Cubase and Nuendo Sandboxing mode. Logic Pro AU Sandboxing. Ableton also has Sandboxing mode. (Not that bridging or sandbox modes are default behaviour though).
There was some question mark over Reason and AAX too, the details I’m not sure about.

sharedMemory.h
class SharedData
{
public:
    SharedData() = default;

    void setValues(int newSmKey, int newSmMode)
    {
        smKey.store(newSmKey, std::memory_order_relaxed);
        smMode.store(newSmMode, std::memory_order_relaxed);
    }

    void getValues(int& outSmKey, int& outSmMode)
    {
        outSmKey = smKey.load(std::memory_order_relaxed);
        outSmMode = smMode.load(std::memory_order_relaxed);
    }
private:
    std::atomic<int> smKey{ 0 };
    std::atomic<int> smMode{ 0 };
};

then in pluginProcessor

private:
    juce::SharedResourcePointer<SharedData> mSharedData;
... //setting 
sharedData->setValues(newSmKey, newSmMode);
//getting
int currentSmKey = sharedData->getSmKey().load(std::memory_order_relaxed);
int currentSmMode = sharedData->getSmMode().load(std::memory_order_relaxed);

Is this AI information?
FL Studio/Cubase/Nuendo/Ableton Live don’t load plugins in a separate process.

Logic Pro does - but it loads all external plugins in the same process for all plugins, it’s just not the same process as Logic.

“Bridging” from native ARM64 to Intel on Mac might in theory use a different process - but it would still be the same process for multiple instances of the same plugin.

As said, even in DAWs like Reaper and Bitwig that do allow that, unless the user chooses some extreme setting the DAW would still prefer to load multiple instances of the plugin in the same process.

2 Likes

We have gathered some experience with inter plugin communication over the last years with out smart:eq (and coincidentally, I’ll even be holding an ADC talk about that topic) and to my knowledge, Bitwig is the only popular host out there in the wild where purely memory based inter plugin communication fails because of completely sandboxed plugin instances – Reaper configured with non default settings taken aside.

So as of today, relying on in process memory options like in your approach should just work in all of the big hosts. However, be prepared that DAWs are moving forward, I’d not be surprised if completely sandboxed plugins will become more common thing in the future. It’s good to actively design your implementation and interfaces in a way that it could be extended to dynamically handle that case. But if you start with that, things quickly get a lot more complicated (e.g. how do plugins in different processes even know about their existence, what if the user runs two different hosts with your plugin in parallel, etc.)

4 Likes

Thank you for this great feedback!

I do think that if hosts would sandbox different instances of the same plugin they would take quite a large performance hit.

Basically things like image memory, thread pools… wavetables… would all have to be multiplied for each instance which would be a major regression for the users of those DAWs.

So I can’t imagine general purpose hosts doing that. Sandboxing different plugins - sure, they might definitely move to that. I can also see ‘specialized’ hosts doing that maybe for live situations where stability means more than the overall usage.

But those are super rare and are usually solved anyway by using a subprocess for all instances of the same plugin.

1 Like