# How to react on VST3 Bypass

**URL:** <https://iplug2.discourse.group/t/how-to-react-on-vst3-bypass/337>\
**Category:** Uncategorized\
**Created:** [9 March 2021 08:35 UTC](https://iplug2.discourse.group/t/how-to-react-on-vst3-bypass/337 "2021-03-09T08:35:45Z")\
**Posts on this page:** 16\
**Page:** 1

<div class="post-metadata">

**Author:** ![ToneSpotter](https://yyz2.discourse-cdn.com/free1/user_avatar/iplug2.discourse.group/tonespotter/32/148_2.png) [@ToneSpotter](https://iplug2.discourse.group/u/ToneSpotter)\
**Post date:** [9 March 2021 08:35 UTC](https://iplug2.discourse.group/t/how-to-react-on-vst3-bypass/337/1 "2021-03-09T08:35:45Z")

</div>

Hi,

I want to connect my own bypass procedures to the VST3 bypass state. I’ve found the Get/SetBypassed() method in IPlugVST3ProcessorBase, but this seems not what I need.

I want something like a “normal” IParam, to which I can connect to change the behavior of my plugin.

Is it possible without digging in VST3 code? Any idea?

Thank you in advance!

Hermann

---

<div class="post-metadata">

**Author:** ![vasyan](https://yyz2.discourse-cdn.com/free1/user_avatar/iplug2.discourse.group/vasyan/32/82_2.png) [@vasyan](https://iplug2.discourse.group/u/vasyan)\
**Post date:** [9 March 2021 10:15 UTC](https://iplug2.discourse.group/t/how-to-react-on-vst3-bypass/337/2 "2021-03-09T10:15:55Z")

</div>

My solution to this:

```
#if IPLUG_DSP
void AmbientReverb::ProcessBlock(sample **inputs, sample** outputs, int nFrames)
{
  const int nChans = NOutChansConnected();
  if (GetBypassed())
  {
    for (int s = 0; s < nFrames; ++s)
    {
      for (int c = 0; c < nChans; ++c)
      {
        outputs[c][s] = inputs[c][s];
      }
    }
  }
  else
    fReverb.ProcessBlock(inputs, outputs, nFrames, nChans);
}
#endif
```

---

<div class="post-metadata">

**Author:** ![ToneSpotter](https://yyz2.discourse-cdn.com/free1/user_avatar/iplug2.discourse.group/tonespotter/32/148_2.png) [@ToneSpotter](https://iplug2.discourse.group/u/ToneSpotter)\
**Post date:** [9 March 2021 10:50 UTC](https://iplug2.discourse.group/t/how-to-react-on-vst3-bypass/337/3 "2021-03-09T10:50:18Z")

</div>

Great idea, but ProcessBlock isn’t called, if the bypass is enabled… So we first must ensure. that this happens.

---

<div class="post-metadata">

**Author:** ![ToneSpotter](https://yyz2.discourse-cdn.com/free1/user_avatar/iplug2.discourse.group/tonespotter/32/148_2.png) [@ToneSpotter](https://iplug2.discourse.group/u/ToneSpotter)\
**Post date:** [9 March 2021 11:03 UTC](https://iplug2.discourse.group/t/how-to-react-on-vst3-bypass/337/4 "2021-03-09T11:03:34Z")

</div>

Hi vasyan,

I’ve just looked at your “Chorus GAS” VST3 plugin and you managed to ignore the hosts Bypass switch. What did you do for this?

Thanks!

---

<div class="post-metadata">

**Author:** ![vasyan](https://yyz2.discourse-cdn.com/free1/user_avatar/iplug2.discourse.group/vasyan/32/82_2.png) [@vasyan](https://iplug2.discourse.group/u/vasyan)\
**Post date:** [9 March 2021 11:16 UTC](https://iplug2.discourse.group/t/how-to-react-on-vst3-bypass/337/5 "2021-03-09T11:16:08Z")

</div>

This plugin does not provide a bypass at all.  
Believe me, the traversal should be done by the host and not by the plugin. All modern DAWs have a plugin bypass function. Therefore, I do not consider it necessary to duplicate this function with a plugin.

---

<div class="post-metadata">

**Author:** ![ToneSpotter](https://yyz2.discourse-cdn.com/free1/user_avatar/iplug2.discourse.group/tonespotter/32/148_2.png) [@ToneSpotter](https://iplug2.discourse.group/u/ToneSpotter)\
**Post date:** [9 March 2021 11:17 UTC](https://iplug2.discourse.group/t/how-to-react-on-vst3-bypass/337/6 "2021-03-09T11:17:07Z")

</div>

Debugging helps…

In case of GetBypassed() == true, IPlugVST3ProcessorBase::ProcessAudio() calls PassThroughBuffers() instead of ProcessBuffers(). As this is not a virtual method, I can not overwrite PassThroughBuffers()…

---

<div class="post-metadata">

**Author:** ![ToneSpotter](https://yyz2.discourse-cdn.com/free1/user_avatar/iplug2.discourse.group/tonespotter/32/148_2.png) [@ToneSpotter](https://iplug2.discourse.group/u/ToneSpotter)\
**Post date:** [9 March 2021 11:19 UTC](https://iplug2.discourse.group/t/how-to-react-on-vst3-bypass/337/7 "2021-03-09T11:19:43Z")

</div>

Oh, if you have a latency in you DSP algorithm it makes sense to ensure a click free bypass. Most of the plugins I examined react on host bypass and I want to do it also…

EDIT: latency corrected bypass is done by iPlug2 itself. This helps a little…

---

<div class="post-metadata">

**Author:** ![vasyan](https://yyz2.discourse-cdn.com/free1/user_avatar/iplug2.discourse.group/vasyan/32/82_2.png) [@vasyan](https://iplug2.discourse.group/u/vasyan)\
**Post date:** [9 March 2021 11:20 UTC](https://iplug2.discourse.group/t/how-to-react-on-vst3-bypass/337/8 "2021-03-09T11:20:14Z")

</div>

> [@ToneSpotter](#):
>
> I’ve just looked at your “Chorus GAS” VST3 plugin and you managed to ignore the hosts Bypass switch.

There is nothing special here.  
P.S. Since I have been programming in Delphi for a very long time, I have a habit of using Pascal identifiers to designate classes and their fields. 😉

```
#if IPLUG_DSP
void TChorusGAS::ProcessBlock(sample **inputs, sample** outputs, int nFrames)
{
  const int nChans = NOutChansConnected();
  fBands->ProcessBlock(inputs, outputs, nFrames, nChans);
}
#endif

```

---

<div class="post-metadata">

**Author:** ![ToneSpotter](https://yyz2.discourse-cdn.com/free1/user_avatar/iplug2.discourse.group/tonespotter/32/148_2.png) [@ToneSpotter](https://iplug2.discourse.group/u/ToneSpotter)\
**Post date:** [9 March 2021 12:48 UTC](https://iplug2.discourse.group/t/how-to-react-on-vst3-bypass/337/9 "2021-03-09T12:48:47Z")

</div>

Still open and when I am more precise:

I want to couple the host Bypass mode with my own Bypass toggle switch.

---

<div class="post-metadata">

**Author:** ![vasyan](https://yyz2.discourse-cdn.com/free1/user_avatar/iplug2.discourse.group/vasyan/32/82_2.png) [@vasyan](https://iplug2.discourse.group/u/vasyan)\
**Post date:** [10 March 2021 03:01 UTC](https://iplug2.discourse.group/t/how-to-react-on-vst3-bypass/337/10 "2021-03-10T03:01:05Z")

</div>

```
bool Bypass;
    ...

    void Effect::ProcessBlock(sample **inputs, sample** outputs, int nFrames)
    {
      const int nChans = NOutChansConnected();
      if (Bypass)
      {
        for (int s = 0; s < nFrames; ++s)
        {
          for (int c = 0; c < nChans; ++c)
          {
            outputs[c][s] = inputs[c][s];
          }
        }
      }
      else
      {
        // Effect implementation
      }
    }
```

---

<div class="post-metadata">

**Author:** ![ToneSpotter](https://yyz2.discourse-cdn.com/free1/user_avatar/iplug2.discourse.group/tonespotter/32/148_2.png) [@ToneSpotter](https://iplug2.discourse.group/u/ToneSpotter)\
**Post date:** [10 March 2021 07:32 UTC](https://iplug2.discourse.group/t/how-to-react-on-vst3-bypass/337/11 "2021-03-10T07:32:40Z")

</div>

Sorry, you misunderstood me. Most of the VST3 plugins I have installed have a Bypass Button, which is linked to the hosts Bypass mode. Here an example, TDR Nova:

 ![Screenshot 2021-03-10 082343](https://global.discourse-cdn.com/free1/uploads/iplug2/original/1X/71d6e8742152a4cf5774e8594e86c0d7c89e4285.png)

You see in the upper left corner the bypass switch of Studio One and in the lower right corner the bypass switch of TDR Nova. This is what I want. (TDR Nova was written using JUCE, but I hoped this would also possible with iPlug2 without forking my own version…)

Beside this: your bypass solution will have problems if latency \> 0 and even the latency corrected version of iPlug2 results in audible clicks, as it jumps from the DSP output to the original signal. In my DSP code I have already a click free version which blends smoothly between these two states and therefore I want iPlug2 to use my version of ProcessBlock() when host bypass is enabled.

---

<div class="post-metadata">

**Author:** ![ToneSpotter](https://yyz2.discourse-cdn.com/free1/user_avatar/iplug2.discourse.group/tonespotter/32/148_2.png) [@ToneSpotter](https://iplug2.discourse.group/u/ToneSpotter)\
**Post date:** [11 March 2021 06:12 UTC](https://iplug2.discourse.group/t/how-to-react-on-vst3-bypass/337/12 "2021-03-11T06:12:42Z")

</div>

For the problem of calling `ProcessBlock()`, when Bypass is enabled, I’ve created a small patch. I will create a merge request for this later.

---

<div class="post-metadata">

**Author:** ![ToneSpotter](https://yyz2.discourse-cdn.com/free1/user_avatar/iplug2.discourse.group/tonespotter/32/148_2.png) [@ToneSpotter](https://iplug2.discourse.group/u/ToneSpotter)\
**Post date:** [11 March 2021 13:14 UTC](https://iplug2.discourse.group/t/how-to-react-on-vst3-bypass/337/13 "2021-03-11T13:14:15Z")

</div>

Linking the bypass states is a little bit more complicated. My solution:

In my bypass control I define:

```auto
void BypassSchalter::OnMouseDown(float x, float y, const IMouseMod& mod)
{
    double new_state = (GetValue() < 0.5 ? 1.0 : 0.0);

    SetValue(new_state);
    SetDirty(true);

#ifdef VST3_API
    IPlugVST3* p = dynamic_cast<IPlugVST3*>(GetDelegate());
    if (p != nullptr) {
        p->BeginInformHostOfParamChange(kBypassParam);
        p->InformHostOfParamChange(kBypassParam, new_state);
        p->EndInformHostOfParamChange(kBypassParam);
        while (p->GetBypassed() != new_state) {
            // Sometimes the param change doesn't work (timimg?). Wait and try it again...
            std::this_thread::sleep_for(std::chrono::milliseconds(5));
            p->BeginInformHostOfParamChange(kBypassParam);
            p->InformHostOfParamChange(kBypassParam, new_state);
            p->EndInformHostOfParamChange(kBypassParam);
        }
    }
#endif
}

```

I the DSP thread I define an ISender, which sends the current host bypass state to my bypass control:

```auto
void Oberton::ProcessBlock(sample **inputs, sample** outputs, int nFrames)
{
    bool bypass = (GetParam(kInternalBypass)->Value() > 0.5);

#ifdef VST3_API
    if (bypass != GetBypassed()) {
        bypass = GetBypassed();
        mBypassData.vals[0] = (bypass ? 1.0 : 0.0);
        mBypassSender.PushData(mBypassData);
    }
#endif

    // DPS code, which respects the bypass variable

}

```

A little complicated, but I see no other way, Has someone other ideas?

Btw. here the patch for iPlug2, which I mentioned in the previous post:

```diff
diff --git a/IPlug/IPlugProcessor.h b/IPlug/IPlugProcessor.h
index afc0e10a4..7779261e7 100644
--- a/IPlug/IPlugProcessor.h
+++ b/IPlug/IPlugProcessor.h
@@ -112,8 +112,11 @@ public:
   /** @return \c true if the plugin is currently bypassed*/
   bool GetBypassed() const { return mBypassed; }

+ /** @return \c true if ProcessBlock should be called even if the plugin is currently bypassed*/
+ bool GetProcessIfBypassed() const { return mProcessIfBypassed; }
+
   /** @return \c true if the plugin is currently rendering off-line */
- bool GetRenderingOffline() const { return mRenderingOffline; };
+ bool GetRenderingOffline() const { return mRenderingOffline; }

 #pragma mark -
   /** @return The number of samples elapsed since start of project timeline. */
@@ -264,6 +267,7 @@ protected:
   void SetSampleRate(double sampleRate) { mSampleRate = sampleRate; }
   void SetBlockSize(int blockSize);
   void SetBypassed(bool bypassed) { mBypassed = bypassed; }
+ void SetProcessIfBypassed(bool process) { mProcessIfBypassed = process; }
   void SetTimeInfo(const ITimeInfo& timeInfo) { mTimeInfo = timeInfo; }
   void SetRenderingOffline(bool renderingOffline) { mRenderingOffline = renderingOffline; }
   const WDL_String& GetChannelLabel(ERoute direction, int idx) { return mChannelData[direction].Get(idx)->mLabel; }
@@ -287,6 +291,8 @@ private:
   int mTailSize = 0;
   /** \c true if the plug-in is bypassed */
   bool mBypassed = false;
+ /** \c true if the plug-in should call ProcessBlock even if bypassed */
+ bool mProcessIfBypassed = false;
   /** \c true if the plug-in is rendering off-line*/
   bool mRenderingOffline = false;
   /** A list of IOConfig structures populated by ParseChannelIOStr in the IPlugProcessor constructor */
diff --git a/IPlug/VST3/IPlugVST3_ProcessorBase.cpp b/IPlug/VST3/IPlugVST3_ProcessorBase.cpp
index 56267d38a..dbd3f99ec 100644
--- a/IPlug/VST3/IPlugVST3_ProcessorBase.cpp
+++ b/IPlug/VST3/IPlugVST3_ProcessorBase.cpp
@@ -398,7 +398,7 @@ void IPlugVST3ProcessorBase::ProcessAudio(ProcessData& data, ProcessSetup& setup
       chanOffset += busChannels;
     }

- if (GetBypassed())
+ if (GetBypassed() && !GetProcessIfBypassed())
     {
       if (sampleSize == kSample32)
         PassThroughBuffers(0.f, data.numSamples); // single precision

```

---

<div class="post-metadata">

**Author:** ![TBProAudio](https://yyz2.discourse-cdn.com/free1/user_avatar/iplug2.discourse.group/tbproaudio/32/111_2.png) [@TBProAudio](https://iplug2.discourse.group/u/TBProAudio)\
**Post date:** [12 March 2021 15:09 UTC](https://iplug2.discourse.group/t/how-to-react-on-vst3-bypass/337/14 "2021-03-12T15:09:04Z")

</div>

Patching is the best and most reliable solution. Maybe some does a CR?

IPlugProcessor.h:

> bool bcanhandlebypass=false;  
> SetCanHandleByPass(bool state){bcanhandlebypass = \_state;}  
> GetCanHandleByPass(void){return bcanhandlebypass;}

each plugin api, process:

> if (GetBypassed() && !GetCanHandleByPass())  
> PassThroughBuffers(…)  
> else  
> ProcessBuffers(…)

---

<div class="post-metadata">

**Author:** ![ToneSpotter](https://yyz2.discourse-cdn.com/free1/user_avatar/iplug2.discourse.group/tonespotter/32/148_2.png) [@ToneSpotter](https://iplug2.discourse.group/u/ToneSpotter)\
**Post date:** [12 March 2021 17:58 UTC](https://iplug2.discourse.group/t/how-to-react-on-vst3-bypass/337/15 "2021-03-12T17:58:18Z")

</div>

I will create a PR next week…

---

<div class="post-metadata">

**Author:** ![ToneSpotter](https://yyz2.discourse-cdn.com/free1/user_avatar/iplug2.discourse.group/tonespotter/32/148_2.png) [@ToneSpotter](https://iplug2.discourse.group/u/ToneSpotter)\
**Post date:** [25 March 2021 08:53 UTC](https://iplug2.discourse.group/t/how-to-react-on-vst3-bypass/337/16 "2021-03-25T08:53:44Z")

</div>

After reading the VST3 documentation I realized, that the problem is in iPlug2 itself, as it defines it’s own bypass parameter. I patched the VST3 part, added a new IParam flag IParam::kFlagBypass and now I can use a “normal” IParam as bypass. This also switches off the GetBypassed() logic of iPlug2, so all is fine for me.

```auto
diff --git a/IPlug/IPlugParameter.h b/IPlug/IPlugParameter.h
index 1ed0c9205..b8141a7cc 100644
--- a/IPlug/IPlugParameter.h
+++ b/IPlug/IPlugParameter.h
@@ -55,6 +55,8 @@ public:
     kFlagSignDisplay = 0x8,
     /** Indicates that the parameter may influence the state of other parameters */
     kFlagMeta = 0x10,
+ /** Indicates that the parameter may act as host bypass parameter */
+ kFlagBypass = 0x20,
   };

   /** DisplayFunc allows custom parameter display functions, defined by a lambda matching this signature */
@@ -470,7 +472,10 @@ public:
   /** @return \c true If the parameter is flagged as a "meta" parameter, e.g. one that could modify other parameters */
   bool GetMeta() const { return mFlags & kFlagMeta; }

- /** Get a JSON description of the parameter.
+ /** @return \c true If the parameter may act as host bypass parameter */
+ bool GetHandlesBypass() const { return mFlags & kFlagBypass; }
+
+ /** Get a JSON description of the parameter.
    * @param json WDL_String to fill with the JSON
    * @param idx Index of the parameter, to place in the JSON */
   void GetJSON(WDL_String& json, int idx) const;
diff --git a/IPlug/VST3/IPlugVST3_ControllerBase.h b/IPlug/VST3/IPlugVST3_ControllerBase.h
index 90ded4eb8..d4362439e 100644
--- a/IPlug/VST3/IPlugVST3_ControllerBase.h
+++ b/IPlug/VST3/IPlugVST3_ControllerBase.h
@@ -76,10 +76,22 @@ public:
     }
     else
     #endif
- unitInfo.programListId = Steinberg::Vst::kNoProgramListId;
-
- if (!plugIsInstrument)
- mParameters.addParameter(mBypassParameter = new IPlugVST3BypassParameter());
+ unitInfo.programListId = Steinberg::Vst::kNoProgramListId;
+
+ if (!plugIsInstrument) {
+ bool existsBypassParameter = false;
+ for (int i = 0; i < pPlug->NParams() && !existsBypassParameter; i++)
+ {
+ if (pPlug->GetParam(i)->GetHandlesBypass())
+ {
+ existsBypassParameter = true;
+ }
+ }
+
+ if (!existsBypassParameter) {
+ mParameters.addParameter(mBypassParameter = new IPlugVST3BypassParameter());
+ }
+ }

     pEditController->addUnit(new Steinberg::Vst::Unit(unitInfo));

diff --git a/IPlug/VST3/IPlugVST3_Parameter.h b/IPlug/VST3/IPlugVST3_Parameter.h
index a7158b34b..71b312263 100644
--- a/IPlug/VST3/IPlugVST3_Parameter.h
+++ b/IPlug/VST3/IPlugVST3_Parameter.h
@@ -39,6 +39,7 @@ public:

     if (pParam->GetCanAutomate()) flags |= Steinberg::Vst::ParameterInfo::kCanAutomate;
     if (pParam->Type() == IParam::kTypeEnum) flags |= Steinberg::Vst::ParameterInfo::kIsList;
+ if (pParam->GetHandlesBypass()) flags |= Steinberg::Vst::ParameterInfo::kIsBypass;

     info.defaultNormalizedValue = valueNormalized = pParam->ToNormalized(pParam->GetDefault());
     info.flags = flags;

```

This is only for VST3…

To use, just write:

`GetParam(kBypass)->InitBool("Bypass", false, "", IParam::kFlagBypass);`
