From 232eada0c4b72c85a94f4a1638f4f7c1aa2796ac Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Mon, 6 Apr 2020 19:26:39 +0200 Subject: [PATCH] Add the modifier helpers to another file --- src/sfizz/ModifierHelpers.h | 234 ++++++++++++++++++++++++++++++++++++ src/sfizz/Region.cpp | 3 +- src/sfizz/Region.h | 4 +- src/sfizz/SfzHelpers.h | 195 ------------------------------ src/sfizz/Voice.cpp | 51 +------- src/sfizz/Voice.h | 5 +- tests/EventEnvelopesT.cpp | 2 +- 7 files changed, 240 insertions(+), 254 deletions(-) create mode 100644 src/sfizz/ModifierHelpers.h diff --git a/src/sfizz/ModifierHelpers.h b/src/sfizz/ModifierHelpers.h new file mode 100644 index 00000000..5dd3fd78 --- /dev/null +++ b/src/sfizz/ModifierHelpers.h @@ -0,0 +1,234 @@ +#pragma once + +#include "Range.h" +#include "Defaults.h" +#include "SfzHelpers.h" +#include "Resources.h" +#include "absl/types/span.h" + +namespace sfz { + +/** + * @brief Compute a crossfade in value with respect to a crossfade range (note, velocity, cc, ...) + */ +template +float crossfadeIn(const sfz::Range& crossfadeRange, U value, SfzCrossfadeCurve curve) +{ + if (value < crossfadeRange.getStart()) + return 0.0f; + + const auto length = static_cast(crossfadeRange.length()); + if (length == 0.0f) + return 1.0f; + + else if (value < crossfadeRange.getEnd()) { + const auto crossfadePosition = static_cast(value - crossfadeRange.getStart()) / length; + if (curve == SfzCrossfadeCurve::power) + return sqrt(crossfadePosition); + if (curve == SfzCrossfadeCurve::gain) + return crossfadePosition; + } + + return 1.0f; +} + +/** + * @brief Compute a crossfade out value with respect to a crossfade range (note, velocity, cc, ...) + */ +template +float crossfadeOut(const sfz::Range& crossfadeRange, U value, SfzCrossfadeCurve curve) +{ + if (value > crossfadeRange.getEnd()) + return 0.0f; + + const auto length = static_cast(crossfadeRange.length()); + if (length == 0.0f) + return 1.0f; + + else if (value > crossfadeRange.getStart()) { + const auto crossfadePosition = static_cast(value - crossfadeRange.getStart()) / length; + if (curve == SfzCrossfadeCurve::power) + return std::sqrt(1 - crossfadePosition); + if (curve == SfzCrossfadeCurve::gain) + return 1 - crossfadePosition; + } + + return 1.0f; +} + +template +void linearEnvelope(const EventVector& events, absl::Span envelope, F&& lambda) +{ + ASSERT(events.size() > 0); + ASSERT(events[0].delay == 0); + if (envelope.size() == 0) + return; + + const auto maxDelay = static_cast(envelope.size() - 1); + + auto lastValue = lambda(events[0].value); + auto lastDelay = events[0].delay; + for (unsigned i = 1; i < events.size() && lastDelay < maxDelay; ++i) { + const auto length = min(events[i].delay, maxDelay) - lastDelay; + const auto step = (lambda(events[i].value) - lastValue) / length; + lastValue = linearRamp(envelope.subspan(lastDelay, length), lastValue, step); + lastDelay += length; + } + fill(envelope.subspan(lastDelay), lastValue); +} + +template +void linearEnvelope(const EventVector& events, absl::Span envelope, F&& lambda, float step) +{ + ASSERT(events.size() > 0); + ASSERT(events[0].delay == 0); + ASSERT(step != 0.0); + + if (envelope.size() == 0) + return; + + auto quantize = [step](float value) -> float { + return std::round(value / step) * step; + }; + const auto maxDelay = static_cast(envelope.size() - 1); + + auto lastValue = quantize(lambda(events[0].value)); + auto lastDelay = events[0].delay; + for (unsigned i = 1; i < events.size() && lastDelay < maxDelay; ++i) { + const auto nextValue = quantize(lambda(events[i].value)); + const auto difference = std::abs(nextValue - lastValue); + const auto length = min(events[i].delay, maxDelay) - lastDelay; + + if (difference < step) { + fill(envelope.subspan(lastDelay, length), lastValue); + lastValue = nextValue; + lastDelay += length; + continue; + } + + const auto numSteps = static_cast(difference / step); + const auto stepLength = static_cast(length / numSteps); + for (int i = 0; i < numSteps; ++i) { + fill(envelope.subspan(lastDelay, stepLength), lastValue); + lastValue += lastValue <= nextValue ? step : -step; + lastDelay += stepLength; + } + } + fill(envelope.subspan(lastDelay), lastValue); +} + +template +void multiplicativeEnvelope(const EventVector& events, absl::Span envelope, F&& lambda) +{ + ASSERT(events.size() > 0); + ASSERT(events[0].delay == 0); + + if (envelope.size() == 0) + return; + const auto maxDelay = static_cast(envelope.size() - 1); + + auto lastValue = lambda(events[0].value); + auto lastDelay = events[0].delay; + for (unsigned i = 1; i < events.size() && lastDelay < maxDelay; ++i) { + const auto length = min(events[i].delay, maxDelay) - lastDelay; + const auto nextValue = lambda(events[i].value); + const auto step = std::exp((std::log(nextValue) - std::log(lastValue)) / length); + multiplicativeRamp(envelope.subspan(lastDelay, length), lastValue, step); + lastValue = nextValue; + lastDelay += length; + } + fill(envelope.subspan(lastDelay), lastValue); +} + +template +void multiplicativeEnvelope(const EventVector& events, absl::Span envelope, F&& lambda, float step) +{ + ASSERT(events.size() > 0); + ASSERT(events[0].delay == 0); + ASSERT(step != 0.0f); + + if (envelope.size() == 0) + return; + const auto maxDelay = static_cast(envelope.size() - 1); + + const auto logStep = std::log(step); + // If we assume that a = b.q^r for b in (1, q) then + // log a log b + // ----- = ----- + r + // log q log q + // and log(b)\log(q) is between 0 and 1. + auto quantize = [logStep](float value) -> float { + return std::exp(logStep * std::round(std::log(value) / logStep)); + }; + + auto lastValue = quantize(lambda(events[0].value)); + auto lastDelay = events[0].delay; + for (unsigned i = 1; i < events.size() && lastDelay < maxDelay; ++i) { + const auto length = min(events[i].delay, maxDelay) - lastDelay; + const auto nextValue = quantize(lambda(events[i].value)); + const auto difference = nextValue > lastValue ? nextValue / lastValue : lastValue / nextValue; + + if (difference < step) { + fill(envelope.subspan(lastDelay, length), lastValue); + lastValue = nextValue; + lastDelay += length; + continue; + } + + const auto numSteps = static_cast(std::log(difference) / logStep); + const auto stepLength = static_cast(length / numSteps); + for (int i = 0; i < numSteps; ++i) { + fill(envelope.subspan(lastDelay, stepLength), lastValue); + lastValue = nextValue > lastValue ? lastValue * step : lastValue / step; + lastDelay += stepLength; + } + } + fill(envelope.subspan(lastDelay), lastValue); +} + +template +void linearModifier(const sfz::Resources& resources, absl::Span span, const sfz::CCData& ccData, F&& lambda) +{ + const auto events = resources.midiState.getCCEvents(ccData.cc); + const auto curve = resources.curves.getCurve(ccData.data.curve); + if (ccData.data.steps == 0) { + linearEnvelope(events, span, [&ccData, &curve, &lambda](float x) { + return lambda(curve.evalNormalized(x) * ccData.data.value); + }); + } else { + const float stepSize { ccData.data.value / ccData.data.steps }; + linearEnvelope(events, span, [&ccData, &curve, &lambda](float x) { + return lambda(curve.evalNormalized(x) * ccData.data.value); + }, stepSize); + } +} + +template +void multiplicativeModifier(const sfz::Resources& resources, absl::Span span, const sfz::CCData& ccData, F&& lambda) +{ + const auto events = resources.midiState.getCCEvents(ccData.cc); + const auto curve = resources.curves.getCurve(ccData.data.curve); + if (ccData.data.steps == 0) { + multiplicativeEnvelope(events, span, [&ccData, &curve, &lambda](float x) { + return lambda(curve.evalNormalized(x) * ccData.data.value); + }); + } else { + // FIXME: not sure about this step size for multiplicative envelopes + const float stepSize { ccData.data.value / ccData.data.steps }; + multiplicativeEnvelope(events, span, [&ccData, &curve, &lambda](float x) { + return lambda(curve.evalNormalized(x) * ccData.data.value); + }, stepSize); + } +} + +inline void linearModifier(const sfz::Resources& resources, absl::Span span, const sfz::CCData& ccData) +{ + linearModifier(resources, span, ccData, [](float x) { return x; }); +} + +inline void multiplicativeModifier(const sfz::Resources& resources, absl::Span span, const sfz::CCData& ccData) +{ + multiplicativeModifier(resources, span, ccData, [](float x) { return x; }); +} + +} diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index f150728d..066c75e3 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -5,13 +5,12 @@ // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz #include "Region.h" -#include "Defaults.h" #include "MathHelpers.h" #include "Macros.h" #include "Debug.h" #include "Opcode.h" #include "StringViewHelpers.h" -#include "MidiState.h" +#include "ModifierHelpers.h" #include "absl/strings/str_replace.h" #include "absl/strings/str_cat.h" #include "absl/algorithm/container.h" diff --git a/src/sfizz/Region.h b/src/sfizz/Region.h index 572d3eeb..fa952675 100644 --- a/src/sfizz/Region.h +++ b/src/sfizz/Region.h @@ -14,10 +14,8 @@ #include "Opcode.h" #include "AudioBuffer.h" #include "MidiState.h" -#include "absl/strings/str_cat.h" +#include "absl/types/optional.h" #include -#include -#include #include #include diff --git a/src/sfizz/SfzHelpers.h b/src/sfizz/SfzHelpers.h index 838ac863..de19d2ed 100644 --- a/src/sfizz/SfzHelpers.h +++ b/src/sfizz/SfzHelpers.h @@ -264,200 +264,5 @@ bool findDefine(absl::string_view line, absl::string_view& variable, absl::strin */ bool findInclude(absl::string_view line, std::string& path); -/** - * @brief multiply a value by a factor, in cents. To be used for pitch variations. - * - * @param base - * @param modifier - */ -inline CXX14_CONSTEXPR float multiplyByCentsModifier(int modifier, float base) -{ - return base * centsFactor(modifier); -} - -template -inline CXX14_CONSTEXPR float gainModifier(T modifier, float value) -{ - return value * modifier; -} - -/** - * @brief Compute a crossfade in value with respect to a crossfade range (note, velocity, cc, ...) - */ -template -float crossfadeIn(const sfz::Range& crossfadeRange, U value, SfzCrossfadeCurve curve) -{ - if (value < crossfadeRange.getStart()) - return 0.0f; - - const auto length = static_cast(crossfadeRange.length()); - if (length == 0.0f) - return 1.0f; - - else if (value < crossfadeRange.getEnd()) { - const auto crossfadePosition = static_cast(value - crossfadeRange.getStart()) / length; - if (curve == SfzCrossfadeCurve::power) - return sqrt(crossfadePosition); - if (curve == SfzCrossfadeCurve::gain) - return crossfadePosition; - } - - return 1.0f; -} - -/** - * @brief Compute a crossfade out value with respect to a crossfade range (note, velocity, cc, ...) - */ -template -float crossfadeOut(const sfz::Range& crossfadeRange, U value, SfzCrossfadeCurve curve) -{ - if (value > crossfadeRange.getEnd()) - return 0.0f; - - const auto length = static_cast(crossfadeRange.length()); - if (length == 0.0f) - return 1.0f; - - else if (value > crossfadeRange.getStart()) { - const auto crossfadePosition = static_cast(value - crossfadeRange.getStart()) / length; - if (curve == SfzCrossfadeCurve::power) - return std::sqrt(1 - crossfadePosition); - if (curve == SfzCrossfadeCurve::gain) - return 1 - crossfadePosition; - } - - return 1.0f; -} - -template -void linearEnvelope(const EventVector& events, absl::Span envelope, F&& lambda) -{ - ASSERT(events.size() > 0); - ASSERT(events[0].delay == 0); - if (envelope.size() == 0) - return; - - const auto maxDelay = static_cast(envelope.size() - 1); - - auto lastValue = lambda(events[0].value); - auto lastDelay = events[0].delay; - for (unsigned i = 1; i < events.size() && lastDelay < maxDelay; ++i) { - const auto length = min(events[i].delay, maxDelay) - lastDelay; - const auto step = (lambda(events[i].value) - lastValue) / length; - lastValue = linearRamp(envelope.subspan(lastDelay, length), lastValue, step); - lastDelay += length; - } - fill(envelope.subspan(lastDelay), lastValue); -} - -template -void linearEnvelope(const EventVector& events, absl::Span envelope, F&& lambda, float step) -{ - ASSERT(events.size() > 0); - ASSERT(events[0].delay == 0); - ASSERT(step != 0.0); - - if (envelope.size() == 0) - return; - - auto quantize = [step](float value) -> float { - return std::round(value / step) * step; - }; - const auto maxDelay = static_cast(envelope.size() - 1); - - auto lastValue = quantize(lambda(events[0].value)); - auto lastDelay = events[0].delay; - for (unsigned i = 1; i < events.size() && lastDelay < maxDelay; ++i) { - const auto nextValue = quantize(lambda(events[i].value)); - const auto difference = std::abs(nextValue - lastValue); - const auto length = min(events[i].delay, maxDelay) - lastDelay; - - if (difference < step) { - fill(envelope.subspan(lastDelay, length), lastValue); - lastValue = nextValue; - lastDelay += length; - continue; - } - - const auto numSteps = static_cast(difference / step); - const auto stepLength = static_cast(length / numSteps); - for (int i = 0; i < numSteps; ++i) { - fill(envelope.subspan(lastDelay, stepLength), lastValue); - lastValue += lastValue <= nextValue ? step : -step; - lastDelay += stepLength; - } - } - fill(envelope.subspan(lastDelay), lastValue); -} - -template -void multiplicativeEnvelope(const EventVector& events, absl::Span envelope, F&& lambda) -{ - ASSERT(events.size() > 0); - ASSERT(events[0].delay == 0); - - if (envelope.size() == 0) - return; - const auto maxDelay = static_cast(envelope.size() - 1); - - auto lastValue = lambda(events[0].value); - auto lastDelay = events[0].delay; - for (unsigned i = 1; i < events.size() && lastDelay < maxDelay; ++i) { - const auto length = min(events[i].delay, maxDelay) - lastDelay; - const auto nextValue = lambda(events[i].value); - const auto step = std::exp((std::log(nextValue) - std::log(lastValue)) / length); - multiplicativeRamp(envelope.subspan(lastDelay, length), lastValue, step); - lastValue = nextValue; - lastDelay += length; - } - fill(envelope.subspan(lastDelay), lastValue); -} - -template -void multiplicativeEnvelope(const EventVector& events, absl::Span envelope, F&& lambda, float step) -{ - ASSERT(events.size() > 0); - ASSERT(events[0].delay == 0); - ASSERT(step != 0.0f); - - if (envelope.size() == 0) - return; - const auto maxDelay = static_cast(envelope.size() - 1); - - const auto logStep = std::log(step); - // If we assume that a = b.q^r for b in (1, q) then - // log a log b - // ----- = ----- + r - // log q log q - // and log(b)\log(q) is between 0 and 1. - auto quantize = [logStep](float value) -> float { - return std::exp(logStep * std::round(std::log(value) / logStep)); - }; - - auto lastValue = quantize(lambda(events[0].value)); - auto lastDelay = events[0].delay; - for (unsigned i = 1; i < events.size() && lastDelay < maxDelay; ++i) { - const auto length = min(events[i].delay, maxDelay) - lastDelay; - const auto nextValue = quantize(lambda(events[i].value)); - const auto difference = nextValue > lastValue ? nextValue / lastValue : lastValue / nextValue; - - if (difference < step) { - fill(envelope.subspan(lastDelay, length), lastValue); - lastValue = nextValue; - lastDelay += length; - continue; - } - - const auto numSteps = static_cast(std::log(difference) / logStep); - const auto stepLength = static_cast(length / numSteps); - for (int i = 0; i < numSteps; ++i) { - fill(envelope.subspan(lastDelay, stepLength), lastValue); - lastValue = nextValue > lastValue ? lastValue * step : lastValue / step; - lastDelay += stepLength; - } - } - fill(envelope.subspan(lastDelay), lastValue); -} - } // namespace sfz diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index ee3484d1..d7deb618 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -4,16 +4,14 @@ // license. You should have receive a LICENSE.md file along with the code. // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz -#include "Macros.h" #include "Voice.h" -#include "AudioSpan.h" -#include "Config.h" +#include "Macros.h" #include "Defaults.h" +#include "ModifierHelpers.h" #include "MathHelpers.h" #include "SIMDHelpers.h" #include "SfzHelpers.h" #include "absl/algorithm/container.h" -#include sfz::Voice::Voice(sfz::Resources& resources) : resources(resources) @@ -249,51 +247,6 @@ void sfz::Voice::renderBlock(AudioSpan buffer) noexcept #endif } -template -void linearModifier(const sfz::Resources& resources, absl::Span span, const sfz::CCData& ccData, F&& lambda) -{ - const auto events = resources.midiState.getCCEvents(ccData.cc); - const auto curve = resources.curves.getCurve(ccData.data.curve); - if (ccData.data.steps == 0) { - linearEnvelope(events, span, [&ccData, &curve, &lambda](float x) { - return lambda(curve.evalNormalized(x) * ccData.data.value); - }); - } else { - const float stepSize { ccData.data.value / ccData.data.steps }; - linearEnvelope(events, span, [&ccData, &curve, &lambda](float x) { - return lambda(curve.evalNormalized(x) * ccData.data.value); - }, stepSize); - } -} - -template -void multiplicativeModifier(const sfz::Resources& resources, absl::Span span, const sfz::CCData& ccData, F&& lambda) -{ - const auto events = resources.midiState.getCCEvents(ccData.cc); - const auto curve = resources.curves.getCurve(ccData.data.curve); - if (ccData.data.steps == 0) { - multiplicativeEnvelope(events, span, [&ccData, &curve, &lambda](float x) { - return lambda(curve.evalNormalized(x) * ccData.data.value); - }); - } else { - // FIXME: not sure about this step size for multiplicative envelopes - const float stepSize { ccData.data.value / ccData.data.steps }; - multiplicativeEnvelope(events, span, [&ccData, &curve, &lambda](float x) { - return lambda(curve.evalNormalized(x) * ccData.data.value); - }, stepSize); - } -} - -void linearModifier(const sfz::Resources& resources, absl::Span span, const sfz::CCData& ccData) -{ - linearModifier(resources, span, ccData, [](float x) { return x; }); -} - -void multiplicativeModifier(const sfz::Resources& resources, absl::Span span, const sfz::CCData& ccData) -{ - multiplicativeModifier(resources, span, ccData, [](float x) { return x; }); -} - void sfz::Voice::amplitudeEnvelope(absl::Span modulationSpan) noexcept { const auto numSamples = modulationSpan.size(); diff --git a/src/sfizz/Voice.h b/src/sfizz/Voice.h index 1723c416..b4e61c5e 100644 --- a/src/sfizz/Voice.h +++ b/src/sfizz/Voice.h @@ -10,13 +10,10 @@ #include "HistoricalBuffer.h" #include "Region.h" #include "AudioBuffer.h" -#include "MidiState.h" -#include "Wavetables.h" #include "Resources.h" #include "AudioSpan.h" #include "LeakDetector.h" -#include -#include +#include "absl/types/span.h" #include #include diff --git a/tests/EventEnvelopesT.cpp b/tests/EventEnvelopesT.cpp index 545ea594..10954504 100644 --- a/tests/EventEnvelopesT.cpp +++ b/tests/EventEnvelopesT.cpp @@ -4,7 +4,7 @@ // license. You should have receive a LICENSE.md file along with the code. // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz -#include "sfizz/SfzHelpers.h" +#include "sfizz/ModifierHelpers.h" #include "sfizz/Buffer.h" #include "catch2/catch.hpp" #include