diff --git a/modules/juce_audio_basics/effects/juce_LinearSmoothedValue.h b/modules/juce_audio_basics/effects/juce_LinearSmoothedValue.h index cd39b0aa48..afb92de3d0 100644 --- a/modules/juce_audio_basics/effects/juce_LinearSmoothedValue.h +++ b/modules/juce_audio_basics/effects/juce_LinearSmoothedValue.h @@ -186,6 +186,26 @@ public: } } + //============================================================================== + /** Skip the next numSamples samples. + + This is identical to calling getNextValue numSamples times. + @see getNextValue + */ + void skip (int numSamples) noexcept + { + if (numSamples >= countdown) + { + currentValue = target; + countdown = 0; + } + else + { + currentValue += (step * static_cast (numSamples)); + countdown -= numSamples; + } + } + private: //============================================================================== FloatType currentValue = 0, target = 0, step = 0; diff --git a/modules/juce_dsp/processors/juce_Bias.h b/modules/juce_dsp/processors/juce_Bias.h index 1937af9573..61ff049f72 100644 --- a/modules/juce_dsp/processors/juce_Bias.h +++ b/modules/juce_dsp/processors/juce_Bias.h @@ -107,6 +107,16 @@ public: auto len = inBlock.getNumSamples(); auto numChannels = inBlock.getNumChannels(); + if (context.isBypassed) + { + bias.skip (static_cast (len)); + + if (context.usesSeparateInputAndOutputBlocks()) + outBlock.copy (inBlock); + + return; + } + if (numChannels == 1) { auto* src = inBlock.getChannelPointer (0); diff --git a/modules/juce_dsp/processors/juce_FIRFilter.h b/modules/juce_dsp/processors/juce_FIRFilter.h index 09d2aaa94c..4b589028f9 100644 --- a/modules/juce_dsp/processors/juce_FIRFilter.h +++ b/modules/juce_dsp/processors/juce_FIRFilter.h @@ -138,8 +138,19 @@ namespace FIR auto* fir = coefficients->getRawCoefficients(); size_t p = pos; - for (size_t i = 0; i < numSamples; ++i) - dst[i] = processSingleSample (src[i], fifo, fir, size, p); + if (context.isBypassed) + { + for (size_t i = 0; i < numSamples; ++i) + { + fifo[p] = dst[i] = src[i]; + p = (p == 0 ? size - 1 : p - 1); + } + } + else + { + for (size_t i = 0; i < numSamples; ++i) + dst[i] = processSingleSample (src[i], fifo, fir, size, p); + } pos = p; } diff --git a/modules/juce_dsp/processors/juce_Gain.h b/modules/juce_dsp/processors/juce_Gain.h index e8e206f333..e7195369a2 100644 --- a/modules/juce_dsp/processors/juce_Gain.h +++ b/modules/juce_dsp/processors/juce_Gain.h @@ -103,6 +103,16 @@ public: auto len = inBlock.getNumSamples(); auto numChannels = inBlock.getNumChannels(); + if (context.isBypassed) + { + gain.skip (static_cast (len)); + + if (context.usesSeparateInputAndOutputBlocks()) + outBlock.copy (inBlock); + + return; + } + if (numChannels == 1) { auto* src = inBlock.getChannelPointer (0); diff --git a/modules/juce_dsp/processors/juce_IIRFilter.h b/modules/juce_dsp/processors/juce_IIRFilter.h index ec048bba6b..f665ef6e1c 100644 --- a/modules/juce_dsp/processors/juce_IIRFilter.h +++ b/modules/juce_dsp/processors/juce_IIRFilter.h @@ -101,7 +101,13 @@ namespace IIR /** Processes as a block of samples */ template - void process (const ProcessContext& context) noexcept; + void process (const ProcessContext& context) noexcept + { + if (context.isBypassed) + processInternal (context); + else + processInternal (context); + } /** Processes a single sample, without any locking. @@ -122,6 +128,10 @@ namespace IIR //============================================================================== void check(); + /** Processes as a block of samples */ + template + void processInternal (const ProcessContext& context) noexcept; + //============================================================================== HeapBlock memory; SampleType* state = nullptr; diff --git a/modules/juce_dsp/processors/juce_IIRFilter_Impl.h b/modules/juce_dsp/processors/juce_IIRFilter_Impl.h index 5ef75be08c..f4f8848f66 100644 --- a/modules/juce_dsp/processors/juce_IIRFilter_Impl.h +++ b/modules/juce_dsp/processors/juce_IIRFilter_Impl.h @@ -69,8 +69,8 @@ void Filter::prepare (const ProcessSpec&) noexcept { reset(); } template -template -void Filter::process (const ProcessContext& context) noexcept +template +void Filter::processInternal (const ProcessContext& context) noexcept { static_assert (std::is_same::value, "The sample-type of the IIR filter must match the sample-type supplied to this process callback"); @@ -89,6 +89,11 @@ void Filter::process (const ProcessContext& context) noexcept auto* dst = outputBlock.getChannelPointer (0); auto* coeffs = coefficients->getRawCoefficients(); + // we need to copy this template parameter into a constexpr + // otherwise MSVC will moan that the tenary expressions below + // are constant conditional expressions + constexpr bool isBypassed = bypassed; + switch (order) { case 1: @@ -103,7 +108,8 @@ void Filter::process (const ProcessContext& context) noexcept { auto in = src[i]; auto out = in * b0 + lv1; - dst[i] = out; + + dst[i] = isBypassed ? in : out; lv1 = (in * b1) - (out * a1); } @@ -127,7 +133,7 @@ void Filter::process (const ProcessContext& context) noexcept { auto in = src[i]; auto out = (in * b0) + lv1; - dst[i] = out; + dst[i] = isBypassed ? in : out; lv1 = (in * b1) - (out * a1) + lv2; lv2 = (in * b2) - (out * a2); @@ -156,7 +162,7 @@ void Filter::process (const ProcessContext& context) noexcept { auto in = src[i]; auto out = (in * b0) + lv1; - dst[i] = out; + dst[i] = isBypassed ? in : out; lv1 = (in * b1) - (out * a1) + lv2; lv2 = (in * b2) - (out * a2) + lv3; @@ -175,7 +181,7 @@ void Filter::process (const ProcessContext& context) noexcept { auto in = src[i]; auto out = (in * coeffs[0]) + state[0]; - dst[i] = out; + dst[i] = isBypassed ? in : out; for (size_t j = 0; j < order - 1; ++j) state[j] = (in * coeffs[j + 1]) - (out * coeffs[order + j + 1]) + state[j + 1]; diff --git a/modules/juce_dsp/processors/juce_Oscillator.h b/modules/juce_dsp/processors/juce_Oscillator.h index 0363a161ea..300f5f7685 100644 --- a/modules/juce_dsp/processors/juce_Oscillator.h +++ b/modules/juce_dsp/processors/juce_Oscillator.h @@ -129,6 +129,9 @@ public: auto numChannels = outBlock.getNumChannels(); auto baseIncrement = MathConstants::twoPi / sampleRate; + if (context.isBypassed) + context.getOutputBlock().clear(); + if (frequency.isSmoothing()) { auto* buffer = rampBuffer.getRawDataPointer(); @@ -137,12 +140,15 @@ public: buffer[i] = phase.advance (baseIncrement * frequency.getNextValue()) - MathConstants::pi; - for (size_t ch = 0; ch < numChannels; ++ch) + if (! context.isBypassed) { - auto* dst = outBlock.getChannelPointer (ch); + for (size_t ch = 0; ch < numChannels; ++ch) + { + auto* dst = outBlock.getChannelPointer (ch); - for (size_t i = 0; i < len; ++i) - dst[i] = generator (buffer[i]); + for (size_t i = 0; i < len; ++i) + dst[i] = generator (buffer[i]); + } } } else @@ -150,13 +156,21 @@ public: auto freq = baseIncrement * frequency.getNextValue(); auto p = phase; - for (size_t ch = 0; ch < numChannels; ++ch) + if (context.isBypassed) { - p = phase; - auto* dst = outBlock.getChannelPointer (ch); - - for (size_t i = 0; i < len; ++i) - dst[i] = generator (p.advance (freq) - MathConstants::pi); + frequency.skip (static_cast (len)); + p.advance (freq * static_cast (len)); + } + else + { + for (size_t ch = 0; ch < numChannels; ++ch) + { + p = phase; + auto* dst = outBlock.getChannelPointer (ch); + + for (size_t i = 0; i < len; ++i) + dst[i] = generator (p.advance (freq) - MathConstants::pi); + } } phase = p; diff --git a/modules/juce_dsp/processors/juce_ProcessorChain.h b/modules/juce_dsp/processors/juce_ProcessorChain.h index 4566c463ea..74e119264b 100644 --- a/modules/juce_dsp/processors/juce_ProcessorChain.h +++ b/modules/juce_dsp/processors/juce_ProcessorChain.h @@ -33,88 +33,85 @@ namespace dsp namespace ProcessorHelpers // Internal helper classes used in building the ProcessorChain { template - struct GetterHelper + struct AccessHelper { template - static auto& get (ProcessorType& a) noexcept { return GetterHelper::get (a.processors); } - }; + static auto& get (ProcessorType& a) noexcept { return AccessHelper::get (a.processors); } - template <> - struct GetterHelper<0> - { template - static auto& get (ProcessorType& a) noexcept { return a.getProcessor(); } + static void setBypassed (ProcessorType& a, bool bypassed) { AccessHelper::setBypassed (a.processors, bypassed); } }; - template - struct ChainBase + template <> + struct AccessHelper<0> { - Processor processor; - - Processor& getProcessor() noexcept { return processor; } - Subclass& getThis() noexcept { return *static_cast (this); } + template + static auto& get (ProcessorType& a) noexcept { return a.getProcessor(); } - template auto& get() noexcept { return GetterHelper::get (getThis()); } + template + static void setBypassed (ProcessorType& a, bool bypassed) { a.isBypassed = bypassed; } }; - template - struct Chain : public ChainBase> + //============================================================================== + template + struct ChainElement { - using Base = ChainBase>; - void prepare (const ProcessSpec& spec) { - Base::processor.prepare (spec); - processors.prepare (spec); + processor.prepare (spec); } template - void process (ProcessContext& context) noexcept + void process (const ProcessContext& context) noexcept { - Base::processor.process (context); - - if (context.usesSeparateInputAndOutputBlocks()) + if (context.usesSeparateInputAndOutputBlocks() && ! isFirst) { jassert (context.getOutputBlock().getNumChannels() == context.getInputBlock().getNumChannels()); ProcessContextReplacing replacingContext (context.getOutputBlock()); - processors.process (replacingContext); + replacingContext.isBypassed = (isBypassed || context.isBypassed); + + processor.process (replacingContext); } else { - processors.process (context); + ProcessContext contextCopy (context); + contextCopy.isBypassed = (isBypassed || context.isBypassed); + + processor.process (contextCopy); } } void reset() { - Base::processor.reset(); - processors.reset(); + processor.reset(); } - Chain processors; + bool isBypassed = false; + Processor processor; + + Processor& getProcessor() noexcept { return processor; } + Subclass& getThis() noexcept { return *static_cast (this); } + + template auto& get() noexcept { return AccessHelper::get (getThis()); } + template void setBypassed (bool bypassed) noexcept { AccessHelper::setBypassed (getThis(), bypassed); } }; - template - struct Chain : public ChainBase> + //============================================================================== + template + struct ChainBase : public ChainElement> { - using Base = ChainBase>; + using Base = ChainElement>; template - void process (ProcessContext& context) noexcept - { - Base::processor.process (context); - } - - void prepare (const ProcessSpec& spec) - { - Base::processor.prepare (spec); - } + void process (const ProcessContext& context) noexcept { Base::process (context); processors.process (context); } + void prepare (const ProcessSpec& spec) { Base::prepare (spec); processors.prepare (spec); } + void reset() { Base::reset(); processors.reset(); } - void reset() - { - Base::processor.reset(); - } + ChainBase processors; }; + + template + struct ChainBase : public ChainElement> {}; } #endif @@ -125,7 +122,7 @@ namespace ProcessorHelpers // Internal helper classes used in building the Proc classes into a single processor which will call process() on them all in sequence. */ template -using ProcessorChain = ProcessorHelpers::Chain; +using ProcessorChain = ProcessorHelpers::ChainBase; } // namespace dsp } // namespace juce diff --git a/modules/juce_dsp/processors/juce_StateVariableFilter.h b/modules/juce_dsp/processors/juce_StateVariableFilter.h index de396c8505..be356f8560 100644 --- a/modules/juce_dsp/processors/juce_StateVariableFilter.h +++ b/modules/juce_dsp/processors/juce_StateVariableFilter.h @@ -92,25 +92,10 @@ namespace StateVariableFilter static_assert (std::is_same::value, "The sample-type of the filter must match the sample-type supplied to this process callback"); - auto&& inputBlock = context.getInputBlock(); - auto&& outputBlock = context.getOutputBlock(); - - // This class can only process mono signals. Use the ProcessorDuplicator class - // to apply this filter on a multi-channel audio stream. - jassert (inputBlock.getNumChannels() == 1); - jassert (outputBlock.getNumChannels() == 1); - - auto n = inputBlock.getNumSamples(); - auto* src = inputBlock .getChannelPointer (0); - auto* dst = outputBlock.getChannelPointer (0); - - switch (parameters->type) - { - case Parameters::Type::lowPass: processBlock::Type::lowPass> (src, dst, n); break; - case Parameters::Type::bandPass: processBlock::Type::bandPass> (src, dst, n); break; - case Parameters::Type::highPass: processBlock::Type::highPass> (src, dst, n); break; - default: jassertfalse; - } + if (context.isBypassed) + processInternal (context); + else + processInternal (context); } /** Processes a single sample, without any locking or checking. @@ -119,9 +104,9 @@ namespace StateVariableFilter { switch (parameters->type) { - case Parameters::Type::lowPass: return processLoop::Type::lowPass> (sample, *parameters); break; - case Parameters::Type::bandPass: return processLoop::Type::bandPass> (sample, *parameters); break; - case Parameters::Type::highPass: return processLoop::Type::highPass> (sample, *parameters); break; + case Parameters::Type::lowPass: return processLoop::Type::lowPass> (sample, *parameters); break; + case Parameters::Type::bandPass: return processLoop::Type::bandPass> (sample, *parameters); break; + case Parameters::Type::highPass: return processLoop::Type::highPass> (sample, *parameters); break; default: jassertfalse; } @@ -130,7 +115,7 @@ namespace StateVariableFilter private: //============================================================================== - template ::Type type> + template ::Type type> SampleType JUCE_VECTOR_CALLTYPE processLoop (SampleType sample, Parameters& state) noexcept { y[2] = (sample - s1 * state.R2 - s1 * state.g - s2) * state.h; @@ -141,21 +126,45 @@ namespace StateVariableFilter y[0] = y[1] * state.g + s2; s2 = y[1] * state.g + y[0]; - return y[static_cast (type)]; + return isBypassed ? sample : y[static_cast (type)]; } - template ::Type type> + template ::Type type> void processBlock (const SampleType* input, SampleType* output, size_t n) noexcept { auto state = *parameters; for (size_t i = 0 ; i < n; ++i) - output[i] = processLoop (input[i], state); + output[i] = processLoop (input[i], state); snapToZero(); *parameters = state; } + template + void processInternal (const ProcessContext& context) noexcept + { + auto&& inputBlock = context.getInputBlock(); + auto&& outputBlock = context.getOutputBlock(); + + // This class can only process mono signals. Use the ProcessorDuplicator class + // to apply this filter on a multi-channel audio stream. + jassert (inputBlock.getNumChannels() == 1); + jassert (outputBlock.getNumChannels() == 1); + + auto n = inputBlock.getNumSamples(); + auto* src = inputBlock .getChannelPointer (0); + auto* dst = outputBlock.getChannelPointer (0); + + switch (parameters->type) + { + case Parameters::Type::lowPass: processBlock::Type::lowPass> (src, dst, n); break; + case Parameters::Type::bandPass: processBlock::Type::bandPass> (src, dst, n); break; + case Parameters::Type::highPass: processBlock::Type::highPass> (src, dst, n); break; + default: jassertfalse; + } + } + //============================================================================== std::array y; SampleType s1, s2; diff --git a/modules/juce_dsp/processors/juce_WaveShaper.h b/modules/juce_dsp/processors/juce_WaveShaper.h index bc13c4185f..6ea543ebe3 100644 --- a/modules/juce_dsp/processors/juce_WaveShaper.h +++ b/modules/juce_dsp/processors/juce_WaveShaper.h @@ -53,9 +53,17 @@ struct WaveShaper template void process (const ProcessContext& context) const noexcept { - AudioBlock::process (context.getInputBlock(), - context.getOutputBlock(), - functionToUse); + if (context.isBypassed) + { + if (context.usesSeparateInputAndOutputBlocks()) + context.getOutputBlock().copy (context.getInputBlock()); + } + else + { + AudioBlock::process (context.getInputBlock(), + context.getOutputBlock(), + functionToUse); + } } void reset() noexcept {}