Changing AU Plugin Channel Layouts Causes Silence

We’ve had reports from a lot of users that AU plugins now only produce silence (this is since the multibus introduction).

I’m not sure exactly how all the formats used to work but I believe pre-multibus the maximum number of channels was used. To work around problems with plugins that used to open in stereo but now by default open in mono, we iterate all the channel layouts and try and set them to the maximum (to try and mimic the old behaviour).
However, it appears that a lot of AU plugins don’t seem to like this and only produce silence once we’ve changed the channel layout. I’ve check and we are calling prepareToPlay after setting the layout.

You can replicate this behaviour in the JUCE demo host using the Sinevibes Torsion demo. If you add the plugin it will open in stereo and produce sound. If you change the channel layout it will forever then be silent.

Interestingly, changing the layout back to stereo still produces silence.
Is there an additional initialisation step we’re missing here?

Thanks @dave96, I’ll have a look.

I’ve been looking into this with the plug-in you mentioned. I can’t quite reproduce it (see attached code below - which plays “Für Elise” through the torision demo - and attempts to change the layout to mono and then back to stereo) but the plug-in has some odd behaviour that may mess up your layout changing:

  • The plug-in only appears to support stereo. In an app (not using JUCE), I tried to change the layout to anything but stereo and the plug-in does not support it.
  • The plug-in does not support the kAudioUnitProperty_SupportedNumChannels property which Apple recommends that all AUs should implement. I’m a bit unsure how JUCE should react when an AU does not support this property. Does this mean that the plug-in supports an arbitrary layout (this is what JUCE currently assumes)? Or should this be interpreted that the plug-in only supports the default layout? Apple does not give any guidance what to do in this case.
  • Due to the above, JUCE will assume that the plug-in supports any layout - so checkBusesLayoutSupported will always return true (for example when trying to change the plug-in to mono - which Torsion actually doesn’t support). However, if you actually try to switch the layout with setBusesLayout (for example to mono), the plug-in will refuse the layout change by returning an error FormatNotSupported This might confuse your JUCE app.

However, in contrast to what you wrote, I was able to change the layout back to stereo without any problems.

There are two solutions to the problem that JUCE returns true when you call checkBusesLayoutSupported with a layout that the plug-in actually doesn’t seem to support.

  • As mentioned above, we could change JUCE to assume that AUs only support the default layout if they do not implement the kAudioUnitProperty_SupportedNumChannels property. To try if this works for you (and if it works for all the other AUs) use this patch:

 

diff --git a/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm b/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm
index ccbac5c..b77a927 100644
--- a/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm
+++ b/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm
@@ -1661,6 +1661,9 @@ void updateSupportedLayouts()
		 numChannelInfos = 0;
		 channelInfos.free();

+        int defaultInChannels  = 0;
+        int defaultOutChannels = 0;
+
		 for (int dir = 0; dir < 2; ++dir)
		 {
			 const bool isInput = (dir == 0);
@@ -1689,6 +1692,9 @@ void updateSupportedLayouts()
						 currentLayout = AudioChannelSet::canonicalChannelSet (static_cast<int> (descr.mChannelsPerFrame));
				 }

+                if (busIdx == 0)
+                    (isInput ? defaultInChannels : defaultOutChannels) = currentLayout.size();
+
				 supported.clear();
				 {
					 UInt32 propertySize = 0;
@@ -1740,8 +1746,8 @@ void updateSupportedLayouts()
			 {
				 numChannelInfos = 1;
				 channelInfos.malloc (static_cast<size_t> (numChannelInfos));
-                channelInfos.getData()->inChannels  = -1;
-                channelInfos.getData()->outChannels = -1;
+                channelInfos.getData()->inChannels  = defaultInChannels;
+                channelInfos.getData()->outChannels = defaultOutChannels;
			 }
		 }
	 }
  • Another solution is to force JUCE to try to actually change the layout of the underlying AU when you call checkBusesLayoutSupported. Obviously, JUCE can only try to change the layout if the plug-in is not prepared (prepareToPlay hasn’t been called yet). So the downside is that checkBusesLayoutSupported could return different results depending on if the plug-in is prepared or not. For this solution, use this patch:

 

