From 2964850f80bdb0d1b149abc700ac172cd6743fc9 Mon Sep 17 00:00:00 2001 From: paulfd Date: Thu, 29 Aug 2019 19:16:14 +0200 Subject: [PATCH] If an opcode is rejected by a region as unknown it is ignored thereafter --- sources/Defaults.h | 3 ++ sources/Region.cpp | 97 +++++++++++++++++++++++++++++++++++++++------- sources/Region.h | 4 ++ sources/Synth.cpp | 8 +++- 4 files changed, 98 insertions(+), 14 deletions(-) diff --git a/sources/Defaults.h b/sources/Defaults.h index 45de4c8b..7487c97a 100644 --- a/sources/Defaults.h +++ b/sources/Defaults.h @@ -85,8 +85,11 @@ namespace Default inline constexpr Range crossfadeKeyOutRange { 127, 127 }; inline constexpr Range crossfadeVelInRange { 0, 0 }; inline constexpr Range crossfadeVelOutRange { 127, 127 }; + inline constexpr Range crossfadeCCInRange { 0, 0 }; + inline constexpr Range crossfadeCCOutRange { 127, 127 }; inline constexpr SfzCrossfadeCurve crossfadeKeyCurve { SfzCrossfadeCurve::power }; inline constexpr SfzCrossfadeCurve crossfadeVelCurve { SfzCrossfadeCurve::power }; + inline constexpr SfzCrossfadeCurve crossfadeCCCurve { SfzCrossfadeCurve::power }; // Performance parameters: pitch inline constexpr uint8_t pitchKeycenter { 60 }; diff --git a/sources/Region.cpp b/sources/Region.cpp index 40260842..111b224e 100644 --- a/sources/Region.cpp +++ b/sources/Region.cpp @@ -5,6 +5,12 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) { + // Check that the parameter is well formed + if (opcode.parameter && !sfz::Default::ccRange.containsWithEnd(*opcode.parameter)) { + DBG("Wrong parameter value (" << std::to_string(*opcode.parameter) << ") for opcode " << opcode.opcode); + return false; + } + switch (hash(opcode.opcode)) { // Sound source: sample playback case hash("sample"): @@ -309,6 +315,38 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) DBG("Unknown crossfade power curve: " << std::string(opcode.value)); } break; + case hash("xfin_locc"): + if (opcode.parameter) { + setRangeStartFromOpcode(opcode, crossfadeCCInRange[*opcode.parameter], Default::ccRange); + } + break; + case hash("xfin_hicc"): + if (opcode.parameter) { + setRangeEndFromOpcode(opcode, crossfadeCCInRange[*opcode.parameter], Default::velocityRange); + } + break; + case hash("xfout_locc"): + if (opcode.parameter) { + setRangeStartFromOpcode(opcode, crossfadeCCOutRange[*opcode.parameter], Default::velocityRange); + } + break; + case hash("xfout_hicc"): + if (opcode.parameter) { + setRangeEndFromOpcode(opcode, crossfadeCCOutRange[*opcode.parameter], Default::velocityRange); + } + break; + case hash("xf_cccurve"): + switch (hash(opcode.value)) { + case hash("power"): + crossfadeCCCurve = SfzCrossfadeCurve::power; + break; + case hash("gain"): + crossfadeCCCurve = SfzCrossfadeCurve::gain; + break; + default: + DBG("Unknown crossfade power curve: " << std::string(opcode.value)); + } + break; // Performance parameters: pitch case hash("pitch_keycenter"): @@ -593,6 +631,7 @@ bool sfz::Region::isStereo() const noexcept return this->numChannels == 2; } +// TODO: lots and lots of repetition here... float sfz::Region::getNoteGain(int noteNumber, uint8_t velocity) noexcept { float baseGain { 1.0f }; @@ -605,7 +644,7 @@ float sfz::Region::getNoteGain(int noteNumber, uint8_t velocity) noexcept if (noteNumber < crossfadeKeyInRange.getStart()) baseGain = 0.0f; else if (noteNumber < crossfadeKeyInRange.getEnd()) { - const auto crossfadePosition = static_cast(noteNumber - crossfadeKeyInRange.getStart()) / crossfadeKeyInRange.length(); + const auto crossfadePosition = static_cast(noteNumber - crossfadeKeyInRange.getStart()) / (crossfadeKeyInRange.length() > 0 ? crossfadeKeyInRange.length() : 1); if (crossfadeKeyCurve == SfzCrossfadeCurve::power) baseGain *= sqrt(crossfadePosition); if (crossfadeKeyCurve == SfzCrossfadeCurve::gain) @@ -615,7 +654,7 @@ float sfz::Region::getNoteGain(int noteNumber, uint8_t velocity) noexcept if (noteNumber > crossfadeKeyOutRange.getEnd()) baseGain = 0.0f; else if (noteNumber > crossfadeKeyOutRange.getStart()) { - const auto crossfadePosition = static_cast(noteNumber - crossfadeKeyOutRange.getStart()) / crossfadeKeyOutRange.length(); + const auto crossfadePosition = static_cast(noteNumber - crossfadeKeyOutRange.getStart()) / (crossfadeKeyOutRange.length() > 0 ? crossfadeKeyOutRange.length() : 1); if (crossfadeKeyCurve == SfzCrossfadeCurve::power) baseGain *= sqrt(1 - crossfadePosition); if (crossfadeKeyCurve == SfzCrossfadeCurve::gain) @@ -625,7 +664,7 @@ float sfz::Region::getNoteGain(int noteNumber, uint8_t velocity) noexcept if (velocity < crossfadeVelInRange.getStart()) baseGain = 0; else if (velocity < crossfadeVelInRange.getEnd()) { - const auto crossfadePosition = static_cast(noteNumber - crossfadeVelInRange.getStart()) / crossfadeVelInRange.length(); + const auto crossfadePosition = static_cast(noteNumber - crossfadeVelInRange.getStart()) / (crossfadeVelInRange.length() > 0 ? crossfadeVelInRange.length() : 1); if (crossfadeVelCurve == SfzCrossfadeCurve::power) baseGain *= sqrt(crossfadePosition); if (crossfadeVelCurve == SfzCrossfadeCurve::gain) @@ -635,7 +674,7 @@ float sfz::Region::getNoteGain(int noteNumber, uint8_t velocity) noexcept if (velocity > crossfadeVelOutRange.getEnd()) baseGain = 0; else if (velocity > crossfadeVelOutRange.getStart()) { - const auto crossfadePosition = static_cast(noteNumber - crossfadeVelOutRange.getStart()) / crossfadeVelOutRange.length(); + const auto crossfadePosition = static_cast(noteNumber - crossfadeVelOutRange.getStart()) / (crossfadeVelOutRange.length() > 0 ? crossfadeVelOutRange.length() : 1); if (crossfadeVelCurve == SfzCrossfadeCurve::power) baseGain *= sqrt(1 - crossfadePosition); if (crossfadeVelCurve == SfzCrossfadeCurve::gain) @@ -645,25 +684,57 @@ float sfz::Region::getNoteGain(int noteNumber, uint8_t velocity) noexcept return baseGain; } +float sfz::Region::getCCGain(const sfz::CCValueArray& ccState) noexcept +{ + float gain { 1.0f }; + + for (const auto& valuePair : crossfadeCCInRange) { + const auto ccValue = ccState[valuePair.first]; + const auto crossfadeRange = valuePair.second; + if (ccValue < crossfadeRange.getStart()) { + gain = 0.0f; + } else if (ccValue < crossfadeRange.getEnd()) { + const auto crossfadePosition = static_cast(ccValue - crossfadeRange.getStart()) / (crossfadeRange.length() > 0 ? crossfadeRange.length() : 1); + if (crossfadeCCCurve == SfzCrossfadeCurve::power) + gain *= sqrt(crossfadePosition); + if (crossfadeVelCurve == SfzCrossfadeCurve::gain) + gain *= crossfadePosition; + } + } + + for (const auto& valuePair : crossfadeCCOutRange) { + const auto ccValue = ccState[valuePair.first]; + const auto crossfadeRange = valuePair.second; + if (ccValue > crossfadeRange.getEnd()) { + gain = 0.0f; + } else if (ccValue > crossfadeRange.getStart()) { + const auto crossfadePosition = static_cast(ccValue - crossfadeRange.getStart()) / (crossfadeRange.length() > 0 ? crossfadeRange.length() : 1); + if (crossfadeCCCurve == SfzCrossfadeCurve::power) + gain *= sqrt(1 - crossfadePosition); + if (crossfadeVelCurve == SfzCrossfadeCurve::gain) + gain *= 1 - crossfadePosition; + } + } + + return gain; +} + float sfz::Region::velocityGain(uint8_t velocity) const noexcept { float gaindB { 0.0 }; - if (velocityPoints.size() > 0) - { + if (velocityPoints.size() > 0) { auto after = std::find_if(velocityPoints.begin(), velocityPoints.end(), [velocity](auto& val) { return val.first >= velocity; }); auto before = after == velocityPoints.begin() ? velocityPoints.begin() : after - 1; // Linear interpolation float relativePositionInSegment { static_cast(velocity - before->first) / (after->first - before->first) }; float segmentEndpoints { after->second - before->second }; - gaindB = db2pow(relativePositionInSegment * segmentEndpoints); - } - else - { - float floatVelocity { static_cast(velocity)/127 }; + gaindB = db2pow(relativePositionInSegment * segmentEndpoints); + } else { + float floatVelocity { static_cast(velocity) / 127 }; if (ampVeltrack > 0) - gaindB = 40 * std::log(floatVelocity) / std::log(10.0f); + gaindB = 40 * std::log(floatVelocity) / std::log(10.0f); else - gaindB = 40 * std::log(1-floatVelocity) / std::log(10.0f); + gaindB = 40 * std::log(1 - floatVelocity) / std::log(10.0f); } gaindB *= std::abs(ampVeltrack) / sfz::Default::ampVeltrackRange.getEnd(); return db2pow(gaindB); diff --git a/sources/Region.h b/sources/Region.h index 82ed1d2b..2f71ee88 100644 --- a/sources/Region.h +++ b/sources/Region.h @@ -32,6 +32,7 @@ struct Region { bool isStereo() const noexcept; float getBasePitchVariation(int noteNumber, uint8_t velocity) noexcept; float getNoteGain(int noteNumber, uint8_t velocity) noexcept; + float getCCGain(const CCValueArray& ccState) noexcept; float getBaseGain() noexcept; float velocityGain(uint8_t velocity) const noexcept; uint32_t getOffset() noexcept; @@ -105,6 +106,9 @@ struct Region { Range crossfadeVelOutRange { Default::crossfadeVelOutRange }; SfzCrossfadeCurve crossfadeKeyCurve { Default::crossfadeKeyCurve }; SfzCrossfadeCurve crossfadeVelCurve { Default::crossfadeVelCurve }; + SfzCrossfadeCurve crossfadeCCCurve { Default::crossfadeCCCurve }; + CCMap> crossfadeCCInRange { Default::crossfadeCCInRange }; // xfin_loccN xfin_hiccN + CCMap> crossfadeCCOutRange { Default::crossfadeCCOutRange }; // xfout_loccN xfout_hiccN // Performance parameters: pitch uint8_t pitchKeycenter { Default::pitchKeycenter }; // pitch_keycenter diff --git a/sources/Synth.cpp b/sources/Synth.cpp index 666971a3..c29e86d2 100644 --- a/sources/Synth.cpp +++ b/sources/Synth.cpp @@ -54,9 +54,15 @@ void sfz::Synth::buildRegion(const std::vector& regionOpcodes) auto lastRegion = std::make_unique(); auto parseOpcodes = [&](const auto& opcodes) { - for (auto& opcode : opcodes) + for (auto& opcode : opcodes) { + const auto unknown = absl::c_find_if(unknownOpcodes, [&](std::string_view sv) { return sv.compare(opcode.opcode) == 0; }); + if (unknown != unknownOpcodes.end()) { + continue; + } + if (!lastRegion->parseOpcode(opcode)) unknownOpcodes.insert(opcode.opcode); + } }; parseOpcodes(globalOpcodes);