surge-synthesizer / surge

Synthesizer plug-in (previously released as Vember Audio Surge)
https://surge-synthesizer.github.io/
GNU General Public License v3.0
3.14k stars 400 forks source link

VST3 multi-channel configuration - bus connection order depencency #6847

Closed x42 closed 1 year ago

x42 commented 1 year ago

Bug Description:

Surge VST3 becomes silent when first using it in 6 channel mode, and then changing it back to stereo:

/* Enable all output busses */
_component->activateBus (Vst::kAudio, Vst::kOutput, 0, true);
_component->activateBus (Vst::kAudio, Vst::kOutput, 1, true);
_component->activateBus (Vst::kAudio, Vst::kOutput, 2, true);

/* Now only enable stereo-out */
_component->activateBus (Vst::kAudio, Vst::kOutput, 0, true);
_component->activateBus (Vst::kAudio, Vst::kOutput, 1, false); // XXX
_component->activateBus (Vst::kAudio, Vst::kOutput, 2, false);

At XXX above

https://github.com/surge-synthesizer/surge/blob/aa66b292bdb21c229820ff93fd936fda0cf976ae/src/surge-xt/SurgeSynthProcessor.cpp#L212

c1 == 0 (first output bus was just disabled) but c2 ==2 (second bus is still active): isBusesLayoutSupported returns false;

JUCE's AudioProcessor::getNextBestLayout ends up with an empty layout, and AudioProcessor::applyBusLayouts disables all outputs incl. the default stereo output. newNumberOfOuts == 0.

Even if the final configuration step succeeds, the first bus remains disabled, and then SurgeSynthProcessor::processBlock just returns without producing sound.

This does not happen with JUCE's default implementation of AudioPluginAudioProcessor::isBusesLayoutSupported in which the connection order is not significant.

Proposed solution, allow any stereo bus configuration:

diff --git a/src/surge-xt/SurgeSynthProcessor.cpp b/src/surge-xt/SurgeSynthProcessor.cpp
index 97634083..2d47c4c4 100644
--- a/src/surge-xt/SurgeSynthProcessor.cpp
+++ b/src/surge-xt/SurgeSynthProcessor.cpp
@@ -209,7 +209,7 @@ bool SurgeSynthProcessor::isBusesLayoutSupported(const BusesLayout &layouts) con
      */
     auto c1 = layouts.getNumChannels(false, 1);
     auto c2 = layouts.getNumChannels(false, 2);
-    auto sceneOut = (c1 == 0 && c2 == 0) || (c1 == 2 && c2 == 2);
+    auto sceneOut = c1 == 0 || c2 == 0 || c1 == 2 || c2 == 2;

     return outputValid && inputValid && sceneOut;
 }

It seems overly restrictive, to only allow both scenes or none of them. That being said, JUCE's logic in Bus::isLayoutSupported -> AudioProcessor::getNextBestLayout is also questionable.

Surge XT Version release_xt_1.1.2-101-gaa66b292

Reproduction Steps: Load the Plugin in Ardour 7.3.0

With a debug version of Ardour you can use Ardour7 -DVST3Config to print calls to activateBus as they' are made. Then play with plugin pin connections. Initially Ardour enables all outputs and then the plugin is reconfigured as stereo:

image

baconpaul commented 1 year ago

Thanks

I get your patch but then I also need to change my scene out Logic above to not write to a null bus in the case we have b only or an only momentarily so will need to look at that too!

Appreciate the report

baconpaul commented 1 year ago

The other problem with your proposed patch is it would allow c1==0 and c2==1 through

I think we really want (c1 = 0 || 2) && (c2 == 0||2)

x42 commented 1 year ago

Yes, please ignore my suggested patch. You are in a much better position to solve this properly.

I have never used JUCE and am also not very familiar with surge's codebase, but since I can easily reproduce this issue, I can provide additional information if needed.

baconpaul commented 1 year ago

Cool.

The real problem is that juce doesn’t recover from a transiently invalid layout right? Nothing special about surge here. a juce sampler would have the same problem if it supported 1 or 8 outs and the host tried to configure it with 5.

baconpaul commented 1 year ago

Actually hmm I’m not sure what juce correct behavior is - you do ask for an invalid bus config

does activate bus return tresult ok in the xx call above in your real code?

x42 commented 1 year ago

