Add the modifier helpers to another file

This commit is contained in:
Paul Fd 2020-04-06 19:26:39 +02:00
parent 9d6b2ede2f
commit 232eada0c4
7 changed files with 240 additions and 254 deletions

234
src/sfizz/ModifierHelpers.h Normal file
View file

@ -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 <class T, class U>
float crossfadeIn(const sfz::Range<T>& crossfadeRange, U value, SfzCrossfadeCurve curve)
{
if (value < crossfadeRange.getStart())
return 0.0f;
const auto length = static_cast<float>(crossfadeRange.length());
if (length == 0.0f)
return 1.0f;
else if (value < crossfadeRange.getEnd()) {
const auto crossfadePosition = static_cast<float>(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 <class T, class U>
float crossfadeOut(const sfz::Range<T>& crossfadeRange, U value, SfzCrossfadeCurve curve)
{
if (value > crossfadeRange.getEnd())
return 0.0f;
const auto length = static_cast<float>(crossfadeRange.length());
if (length == 0.0f)
return 1.0f;
else if (value > crossfadeRange.getStart()) {
const auto crossfadePosition = static_cast<float>(value - crossfadeRange.getStart()) / length;
if (curve == SfzCrossfadeCurve::power)
return std::sqrt(1 - crossfadePosition);
if (curve == SfzCrossfadeCurve::gain)
return 1 - crossfadePosition;
}
return 1.0f;
}
template <class F>
void linearEnvelope(const EventVector& events, absl::Span<float> envelope, F&& lambda)
{
ASSERT(events.size() > 0);
ASSERT(events[0].delay == 0);
if (envelope.size() == 0)
return;
const auto maxDelay = static_cast<int>(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<float>(envelope.subspan(lastDelay, length), lastValue, step);
lastDelay += length;
}
fill<float>(envelope.subspan(lastDelay), lastValue);
}
template <class F>
void linearEnvelope(const EventVector& events, absl::Span<float> 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<int>(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<float>(envelope.subspan(lastDelay, length), lastValue);
lastValue = nextValue;
lastDelay += length;
continue;
}
const auto numSteps = static_cast<int>(difference / step);
const auto stepLength = static_cast<int>(length / numSteps);
for (int i = 0; i < numSteps; ++i) {
fill<float>(envelope.subspan(lastDelay, stepLength), lastValue);
lastValue += lastValue <= nextValue ? step : -step;
lastDelay += stepLength;
}
}
fill<float>(envelope.subspan(lastDelay), lastValue);
}
template <class F>
void multiplicativeEnvelope(const EventVector& events, absl::Span<float> envelope, F&& lambda)
{
ASSERT(events.size() > 0);
ASSERT(events[0].delay == 0);
if (envelope.size() == 0)
return;
const auto maxDelay = static_cast<int>(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<float>(envelope.subspan(lastDelay, length), lastValue, step);
lastValue = nextValue;
lastDelay += length;
}
fill<float>(envelope.subspan(lastDelay), lastValue);
}
template <class F>
void multiplicativeEnvelope(const EventVector& events, absl::Span<float> 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<int>(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<float>(envelope.subspan(lastDelay, length), lastValue);
lastValue = nextValue;
lastDelay += length;
continue;
}
const auto numSteps = static_cast<int>(std::log(difference) / logStep);
const auto stepLength = static_cast<int>(length / numSteps);
for (int i = 0; i < numSteps; ++i) {
fill<float>(envelope.subspan(lastDelay, stepLength), lastValue);
lastValue = nextValue > lastValue ? lastValue * step : lastValue / step;
lastDelay += stepLength;
}
}
fill<float>(envelope.subspan(lastDelay), lastValue);
}
template<class F>
void linearModifier(const sfz::Resources& resources, absl::Span<float> span, const sfz::CCData<sfz::Modifier>& 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<class F>
void multiplicativeModifier(const sfz::Resources& resources, absl::Span<float> span, const sfz::CCData<sfz::Modifier>& 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<float> span, const sfz::CCData<sfz::Modifier>& ccData)
{
linearModifier(resources, span, ccData, [](float x) { return x; });
}
inline void multiplicativeModifier(const sfz::Resources& resources, absl::Span<float> span, const sfz::CCData<sfz::Modifier>& ccData)
{
multiplicativeModifier(resources, span, ccData, [](float x) { return x; });
}
}

View file

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

View file

@ -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 <bitset>
#include <absl/types/optional.h>
#include <random>
#include <string>
#include <vector>

View file

@ -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 <class T>
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 <class T, class U>
float crossfadeIn(const sfz::Range<T>& crossfadeRange, U value, SfzCrossfadeCurve curve)
{
if (value < crossfadeRange.getStart())
return 0.0f;
const auto length = static_cast<float>(crossfadeRange.length());
if (length == 0.0f)
return 1.0f;
else if (value < crossfadeRange.getEnd()) {
const auto crossfadePosition = static_cast<float>(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 <class T, class U>
float crossfadeOut(const sfz::Range<T>& crossfadeRange, U value, SfzCrossfadeCurve curve)
{
if (value > crossfadeRange.getEnd())
return 0.0f;
const auto length = static_cast<float>(crossfadeRange.length());
if (length == 0.0f)
return 1.0f;
else if (value > crossfadeRange.getStart()) {
const auto crossfadePosition = static_cast<float>(value - crossfadeRange.getStart()) / length;
if (curve == SfzCrossfadeCurve::power)
return std::sqrt(1 - crossfadePosition);
if (curve == SfzCrossfadeCurve::gain)
return 1 - crossfadePosition;
}
return 1.0f;
}
template <class F>
void linearEnvelope(const EventVector& events, absl::Span<float> envelope, F&& lambda)
{
ASSERT(events.size() > 0);
ASSERT(events[0].delay == 0);
if (envelope.size() == 0)
return;
const auto maxDelay = static_cast<int>(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<float>(envelope.subspan(lastDelay, length), lastValue, step);
lastDelay += length;
}
fill<float>(envelope.subspan(lastDelay), lastValue);
}
template <class F>
void linearEnvelope(const EventVector& events, absl::Span<float> 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<int>(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<float>(envelope.subspan(lastDelay, length), lastValue);
lastValue = nextValue;
lastDelay += length;
continue;
}
const auto numSteps = static_cast<int>(difference / step);
const auto stepLength = static_cast<int>(length / numSteps);
for (int i = 0; i < numSteps; ++i) {
fill<float>(envelope.subspan(lastDelay, stepLength), lastValue);
lastValue += lastValue <= nextValue ? step : -step;
lastDelay += stepLength;
}
}
fill<float>(envelope.subspan(lastDelay), lastValue);
}
template <class F>
void multiplicativeEnvelope(const EventVector& events, absl::Span<float> envelope, F&& lambda)
{
ASSERT(events.size() > 0);
ASSERT(events[0].delay == 0);
if (envelope.size() == 0)
return;
const auto maxDelay = static_cast<int>(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<float>(envelope.subspan(lastDelay, length), lastValue, step);
lastValue = nextValue;
lastDelay += length;
}
fill<float>(envelope.subspan(lastDelay), lastValue);
}
template <class F>
void multiplicativeEnvelope(const EventVector& events, absl::Span<float> 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<int>(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<float>(envelope.subspan(lastDelay, length), lastValue);
lastValue = nextValue;
lastDelay += length;
continue;
}
const auto numSteps = static_cast<int>(std::log(difference) / logStep);
const auto stepLength = static_cast<int>(length / numSteps);
for (int i = 0; i < numSteps; ++i) {
fill<float>(envelope.subspan(lastDelay, stepLength), lastValue);
lastValue = nextValue > lastValue ? lastValue * step : lastValue / step;
lastDelay += stepLength;
}
}
fill<float>(envelope.subspan(lastDelay), lastValue);
}
} // namespace sfz

View file

@ -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 <memory>
sfz::Voice::Voice(sfz::Resources& resources)
: resources(resources)
@ -249,51 +247,6 @@ void sfz::Voice::renderBlock(AudioSpan<float> buffer) noexcept
#endif
}
template<class F>
void linearModifier(const sfz::Resources& resources, absl::Span<float> span, const sfz::CCData<sfz::Modifier>& 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<class F>
void multiplicativeModifier(const sfz::Resources& resources, absl::Span<float> span, const sfz::CCData<sfz::Modifier>& 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<float> span, const sfz::CCData<sfz::Modifier>& ccData)
{
linearModifier(resources, span, ccData, [](float x) { return x; });
}
void multiplicativeModifier(const sfz::Resources& resources, absl::Span<float> span, const sfz::CCData<sfz::Modifier>& ccData)
{
multiplicativeModifier(resources, span, ccData, [](float x) { return x; });
}
void sfz::Voice::amplitudeEnvelope(absl::Span<float> modulationSpan) noexcept
{
const auto numSamples = modulationSpan.size();

View file

@ -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 <absl/types/span.h>
#include <atomic>
#include "absl/types/span.h"
#include <memory>
#include <random>

View file

@ -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 <absl/algorithm/container.h>