diff --git a/modules/juce_dsp/processors/juce_ProcessorChain.h b/modules/juce_dsp/processors/juce_ProcessorChain.h index 466b11c789..b8808cdd11 100644 --- a/modules/juce_dsp/processors/juce_ProcessorChain.h +++ b/modules/juce_dsp/processors/juce_ProcessorChain.h @@ -37,9 +37,8 @@ namespace detail { template constexpr void forEachInTuple (Fn&& fn, Tuple&& tuple, std::index_sequence) - noexcept (noexcept (std::initializer_list { (fn (std::get (tuple), Ix), 0)... })) { - (void) std::initializer_list { ((void) fn (std::get (tuple), Ix), 0)... }; + (void) std::initializer_list { ((void) fn (std::get (tuple), std::integral_constant()), 0)... }; } template @@ -47,10 +46,16 @@ namespace detail template constexpr void forEachInTuple (Fn&& fn, Tuple&& tuple) - noexcept (noexcept (forEachInTuple (std::forward (fn), std::forward (tuple), TupleIndexSequence{}))) { forEachInTuple (std::forward (fn), std::forward (tuple), TupleIndexSequence{}); } + + // This could be a template variable, but that code causes an internal compiler error in MSVC 19.00.24215 + template + struct UseContextDirectly + { + static constexpr auto value = ! Context::usesSeparateInputAndOutputBlocks() || Ix == 0; + }; } #endif @@ -80,40 +85,43 @@ public: /** Prepare all inner processors with the provided `ProcessSpec`. */ void prepare (const ProcessSpec& spec) { - detail::forEachInTuple ([&] (auto& proc, size_t) { proc.prepare (spec); }, processors); + detail::forEachInTuple ([&] (auto& proc, auto) { proc.prepare (spec); }, processors); } /** Reset all inner processors. */ void reset() { - detail::forEachInTuple ([] (auto& proc, size_t) { proc.reset(); }, processors); + detail::forEachInTuple ([] (auto& proc, auto) { proc.reset(); }, processors); } /** Process `context` through all inner processors in sequence. */ template void process (const ProcessContext& context) noexcept { - detail::forEachInTuple ([&] (auto& proc, size_t index) noexcept - { - if (context.usesSeparateInputAndOutputBlocks() && index != 0) - { - jassert (context.getOutputBlock().getNumChannels() == context.getInputBlock().getNumChannels()); - ProcessContextReplacing replacingContext (context.getOutputBlock()); - replacingContext.isBypassed = (bypassed[index] || context.isBypassed); - - proc.process (replacingContext); - } - else - { - ProcessContext contextCopy (context); - contextCopy.isBypassed = (bypassed[index] || context.isBypassed); - - proc.process (contextCopy); - } - }, processors); + detail::forEachInTuple ([this, &context] (auto& proc, auto index) noexcept { this->processOne (context, proc, index); }, + processors); } private: + template ::value, int> = 0> + void processOne (const Context& context, Proc& proc, std::integral_constant) noexcept + { + jassert (context.getOutputBlock().getNumChannels() == context.getInputBlock().getNumChannels()); + ProcessContextReplacing replacingContext (context.getOutputBlock()); + replacingContext.isBypassed = (bypassed[Ix] || context.isBypassed); + + proc.process (replacingContext); + } + + template ::value, int> = 0> + void processOne (const Context& context, Proc& proc, std::integral_constant) noexcept + { + auto contextCopy = context; + contextCopy.isBypassed = (bypassed[Ix] || context.isBypassed); + + proc.process (contextCopy); + } + std::tuple processors; std::array bypassed { {} }; }; diff --git a/modules/juce_dsp/processors/juce_ProcessorChain_test.cpp b/modules/juce_dsp/processors/juce_ProcessorChain_test.cpp index 4e4f6b126b..0ce1ea0099 100644 --- a/modules/juce_dsp/processors/juce_ProcessorChain_test.cpp +++ b/modules/juce_dsp/processors/juce_ProcessorChain_test.cpp @@ -132,6 +132,32 @@ public: expect (get<0> (chain).bufferWasClear); expect (! get<1> (chain).bufferWasClear); } + + beginTest ("Chains with trailing items that only support replacing contexts can be built"); + { + AudioBuffer inBuf (1, 1), outBuf (1, 1); + juce::dsp::AudioBlock in (inBuf), out (outBuf); + + struct OnlyReplacing + { + void prepare (const juce::dsp::ProcessSpec&) {} + void process (const juce::dsp::ProcessContextReplacing& c) + { + c.getOutputBlock().multiplyBy (2.0f); + } + void reset() {} + }; + + { + juce::dsp::ProcessorChain, OnlyReplacing, OnlyReplacing> c; + juce::dsp::ProcessContextNonReplacing context (in, out); + get<0> (c).setGainLinear (1.0f); + c.prepare (ProcessSpec{}); + inBuf.setSample (0, 0, 1.0f); + c.process (context); + expectEquals (outBuf.getSample (0, 0), 4.0f); + } + } } };