Yes, kResultTrue is returned in all cases.

The last call to disable bus2 even returns early.

in modules/juce_audio_processors/processors/juce_AudioProcessor.cpp:1045 AudioProcessor::Bus::enable the condition if (isEnabled() == shouldEnable) is already true, since the earlier call to disable bus 1 disabled all busses.

baconpaul commented 1 year ago

Right so it definitely seems like an error that the xxx activste returns true

reaper and bitwig and logic are able to probe for the valid configs (logic with the au of course) and not offer invalid ones. Any idea how that works? I’ve never written a host.

It’s possible to fix surge of course (although I really do want either 1 or 3 stereo out and not 2) but this problem will also break shortcircuit later this year and that is not fixable in the same way which is why I’d like to figure out the actual core problem

x42 commented 1 year ago

AudioProcessor::applyBusLayouts only returns false if the number of busses mismatch. There does indeed seem no way for it to return false. Which makes some sense. Enabling or disabling a bus should always work.

To not allow mono operation, or to enforce a given channel layout Vst::SpeakerArrangement is used.

Anyway the callgraph is something like this (top to bottom)

AudioProcessor::Bus::enable
AudioProcessor::Bus::setCurrentLayout
AudioProcessor::setChannelLayoutOfBus
 -> AudioProcessor::Bus::getBusesLayoutForLayoutChangeOfBus
  -> isLayoutSupported()
     here the layout is changed to mute all busses via calls to
     -> getNextBestLayout()
AudioProcessor::applyBusLayouts
  here the *best* layout is applied to *all* busses, not just the one that is being en/disabled.

The problem may be the relation between AudioProcessor::getNextBestLayout in juce_AudioProcessor.cpp and your implementation of checkBusesLayoutSupported

baconpaul commented 1 year ago

Does juce surge return a proper speaker arrangement? Or does it return a speaker arrangement which would allow on off on right now.

baconpaul commented 1 year ago

And thanks for the check / get hint, that’s useful

x42 commented 1 year ago

setBusArrangements returns kResultFalse when trying to configure any given Bus as Mono, but otherwise succeeds.

Regardless if the bus is active or not, getBusArrangement always reports SpeakerArrangement::kStereo for each bus.

x42 commented 1 year ago

Can you check if Reaper or Bitwig ever configures the synth as stereo, or are all busses always active?

baconpaul commented 1 year ago

Sure will do!

when you draf into reaper it prompts for 2 out or 6 out. When you pull into bitwig the device has an option to add the extra pair of tracks. And when you create in logic it shows you 1x or 3x stereo. But I’ve never actually checked if the other buses are activated anyway when you choose the smaller config! Lol. Will look tomorrow and update here

have some travel over the long weekend which will have me not in dev mode but will try and answer that before I split!

x42 commented 1 year ago

What happens if you change it after the fact in Reaper? Start with 6 out and the change it to Stereo.

It would work when the busses are deactivated in reverse order, but otherwise it would also result in silence.

baconpaul commented 1 year ago

OK just did a quick check in latest reaper

If you choose to add as stereo, the sceneA and sceneB bus have isEnabled false, and have channel count 0.

If you add as 3x stereo they are true and count 2.

When I add surge it asks me this

Screen Shot 2023-02-16 at 6 15 20 PM

but before it does that it calls isBusLayoutSupported what looks like about 15 times. When I add

    std::cout << "isBusLayoutSuported " << outputValid << " " << inputValid << " " << sceneOut << " " << c1 << " " << c2 << std::endl;

I see

isBusLayoutSuported 0 0 0 1 1
isBusLayoutSuported 0 1 0 1 1
isBusLayoutSuported 1 1 1 2 2
isBusLayoutSuported 1 1 1 2 2
isBusLayoutSuported 1 1 1 2 2
isBusLayoutSuported 0 1 0 1 1
isBusLayoutSuported 1 1 1 2 2
isBusLayoutSuported 1 1 1 2 2
isBusLayoutSuported 1 1 1 2 2
isBusLayoutSuported 1 1 1 0 0
isBusLayoutSuported 1 1 1 0 0
isBusLayoutSuported 0 1 0 1 1
isBusLayoutSuported 1 1 1 2 2
isBusLayoutSuported 1 1 1 2 2
isBusLayoutSuported 1 1 1 2 2
isBusLayoutSuported 0 1 0 1 1
isBusLayoutSuported 1 1 1 2 2
isBusLayoutSuported 1 1 1 2 2
isBusLayoutSuported 1 1 1 2 2
isBusLayoutSuported 1 1 1 0 0
isBusLayoutSuported 1 1 1 0 0
isBusLayoutSuported 1 1 1 0 0
isBusLayoutSuported 1 1 1 0 0
isBusLayoutSuported 1 1 1 0 0
isBusLayoutSuported 1 1 1 0 0

