mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
DSP: Tidy up ProcessorChain implementation
This commit is contained in:
parent
a7d5d8dd95
commit
04adb2fa79
1 changed files with 102 additions and 76 deletions
|
|
@ -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); }
|
||||
(void) std::initializer_list<int> { ((void) fn (std::get<Ix> (tuple), Ix), 0)... };
|
||||
}
|
||||
|
||||
template <typename ProcessorType>
|
||||
static const auto& get (const ProcessorType& a) noexcept { return AccessHelper<arg - 1>::get (a.processors); }
|
||||
template <typename T>
|
||||
using TupleIndexSequence = std::index_sequence_for<std::remove_cv_t<std::remove_reference_t<T>>>;
|
||||
|
||||
template <typename ProcessorType>
|
||||
static void setBypassed (ProcessorType& a, bool bypassed) { AccessHelper<arg - 1>::setBypassed (a.processors, bypassed); }
|
||||
};
|
||||
|
||||
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)
|
||||
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);
|
||||
}
|
||||
|
||||
/** 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
|
||||
{
|
||||
processor.prepare (spec);
|
||||
}
|
||||
|
||||
template <typename ProcessContext>
|
||||
void process (const ProcessContext& context) 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);
|
||||
}
|
||||
}
|
||||
}, processors);
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
processor.reset();
|
||||
}
|
||||
|
||||
bool isBypassed = false;
|
||||
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); }
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
template <bool isFirst, typename FirstProcessor, typename... SubsequentProcessors>
|
||||
struct ChainBase : public ChainElement<isFirst, FirstProcessor, ChainBase<isFirst, FirstProcessor, SubsequentProcessors...>>
|
||||
private:
|
||||
template <typename Processor>
|
||||
struct ProcessorWithBypass
|
||||
{
|
||||
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(); }
|
||||
|
||||
ChainBase<false, SubsequentProcessors...> processors;
|
||||
Processor processor;
|
||||
bool isBypassed = false;
|
||||
};
|
||||
|
||||
template <bool isFirst, typename ProcessorType>
|
||||
struct ChainBase<isFirst, ProcessorType> : public ChainElement<isFirst, ProcessorType, ChainBase<isFirst, ProcessorType>> {};
|
||||
}
|
||||
#endif
|
||||
std::tuple<ProcessorWithBypass<Processors>...> processors;
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
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::get which avoids awkward
|
||||
member template syntax.
|
||||
*/
|
||||
template <typename... Processors>
|
||||
using ProcessorChain = ProcessorHelpers::ChainBase<true, Processors...>;
|
||||
template <int Index, typename... Processors>
|
||||
inline auto& get (ProcessorChain<Processors...>& chain) noexcept
|
||||
{
|
||||
return chain.template get<Index>();
|
||||
}
|
||||
|
||||
/** 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>();
|
||||
}
|
||||
|
||||
/** 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);
|
||||
}
|
||||
|
||||
/** Non-member equivalent of ProcessorChain::isBypassed which avoids awkward
|
||||
member template syntax.
|
||||
*/
|
||||
template <int Index, typename... Processors>
|
||||
inline bool isBypassed (const ProcessorChain<Processors...>& chain) noexcept
|
||||
{
|
||||
return chain.template isBypassed<Index>();
|
||||
}
|
||||
|
||||
} // namespace dsp
|
||||
} // namespace juce
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue