VST3 side and and rear channels flipped?

I’ve recently encountered what seems to be a bug (?) using VST3 in REAPER, where my surround and rear channels are flipped.

I am interpreting my buffer in processBlock() according to AudioChannelSet::getChannelIndexForType() and can confirm in the debugger that I am writing to what should be the leftSurroundSide according to this API (channel index 4), but in REAPER it ultimately is output to to channel 7 instead of the expected channel 5.

The latest bus layouts reported from processorLayoutsChanged() are:

Bus: Output
Description: 7.1 Surround
SpeakerArrangement: L R C Lfe Lss Rss Lrs Rrs
[0]: Left
[1]: Right
[2]: Centre
[3]: LFE
[4]: Left Surround Side
[5]: Right Surround Side
[6]: Left Surround Rear
[7]: Right Surround Rear

Bus: Input
Description: 7.1 Surround
SpeakerArrangement: L R C Lfe Lss Rss Lrs Rrs
[0]: Left
[1]: Right
[2]: Centre
[3]: LFE
[4]: Left Surround Side
[5]: Right Surround Side
[6]: Left Surround Rear
[7]: Right Surround Rear

Is this a REAPER bug, a JUCE issue, or something I’m misunderstanding?

At least, I noticed that the mapping between VST3’s SpeakerArrangement and JUCE’s AudioChannelSet also flips the sides/rears in juce_VST3Common.h. I don’t know if its something going wrong there or a REAPER specific issue. At the moment, I don’t have access to another VST3 host supporting these formats that I can easily test with.

I should mention -the behavior in the JUCE plugin host matches my expectations. So, I see the correct VST3 channel mapping behavior there --only seeing this issue in REAPER but can’t find much about it anywhere.

I’ve also emailed Cockos support and posted on the REAPER forums, but still no bites anywhere…

Are you using the 7.0.12+dev (which is in effect an unofficial but not actually tagged 7.0.13) version where recently the VST3 SDK was updated and some changes implemented due to adjustments to 9.1.6 support. If not, give that a go, and see if it makes a difference.

Thanks, yeah I should have mentioned what version I’m on. I am indeed using this “7.0.13” version already, specifically on commit 95b49f668b with the update to VST 3.7.11 and 9.1.6 changes. This hasn’t made any difference though --I’ve seen the same behavior in previous 7.X versions I’ve used.

Do you see the same behavior in REAPER, or not have this issue?

EDIT: I should note the channel order in the SurroundPlugin demo is also flipped in REAPER when using 7.1 or 7.1.4 surround if you edit the isBusesLayoutSupported to support these layouts. REAPER doesn’t play very nicely with the demo by default, since it gives it more or less free reign when setting the bus layouts.

As far as I’ve been able to tell, the flip is only occurring for layouts that have Lss/Rss and Lrs/Rrs specifically. I’m not sure if it’s REAPER incorrectly flipping the channels, or something in the VST3 SpeakerArrangement conversion like I reported here: Possible bug in VST3 channel order mapping.

There’s a mismatch between the VST3 API and the REAPER interface when it comes to speaker layouts.

Specifically, VST3 doesn’t have a concept of channel numbers. Instead, the API defines several different types of speaker (e.g. left, right, LFE, etc.), and specifies that there may be either 0 or 1 of each type of speaker on any given bus. At the same time, REAPER doesn’t really have a concept of speaker types. The channels on a bus don’t necessarily correspond to specific speakers.

As a result, when the user asks for an N channel layout in REAPER, REAPER has to determine all common layouts that contain N channels (an 8 channel layout might be k71Cine or k71Music), and then check each with the plugin to see whether that layout is supported. If a supported layout is found, then JUCE will order the channels so that the semantics of the channels match as closely as possible. It’s important to understand that a JUCE AudioChannelSet containing specific channels will always report those channels in the same order, so leftSurroundSide will always be ordered before leftSurroundRear.

The behaviour you’re seeing arises because REAPER requests a channel layout of k71Music, which the plugin claims to support. When REAPER sends an 8 channel bus to the plugin, the speaker types in the bus are interpreted as L R C Lfe Ls Rs Sl Sr, because this is the layout that REAPER requested.

JUCE maps the VST3 speaker types to JUCE channel types as follows: Ls → Left Surround Rear, Rs → Right Surround Rear, Sl → Left Surround Side, Sr → Right Surround Side.

An AudioChannelSet containing the mapped JUCE channels can only report those channels in the order “Left, Right, Centre, LFE, LSS, RSS, LSR, RSR”, so the order of the last two pairs of channels appears swapped.

In short, JUCE is respecting and preserving the semantcis of the channel types requested by REAPER, but this leads to unexpected results because REAPER doesn’t display the channel semantics anywhere.

This isn’t a JUCE bug - JUCE is implementing the VST3 spec.

It could be argued that this is a problem with the REAPER UI, but if no other workflow or plugin format requires channel types to be displayed, I’m not sure why VST3 should be any different.

My preferred solution would be to extend the VST3 API to allow for discrete channel layouts. Then, REAPER and JUCE could just use this new API. On REAPER’s side, they could switch to always requesting discrete layouts, to match their UI. On JUCE’s side, we could report the layout as a discrete layout, and no remapping would be required.

If this is causing a problem for your product, I’d recommend contacting Steinberg and asking whether they’d consider adding such an extension to the VST3 API.

2 Likes

Thanks for the reply. Yeah, I’m aware of the general issues of REAPERs poor mapping onto the VST3 API. But in my case for example, if I constrain isBusesLayout() to specifically ensure my layout is AudioChannelSet::create7point1(), then REAPER should iterate through the possible 8 channel layouts, and ultimately land on the bus layout corresponding to JUCE’s 7.1 AudioChannelSet, right?

From here, the VST3 API has a fixed channel order for each of the discrete arrangements. So, even though REAPER does not expose any named channels, the VST3 API necessarily interprets these channels according to the order defined in the API, in this case L R C Lfe Ls Rs Sl Sr as you mentioned. And then JUCE remaps to its internal channel order based on that VST3 defined order.

So, I guess ultimately the behavior I’m seeing is that REAPER is actually ending up with its channels in a “correct” VST3 order (with the surrounds before the sides), it just has no meaningful interpretation of the channel semantics in the UI?

I guess then the only immediate solution is to have a REAPER specific workaround, since it doesn’t play nicely with the VST3 spec. And I fully agree about the VST3 spec limitations --defined SpeakerArragements should be optional labels. Many, many use cases have no concept of speaker channels anyway…frustrating regression from VST2 that still has no real fix.

Is this actually true, though? I see that the layout tables are defined this way, but the mapping functions in juce_VST3Common.h don’t actually perform the conversion this way, which is why I’m suspicious there could possibly be a JUCE issue as well. See the related: Possible bug in VST3 channel order mapping

I’m pretty sure that the JUCE impl works in the way that I described above. You can check for yourself by adding a test in juce_VST3PluginFormatTest.cpp that calls getChannelSetForSpeakerArrangement (k71Music) and checks the result, or by sticking a breakpoint in getChannelSetForSpeakerArrangement and loading the plugin in a host.

If you add a test which fails incorrectly, please supply the content of the test so that we can debug and fix the issue.