When I look at the stack for those I see things like

-------- Stack Trace (14 frames of 29 depth showing) --------
  [  1]: 1   Surge XT                            0x000000028fe2d234 _ZNK19SurgeSynthProcessor22isBusesLayoutSupportedERKN4juce14AudioProcessor11BusesLayoutE + 40
  [  2]: 2   Surge XT                            0x000000029094dac0 _ZNK4juce14AudioProcessor25checkBusesLayoutSupportedERKNS0_11BusesLayoutE + 140
  [  3]: 3   Surge XT                            0x000000029094d838 _ZN4juce14AudioProcessor29setBusesLayoutWithoutEnablingERKNS0_11BusesLayoutE + 544
  [  4]: 4   Surge XT                            0x000000028fdb4b5c _ZN4juce17JuceVST3Component18setBusArrangementsEPyiS1_i + 400
  [  5]: 5   REAPER                              0x000000010521360c _ZN8VST3Data13RecalcBusInfoEi + 780

which looks like it is calling setBusArrangements with all the permutations of the 6 channels and seeing if it gets a true or false before it tries and offers the choice to the user. Note that setBusArrangements is non destructive and also is transactional (that is you don't set them one by one).

So it looks like reaper does a probe across the channels to figure out the correct configurations.

Helpful?

baconpaul commented 1 year ago

What happens if you change it after the fact in Reaper? Start with 6 out and the change it to Stereo.

It would work when the busses are deactivated in reverse order, but otherwise it would also result in silence.

I don't know how to do that in reaper. Do you?

baconpaul commented 1 year ago

in bitwig if I add missing chains (to get the 3x output) then delete the scene A output, the synth still makes noise when i press the virtual keyboard, if that helps? I didn't check if that actually deactivates the bus and gotta run now.

baconpaul commented 1 year ago

OK here's the deal

1: Bitwig always has the channels at 1x3 no matter what I add or delete. I instrumented the call stack a bit more cleanly and here's how it starts up

JUCE v6.1.6
SurgeXT VST3
  - Version      : 1.1.main.aa66b292 with JUCE 60106
  - Build Info   : 2023-02-16 21:14:43 using AppleClang-14.0.0.14000029
  - Data         : "/Library/Application Support/Surge XT"
  - User Data    : "/Users/paul/Documents/Surge XT"

/Users/paul/dev/music/surge/libs/JUCE/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp:2984 (setBusArrangements) numIns=1 numOuts=3
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:207 (isBusesLayoutSupported) inputDisabled=0 outputDisabled=0
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:227 (isBusesLayoutSupported) out1Chan=2 out2Chan=2 inputValid=1 outputValid=1 res=1
/Users/paul/dev/music/surge/libs/JUCE/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp:2907 (activateBus) type=0 dir=0 index=0 state=TRUE
/Users/paul/dev/music/surge/libs/JUCE/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp:2907 (activateBus) type=1 dir=0 index=0 state=TRUE
/Users/paul/dev/music/surge/libs/JUCE/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp:2907 (activateBus) type=0 dir=1 index=2 state=TRUE
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:207 (isBusesLayoutSupported) inputDisabled=0 outputDisabled=0
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:227 (isBusesLayoutSupported) out1Chan=0 out2Chan=0 inputValid=1 outputValid=1 res=1
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:207 (isBusesLayoutSupported) inputDisabled=0 outputDisabled=0
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:227 (isBusesLayoutSupported) out1Chan=0 out2Chan=2 inputValid=1 outputValid=1 res=0
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:207 (isBusesLayoutSupported) inputDisabled=0 outputDisabled=0
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:227 (isBusesLayoutSupported) out1Chan=0 out2Chan=2 inputValid=1 outputValid=1 res=0
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:207 (isBusesLayoutSupported) inputDisabled=0 outputDisabled=0
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:227 (isBusesLayoutSupported) out1Chan=2 out2Chan=2 inputValid=1 outputValid=1 res=1
/Users/paul/dev/music/surge/libs/JUCE/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp:2907 (activateBus) type=0 dir=1 index=1 state=TRUE
/Users/paul/dev/music/surge/libs/JUCE/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp:2907 (activateBus) type=0 dir=1 index=0 state=TRUE
Applying plugin IO state
Applying plugin instance state
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:291 (processBlock) aEnabled=1 aChan=2 bEnabled=1 bChan=2

Reaper is more chatty. When I create a plugin here's what it does up to and then after me selecting 2

JUCE v6.1.6
SurgeXT VST3
  - Version      : 1.1.main.aa66b292 with JUCE 60106
  - Build Info   : 2023-02-16 21:14:43 using AppleClang-14.0.0.14000029
  - Data         : "/Library/Application Support/Surge XT"
  - User Data    : "/Users/paul/Documents/Surge XT"

/Users/paul/dev/music/surge/libs/JUCE/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp:2984 (setBusArrangements) numIns=1 numOuts=3
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:207 (isBusesLayoutSupported) inputDisabled=0 outputDisabled=0
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:227 (isBusesLayoutSupported) out1Chan=1 out2Chan=1 inputValid=0 outputValid=0 res=0
/Users/paul/dev/music/surge/libs/JUCE/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp:2984 (setBusArrangements) numIns=1 numOuts=3
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:207 (isBusesLayoutSupported) inputDisabled=0 outputDisabled=0
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:227 (isBusesLayoutSupported) out1Chan=1 out2Chan=1 inputValid=1 outputValid=1 res=0
/Users/paul/dev/music/surge/libs/JUCE/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp:2984 (setBusArrangements) numIns=1 numOuts=3
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:207 (isBusesLayoutSupported) inputDisabled=0 outputDisabled=0
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:227 (isBusesLayoutSupported) out1Chan=2 out2Chan=2 inputValid=1 outputValid=1 res=1
/Users/paul/dev/music/surge/libs/JUCE/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp:2984 (setBusArrangements) numIns=1 numOuts=3
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:207 (isBusesLayoutSupported) inputDisabled=0 outputDisabled=0
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:227 (isBusesLayoutSupported) out1Chan=2 out2Chan=2 inputValid=1 outputValid=1 res=1
/Users/paul/dev/music/surge/libs/JUCE/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp:2984 (setBusArrangements) numIns=1 numOuts=3
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:207 (isBusesLayoutSupported) inputDisabled=0 outputDisabled=0
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:227 (isBusesLayoutSupported) out1Chan=2 out2Chan=2 inputValid=1 outputValid=1 res=1
/Users/paul/dev/music/surge/libs/JUCE/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp:2984 (setBusArrangements) numIns=1 numOuts=3
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:207 (isBusesLayoutSupported) inputDisabled=0 outputDisabled=0
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:227 (isBusesLayoutSupported) out1Chan=1 out2Chan=1 inputValid=1 outputValid=1 res=0
/Users/paul/dev/music/surge/libs/JUCE/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp:2984 (setBusArrangements) numIns=1 numOuts=3
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:207 (isBusesLayoutSupported) inputDisabled=0 outputDisabled=0
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:227 (isBusesLayoutSupported) out1Chan=2 out2Chan=2 inputValid=1 outputValid=1 res=1
/Users/paul/dev/music/surge/libs/JUCE/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp:2984 (setBusArrangements) numIns=1 numOuts=3
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:207 (isBusesLayoutSupported) inputDisabled=0 outputDisabled=0
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:227 (isBusesLayoutSupported) out1Chan=2 out2Chan=2 inputValid=1 outputValid=1 res=1
/Users/paul/dev/music/surge/libs/JUCE/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp:2984 (setBusArrangements) numIns=1 numOuts=3
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:207 (isBusesLayoutSupported) inputDisabled=0 outputDisabled=0
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:227 (isBusesLayoutSupported) out1Chan=2 out2Chan=2 inputValid=1 outputValid=1 res=1
/Users/paul/dev/music/surge/libs/JUCE/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp:2907 (activateBus) type=1 dir=0 index=0 state=TRUE
/Users/paul/dev/music/surge/libs/JUCE/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp:2907 (activateBus) type=0 dir=0 index=0 state=TRUE
/Users/paul/dev/music/surge/libs/JUCE/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp:2907 (activateBus) type=0 dir=0 index=0 state=FALSE
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:207 (isBusesLayoutSupported) inputDisabled=0 outputDisabled=0
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:227 (isBusesLayoutSupported) out1Chan=0 out2Chan=0 inputValid=1 outputValid=1 res=1
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:207 (isBusesLayoutSupported) inputDisabled=1 outputDisabled=0
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:227 (isBusesLayoutSupported) out1Chan=0 out2Chan=0 inputValid=1 outputValid=1 res=1
/Users/paul/dev/music/surge/libs/JUCE/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp:2984 (setBusArrangements) numIns=1 numOuts=3
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:207 (isBusesLayoutSupported) inputDisabled=0 outputDisabled=0
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:227 (isBusesLayoutSupported) out1Chan=1 out2Chan=1 inputValid=1 outputValid=1 res=0
/Users/paul/dev/music/surge/libs/JUCE/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp:2984 (setBusArrangements) numIns=1 numOuts=3
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:207 (isBusesLayoutSupported) inputDisabled=0 outputDisabled=0
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:227 (isBusesLayoutSupported) out1Chan=2 out2Chan=2 inputValid=1 outputValid=1 res=1
/Users/paul/dev/music/surge/libs/JUCE/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp:2984 (setBusArrangements) numIns=1 numOuts=3
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:207 (isBusesLayoutSupported) inputDisabled=0 outputDisabled=0
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:227 (isBusesLayoutSupported) out1Chan=2 out2Chan=2 inputValid=1 outputValid=1 res=1
/Users/paul/dev/music/surge/libs/JUCE/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp:2984 (setBusArrangements) numIns=1 numOuts=3
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:207 (isBusesLayoutSupported) inputDisabled=0 outputDisabled=0
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:227 (isBusesLayoutSupported) out1Chan=2 out2Chan=2 inputValid=1 outputValid=1 res=1
/Users/paul/dev/music/surge/libs/JUCE/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp:2984 (setBusArrangements) numIns=1 numOuts=3
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:207 (isBusesLayoutSupported) inputDisabled=0 outputDisabled=0
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:227 (isBusesLayoutSupported) out1Chan=1 out2Chan=1 inputValid=1 outputValid=1 res=0
/Users/paul/dev/music/surge/libs/JUCE/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp:2984 (setBusArrangements) numIns=1 numOuts=3
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:207 (isBusesLayoutSupported) inputDisabled=0 outputDisabled=0
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:227 (isBusesLayoutSupported) out1Chan=2 out2Chan=2 inputValid=1 outputValid=1 res=1
/Users/paul/dev/music/surge/libs/JUCE/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp:2984 (setBusArrangements) numIns=1 numOuts=3
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:207 (isBusesLayoutSupported) inputDisabled=0 outputDisabled=0
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:227 (isBusesLayoutSupported) out1Chan=2 out2Chan=2 inputValid=1 outputValid=1 res=1
/Users/paul/dev/music/surge/libs/JUCE/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp:2984 (setBusArrangements) numIns=1 numOuts=3
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:207 (isBusesLayoutSupported) inputDisabled=0 outputDisabled=0
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:227 (isBusesLayoutSupported) out1Chan=2 out2Chan=2 inputValid=1 outputValid=1 res=1
/Users/paul/dev/music/surge/libs/JUCE/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp:2907 (activateBus) type=1 dir=0 index=0 state=TRUE
/Users/paul/dev/music/surge/libs/JUCE/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp:2907 (activateBus) type=0 dir=0 index=0 state=TRUE
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:207 (isBusesLayoutSupported) inputDisabled=1 outputDisabled=0
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:227 (isBusesLayoutSupported) out1Chan=0 out2Chan=0 inputValid=1 outputValid=1 res=1
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:207 (isBusesLayoutSupported) inputDisabled=0 outputDisabled=0
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:227 (isBusesLayoutSupported) out1Chan=0 out2Chan=0 inputValid=1 outputValid=1 res=1
/Users/paul/dev/music/surge/libs/JUCE/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp:2907 (activateBus) type=0 dir=1 index=0 state=TRUE
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:291 (processBlock) aEnabled=0 aChan=0 bEnabled=0 bChan=0

If I then go and add the scene A output it does this

/Users/paul/dev/music/surge/libs/JUCE/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp:2907 (activateBus) type=0 dir=1 index=1 state=TRUE
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:207 (isBusesLayoutSupported) inputDisabled=0 outputDisabled=0
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:227 (isBusesLayoutSupported) out1Chan=0 out2Chan=0 inputValid=1 outputValid=1 res=1
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:207 (isBusesLayoutSupported) inputDisabled=0 outputDisabled=0
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:227 (isBusesLayoutSupported) out1Chan=2 out2Chan=0 inputValid=1 outputValid=1 res=0
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:207 (isBusesLayoutSupported) inputDisabled=0 outputDisabled=0
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:227 (isBusesLayoutSupported) out1Chan=2 out2Chan=0 inputValid=1 outputValid=1 res=0
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:207 (isBusesLayoutSupported) inputDisabled=0 outputDisabled=0
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:227 (isBusesLayoutSupported) out1Chan=2 out2Chan=2 inputValid=1 outputValid=1 res=1
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:291 (processBlock) aEnabled=1 aChan=2 bEnabled=1 bChan=2

If I remove that mapping it stays at 3x output. I can't see a way to make reaper destroy the channel even if you unmapped the output.

The Juce AudioPlugin Host seems to take the same tack as bitwig. It just activates everything independent of whether you have it bound or not

/Users/paul/dev/music/surge/libs/JUCE/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp:2907 (activateBus) type=1 dir=0 index=0 state=TRUE
/Users/paul/dev/music/surge/libs/JUCE/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp:2907 (activateBus) type=0 dir=0 index=0 state=TRUE
/Users/paul/dev/music/surge/libs/JUCE/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp:2907 (activateBus) type=0 dir=1 index=0 state=TRUE
/Users/paul/dev/music/surge/libs/JUCE/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp:2907 (activateBus) type=0 dir=1 index=1 state=TRUE
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:207 (isBusesLayoutSupported) inputDisabled=0 outputDisabled=0
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:227 (isBusesLayoutSupported) out1Chan=0 out2Chan=0 inputValid=1 outputValid=1 res=1
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:207 (isBusesLayoutSupported) inputDisabled=0 outputDisabled=0
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:227 (isBusesLayoutSupported) out1Chan=2 out2Chan=0 inputValid=1 outputValid=1 res=0
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:207 (isBusesLayoutSupported) inputDisabled=0 outputDisabled=0
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:227 (isBusesLayoutSupported) out1Chan=2 out2Chan=0 inputValid=1 outputValid=1 res=0
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:207 (isBusesLayoutSupported) inputDisabled=0 outputDisabled=0
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:227 (isBusesLayoutSupported) out1Chan=2 out2Chan=2 inputValid=1 outputValid=1 res=1
/Users/paul/dev/music/surge/libs/JUCE/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp:2907 (activateBus) type=0 dir=1 index=2 state=TRUE
/Users/paul/dev/music/surge/libs/JUCE/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp:2984 (setBusArrangements) numIns=1 numOuts=3
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:207 (isBusesLayoutSupported) inputDisabled=0 outputDisabled=0
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:227 (isBusesLayoutSupported) out1Chan=2 out2Chan=2 inputValid=1 outputValid=1 res=1
/Users/paul/dev/music/surge/libs/JUCE/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp:2984 (setBusArrangements) numIns=1 numOuts=3
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:207 (isBusesLayoutSupported) inputDisabled=0 outputDisabled=0
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:227 (isBusesLayoutSupported) out1Chan=2 out2Chan=2 inputValid=1 outputValid=1 res=1
/Users/paul/dev/music/surge/libs/JUCE/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp:2907 (activateBus) type=0 dir=0 index=0 state=TRUE
/Users/paul/dev/music/surge/libs/JUCE/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp:2907 (activateBus) type=0 dir=1 index=0 state=TRUE
/Users/paul/dev/music/surge/libs/JUCE/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp:2907 (activateBus) type=0 dir=1 index=1 state=TRUE
/Users/paul/dev/music/surge/libs/JUCE/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp:2907 (activateBus) type=0 dir=1 index=2 state=TRUE
/Users/paul/dev/music/surge/libs/JUCE/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp:2907 (activateBus) type=1 dir=0 index=0 state=TRUE
/Users/paul/dev/music/surge/src/surge-xt/SurgeSynthProcessor.cpp:291 (processBlock) aEnabled=1 aChan=2 bEnabled=1 bChan=2
baconpaul commented 1 year ago

It might be the case, by the way, that you could get around the problem by calling "setBusArrangements" with 1 in and 1 out before you deactivate the higher buses?

x42 commented 1 year ago

Thanks for the rigorous investigation!

It just activates everything independent of whether you have it bound or not

Wow. What a mess. So Reaper is the only host that even tries to deactivate a bus at one point, only to undo it later.

By default Surge's optional output busses are disabled (BusInfo::kDefaultActive is only set for the first).

I will have to verify this myself, one of our testers reported that deactivating optional outputs apparently reduces DSP load for Kontakt and Addictive Drums. I hazard a guess that this might be true for Surge as well.

I have only checked various audio plugins which reconfigure themselves depending on Input and Sidechain busses (Fabfilter, waves) and that works correctly by first en/disabling a bus and then calling setBusArrangements.

I will try your suggestion and call it first and see if that helps.

x42 commented 1 year ago

Calling setBusArrangements before activateBus makes no difference.

In your Reaper trace above, after "If I then go and add the scene A output it does this"

only the first two busses are active, and the "B" scene bus is disabled. Does Surge produce sound on both the main bus and scene A?

mkruselj commented 1 year ago

Wow. What a mess.

Welcome to VST3...

x42 commented 1 year ago

Actually hmm I’m not sure what juce correct behavior is - you do ask for an invalid bus config

The problem is that this breaks later valid configs.

baconpaul commented 1 year ago

Reaper expands but never contracts is the thing @x42

I dint have a host which goes for full back to default in my test set. So no one does what you do.

@mkruselj you must know does kontact have fixed 1,4,8) or any 1-8 output settings?

