1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

ProcessorChain: Avoid runtime branching on Context::usesSeparateInputAndOutputBlocks

This commit is contained in:
reuk 2021-11-24 13:10:56 +00:00
parent bd01175d55
commit 8407b5ea74
No known key found for this signature in database
GPG key ID: FCB43929F012EE5C
2 changed files with 57 additions and 23 deletions

View file

@ -37,9 +37,8 @@ namespace detail
{
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)... }))
{
(void) std::initializer_list<int> { ((void) fn (std::get<Ix> (tuple), Ix), 0)... };
(void) std::initializer_list<int> { ((void) fn (std::get<Ix> (tuple), std::integral_constant<size_t, Ix>()), 0)... };
}
template <typename T>
@ -47,10 +46,16 @@ namespace detail
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>{})))
{
forEachInTuple (std::forward<Fn> (fn), std::forward<Tuple> (tuple), TupleIndexSequence<Tuple>{});
}
// This could be a template variable, but that code causes an internal compiler error in MSVC 19.00.24215
template <typename Context, size_t Ix>
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 <typename ProcessContext>
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<typename ProcessContext::SampleType> 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 <typename Context, typename Proc, size_t Ix, std::enable_if_t<! detail::UseContextDirectly<Context, Ix>::value, int> = 0>
void processOne (const Context& context, Proc& proc, std::integral_constant<size_t, Ix>) noexcept
{
jassert (context.getOutputBlock().getNumChannels() == context.getInputBlock().getNumChannels());
ProcessContextReplacing<typename Context::SampleType> replacingContext (context.getOutputBlock());
replacingContext.isBypassed = (bypassed[Ix] || context.isBypassed);
proc.process (replacingContext);
}
template <typename Context, typename Proc, size_t Ix, std::enable_if_t<detail::UseContextDirectly<Context, Ix>::value, int> = 0>
void processOne (const Context& context, Proc& proc, std::integral_constant<size_t, Ix>) noexcept
{
auto contextCopy = context;
contextCopy.isBypassed = (bypassed[Ix] || context.isBypassed);
proc.process (contextCopy);
}
std::tuple<Processors...> processors;
std::array<bool, sizeof...(Processors)> bypassed { {} };
};

View file

@ -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<float> inBuf (1, 1), outBuf (1, 1);
juce::dsp::AudioBlock<float> in (inBuf), out (outBuf);
struct OnlyReplacing
{
void prepare (const juce::dsp::ProcessSpec&) {}
void process (const juce::dsp::ProcessContextReplacing<float>& c)
{
c.getOutputBlock().multiplyBy (2.0f);
}
void reset() {}
};
{
juce::dsp::ProcessorChain<juce::dsp::Gain<float>, OnlyReplacing, OnlyReplacing> c;
juce::dsp::ProcessContextNonReplacing<float> 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);
}
}
}
};