| @@ -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 <int arg> | |||
| struct AccessHelper | |||
| template <typename Fn, typename Tuple, size_t... Ix> | |||
| constexpr void forEachInTuple (Fn&& fn, Tuple&& tuple, std::index_sequence<Ix...>) | |||
| noexcept (noexcept (std::initializer_list<int> { (fn (std::get<Ix> (tuple), Ix), 0)... })) | |||
| { | |||
| template <typename ProcessorType> | |||
| static auto& get (ProcessorType& a) noexcept { return AccessHelper<arg - 1>::get (a.processors); } | |||
| template <typename ProcessorType> | |||
| static const auto& get (const ProcessorType& a) noexcept { return AccessHelper<arg - 1>::get (a.processors); } | |||
| (void) std::initializer_list<int> { ((void) fn (std::get<Ix> (tuple), Ix), 0)... }; | |||
| } | |||
| template <typename ProcessorType> | |||
| static void setBypassed (ProcessorType& a, bool bypassed) { AccessHelper<arg - 1>::setBypassed (a.processors, bypassed); } | |||
| }; | |||
| template <typename T> | |||
| using TupleIndexSequence = std::index_sequence_for<std::remove_cv_t<std::remove_reference_t<T>>>; | |||
| template <> | |||
| struct AccessHelper<0> | |||
| template <typename Fn, typename Tuple> | |||
| constexpr void forEachInTuple (Fn&& fn, Tuple&& tuple) | |||
| noexcept (noexcept (forEachInTuple (std::forward<Fn> (fn), std::forward<Tuple> (tuple), TupleIndexSequence<Tuple>{}))) | |||
| { | |||
| template <typename ProcessorType> | |||
| static auto& get (ProcessorType& a) noexcept { return a.getProcessor(); } | |||
| forEachInTuple (std::forward<Fn> (fn), std::forward<Tuple> (tuple), TupleIndexSequence<Tuple>{}); | |||
| } | |||
| } | |||
| #endif | |||
| template <typename ProcessorType> | |||
| 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 <typename... Processors> | |||
| class ProcessorChain | |||
| { | |||
| public: | |||
| /** Get a reference to the processor at index `Index`. */ | |||
| template <int Index> auto& get() noexcept { return std::get<Index> (processors).processor; } | |||
| template <typename ProcessorType> | |||
| static void setBypassed (ProcessorType& a, bool bypassed) { a.isBypassed = bypassed; } | |||
| }; | |||
| /** Get a reference to the processor at index `Index`. */ | |||
| template <int Index> const auto& get() const noexcept { return std::get<Index> (processors).processor; } | |||
| //============================================================================== | |||
| template <bool isFirst, typename Processor, typename Subclass> | |||
| struct ChainElement | |||
| /** Set the processor at index `Index` to be bypassed or enabled. */ | |||
| template <int Index> | |||
| void setBypassed (bool b) noexcept { std::get<Index> (processors).isBypassed = b; } | |||
| /** Query whether the processor at index `Index` is bypassed. */ | |||
| template <int Index> | |||
| bool isBypassed() const noexcept { return std::get<Index> (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 <typename ProcessContext> | |||
| void process (const ProcessContext& context) noexcept | |||
| /** Process `context` through all inner processors in sequence. */ | |||
| template <typename ProcessContext> | |||
| 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<typename ProcessContext::SampleType> 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 <typename Processor> | |||
| struct ProcessorWithBypass | |||
| { | |||
| Processor processor; | |||
| Processor& getProcessor() noexcept { return processor; } | |||
| const Processor& getProcessor() const noexcept { return processor; } | |||
| Subclass& getThis() noexcept { return *static_cast<Subclass*> (this); } | |||
| const Subclass& getThis() const noexcept { return *static_cast<const Subclass*> (this); } | |||
| template <int arg> auto& get() noexcept { return AccessHelper<arg>::get (getThis()); } | |||
| template <int arg> const auto& get() const noexcept { return AccessHelper<arg>::get (getThis()); } | |||
| template <int arg> void setBypassed (bool bypassed) noexcept { AccessHelper<arg>::setBypassed (getThis(), bypassed); } | |||
| bool isBypassed = false; | |||
| }; | |||
| //============================================================================== | |||
| template <bool isFirst, typename FirstProcessor, typename... SubsequentProcessors> | |||
| struct ChainBase : public ChainElement<isFirst, FirstProcessor, ChainBase<isFirst, FirstProcessor, SubsequentProcessors...>> | |||
| { | |||
| using Base = ChainElement<isFirst, FirstProcessor, ChainBase<isFirst, FirstProcessor, SubsequentProcessors...>>; | |||
| template <typename ProcessContext> | |||
| 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<ProcessorWithBypass<Processors>...> processors; | |||
| }; | |||
| ChainBase<false, SubsequentProcessors...> processors; | |||
| }; | |||
| /** Non-member equivalent of ProcessorChain::get which avoids awkward | |||
| member template syntax. | |||
| */ | |||
| template <int Index, typename... Processors> | |||
| inline auto& get (ProcessorChain<Processors...>& chain) noexcept | |||
| { | |||
| return chain.template get<Index>(); | |||
| } | |||
| template <bool isFirst, typename ProcessorType> | |||
| struct ChainBase<isFirst, ProcessorType> : public ChainElement<isFirst, ProcessorType, ChainBase<isFirst, ProcessorType>> {}; | |||
| /** Non-member equivalent of ProcessorChain::get which avoids awkward | |||
| member template syntax. | |||
| */ | |||
| template <int Index, typename... Processors> | |||
| inline auto& get (const ProcessorChain<Processors...>& chain) noexcept | |||
| { | |||
| return chain.template get<Index>(); | |||
| } | |||
| #endif | |||
| /** Non-member equivalent of ProcessorChain::setBypassed which avoids awkward | |||
| member template syntax. | |||
| */ | |||
| template <int Index, typename... Processors> | |||
| inline void setBypassed (ProcessorChain<Processors...>& chain, bool bypassed) noexcept | |||
| { | |||
| chain.template setBypassed<Index> (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 <typename... Processors> | |||
| using ProcessorChain = ProcessorHelpers::ChainBase<true, Processors...>; | |||
| template <int Index, typename... Processors> | |||
| inline bool isBypassed (const ProcessorChain<Processors...>& chain) noexcept | |||
| { | |||
| return chain.template isBypassed<Index>(); | |||
| } | |||
| } // namespace dsp | |||
| } // namespace juce | |||