commit
da23ab281a
5 changed files with 101 additions and 31 deletions
|
|
@ -432,7 +432,8 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode)
|
|||
setValueFromOpcode(opcode, ampKeytrack, Default::ampKeytrackRange);
|
||||
break;
|
||||
case hash("amp_veltrack"):
|
||||
setValueFromOpcode(opcode, ampVeltrack, Default::ampVeltrackRange);
|
||||
if (auto value = readOpcode(opcode.value, Default::ampVeltrackRange))
|
||||
ampVeltrack = normalizePercents(*value);
|
||||
break;
|
||||
case hash("amp_random"):
|
||||
setValueFromOpcode(opcode, ampRandom, Default::ampRandomRange);
|
||||
|
|
@ -1517,19 +1518,14 @@ float sfz::Region::velocityCurve(float velocity) const noexcept
|
|||
{
|
||||
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
|
||||
|
||||
float gain { 1.0f };
|
||||
if (velCurve) { // Custom velocity curve
|
||||
return velCurve->evalNormalized(velocity);
|
||||
} else { // Standard velocity curve
|
||||
// FIXME: Maybe there's a prettier way to check the boundaries?
|
||||
const float gaindB = [&]() {
|
||||
if (ampVeltrack >= 0)
|
||||
return velocity == 0.0f ? -90.0f : 40 * std::log(velocity) / std::log(10.0f);
|
||||
else
|
||||
return velocity == 1.0f ? -90.0f : 40 * std::log(1 - velocity) / std::log(10.0f);
|
||||
}();
|
||||
gain *= db2mag( gaindB * std::abs(ampVeltrack) / sfz::Default::ampVeltrackRange.getEnd());
|
||||
}
|
||||
float gain;
|
||||
if (velCurve)
|
||||
gain = velCurve->evalNormalized(velocity);
|
||||
else
|
||||
gain = velocity * velocity;
|
||||
|
||||
gain = std::fabs(ampVeltrack) * (1.0f - gain);
|
||||
gain = (ampVeltrack < 0) ? gain : (1.0f - gain);
|
||||
|
||||
return gain;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -331,7 +331,7 @@ struct Region {
|
|||
float position { normalizePercents(Default::position) }; // position
|
||||
uint8_t ampKeycenter { Default::ampKeycenter }; // amp_keycenter
|
||||
float ampKeytrack { Default::ampKeytrack }; // amp_keytrack
|
||||
float ampVeltrack { Default::ampVeltrack }; // amp_keytrack
|
||||
float ampVeltrack { normalizePercents(Default::ampVeltrack) }; // amp_keytrack
|
||||
std::vector<std::pair<uint8_t, float>> velocityPoints; // amp_velcurve_N
|
||||
absl::optional<Curve> velCurve {};
|
||||
float ampRandom { Default::ampRandom }; // amp_random
|
||||
|
|
|
|||
|
|
@ -568,7 +568,8 @@ void sfz::Synth::finalizeSfzLoad()
|
|||
|
||||
if (!region->velocityPoints.empty())
|
||||
region->velCurve = Curve::buildFromVelcurvePoints(
|
||||
region->velocityPoints, Curve::Interpolator::Linear, region->ampVeltrack < 0.0f);
|
||||
region->velocityPoints, Curve::Interpolator::Linear);
|
||||
|
||||
region->registerPitchWheel(0);
|
||||
region->registerAftertouch(0);
|
||||
region->registerTempo(2.0f);
|
||||
|
|
|
|||
|
|
@ -692,15 +692,15 @@ TEST_CASE("[Region] Parsing opcodes")
|
|||
|
||||
SECTION("amp_veltrack")
|
||||
{
|
||||
REQUIRE(region.ampVeltrack == 100.0f);
|
||||
REQUIRE(region.ampVeltrack == 1.0f);
|
||||
region.parseOpcode({ "amp_veltrack", "4.2" });
|
||||
REQUIRE(region.ampVeltrack == 4.2f);
|
||||
REQUIRE(region.ampVeltrack == Approx(0.042f));
|
||||
region.parseOpcode({ "amp_veltrack", "-4.2" });
|
||||
REQUIRE(region.ampVeltrack == -4.2f);
|
||||
REQUIRE(region.ampVeltrack == Approx(-0.042f));
|
||||
region.parseOpcode({ "amp_veltrack", "-123" });
|
||||
REQUIRE(region.ampVeltrack == -100.0f);
|
||||
REQUIRE(region.ampVeltrack == -1.0f);
|
||||
region.parseOpcode({ "amp_veltrack", "132" });
|
||||
REQUIRE(region.ampVeltrack == 100.0f);
|
||||
REQUIRE(region.ampVeltrack == 1.0f);
|
||||
}
|
||||
|
||||
SECTION("amp_random")
|
||||
|
|
|
|||
|
|
@ -448,16 +448,89 @@ TEST_CASE("[Synth] velcurve")
|
|||
<region> amp_velcurve_064=1 sample=*sine
|
||||
<region> amp_velcurve_064=1 amp_veltrack=-100 sample=*sine
|
||||
)");
|
||||
REQUIRE( synth.getRegionView(0)->velocityCurve(0_norm) == 0.0_a );
|
||||
REQUIRE( synth.getRegionView(0)->velocityCurve(32_norm) == Approx(0.5f).margin(1e-2) );
|
||||
REQUIRE( synth.getRegionView(0)->velocityCurve(64_norm) == 1.0_a );
|
||||
REQUIRE( synth.getRegionView(0)->velocityCurve(96_norm) == 1.0_a );
|
||||
REQUIRE( synth.getRegionView(0)->velocityCurve(127_norm) == 1.0_a );
|
||||
REQUIRE( synth.getRegionView(1)->velocityCurve(0_norm) == 1.0_a );
|
||||
REQUIRE( synth.getRegionView(1)->velocityCurve(32_norm) == 1.0_a );
|
||||
REQUIRE( synth.getRegionView(1)->velocityCurve(64_norm) == 1.0_a );
|
||||
REQUIRE( synth.getRegionView(1)->velocityCurve(96_norm) == Approx(0.5f).margin(1e-2) );
|
||||
REQUIRE( synth.getRegionView(1)->velocityCurve(127_norm) == 0.0_a );
|
||||
|
||||
struct VelocityData { float velocity, gain; bool exact; };
|
||||
|
||||
static const VelocityData veldata[] = {
|
||||
{ 0_norm, 0.0, true },
|
||||
{ 32_norm, 0.5f, false },
|
||||
{ 64_norm, 1.0, true },
|
||||
{ 96_norm, 1.0, true },
|
||||
{ 127_norm, 1.0, true },
|
||||
};
|
||||
|
||||
REQUIRE(synth.getNumRegions() == 2);
|
||||
const sfz::Region* r1 = synth.getRegionView(0);
|
||||
const sfz::Region* r2 = synth.getRegionView(1);
|
||||
|
||||
for (const VelocityData& vd : veldata) {
|
||||
if (vd.exact) {
|
||||
REQUIRE(r1->velocityCurve(vd.velocity) == vd.gain);
|
||||
REQUIRE(r2->velocityCurve(vd.velocity) == 1.0f - vd.gain);
|
||||
}
|
||||
else {
|
||||
REQUIRE(r1->velocityCurve(vd.velocity) == Approx(vd.gain).margin(1e-2));
|
||||
REQUIRE(r2->velocityCurve(vd.velocity) == Approx(1.0f - vd.gain).margin(1e-2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[Synth] veltrack")
|
||||
{
|
||||
struct VelocityData { float velocity, dBGain; };
|
||||
struct VeltrackData { float veltrack; absl::Span<const VelocityData> veldata; };
|
||||
|
||||
// measured on ARIA
|
||||
const VelocityData veldata25[] = {
|
||||
{ 127_norm, 0.0 },
|
||||
{ 96_norm, -1 },
|
||||
{ 64_norm, -1.8 },
|
||||
{ 32_norm, -2.3 },
|
||||
{ 1_norm, -2.5 },
|
||||
};
|
||||
const VelocityData veldata50[] = {
|
||||
{ 127_norm, 0.0 },
|
||||
{ 96_norm, -2.1 },
|
||||
{ 64_norm, -4.1 },
|
||||
{ 32_norm, -5.5 },
|
||||
{ 1_norm, -6.0 },
|
||||
};
|
||||
const VelocityData veldata75[] = {
|
||||
{ 127_norm, 0.0 },
|
||||
{ 96_norm, -3.4 },
|
||||
{ 64_norm, -7.2 },
|
||||
{ 32_norm, -10.5 },
|
||||
{ 1_norm, -12.0 },
|
||||
};
|
||||
const VelocityData veldata100[] = {
|
||||
{ 127_norm, 0.0 },
|
||||
{ 96_norm, -4.9 },
|
||||
{ 64_norm, -12.0 },
|
||||
{ 32_norm, -24.0 },
|
||||
{ 1_norm, -84.1 },
|
||||
};
|
||||
|
||||
const VeltrackData veltrackdata[] = {
|
||||
{ 25, veldata25 },
|
||||
{ 50, veldata50 },
|
||||
{ 75, veldata75 },
|
||||
{ 100, veldata100 },
|
||||
};
|
||||
|
||||
for (const VeltrackData& vt : veltrackdata) {
|
||||
sfz::Synth synth;
|
||||
const std::string sfzCode = "<region>sample=*sine amp_veltrack=" +
|
||||
std::to_string(vt.veltrack);
|
||||
synth.loadSfzString(fs::current_path() / "tests/TestFiles/veltrack.sfz", sfzCode);
|
||||
|
||||
REQUIRE(synth.getNumRegions() == 1);
|
||||
const sfz::Region* r = synth.getRegionView(0);
|
||||
|
||||
for (const VelocityData& vd : vt.veldata) {
|
||||
float dBGain = 20.0f * std::log10(r->velocityCurve(vd.velocity));
|
||||
REQUIRE(dBGain == Approx(vd.dBGain).margin(0.1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[Synth] Region by identifier")
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue