From 04adb2fa79d8aa124bcf8944ae01bb74d78ccdca Mon Sep 17 00:00:00 2001 From: reuk Date: Thu, 12 Mar 2020 14:04:27 +0000 Subject: [PATCH] DSP: Tidy up ProcessorChain implementation --- .../juce_dsp/processors/juce_ProcessorChain.h | 168 ++++++++++-------- 1 file changed, 97 insertions(+), 71 deletions(-) diff --git a/modules/juce_dsp/processors/juce_ProcessorChain.h b/modules/juce_dsp/processors/juce_ProcessorChain.h index 71fbb23a8e..956e54a18f 100644 --- a/modules/juce_dsp/processors/juce_ProcessorChain.h +++ b/modules/juce_dsp/processors/juce_ProcessorChain.h @@ -29,109 +29,135 @@ namespace juce namespace dsp { +//============================================================================== #ifndef DOXYGEN -namespace ProcessorHelpers // Internal helper classes used in building the ProcessorChain +/** The contents of this namespace are used to implement ProcessorChain and should + not be used elsewhere. Their interfaces (and existence) are liable to change! +*/ +namespace detail { - template - struct AccessHelper + template + constexpr void forEachInTuple (Fn&& fn, Tuple&& tuple, std::index_sequence) + noexcept (noexcept (std::initializer_list { (fn (std::get (tuple), Ix), 0)... })) { - template - static auto& get (ProcessorType& a) noexcept { return AccessHelper::get (a.processors); } - - template - static const auto& get (const ProcessorType& a) noexcept { return AccessHelper::get (a.processors); } + (void) std::initializer_list { ((void) fn (std::get (tuple), Ix), 0)... }; + } - template - static void setBypassed (ProcessorType& a, bool bypassed) { AccessHelper::setBypassed (a.processors, bypassed); } - }; + template + using TupleIndexSequence = std::index_sequence_for>>; - template <> - struct AccessHelper<0> + template + constexpr void forEachInTuple (Fn&& fn, Tuple&& tuple) + noexcept (noexcept (forEachInTuple (std::forward (fn), std::forward (tuple), TupleIndexSequence{}))) { - template - static auto& get (ProcessorType& a) noexcept { return a.getProcessor(); } + forEachInTuple (std::forward (fn), std::forward (tuple), TupleIndexSequence{}); + } +} +#endif - template - static const auto& get (const ProcessorType& a) noexcept { return a.getProcessor(); } +/** This variadically-templated class lets you join together any number of processor + classes into a single processor which will call process() on them all in sequence. +*/ +template +class ProcessorChain +{ +public: + /** Get a reference to the processor at index `Index`. */ + template auto& get() noexcept { return std::get (processors).processor; } - template - static void setBypassed (ProcessorType& a, bool bypassed) { a.isBypassed = bypassed; } - }; + /** Get a reference to the processor at index `Index`. */ + template const auto& get() const noexcept { return std::get (processors).processor; } - //============================================================================== - template - struct ChainElement + /** Set the processor at index `Index` to be bypassed or enabled. */ + template + void setBypassed (bool b) noexcept { std::get (processors).isBypassed = b; } + + /** Query whether the processor at index `Index` is bypassed. */ + template + bool isBypassed() const noexcept { return std::get (processors).isBypassed; } + + /** Prepare all inner processors with the provided `ProcessSpec`. */ + void prepare (const ProcessSpec& spec) { - void prepare (const ProcessSpec& spec) - { - processor.prepare (spec); - } + detail::forEachInTuple ([&] (auto& item, size_t) { item.processor.prepare (spec); }, processors); + } + + /** Reset all inner processors. */ + void reset() + { + detail::forEachInTuple ([] (auto& item, size_t) { item.processor.reset(); }, processors); + } - template - void process (const ProcessContext& context) noexcept + /** Process `context` through all inner processors in sequence. */ + template + void process (const ProcessContext& context) noexcept + { + detail::forEachInTuple ([&] (auto& item, size_t index) noexcept { - if (context.usesSeparateInputAndOutputBlocks() && ! isFirst) + if (context.usesSeparateInputAndOutputBlocks() && index != 0) { jassert (context.getOutputBlock().getNumChannels() == context.getInputBlock().getNumChannels()); ProcessContextReplacing replacingContext (context.getOutputBlock()); - replacingContext.isBypassed = (isBypassed || context.isBypassed); + replacingContext.isBypassed = (item.isBypassed || context.isBypassed); - processor.process (replacingContext); + item.processor.process (replacingContext); } else { ProcessContext contextCopy (context); - contextCopy.isBypassed = (isBypassed || context.isBypassed); + contextCopy.isBypassed = (item.isBypassed || context.isBypassed); - processor.process (contextCopy); + item.processor.process (contextCopy); } - } - - void reset() - { - processor.reset(); - } + }, processors); + } - bool isBypassed = false; +private: + template + struct ProcessorWithBypass + { Processor processor; - - Processor& getProcessor() noexcept { return processor; } - const Processor& getProcessor() const noexcept { return processor; } - Subclass& getThis() noexcept { return *static_cast (this); } - const Subclass& getThis() const noexcept { return *static_cast (this); } - - template auto& get() noexcept { return AccessHelper::get (getThis()); } - template const auto& get() const noexcept { return AccessHelper::get (getThis()); } - template void setBypassed (bool bypassed) noexcept { AccessHelper::setBypassed (getThis(), bypassed); } + bool isBypassed = false; }; - //============================================================================== - template - struct ChainBase : public ChainElement> - { - using Base = ChainElement>; - - template - 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(); } + std::tuple...> processors; +}; - ChainBase processors; - }; +/** Non-member equivalent of ProcessorChain::get which avoids awkward + member template syntax. +*/ +template +inline auto& get (ProcessorChain& chain) noexcept +{ + return chain.template get(); +} - template - struct ChainBase : public ChainElement> {}; +/** Non-member equivalent of ProcessorChain::get which avoids awkward + member template syntax. +*/ +template +inline auto& get (const ProcessorChain& chain) noexcept +{ + return chain.template get(); } -#endif +/** Non-member equivalent of ProcessorChain::setBypassed which avoids awkward + member template syntax. +*/ +template +inline void setBypassed (ProcessorChain& chain, bool bypassed) noexcept +{ + chain.template setBypassed (bypassed); +} -//============================================================================== -/** - This variadically-templated class lets you join together any number of processor - classes into a single processor which will call process() on them all in sequence. +/** Non-member equivalent of ProcessorChain::isBypassed which avoids awkward + member template syntax. */ -template -using ProcessorChain = ProcessorHelpers::ChainBase; +template +inline bool isBypassed (const ProcessorChain& chain) noexcept +{ + return chain.template isBypassed(); +} } // namespace dsp } // namespace juce