What are the chances of dynamic plugin buses ever working?

Such a lovely coincidence to see @fr810 back for a cameo appearance on the forum. And on the very same day that I’m also here to ask my first question in years!

…and even more fun that the question is about the one topic where Fabian is probably the only living human who actually has a vague grasp of it: the dreaded plugin buses!

I remember Fabian implementing the JUCE plugin bus system years ago. I’d had a go, failed and given up. Probably others on the team had also failed and given up. Fabian took the challenge, and after huge effort managed to come up with a system that worked. Sadly it was also unintelligible and nobody else understood it, but it was still probably the best and only way to make it work in all the plugin formats.

Anyway, my question today is because I’m in the middle of trying to get our Cmajor JIT loader plugin working. That means that it’s a plugin that can randomly recompile itself and change things like the number of parameters and bus layout at any moment while running.

I know that changing the list of parameters dynamically will crash most DAWs, and hoping to make that work is a bit of a lost cause. What I’m not sure about is whether dynamically changing buses is equally a waste of effort. I’d have thought that some plugins will add or remove side-chain buses while running, and that mostly works OK, right…?

I did a quick test with the JUCE audio plugin host, but even if I just try to change the number of channels in an existing bus, it all goes horribly wrong. But I’m totally unclear about whether:

  • it’s reasonable to expect the real DAWs to generally cope with bus changes?
  • whether or not I’m doing it all wrong, given the 30 different bus-related virtual methods in AudioProcessor with confusing names, where it’s very hard to fathom whether a host is supposed to call them, or a plugin is supposed to call them, or the plugin is supposed to override them and do something…

So I guess my question is: should I just move on and make our JIT plugin declare 2 ins and 2 outs, and never change them? Or is there any point in trying to make it work before giving up?

3 Likes

Such a lovely coincidence to see @fr810 back for a cameo appearance on the forum. And on the very same day that I’m also here to ask my first question in years!

Happy to be here again and happy to answer your first JUCE question in years. It’s been a while for me too, so some of my knowledge will be a bit rusty.

Sadly it was also unintelligible and nobody else understood it

That’s true and in hindsight, I would have done some things differently. However, for anybody struggling with this, I always like to recommend @noahdayan 's excellent tutorial on this here. It’s actually not that hard once you get the hang of it.

I’d have thought that some plugins will add or remove side-chain buses while running, and that mostly works OK, right…?

That’s actually not true. You need to already have added the side-chain bus to your plug-in right from the start. But it’s in a “disabled” state (i.e. it’s assigned a layout with zero channels). The host simply enables the bus. Only AUs can dynamically add/remove busses but no host actually supports this. There are, however, Apple provided spatial mixer AUs that support this that developers can load in their audio apps.

it’s reasonable to expect the real DAWs to generally cope with bus changes?

The big problem with this whole bus layouts thing is that no plug-in format can actively change their own layout. The plug-in is always passive. This means that only the host can ever request a layout/busses change. The only thing the plug-in can do is advertise a default layout (which the DAW can completely ignore) and reject layouts that the host tries to apply.

I think the best strategy here, as you say, would be to create two input/two output busses. The default layout of the sidechain busses should be AudioChannelSet::disabled - the main busses should either be mono or stereo.

Then, simply forward the isBusesLayoutSupported callback to the JIT’d plug-in. However, you will still run into a problem if the current selected layout (let’s say it’s mono on the main busses) is no longer supported by the new JIT’d plug-in. There is no way for your plug-in to switch away from the no longer supported layout as there is no way for a plug-in to do this pro-actively.


whether or not I’m doing it all wrong, given the 30 different bus-related virtual methods in AudioProcessor with confusing names, where it’s very hard to fathom whether a host is supposed to call them, or a plugin is supposed to call them, or the plugin is supposed to override them and do something…

I think this is more of a problem with JUCE’s original design (touché :smile:) of having a single AudioProcessor class which is used both when hosting and when implementing a plug-in (AUv3 also does this mistake). The same problem you mention also exists for parameters or even processBlockBypassed (<- OK to override when writing a plug-in but don’t use when hosting one).

2 Likes

Here is also some sample code I wrote a while back on the correct way to forward the bus layout callbacks to a wrapped plug-in. Maybe this will help you:

Thanks!

Ah… I had at the back of my mind some dim memory of a tutorial about this lurking out there somewhere - ta for the link, I’ll try to refresh my mental model of how it works!

Yeah, my approach was/is to create some default buses and override isBusesLayoutSupported, but for some reason I couldn’t get it working. Could just have been a dumb mistake in the way I was doing it, TBH - I’ll have another go. :slight_smile:

1 Like

Yes, that’s a good point, I remember us talking about this years ago when we both still worked on JUCE!

It’s one of those annoying bits of naming like “input” and “output” where the meaning is reversed depending whether you’re looking at it from the inside or outside. My gut feeling is that even if you split it up, it’d still be very hard to name the two halves in a non-confusing way.

Coming back to these classes after a long absence, there’s an awful lot of cruft I’m noticing… All the places where plugins are still referred to “filters” is a throwback to stuff I must have written back in the early 2000s!

1 Like

Two potential workarrounds:

  • present a dialog informing the user that the bus change will happen the next time the plugin is loaded (i.e. ask the user to unload and reload the plugin). This gives the plugin the opportunity to present a different bus configuration to the DAW.
  • provide multiple versions of your plugin in the binary, with different IO configurations, e.g. 2-in-2-out, 1-in-2-out …etc etc, and let the user load the one they want. obviously these would all be same plugin ‘under the hood’.

What SynthEdit does is: Any plugin made with SynthEdit is the exact same binary, all that changes is a XML file that gets inserted into its resources that describes the number of inputs and outputs and parameters. When the plugin loads, it reads the XML file and presents itself to the DAW as whatever it needs to be (an instrument, an effect, 2 inputs, 0 inputs, 16 inputs or whatever).

All plugin binaries have a ‘factory’ that lists the various plugins available in that binary. Could be one plugin, could be many. Could be one ‘real’ plugin, but the factory presents it to the DAW as many ‘virtual’ plugins. One might have 5 parameters, another 500. But they are really all just the one plugin, instantiated with some ‘metadata’ about what IO configuration it should present to the DAW.

If you made a plugin with a ‘Save-As’ functionality, you can give the illusion that the end user is creating a brand-new plugin, when all they are really doing is adding an entry into the plugins resources, that will result in the factory listing the binary as having one extra plugin available, with as many busses and parameters as the user wants. (available next time it’s scanned I guess).

Hope that makes some sense.

2 Likes

I think its the hosts responsibility to choose the from right layouts (which imho makes sense)

And its working Cubase/Logic/ProTools, some other always choose the default configuration (like Ableton last time I checked)

The rest of daw manufacturers should wake up and give the user the possibility to change the I/0 settings without having to use those ugly workarounds that only lead to ugly problems like multiple plugin ids etc. for the same plugin.

1 Like

Hey Fabian, that surprises me because in my experience I cannot add or remove busses when loading 3rd party AUs as a plugin host (try it with the JUCE plugin host and any AU plugin that has a sidechain input). It’s even impossible to disable the non-main buses in AU plugins, which is something that has been bothering me for years. Or are you talking about the AUv3 format?

One can, however, change or disable buses in VST3 plugins in the JUCE plugin host.