Plugins hosting and channels handling


#1

Hello coders,
I’m experiencing some troubles with channels handling in my plugin host.

In my host I have some tracks (AudioPluginInstance) that can host plugins (AudioPluginInstance).
These tracks can be mono or stereo.
All these tracks are “internal”, just like the AudioProcessorGraph::AudioGraphIOProcessor.
So in the tracks’ prepareToPlay method I call setPlayConfigDetails(numInputs, numOutputs, sampleRate, samplesPerBlock).
And this works perfectly, the tracks are actually mono (1 input, 1 output) or stereo (2 inputs, 2 outputs).

When I add an external plugin -an effect- I try to do the same thing, calling:

//track is an AudioPluginInstance (internal) //pluginInstance is an AudioPluginInstance (external: au, vst etc) pluginInstance->setPlayConfigDetails(numInputs, numOutputs, track->getSampleRate(), track->getBlockSize()); pluginInstance->prepareToPlay(track->getSampleRate(), track->getBlockSize());

As soon as the effect’s processBlock method is called in a mono track I fall into an assertion from juce_AudioSampleBuffer.h at line 146,
which tells me that I’m trying to read from an out-of-range channel of an AudioSampleBuffer.
Debugging I discover that the external plugin’s channel configuration is 2-2 instead of 1-1.
So what could be going wrong?


#2

Let me clarify my request:
how can I make a plugin process as mono (1-1) instead of other configurations?
In the juce plugin host this problem is not taken into consideration, since when a plugin is added the host creates a PinComponent for every plugin’s channel, not forcing it to run with different configurations than the default one.


#3

Ok, I think I found something weird:
let’s take as example the AudioUnit format. in juce_AudioUnitPluginFormat.mm, line 342, there’s a function called

This always returns 2 inputs and 2 outputs. If I hardcode the result to 1 and 1 then the plugin correctly uses a mono configuration.
Jules, is there a nice way to do what I’m trying to achieve?
Did anybody encounter this issue?


#4

I think that you should probably just not be using a 2-channel plugin in a mono track. If a plugin uses 2 chans, you’d need to use some kind of adapter to mix them down to mono if you’re passing it on to something that expects only one channel.


#5

I agree, but how is it possible that other hosts (such as Logic) can do it?
What about adding a function to handle this? Something to tell the plugin to use a desired configuration, and in case it can’t, use it’s default one.
I thought setPlayConfigDetails should have done the job, but it doesn’t.
I know you’re super-busy, but this thing is blocking my project…


#6

I just convert mono tracks to stereo right at the source (duplicating the mono channel), and assume stereo inputs in all my code.


#7

But many plugins out there can change also their gui based on the audio configuration…


#8

I ran into this problem too. I’m using 1.46, so dunno if this applies to you, but here’s what I had to do to get things working with VST plugins on mono tracks:

  1. In VSTPluginInstance::prepareToPlay(), it calls:

That’s broken because numInputs/numOutputs is the max number of inputs/outputs that the plugin supports. I changed it as follows:

// This is broken - it always sets numInputChannels/numOutputChannels // to the MAX values accepted by the effect, not what is actually // currently connected. Here we assume that the host will have called // setPlayConfigDetails() already with the correct I/O config. //setPlayConfigDetails (effect->numInputs, effect->numOutputs, // sampleRate_, samplesPerBlockExpected); setPlayConfigDetails (getNumInputChannels(), getNumOutputChannels(), sampleRate_, samplesPerBlockExpected);

  1. I’m using an AudioProcessorGraph for each track. I found that when preparing the graph for playback I have to call setPlayConfigDetails() with the correct I/O config manually on every processor in the graph, even though that funcion is supposed to be “for internal use only”, and you would think calling setPlayConfigDetails() on the graph would call it on all the nodes.

#9

AFAIK, this still applies. But it’s no big deal, once you found is it ? :slight_smile:


#10

