Bypassing a hosted plugin

Thinking about it more I’m coming around to the idea. I’m wrong about what the host would need to do. As long as each format always returned a valid bypass parameter it could have something like this at the top of the processBlock() method…

if (bypassParameter.getValue() >= 0.5f && ! canPluginBypass)
{
    processBlockBypassed (buffer, midiMessages);
    return;
}

canPluginBypass would have to be an internal bool in each of the formats that determines if the plugin has implemented a bypass mechanism.

This should allow the host to bypass the plugin instance and continue calling processBlock() without it having to do any checks for if a bypass parameter exists.

So then the only slightly annoying thing is calling processor.getBypassParameter()->setValue (1.f) which could be abstracted away for hosts by adding a setBypassed / isBypassed to the AudioPluginInstance class.

My only hesitation is that I would like to hear from someone who is using AudioProcessor’s outside of a plugin or AudioPluginInstance, as to how they would feel creating a bypass parameter, overriding getBypassParameter() for each AudioProcessor, and using the bypass parameter to bypass the AudioProcessor’s?
@Rail_Jon_Rogut is this something you have a lot of experience with?

One advantage of this is that multiple processors could use the same bypass parameter to bypass a whole chain of AudioProcessors, at least on the surface this seems useful, but it’s not something I’ve really done before so it’s hard for me to say.

1 Like

Another quick thought might it be sensible to have a class such as AudioProcessorWithBypass that does some of the bulk work that might be required otherwise?

NOTE: I haven’t completely thought that through yet, just a passing thought.

I think I will just post what I have once it’s done and has passed code review and then we can discuss any issues like that. I think it’s quite difficult to discuss without seeing the actual code changes.

2 Likes

Consider an experimental/bypass branch for that, like you did for multibus, so that tweaks and evolutions on this don’t affect people that use devel for released products

5 Likes

I realise your working on this at the moment @fabian, but I was just talking to a colleague of mine and he had a suggestion I thought was worth adding here.

How about, assuming you have gone with the virtual getBypassParameter() solution, the default implementation returns any parameter in the list of parameters that is marked with a bypassParameter category. Just makes adding a bypass parameter in a plugin that little bit clearer, it will also work for the VST3AudioPluginInstance class too, and any future formats that have a way to mark a parameter as a bypass parameter.

@anthony-nicholls Well, currently I’m using getBypassParameter to see if the AudioProcessor expects processBlockBypassed calls or not. If getBypassParameter returns nullptr it will call processBlockBypassed when bypassed.

I know that this bypass feature request might not seem like a lot of work, but adding support for all the use-cases is really quite tricky.

  1. getBypassParameter returns nullptr
  2. getBypassParameter returns a parameter which is not in the plug-in’s parameter list
  3. getBypassParameter returns a parameter which is in the plug-in’s parameter list

I’ve finished the hosting side for all plug-in formats and finished the client code for AAX and VST3. But still need to do AU/AUv3/VST and then loads of testing.

I need to put this on the back burner for now, as some other priorities have come up. So please don’t expect this to be released this or next week. But I have some time to finish this work the week after. Sorry!

Could you put this on a branch, I’ll be happy to jump in and make pull requests into your branch, which might speed things up and reduce work load for you when you do get to it?

What I was thinking was that there could be a private member AudioProcessorParameter* bypassParameter = nullptr when addParameter is called it checks if the parameter is marked as a bypass parameter and sets bypassParameter to point to this parameter, then it returns bypassParameter in getBypassParameter()

Anyway it’s a small point.

Thanks for looking into this @fabian (and @t0m ), I’m very aware it’s a tricky thing to implement this feature taking into consideration everything. I did look at implementing the bypass parameter idea and quickly stopped, realising how late it was already and that I didn’t want to be coding into the early hours!

I think I’d want this to pass internal code review before we put this on a branch.

1 Like

OK this is now on develop with commit 0db9415.

Every AudioProcessor now has a new method called AudioProcessor::getBypassParamater. If the processor supports bypass control then this will return a non-nullptr to an AudioProcessorParameter. Note that all plug-in backends (except RTAS) support this method.

AudioProcessors can choose if the returned parameter will be part of the “normal” parameter list (returned by AudioProcessor::getParameters) or not. When hosting a plug-in, AudioProcessor::getBypassParamater will return a parameter which is part of the plug-in’s normal parameter list for VST3 and AAX (as this is mandatory for these plug-in types anyway) and for VST2/AU/AUv3 it will not be part of the normal parameter list.

5 Likes

Thanks Fabian. I haven’t had a chance to use this yet myself. However it’s been looked over by one of my colleagues as it will be useful for us to incorporate as we were doing some of the work this commit does in our own version of JUCE, and all looks good as far as we can see.

I’ll try and use this in the command line host I was building tonight if I get a chance.

2 Likes

Hi,
Since I pulled the devel branch (commit 7bea88e) , it generates an EXC_BAD_ACCESS from the line 1320 of Juce_VST3_Wrapper.cpp (return (bypassParam->getValue() != 0.0f);) because the processor of the bypassParam is NULL. I guess it comes from the commit 0db9415.

I’m on macOS 10.13.3 using Reaper 5.78/64

That seems odd, as bypassParam should be guaranteed to be valid due to the line above!

The parameter yes but not the processor in the parameter

Sorry I obviously missed that from your last message, so is the crash actually happening inside getValue()? is it calling a function in the AudioProcessorParameter that tries to access the private processor member?

No problem :slight_smile: I don’t understand the cause of the problem… For your information, it seems to happen when changing the audio configuration (buses layout) and my plugin have only one other (dummy) parameter that is not automatable.

  • juce_VST3_Wrapper.cpp, l. 2144: if (processSetup.symbolicSampleSize == Vst::kSample32) processAudio<float> (data, channelListFloat);
  • juce_VST3_Wrapper.cpp, l. 2333: if (isBypassed())
  • juce_VST3_Wrapper.cpp, l. 1320: return (bypassParam->getValue() != 0.0f);
    com.apple.audio.IOThread.client (15): EXC_BAD_ACCESS (code=1, address=0x10)

I can try with other configuration if necessary

I think this may happen if you don’t add your bypass parameter to the regular AudioProcessor parameters. VST3 requires this. I’ll make sure that the VST3 wrapper will always add the parameter even if the AudioProcessor doesn’t.

2 Likes

Ok, I missed that. Thanks a lot!
So for the moment, I must always create a bypass parameter for my VST3 plugins?

Just for the moment - but I’m fixing this issue right now.

1 Like

OK I can’t really reproduce this crash. As your bypass parameter is not part of your AudioProcessor’s normal parameters, it’s perfectly normal for the processor member variable to be null in the bypassParam parameter.

In the code that crashed for you, are you overriding AudioProcessor::getBypassParameter() (either way will work)? If yes, what exactly are you returning in your AudioProcessor::getBypassParameter()? Can you show me the full stack trace? You are not mixing different versions of JUCE code?

This was my conclusion too I couldn’t see how this crash would actually occur!