@x42 as mentioned I’m not going to be able to look again until in depth afret the us long weekend.

Ardour is a big enough host for us thst we can patch surge and do something (more painfully) different in sc if we need to although I may host condition it. I would just like to make sure we have to before we do that.

x42 commented 1 year ago

No rush, there won't be another Ardour release for at least 6 weeks.

Like other hosts, Ardour could never disable a previously enabled bus. Likely this will also have to be saved with the session-state, and things get ugly quickly. I'll see if I can get my hands on Reaper and compare and also ask around if other plugins are affected.

I'm also prepared to argue that it is a bug in JUCE. When choosing an invalid config it would make a lot more sense to retain the last known working bus configuration rather than disable all busses internally, and doing that without any feedback to the host.

It is not currently possible to detect for the host that enabling only Surge's bus 0 and 1 is invalid.

baconpaul commented 1 year ago

Yes

we were chatting in our discord and studio 1 has a ui which lets you do your “xxx” line and it also silences surge sigh

I agree this is a bug like behavior which lives somewhere in the ether between the vst3 “spec”, the juce choice, the surge strictness, and the ambiguity they present. But the fact that surge is doing something valid which breaks it doesn’t mean there’s no break

Do you have pianoteq? It is also a juce plugin with lots of output configs. Wonder what happens there

but my guess is we need to change surge to work around this indeed and I need to make sure it doesn’t make the plugin create ui in logic a mess and so on. Let me tag this into our 12 milestone

baconpaul commented 1 year ago

OK I pushed a version to baconpaul/busses-6847 which is valid for 2/0/2 and 2/2/0 as well as 2/0/0 and 2/2/2

i tested in reaper and logic and no problem at track creation time

no rush @mkruselj since I'm away anyway but if you get a chance to build that and test it in S1 that would be great, and same @x42 for ardour?

x42 commented 1 year ago

Wow you're fast, and on a long weekend no less.

I can confirm that baconpaul/busses-6847 fixes the issue here with Ardour.

baconpaul commented 1 year ago

Great so let’s check in s1 and then I can merge this and design sc with a similar thing in place

Appreciate the back and forth

mkruselj commented 1 year ago

Works as expected in S1 as well!

baconpaul commented 1 year ago

Awesome I need to clean up the debug prints but can do that and merge next week

mkruselj commented 1 year ago

@baconpaul If you want me to, I can go ahead and push your fix minus the debug prints.

baconpaul commented 1 year ago

Yes that would be great thank you!