diff --git a/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm b/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm
index ccbac5c..6b428a4 100644
--- a/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm
+++ b/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm
@@ -434,6 +434,10 @@ bool isBusesLayoutSupported (const BusesLayout& layouts) const override
		 if (layouts == getBusesLayout())
			 return true;

+        // if the plug-in is not prepared yet, we can simply try changing the layout
+        if (! prepared)
+            return canApplyBusesLayout (layouts);
+
		 for (int dir = 0; dir < 2; ++dir)
		 {
			 const bool isInput = (dir == 0);

As you know, the plug-in backends are notoriously fragile so I don’t want to push any of those solutions to develop just yet. But I think you probably have a fair amount of AUs that you can test these patches with, right?

And here is my amazing Für Elise MIDI Player:

https://drive.google.com/open?id=0B3yZpQ3OG9ShOE15eUJ6YnZVMG8

All this stuff makes my head hurt :weary:

I think your first solution is probably the most sensible. If they haven’t implement kAudioUnitProperty_SupportedNumChannels then surely it’s safest to assume that they can’t play with the channel layout? Currently, Torsion presents all layouts which it sounds like it doesn’t support and hence seems like a bug (possibly with the plugin but seems pervasive enough that JUCE should catch this).

In the cases where kAudioUnitProperty_SupportedNumChannels isn’t implemented, I think it’s safe to assume the plugin simply can’t deal with any layouts apart from the default.

I don’t think the second solution sounds very good. Having this return two different results depending on when called sounds like a recipe for disaster. Having said that I do only try this when un-prepared.

I’ll take a look at the proposals and see if they improve the situation.

It’s worth noting that the difficulty with this whole situation is that JUCE never used to support all these bus and channel layout changes so our app and 10 years worth of users projects are not built to support them.
It seems there isn’t a simple solution to maintain the legacy behaviour without having to build in multiple layout awareness throughout the app…

I don’t really understand this. The multi-bus stuff is intended as a layer on top of the “legacy” AudioProcessor stuff. However, all of the legacy stuff should work without the multibus layer. I wrote the multi-bus stuff specifically with this in mind. If you are not using any of the new multibus methods (see the old methods here), then behaviour between pre-multibus and post-multibus should be the same - if not, then it’s either a bug in new JUCE versions or bugs in old JUCE versions.

Specifically, looking at the old pre-multibus code, you can see that JUCE will initialise the number of channels to the default that is reported by the plug-in - just as JUCE does today. However, the same uncertainty around a missing kAudioUnitProperty_SupportedNumChannels property was present in the pre-multibus code. Jules just sets the in/out channels to two in this case.

Now I’m really not sure if the old behaviour (setting in/out to two if the kAudioUnitProperty_SupportedNumChannels property is missing) is better than the new behaviour (allowing all possible formats). In any case, both will default to stereo if we are talking about the Torsion plug-in.

Ok, well it seems that there is an inconsistency between the wrapper formats then.
The problem that we were originally trying to solve was that in pre-multibus some plugins (GLow Windows VST was one that was reported to us and we were testing with) opened in stereo, now, post-multibus it opens in mono. This was detailed here: Stereo VST appearing as Mono
Therefore, the default behaviour by JUCE has changed.

At the end of the thread you recommend to open to change the channel layout to stereo if it supports that. We did something similar to this but thought that the old behaviour would be opening the maximum number of channels so decided to iterate all the possible channels and try and open the maximum.

The difficulty here is that I’ve really know way of know what the old default behaviour was for all the various platforms and formats and then trying to match it up in our app by changing the channel layouts. We can’t just blindly set the layouts to stereo, what if someone wants to use a 16-out plugin and we set it to stereo, they will have lost all their available channels.

I guess the question really is what was it about pre-multibus that opened GLow in stereo rather than mono if mono is the default (as would explain the mono opening of post-multibus).
If I can figure that out, perhaps I don’t have to try and open the maximum number of channels for all new plugins and just leave it at the default (which would be my preferred choice).