[quote=“jimw”]I ran into this problem too. I’m using 1.46, so dunno if this applies to you, but here’s what I had to do to get things working with VST plugins on mono tracks:

  1. In VSTPluginInstance::prepareToPlay(), it calls:

That’s broken because numInputs/numOutputs is the max number of inputs/outputs that the plugin supports. I changed it as follows:

// This is broken - it always sets numInputChannels/numOutputChannels // to the MAX values accepted by the effect, not what is actually // currently connected. Here we assume that the host will have called // setPlayConfigDetails() already with the correct I/O config. //setPlayConfigDetails (effect->numInputs, effect->numOutputs, // sampleRate_, samplesPerBlockExpected); setPlayConfigDetails (getNumInputChannels(), getNumOutputChannels(), sampleRate_, samplesPerBlockExpected);
[/quote]
Forgot to mention a couple related changes in the same file (juce_VSTPluginFormat.cpp). Again, this is from the 1.46 tree so not sure if this applies in more recent builds.

  • VSTPluginInstance::processBlock() needs to be changed to check the actual number of channels before calling getSampleData() on the buffer. This fixes the actual assertion reported in the OP.

[code]// This is broken - this needs to work on the actual I/O config, not
// the max I/O config supported by the plugin. For example you could
// be passing in a mono buffer to a plugin that supports stereo, but
// is configured in mono. The old way could cause the getSampleData()
// call to access invalid memory.
//const int maxChans = jmax ((int) effect->numInputs, (int) effect->numOutputs);
const int maxChans = jmax (getNumInputChannels(), getNumOutputChannels());

for (i = 0; i < maxChans; ++i)
channels[i] = buffer.getSampleData (i);
[/code]

  • VSTPluginInstance::handleCallback() needs to handle the audioMasterPinConnected callback, so that the plugin can query the host to see what the actual I/O config is.

case audioMasterPinConnected: // Our plugins call this from resume() to see how many inputs/outputs are actually being used by the host. // We assume that the host has called AudioProcessor::setPlayConfigDetails() prior to resume() with the // correct I/O config. // From AEffect.h: // [return value]: 0=true, 1=false [index]: pin index [value]: 0=input, 1=output // @see AudioEffect::isInputConnected @see AudioEffect::isOutputConnected if (value == 0) { // Check inputs if (index < getNumInputChannels()) return 0; } else { // Check outputs if (index < getNumOutputChannels()) return 0; } // If we got here, the pin isn't connected return 1; break;


#11

Thanks - I’ll add the pin connected stuff. The processblock method has completely changed since 1.46 so I don’t think that’ll be an issue any more.


#12

Great! Could you do the same for audiounits?


#13

How is the VST pinconnected stuff relevant to AUs?


#14

That’s exactly what I was thinking about after writing my post :oops:


#15

Also, note that I believe some VST plugins deal with this by handling the newer effSetSpeakerArrangement call. I think that plugins which handle that call might not configure themselves properly with just the pinConnected stuff implemented in the host, i.e. they might not make those calls, and might be expecting the host to call setSpeakerArrangement. In my case I only need to host my own plugins, so this isn’t an issue.


#16

Although jimw’s audioMasterPinConnected code above looks right (according to this: http://www.asseca.com/vst-24-specs/amPinConnected.html) i.e. counter-intuitively return 0 for connected and 1 for not connected, the latest Juce hosting code does the opposite (juce_VSTPluginFormat.cpp:1324):

        case audioMasterPinConnected:       return isValidChannel (index, value == 0) ? 1 : 0;

I think this is a Juce bug. Just to confirm, I find that if I change it to the following, my UAD plugins start working where they weren’t before:

        case audioMasterPinConnected:       return isValidChannel (index, value == 0) ? 0 : 1;

#17

Nice catch!

Yes, looking at the VST spec, it does appear that they’ve used 0 = true and 1 = false… Sigh… (Although that function’s actually deprecated now anyway, so probably very few plugins will use it).

I’ve swapped it around, and checked-in the fix, thanks!