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"); +}