From acbbb8354aabe6ec841e0472764935145295a9fe Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sun, 3 May 2020 15:08:05 +0200 Subject: [PATCH 1/2] Add categorization of opcodes --- src/sfizz/Opcode.cpp | 31 +++++++++++++++++++++++++++++++ src/sfizz/Opcode.h | 19 +++++++++++++++++++ tests/OpcodeT.cpp | 10 ++++++++++ 3 files changed, 60 insertions(+) diff --git a/src/sfizz/Opcode.cpp b/src/sfizz/Opcode.cpp index fbe8bcc5..7451fa3d 100644 --- a/src/sfizz/Opcode.cpp +++ b/src/sfizz/Opcode.cpp @@ -6,11 +6,14 @@ #include "Opcode.h" #include "StringViewHelpers.h" +#include "absl/strings/ascii.h" +#include "absl/strings/match.h" #include sfz::Opcode::Opcode(absl::string_view inputOpcode, absl::string_view inputValue) : opcode(trim(inputOpcode)) , value(trim(inputValue)) + , category(identifyCategory(inputOpcode)) { size_t nextCharIndex { 0 }; int parameterPosition { 0 }; @@ -34,3 +37,31 @@ sfz::Opcode::Opcode(absl::string_view inputOpcode, absl::string_view inputValue) if (nextCharIndex != opcode.npos) lettersOnlyHash = hashNoAmpersand(opcode.substr(nextCharIndex), lettersOnlyHash); } + +static absl::string_view extractBackInteger(absl::string_view opcodeName) +{ + size_t n = opcodeName.size(); + size_t i = n; + while (i > 0 && absl::ascii_isdigit(opcodeName[i - 1])) --i; + return opcodeName.substr(i); +} + +sfz::OpcodeCategory sfz::Opcode::identifyCategory(absl::string_view name) +{ + sfz::OpcodeCategory category = kOpcodeNormal; + + if (!name.empty() && absl::ascii_isdigit(name.back())) { + absl::string_view part = name; + part.remove_suffix(extractBackInteger(name).size()); + if (absl::EndsWith(part, "_oncc") || absl::EndsWith(part, "_cc")) + category = kOpcodeOnCcN; + else if (absl::EndsWith(part, "_curvecc")) + category = kOpcodeCurveCcN; + else if (absl::EndsWith(part, "_stepcc")) + category = kOpcodeStepCcN; + else if (absl::EndsWith(part, "_smoothcc")) + category = kOpcodeSmoothCcN; + } + + return category; +} diff --git a/src/sfizz/Opcode.h b/src/sfizz/Opcode.h index 492cfb1e..25d2f931 100644 --- a/src/sfizz/Opcode.h +++ b/src/sfizz/Opcode.h @@ -20,6 +20,22 @@ #include "absl/strings/numbers.h" namespace sfz { +/** + * @brief A category which an opcode may belong to. + */ +enum OpcodeCategory { + //! An ordinary opcode + kOpcodeNormal, + //! A region opcode which matches *_onccN or *_ccN + kOpcodeOnCcN, + //! A region opcode which matches *_curveccN + kOpcodeCurveCcN, + //! A region opcode which matches *_stepccN + kOpcodeStepCcN, + //! A region opcode which matches *_smoothccN + kOpcodeSmoothCcN, +}; + /** * @brief Opcode description class. The class parses the parameters * of the opcode on construction. @@ -33,6 +49,9 @@ struct Opcode { uint64_t lettersOnlyHash { Fnv1aBasis }; // This is to handle the integer parameters of some opcodes std::vector parameters; + OpcodeCategory category; +private: + static OpcodeCategory identifyCategory(absl::string_view name); LEAK_DETECTOR(Opcode); }; diff --git a/tests/OpcodeT.cpp b/tests/OpcodeT.cpp index ffbb07ab..6a0123cf 100644 --- a/tests/OpcodeT.cpp +++ b/tests/OpcodeT.cpp @@ -138,3 +138,13 @@ TEST_CASE("[Opcode] Note values") REQUIRE(noteValue); REQUIRE(*noteValue == 61); } + +TEST_CASE("[Opcode] Categories") +{ + REQUIRE(sfz::Opcode("sample", "").category == sfz::kOpcodeNormal); + REQUIRE(sfz::Opcode("amplitude_oncc11", "").category == sfz::kOpcodeOnCcN); + REQUIRE(sfz::Opcode("cutoff_cc22", "").category == sfz::kOpcodeOnCcN); + REQUIRE(sfz::Opcode("lfo01_pitch_curvecc33", "").category == sfz::kOpcodeCurveCcN); + REQUIRE(sfz::Opcode("pan_stepcc44", "").category == sfz::kOpcodeStepCcN); + REQUIRE(sfz::Opcode("noise_level_smoothcc55", "").category == sfz::kOpcodeSmoothCcN); +} From 3a43998142e71ff39b4d1533368bcdb05a889782 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sun, 3 May 2020 16:12:34 +0200 Subject: [PATCH 2/2] Add the derived name helper for opcodes --- src/sfizz/Opcode.cpp | 48 ++++++++++++++++++++++++++++++++++++++++++++ src/sfizz/Opcode.h | 12 ++++++++++- tests/OpcodeT.cpp | 10 +++++++++ 3 files changed, 69 insertions(+), 1 deletion(-) diff --git a/src/sfizz/Opcode.cpp b/src/sfizz/Opcode.cpp index 7451fa3d..657bae1b 100644 --- a/src/sfizz/Opcode.cpp +++ b/src/sfizz/Opcode.cpp @@ -8,7 +8,9 @@ #include "StringViewHelpers.h" #include "absl/strings/ascii.h" #include "absl/strings/match.h" +#include "absl/strings/str_cat.h" #include +#include sfz::Opcode::Opcode(absl::string_view inputOpcode, absl::string_view inputValue) : opcode(trim(inputOpcode)) @@ -46,6 +48,52 @@ static absl::string_view extractBackInteger(absl::string_view opcodeName) return opcodeName.substr(i); } +std::string sfz::Opcode::getDerivedName(sfz::OpcodeCategory newCategory, unsigned number) const +{ + std::string derivedName(opcode); + + switch (category) { + case kOpcodeNormal: + break; + case kOpcodeOnCcN: + case kOpcodeCurveCcN: + case kOpcodeStepCcN: + case kOpcodeSmoothCcN: + { + // when the input is cc, first delete the suffix `_*cc` + size_t pos = opcode.rfind('_'); + assert(pos != opcode.npos); + derivedName.resize(pos); + } + break; + } + + // helper to extract the cc number optionally if the next part needs it + auto ccNumberSuffix = [this, number]() -> std::string { + return (number != ~0u) ? std::to_string(number) : + std::string(extractBackInteger(opcode)); + }; + + switch (newCategory) { + case kOpcodeNormal: + break; + case kOpcodeOnCcN: + absl::StrAppend(&derivedName, "_oncc", ccNumberSuffix()); + break; + case kOpcodeCurveCcN: + absl::StrAppend(&derivedName, "_curvecc", ccNumberSuffix()); + break; + case kOpcodeStepCcN: + absl::StrAppend(&derivedName, "_stepcc", ccNumberSuffix()); + break; + case kOpcodeSmoothCcN: + absl::StrAppend(&derivedName, "_smoothcc", ccNumberSuffix()); + break; + } + + return derivedName; +} + sfz::OpcodeCategory sfz::Opcode::identifyCategory(absl::string_view name) { sfz::OpcodeCategory category = kOpcodeNormal; diff --git a/src/sfizz/Opcode.h b/src/sfizz/Opcode.h index 25d2f931..189aca0b 100644 --- a/src/sfizz/Opcode.h +++ b/src/sfizz/Opcode.h @@ -10,7 +10,7 @@ #include "Range.h" #include "SfzHelpers.h" #include "StringViewHelpers.h" -#include +#include "absl/types/optional.h" #include "absl/meta/type_traits.h" #include #include @@ -50,6 +50,16 @@ struct Opcode { // This is to handle the integer parameters of some opcodes std::vector parameters; OpcodeCategory category; + + /* + * @brief Get the derived opcode name to convert it to another category. + * + * @param newCategory category to convert to + * @param number optional CC number, needed if destination is CC and source is not + * @return derived opcode name + */ + std::string getDerivedName(OpcodeCategory newCategory, unsigned number = ~0u) const; + private: static OpcodeCategory identifyCategory(absl::string_view name); LEAK_DETECTOR(Opcode); diff --git a/tests/OpcodeT.cpp b/tests/OpcodeT.cpp index 6a0123cf..c45357ff 100644 --- a/tests/OpcodeT.cpp +++ b/tests/OpcodeT.cpp @@ -148,3 +148,13 @@ TEST_CASE("[Opcode] Categories") REQUIRE(sfz::Opcode("pan_stepcc44", "").category == sfz::kOpcodeStepCcN); REQUIRE(sfz::Opcode("noise_level_smoothcc55", "").category == sfz::kOpcodeSmoothCcN); } + +TEST_CASE("[Opcode] Derived names") +{ + REQUIRE(sfz::Opcode("sample", "").getDerivedName(sfz::kOpcodeNormal) == "sample"); + REQUIRE(sfz::Opcode("cutoff_cc22", "").getDerivedName(sfz::kOpcodeNormal) == "cutoff"); + REQUIRE(sfz::Opcode("lfo01_pitch_curvecc33", "").getDerivedName(sfz::kOpcodeOnCcN) == "lfo01_pitch_oncc33"); + REQUIRE(sfz::Opcode("pan_stepcc44", "").getDerivedName(sfz::kOpcodeCurveCcN) == "pan_curvecc44"); + REQUIRE(sfz::Opcode("noise_level_smoothcc55", "").getDerivedName(sfz::kOpcodeStepCcN) == "noise_level_stepcc55"); + REQUIRE(sfz::Opcode("sample", "").getDerivedName(sfz::kOpcodeSmoothCcN, 66) == "sample_smoothcc66"); +}