Hi, I’m waiting because I have a really big problem.
There is a way to have a static variable shared among different plugins instances of different types?
so for example
struct Shared
{
static int variable;
};
class PluginA
{
Shared shared;
};
class PluginB
{
Shared shared;
};
obviously in projucer I created two project first is PluginA and second is PluginB and both once launched must see only a shared instance of shared.variable.
up to now if I launch in cubase for example 2 instance of PluginA shared.variable is shared among the two, but not among for example an instance of PluginA and one of PluginB
Indeed, @JeffMcClintock’s advice is good .. here’s something I have in my lab folder that might be useful:
(EDIT: I decided I need to use this myself personally, so I fleshed it out quite a bit and made it a bit more robust .. updated code below - to test, run two copies of the ‘smm’ binary ..)
To compile and test (assuming you saved the code as ‘smm.cpp’):
g++ -std=c++11 smm.cpp -o smm
Run results:
# Terminal 1
# ./smm
SharedData size: 404
Attempting to create shared memory '/MySharedMem' with size 404
Creator failed: Shared memory already exists
Attempting to open shared memory '/MySharedMem' with size 404
Opened existing shared memory with size: 16384
Process 28287 sees PIDs: 28142 28287 (Total: 2)
Process 28287 sees PIDs: 28287 (Total: 1)
Process 28287 sees PIDs: 28287 (Total: 1)
Process 28287 sees PIDs: 28287 (Total: 1)
Process 28287 sees PIDs: 28291 28287 (Total: 2)
Process 28287 sees PIDs: 28291 28287 (Total: 2)
Process 28287 sees PIDs: 28291 28287 (Total: 2)
Process 28287 sees PIDs: 28291 28287 (Total: 2)
Process 28287 sees PIDs: 28291 28287 (Total: 2)
Process 28287 sees PIDs: 28287 (Total: 1)
Process 28287 sees PIDs: 28287 (Total: 1)
Process 28287 sees PIDs: 28287 (Total: 1)
# Terminal 2:
./smm
SharedData size: 404
Attempting to create shared memory '/MySharedMem' with size 404
Creator failed: Shared memory already exists
Attempting to open shared memory '/MySharedMem' with size 404
Opened existing shared memory with size: 16384
Process 28291 sees PIDs: 28291 28287 (Total: 2)
Process 28291 sees PIDs: 28291 28287 (Total: 2)
Process 28291 sees PIDs: 28291 28287 (Total: 2)
Process 28291 sees PIDs: 28291 28287 (Total: 2)
^C
(Disclaimer: untested on Windows/iOS …)
EDIT2: I decided to use this in a project, so I put it in a repo:
or you could put your shared variables in a DLL, and load the same DLL from both plug-in types? Disclaimer, I’ve never tried this in practice, but I am aware that both JUCE and CHOC offer mechanisms to load dynamic libraries
Or you could use some sort of inter-process communication, if your model is more the kind of “one server” (e.g. a license manager that runs as a standalone app) and multiple clients (the plug-ins)
Within a plugin, you can also use JUCE’ built-in SharedResourcePointer:
SharedResourcePointer<SharedParams> sharedParams;
.. to share a struct between multiple instances of your plugin.
It should be noted also that Shared memory-mapped structs are not necessarily a ‘safe’ way to do things in this era of sandboxes and isolation .. better is to use the Inter-process mechanisms that are available for your chosen target platforms .. and of course, if JUCE’ own MemoryMappedFile works for your use case, its preferable, also…
IPC methods are your only option. Modern DAWs can load your plugin instances in seperate processes which makes the static/shared DLL methods not work anymore.
I believe most DAWs will put all the same plugin in a single process space – so SharedResourcePointer should work within the same plugin.. but not across plugins which will likely be in a separate process.
Has anybody experimented with this and can offer some insights?
I am also looking for ways to share a variable between different plugins across Windows or Mac DAWs. Do you reckon it is possible via named pipes between those plugins? Or is it going to need a local service to synchronize those queries?
Memory mapped files with proper synchronization is the best approach.
JUCE’ MemoryMappedFile and InterprocessLock are within the grasp of the average JUCE project.
In a more sophisticated setting, having your own ‘helper agent’ which accepts socket connections from your other plugins might be a fruitful approach as well, albeit with heavier load for the user.
Has anybody experimented with this and can offer some insights?
Not in the context of juce or audio programming, but I successfully put an atomic variable in IPC shared memory on windows, and it worked great (this was in the context of sharing gui slider values between two instances of the same process) Here’s the tutorial I used
Also if you do choose to go the shared memory route, you should note that it’s technically not okay to put an std::atomic in shared memory, even if it’s lock-free (because yada yada standard says nothing about different processes). It will almost certainly be fine in practice, but if you want to be 100% safe you should use platform specific atomic functions
Hi ibisum, thanks a lot for sharing this.
I only have one concern. Is this going to work with sandboxed plugins? (e.g. in Bitwig). Does it work in Windows and Mac? In what context have you tried it?
I have used shared memory/IPC techniques successfully for decades in projects spanning a wide variety of contexts, but in the context of JUCE plugins I have only needed to use JUCE’ SharedResourcePointer for local sharing between multiple instances of the same plugin. You can see this in effect in the Austrian Audio PolarDesigner plugin, which provides the ability to copy parameter settings between up to 5 different instances of the plugin (the “Sync Group” feature).
Modern sandbox’ing techniques intend to limit IPC through gatekeeping methods, since data exfiltration through IPC mechanisms is a very common security nightmare, but as long as you use file-based IPC through memory mapped files to a common sandbox directory (e.g. ~/Music), with synchronisation primitives, you should be okay. Choose a sandbox-compliant shared directory for your mapped file and set up a timer to poll for updates to make it robust, and things should work fine.
Report in when you get it working and let us know how it goes.