VST Hosting issue


#1

Not sure if this is a problem with the plugin (wrong implementation of processReplacing?), or the AudioProcessorGraph or the VST-Wrapper.

You can recreate the problem with the Juce Plugin Host (windows)

Host this two freeware plugins
mda-Testtone (http://mda.smartelectronix.com/effects.htm)

[attachment=0]vsthosting.png[/attachment]

The tone generate by testone will be muted by Zebralette.


#2

Is it specific to those two plugins?


#3

In the host I’m writing I’ve found that many, and I do mean many, free vst plugins have a variety of anomalous behaviors. In fact the paid-for plugins can react strangely. Cubase will load this one, but not that one. Abelton likes that one but not this one etc. etc. In the worst cases all seems well - for awhile, but on shutdown strange things happen, or a group of plugins will work just fine unless plugin z is loaded first - which sets a time bomb which affects plugins loaded later. Not all plugins are created equal!

Like Jule’s says - Is it specific to that plugin?


#4

Now that I think about it - I’ve seen instances where a plugin doesn’t like having it’s output directly ‘colliding’ with another plugins. And it was plugin specific. I solved it by adding a ‘mixer plugin’ so that the troublesome plugin has it’s own input channel, the mixer does it’s mixing thing and then spits the output to the main audio output node. I don’t know if there’s a juce factor involved, but I do know that the ‘Capture it’ plugin exhibited this behavior. At least 40 other plugins didn’t mind if their outputs were directly mixed together.

Does the problem still occur when both outputs of both plugins are connected to the audio outputs?


#5

i found another plugin that also behaves this way like zebralette (http://www.x-buz.com/BuzMaxi3.html),

first i thought using the same buffer for input and output can be a problem, but it doesn’t seem so.

jules, is processReplacing somehow used to summing two buffers, not sure, i think it can’t, its not designed to do that.
But how should this have an influence on the main output (besides memory violation), mhhhh?

there are other synths with inputs (Absynth FX for example) which are working, so what can be the difference?

ps: i send you pm

yes


#6

I’m really not sure what’s going on there - it could be that the graph builder is making a mistake and overwriting a buffer rather than summing it. That’d require some deep debugging to track down though…


#7

When you get a bit of time would you mind eyeballing that bit of code for us Jules? - I’m off working on another aspect of my host right now but if you can refresh your memory and give some guidance on what might need looking at when I get done with the ‘find and fix missing plugins’ component I’m working on I’ll take a look at that (Saturday/Sunday most likely) I’m hoping for ‘wild goose chase’ (or is that foxes?) prevention advice. I can see this one coming back to haunt me when I release my host, Which I would like to talk to you about btw. I’ll pm’ya soon.


#8

bump :wink:


#9

I’d say an easy way to at least get some idea of what the problem might be is, with the debugger, put a breakpoint after the renderingOps have been generated and see if they make sense. You’ll probably need to start from no connections to the layout you showed earlier and see where it goes pear shaped. If it is the graph at fault, of course.

void AudioProcessorGraph::buildRenderingSequence()
{
    Array<void*> newRenderingOps;
......
        GraphRenderingOps::RenderingOpSequenceCalculator calculator (*this, orderedNodes, newRenderingOps);
<- breakpoint
        numRenderingBuffersNeeded = calculator.getNumBuffersNeeded();
        numMidiBuffersNeeded = calculator.getNumMidiBuffersNeeded();
....

#10

i tried exactly that, but at some point i had to little brain capacity.


#11

There’s one buffer operation missing in the createRenderingOpsForNode function, when two plugins are connected to one pin and one of the plugins introduces an additional latency (like Zebralette, with 16 samples). Just add the line I marked in the code below and it’s all good.

[code]for (int j = 0; j < sourceNodes.size(); ++j)
{
if (j != reusableInputIndex)
{
const int srcIndex = getBufferContaining (sourceNodes.getUnchecked(j),
sourceOutputChans.getUnchecked(j));
if (srcIndex >= 0)
{
const int nodeDelay = getNodeDelay (sourceNodes.getUnchecked (j));

						if (nodeDelay < maxLatency)
						{
							if (! isBufferNeededLater (ourRenderingIndex, inputChan,
													   sourceNodes.getUnchecked(j),
													   sourceOutputChans.getUnchecked(j)))
							{
								renderingOps.add (new DelayChannelOp (srcIndex, maxLatency - nodeDelay));
								renderingOps.add (new AddChannelOp (srcIndex, bufIndex)); //THIS ONE LINE IS MISSING!
							}
							else // buffer is reused elsewhere, can't be delayed
							{
								const int bufferToDelay = getFreeBuffer (false);
								renderingOps.add (new CopyChannelOp (srcIndex, bufferToDelay));
								renderingOps.add (new DelayChannelOp (bufferToDelay, maxLatency - nodeDelay));
								renderingOps.add (new AddChannelOp (bufferToDelay, bufIndex));
							}
						}
						else
						{
							renderingOps.add (new AddChannelOp (srcIndex, bufIndex));
						}
					}
				}
			}[/code]

#12

Excellent detective-work! Thanks! I’ll check that and check something in shortly…!


#13

Indeed, great detective work! I’ve had this issue for quite a long time and now it’s finally gone. Thank you!
BTW, Jules, you look like Jesus Christ :smiley:


#14

Thanks, this will solve some problems for me, I haven’t had time to look into it yet and am happy you found it.