Browse Source

DSP: Tidy up ProcessorChain implementation

tags/2021-05-28
reuk 5 years ago
parent
commit
04adb2fa79
1 changed files with 97 additions and 71 deletions
  1. +97
    -71
      modules/juce_dsp/processors/juce_ProcessorChain.h

+ 97
- 71
modules/juce_dsp/processors/juce_ProcessorChain.h View File

@@ -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

Loading…
Cancel
Save