Crossfade with S-shape curve

This commit is contained in:
Jean Pierre Cimalando 2020-10-01 13:43:22 +02:00
parent 453d489d4a
commit 893d0361c2
2 changed files with 39 additions and 5 deletions

View file

@ -34,6 +34,9 @@ sfz::Voice::Voice(int voiceNumber, sfz::Resources& resources)
gainSmoother.setSmoothing(config::gainSmoothing, sampleRate); gainSmoother.setSmoothing(config::gainSmoothing, sampleRate);
xfadeSmoother.setSmoothing(config::xfadeSmoothing, sampleRate); xfadeSmoother.setSmoothing(config::xfadeSmoothing, sampleRate);
// prepare curves
getSCurve();
} }
sfz::Voice::~Voice() sfz::Voice::~Voice()
@ -626,7 +629,9 @@ void sfz::Voice::fillWithData(AudioSpan<float> buffer) noexcept
} }
// loop crossfade settings // loop crossfade settings
constexpr bool loopXfadeUseCurves = false; // 0: linear, 1: use curves 5 & 6 constexpr int loopXfadeUseCurves = 2; // 0: linear
// 1: use curves 5 & 6
// 2: use S-shaped curve
// index preprocessing for loops // index preprocessing for loops
if (isLooping) { if (isLooping) {
@ -715,12 +720,17 @@ void sfz::Voice::fillWithData(AudioSpan<float> buffer) noexcept
{ {
// compute out curve // compute out curve
absl::Span<float> xfCurve = xfadeTemp[1]->first(ptSize); absl::Span<float> xfCurve = xfadeTemp[1]->first(ptSize);
IF_CONSTEXPR (loopXfadeUseCurves) { IF_CONSTEXPR (loopXfadeUseCurves == 2) {
const Curve& xfIn = getSCurve();
for (unsigned i = 0; i < ptSize; ++i)
xfCurve[i] = xfIn.evalNormalized(1.0f - xfCoeff[i]);
}
else IF_CONSTEXPR (loopXfadeUseCurves == 1) {
const Curve& xfOut = resources.curves.getCurve(6); const Curve& xfOut = resources.curves.getCurve(6);
for (unsigned i = 0; i < ptSize; ++i) for (unsigned i = 0; i < ptSize; ++i)
xfCurve[i] = xfOut.evalNormalized(xfCoeff[i]); xfCurve[i] = xfOut.evalNormalized(xfCoeff[i]);
} }
else { else IF_CONSTEXPR (loopXfadeUseCurves == 0) {
// TODO(jpc) vectorize this // TODO(jpc) vectorize this
for (unsigned i = 0; i < ptSize; ++i) for (unsigned i = 0; i < ptSize; ++i)
xfCurve[i] = clamp(1.0f - xfCoeff[i], 0.0f, 1.0f); xfCurve[i] = clamp(1.0f - xfCoeff[i], 0.0f, 1.0f);
@ -763,12 +773,17 @@ void sfz::Voice::fillWithData(AudioSpan<float> buffer) noexcept
// compute in curve // compute in curve
absl::Span<float> xfCurve = xfadeTemp[1]->first(applySize); absl::Span<float> xfCurve = xfadeTemp[1]->first(applySize);
IF_CONSTEXPR (loopXfadeUseCurves) { IF_CONSTEXPR (loopXfadeUseCurves == 2) {
const Curve& xfIn = getSCurve();
for (unsigned i = 0; i < applySize; ++i)
xfCurve[i] = xfIn.evalNormalized(xfInCoeff[i]);
}
else IF_CONSTEXPR (loopXfadeUseCurves == 1) {
const Curve& xfIn = resources.curves.getCurve(5); const Curve& xfIn = resources.curves.getCurve(5);
for (unsigned i = 0; i < applySize; ++i) for (unsigned i = 0; i < applySize; ++i)
xfCurve[i] = xfIn.evalNormalized(xfInCoeff[i]); xfCurve[i] = xfIn.evalNormalized(xfInCoeff[i]);
} }
else { else IF_CONSTEXPR (loopXfadeUseCurves == 0) {
// TODO(jpc) vectorize this // TODO(jpc) vectorize this
for (unsigned i = 0; i < applySize; ++i) for (unsigned i = 0; i < applySize; ++i)
xfCurve[i] = clamp(xfInCoeff[i], 0.0f, 1.0f); xfCurve[i] = clamp(xfInCoeff[i], 0.0f, 1.0f);
@ -865,6 +880,20 @@ void sfz::Voice::fillInterpolatedWithQuality(
} }
} }
const sfz::Curve& sfz::Voice::getSCurve()
{
static const Curve curve = []() -> Curve {
constexpr unsigned N = Curve::NumValues;
float values[N];
for (unsigned i = 0; i < N; ++i) {
double x = i / static_cast<double>(N - 1);
values[i] = (1.0 - std::cos(M_PI * x)) * 0.5;
}
return Curve::buildFromPoints(values);
}();
return curve;
}
void sfz::Voice::fillWithGenerator(AudioSpan<float> buffer) noexcept void sfz::Voice::fillWithGenerator(AudioSpan<float> buffer) noexcept
{ {
const auto leftSpan = buffer.getSpan(0); const auto leftSpan = buffer.getSpan(0);

View file

@ -396,6 +396,11 @@ private:
absl::Span<const int> indices, absl::Span<const float> coeffs, absl::Span<const int> indices, absl::Span<const float> coeffs,
absl::Span<const float> addingGains, int quality); absl::Span<const float> addingGains, int quality);
/**
* @brief Get a S-shaped curve that is applicable to loop crossfading.
*/
static const Curve& getSCurve();
/** /**
* @brief Compute the amplitude envelope, applied as a gain to a mono * @brief Compute the amplitude envelope, applied as a gain to a mono
* or stereo buffer * or stereo buffer