This isn’t really a plugin-specific problem, it’s more of a general software architecture problem. Really, you shouldn’t be sharing a single piece of data across that many scopes at all – and, this information “how many voices are active” also doesn’t have to live as a piece of stateful data at all, because it can be a member function of the object managing the voices (ie, the Synth class), like I mentioned above. If this is the case, then the only dependency cycle that needs to exist is that the SynthVoice must know about the Synth, but this cycle already exists in the juce Synth/SynthVoice class pair anyway.
Here’s what I would do:
struct MySynth : juce::Synthesiser
int numActiveVoices() const
int num = 0;
for (auto* voice : voices)
struct MySynthVoice : juce::SynthesiserVoice
MySynthVoice (MySynth& parent)
: SynthesiserVoice (&parent), synth(parent)
// now you can call synth.numActiveVoices() anywhere in this class!
The synth voice doesn’t need to know about the processor, and the processor doesn’t need to know about the synth voices. The synth is entirely encapsulated as one mega-object.
Hope this helps.