- Add some expected behavior tests- Correct the evalNormalized in curves- Remove the no-lambda multiplicative modifiers... it's a trap that makes you create nans and infs

This commit is contained in:
Paul Fd 2020-04-06 20:12:59 +02:00
parent 5e6584a51a
commit e975877569
4 changed files with 107 additions and 12 deletions

View file

@ -45,7 +45,7 @@ public:
*/ */
float evalNormalized(float value) const float evalNormalized(float value) const
{ {
return evalCC7(denormalize7Bits<int>(value)); return evalCC7(value * 127.0f);
} }
/** /**

View file

@ -214,7 +214,7 @@ void multiplicativeModifier(const sfz::Resources& resources, absl::Span<float> s
}); });
} else { } else {
// FIXME: not sure about this step size for multiplicative envelopes // FIXME: not sure about this step size for multiplicative envelopes
const float stepSize { ccData.data.value / ccData.data.steps }; const float stepSize { lambda(ccData.data.value / ccData.data.steps) };
multiplicativeEnvelope(events, span, [&ccData, &curve, &lambda](float x) { multiplicativeEnvelope(events, span, [&ccData, &curve, &lambda](float x) {
return lambda(curve.evalNormalized(x) * ccData.data.value); return lambda(curve.evalNormalized(x) * ccData.data.value);
}, stepSize); }, stepSize);
@ -225,10 +225,4 @@ inline void linearModifier(const sfz::Resources& resources, absl::Span<float> sp
{ {
linearModifier(resources, span, ccData, [](float x) { return x; }); 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

@ -33,7 +33,7 @@ TEST_CASE("[Curve] Bipolar -1 to 1")
REQUIRE( curve.evalCC7(85) == Approx(0.3386).margin(1e-3) ); REQUIRE( curve.evalCC7(85) == Approx(0.3386).margin(1e-3) );
REQUIRE( curve.evalNormalized(0.0f) == -1.0f ); REQUIRE( curve.evalNormalized(0.0f) == -1.0f );
REQUIRE( curve.evalNormalized(1.0f) == 1.0f ); REQUIRE( curve.evalNormalized(1.0f) == 1.0f );
REQUIRE( curve.evalNormalized(0.3f) == Approx(-0.402).margin(1e-3) ); REQUIRE( curve.evalNormalized(0.3f) == Approx(-0.4).margin(1e-3) );
} }
TEST_CASE("[Curve] Bipolar 1 to 0") TEST_CASE("[Curve] Bipolar 1 to 0")
@ -59,7 +59,7 @@ TEST_CASE("[Curve] Bipolar 1 to -1")
REQUIRE( curve.evalCC7(85) == Approx(-0.3386).margin(1e-3) ); REQUIRE( curve.evalCC7(85) == Approx(-0.3386).margin(1e-3) );
REQUIRE( curve.evalNormalized(0.0f) == 1.0f ); REQUIRE( curve.evalNormalized(0.0f) == 1.0f );
REQUIRE( curve.evalNormalized(1.0f) == -1.0f ); REQUIRE( curve.evalNormalized(1.0f) == -1.0f );
REQUIRE( curve.evalNormalized(0.3f) == Approx(0.402).margin(1e-3) ); REQUIRE( curve.evalNormalized(0.3f) == Approx(0.4).margin(1e-3) );
} }
TEST_CASE("[Curve] x**2") TEST_CASE("[Curve] x**2")
@ -220,7 +220,7 @@ TEST_CASE("[Curve] Default CurveSet")
REQUIRE( curveSet.getCurve(1).evalNormalized(0.0f) == -1.0f ); REQUIRE( curveSet.getCurve(1).evalNormalized(0.0f) == -1.0f );
REQUIRE( curveSet.getCurve(1).evalNormalized(1.0f) == 1.0f ); REQUIRE( curveSet.getCurve(1).evalNormalized(1.0f) == 1.0f );
REQUIRE( curveSet.getCurve(1).evalNormalized(0.3f) == Approx(-0.402).margin(1e-3) ); REQUIRE( curveSet.getCurve(1).evalNormalized(0.3f) == Approx(-0.4).margin(1e-3) );
REQUIRE( curveSet.getCurve(2).evalNormalized(0.0f) == 1.0f ); REQUIRE( curveSet.getCurve(2).evalNormalized(0.0f) == 1.0f );
REQUIRE( curveSet.getCurve(2).evalNormalized(1.0f) == 0.0f ); REQUIRE( curveSet.getCurve(2).evalNormalized(1.0f) == 0.0f );
@ -228,7 +228,7 @@ TEST_CASE("[Curve] Default CurveSet")
REQUIRE( curveSet.getCurve(3).evalNormalized(0.0f) == 1.0f ); REQUIRE( curveSet.getCurve(3).evalNormalized(0.0f) == 1.0f );
REQUIRE( curveSet.getCurve(3).evalNormalized(1.0f) == -1.0f ); REQUIRE( curveSet.getCurve(3).evalNormalized(1.0f) == -1.0f );
REQUIRE( curveSet.getCurve(3).evalNormalized(0.3f) == Approx(0.402).margin(1e-3) ); REQUIRE( curveSet.getCurve(3).evalNormalized(0.3f) == Approx(0.4).margin(1e-3) );
REQUIRE( curveSet.getCurve(4).evalNormalized(0.0f) == 0.0f ); REQUIRE( curveSet.getCurve(4).evalNormalized(0.0f) == 0.0f );
REQUIRE( curveSet.getCurve(4).evalNormalized(1.0f) == 1.0f ); REQUIRE( curveSet.getCurve(4).evalNormalized(1.0f) == 1.0f );

View file

@ -273,3 +273,104 @@ TEST_CASE("[MultiplicativeEnvelope] Get quantized with unclean events")
multiplicativeEnvelope(events, absl::MakeSpan(output), idModifier, 2.0f); multiplicativeEnvelope(events, absl::MakeSpan(output), idModifier, 2.0f);
REQUIRE(output == expected); REQUIRE(output == expected);
} }
TEST_CASE("[linearModifiers] Compare with envelopes")
{
sfz::Resources resources;
resources.curves = sfz::CurveSet::createPredefined();
sfz::CCData<sfz::Modifier> ccData;
ccData.cc = 20;
ccData.data.value = 100.0f;
resources.midiState.ccEvent(5, 20, 0.1);
resources.midiState.ccEvent(10, 20, 0.2);
std::array<float, 16> output;
std::array<float, 16> envelope;
linearEnvelope(resources.midiState.getCCEvents(20), absl::MakeSpan(envelope), [&ccData](float x) {
return ccData.data.value * x;
});
linearModifier(resources, absl::MakeSpan(output), ccData);
REQUIRE(approxEqual<float>(output, envelope));
ccData.data.curve = 1;
linearEnvelope(resources.midiState.getCCEvents(20), absl::MakeSpan(envelope), [&ccData](float x) {
return ccData.data.value * (2 * x - 1);
});
linearModifier(resources, absl::MakeSpan(output), ccData);
REQUIRE(approxEqual<float>(output, envelope));
ccData.data.curve = 3;
ccData.data.value = 10.0f;
linearEnvelope(resources.midiState.getCCEvents(20), absl::MakeSpan(envelope), [&ccData](float x) {
return ccData.data.value * (1 - 2 * x);
});
linearModifier(resources, absl::MakeSpan(output), ccData);
REQUIRE(approxEqual<float>(output, envelope));
ccData.data.curve = 2;
ccData.data.value = 20.0f;
ccData.data.steps = 10;
linearEnvelope(resources.midiState.getCCEvents(20), absl::MakeSpan(envelope), [&ccData](float x) {
return ccData.data.value * (1 - x);
}, ccData.data.value / ccData.data.steps);
linearModifier(resources, absl::MakeSpan(output), ccData);
REQUIRE(approxEqual<float>(output, envelope));
}
TEST_CASE("[multiplicativeModifiers] Compare with envelopes")
{
sfz::Resources resources;
resources.curves = sfz::CurveSet::createPredefined();
sfz::CCData<sfz::Modifier> ccData;
ccData.cc = 20;
ccData.data.value = 100.0f;
resources.midiState.ccEvent(5, 20, 0.1);
resources.midiState.ccEvent(10, 20, 0.2);
std::array<float, 16> output;
std::array<float, 16> envelope;
multiplicativeEnvelope(resources.midiState.getCCEvents(20), absl::MakeSpan(envelope), [&ccData](float x) {
return db2mag(ccData.data.value * x);
});
multiplicativeModifier(resources, absl::MakeSpan(output), ccData, [](float x) {
return db2mag(x);
});
REQUIRE(approxEqual<float>(output, envelope));
ccData.data.curve = 1;
multiplicativeEnvelope(resources.midiState.getCCEvents(20), absl::MakeSpan(envelope), [&ccData](float x) {
return db2mag(ccData.data.value * (2 * x - 1));
});
multiplicativeModifier(resources, absl::MakeSpan(output), ccData, [](float x) {
return db2mag(x);
});
REQUIRE(approxEqual<float>(output, envelope));
ccData.data.curve = 3;
ccData.data.value = 10.0f;
multiplicativeEnvelope(resources.midiState.getCCEvents(20), absl::MakeSpan(envelope), [&ccData](float x) {
return db2mag(ccData.data.value * (1 - 2 * x));
});
multiplicativeModifier(resources, absl::MakeSpan(output), ccData, [](float x) {
return db2mag(x);
});
REQUIRE(approxEqual<float>(output, envelope));
ccData.data.curve = 2;
ccData.data.value = 20.0f;
ccData.data.steps = 10;
multiplicativeEnvelope(resources.midiState.getCCEvents(20), absl::MakeSpan(envelope), [&ccData](float x) {
return db2mag(ccData.data.value * (1 - x));
}, db2mag(ccData.data.value / ccData.data.steps) );
multiplicativeModifier(resources, absl::MakeSpan(output), ccData, [](float x) {
return db2mag(x);
});
REQUIRE(approxEqual<float>(output, envelope));
}