Merge pull request #233 from jpcima/set-hdcc

Support the opcode set_hdccN
This commit is contained in:
JP Cimalando 2020-05-16 21:01:08 +02:00 committed by GitHub
commit 9c93120b83
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 1582 additions and 1081 deletions

View file

@ -42,6 +42,12 @@ enum OpcodeCategory {
enum OpcodeScope { enum OpcodeScope {
//! unknown scope or other //! unknown scope or other
kOpcodeScopeGeneric, kOpcodeScopeGeneric,
//! global scope
kOpcodeScopeGlobal,
//! control scope
kOpcodeScopeControl,
//! group scope
kOpcodeScopeGroup,
//! region scope //! region scope
kOpcodeScopeRegion, kOpcodeScopeRegion,
//! effect scope //! effect scope

File diff suppressed because it is too large Load diff

View file

@ -8,6 +8,7 @@
#include "Opcode.h" #include "Opcode.h"
#include "absl/strings/string_view.h" #include "absl/strings/string_view.h"
#include "absl/strings/str_cat.h" #include "absl/strings/str_cat.h"
#include "absl/strings/ascii.h"
#include <string> #include <string>
namespace sfz { namespace sfz {
@ -16,6 +17,9 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc
{ {
std::string opcode { rawOpcode }; std::string opcode { rawOpcode };
// always convert it to lower case
absl::AsciiStrToLower(&opcode);
/*!re2c /*!re2c
re2c:flags:posix-captures = 1; re2c:flags:posix-captures = 1;
re2c:define:YYCTYPE = char; re2c:define:YYCTYPE = char;
@ -45,24 +49,28 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
if (scope == kOpcodeScopeRegion) {
YYCURSOR = opcode.c_str(); YYCURSOR = opcode.c_str();
/*!re2c /*!re2c
(any) "_cc" (number) END { (any) "_cc" (number) END {
opcode = absl::StrCat(group(1), "_oncc", group(2)); opcode = absl::StrCat(group(1), "_oncc", group(2));
goto end_generic; goto end_region_oncc;
} }
* { * {
goto end_generic; goto end_region_oncc;
} }
*/ */
end_generic: end_region_oncc:
/* end */; /* end */;
} // scope == kOpcodeScopeRegion
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
if (scope == kOpcodeScopeRegion) { if (scope == kOpcodeScopeRegion) {
@ -132,6 +140,10 @@ end_generic:
opcode = absl::StrCat("start_", group(1), "cc", group(2)); opcode = absl::StrCat("start_", group(1), "cc", group(2));
goto end_region; goto end_region;
} }
"on_" ("hi"|"lo") "hdcc" (number) END {
opcode = absl::StrCat("start_", group(1), "hdcc", group(2));
goto end_region;
}
"fil_" (any) END { "fil_" (any) END {
opcode = absl::StrCat("fil1_", group(1)); opcode = absl::StrCat("fil1_", group(1));
@ -146,6 +158,11 @@ end_generic:
goto end_region; goto end_region;
} }
("hi"|"lo") "realcc" (number) END {
opcode = absl::StrCat(group(1), "hdcc", group(2));
goto end_region;
}
* { * {
goto end_region; goto end_region;
} }
@ -159,6 +176,31 @@ end_region:
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
if (scope == kOpcodeScopeControl) {
YYCURSOR = opcode.c_str();
/*!re2c
"set_realcc" (number) END {
opcode = absl::StrCat("set_hdcc", group(1));
goto end_control;
}
* {
goto end_control;
}
*/
end_control:
/* end */;
} // scope == kOpcodeScopeControl
//--------------------------------------------------------------------------
#undef YYMAXNMATCH #undef YYMAXNMATCH
return opcode; return opcode;

View file

@ -102,6 +102,20 @@ public:
_start = value; _start = value;
} }
/**
* @brief Convert the range to a different value type
*
* @return Range<Other>
*/
template <class Other>
Range<Other> to() const noexcept
{
return Range<Other> {
static_cast<Other>(_start),
static_cast<Other>(_end),
};
}
private: private:
Type _start { static_cast<Type>(0.0) }; Type _start { static_cast<Type>(0.0) };
Type _end { static_cast<Type>(0.0) }; Type _end { static_cast<Type>(0.0) };

View file

@ -206,6 +206,18 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode)
if (auto value = readOpcode(opcode.value, Default::midi7Range)) if (auto value = readOpcode(opcode.value, Default::midi7Range))
ccConditions[opcode.parameters.back()].setEnd(normalizeCC(*value)); ccConditions[opcode.parameters.back()].setEnd(normalizeCC(*value));
break; break;
case hash("lohdcc&"): // also lorealcc&
if (opcode.parameters.back() >= config::numCCs)
return false;
if (auto value = readOpcode(opcode.value, Default::normalizedRange))
ccConditions[opcode.parameters.back()].setStart(*value);
break;
case hash("hihdcc&"): // also hirealcc&
if (opcode.parameters.back() >= config::numCCs)
return false;
if (auto value = readOpcode(opcode.value, Default::normalizedRange))
ccConditions[opcode.parameters.back()].setEnd(*value);
break;
case hash("sw_lokey"): case hash("sw_lokey"):
setRangeStartFromOpcode(opcode, keyswitchRange, Default::keyRange); setRangeStartFromOpcode(opcode, keyswitchRange, Default::keyRange);
break; break;
@ -309,6 +321,18 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode)
if (auto value = readOpcode(opcode.value, Default::midi7Range)) if (auto value = readOpcode(opcode.value, Default::midi7Range))
ccTriggers[opcode.parameters.back()].setEnd(normalizeCC(*value)); ccTriggers[opcode.parameters.back()].setEnd(normalizeCC(*value));
break; break;
case hash("start_lohdcc&"): // also on_lohdcc&
if (opcode.parameters.back() >= config::numCCs)
return false;
if (auto value = readOpcode(opcode.value, Default::midi7Range.to<float>()))
ccTriggers[opcode.parameters.back()].setStart(normalizeCC(*value));
break;
case hash("start_hihdcc&"): // also on_hihdcc&
if (opcode.parameters.back() >= config::numCCs)
return false;
if (auto value = readOpcode(opcode.value, Default::midi7Range.to<float>()))
ccTriggers[opcode.parameters.back()].setEnd(normalizeCC(*value));
break;
// Performance parameters: amplifier // Performance parameters: amplifier
case hash("volume"): // also gain case hash("volume"): // also gain

View file

@ -194,6 +194,16 @@ namespace literals {
return normalize7Bits(value); return normalize7Bits(value);
} }
inline float operator""_norm(long double value)
{
if (value < 0)
value = 0;
if (value > 127)
value = 127;
return normalize7Bits(value);
}
} }
/** /**

View file

@ -158,7 +158,9 @@ void sfz::Synth::clear()
void sfz::Synth::handleGlobalOpcodes(const std::vector<Opcode>& members) void sfz::Synth::handleGlobalOpcodes(const std::vector<Opcode>& members)
{ {
for (auto& member : members) { for (auto& rawMember : members) {
const Opcode member = rawMember.cleanUp(kOpcodeScopeGlobal);
switch (member.lettersOnlyHash) { switch (member.lettersOnlyHash) {
case hash("sw_default"): case hash("sw_default"):
setValueFromOpcode(member, defaultSwitch, Default::keyRange); setValueFromOpcode(member, defaultSwitch, Default::keyRange);
@ -176,7 +178,9 @@ void sfz::Synth::handleGroupOpcodes(const std::vector<Opcode>& members, const st
absl::optional<unsigned> groupIdx; absl::optional<unsigned> groupIdx;
unsigned maxPolyphony { config::maxVoices }; unsigned maxPolyphony { config::maxVoices };
const auto parseOpcode = [&](const Opcode& member) { const auto parseOpcode = [&](const Opcode& rawMember) {
const Opcode member = rawMember.cleanUp(kOpcodeScopeGroup);
switch (member.lettersOnlyHash) { switch (member.lettersOnlyHash) {
case hash("group"): case hash("group"):
setValueFromOpcode(member, groupIdx, Default::groupRange); setValueFromOpcode(member, groupIdx, Default::groupRange);
@ -199,9 +203,10 @@ void sfz::Synth::handleGroupOpcodes(const std::vector<Opcode>& members, const st
void sfz::Synth::handleControlOpcodes(const std::vector<Opcode>& members) void sfz::Synth::handleControlOpcodes(const std::vector<Opcode>& members)
{ {
for (auto& member : members) { for (auto& rawMember : members) {
const Opcode member = rawMember.cleanUp(kOpcodeScopeControl);
switch (member.lettersOnlyHash) { switch (member.lettersOnlyHash) {
case hash("Set_cc&"): // fallthrough
case hash("set_cc&"): case hash("set_cc&"):
if (Default::ccNumberRange.containsWithEnd(member.parameters.back())) { if (Default::ccNumberRange.containsWithEnd(member.parameters.back())) {
const auto ccValue = readOpcode(member.value, Default::midi7Range); const auto ccValue = readOpcode(member.value, Default::midi7Range);
@ -209,7 +214,13 @@ void sfz::Synth::handleControlOpcodes(const std::vector<Opcode>& members)
resources.midiState.ccEvent(0, member.parameters.back(), normalizeCC(*ccValue)); resources.midiState.ccEvent(0, member.parameters.back(), normalizeCC(*ccValue));
} }
break; break;
case hash("Label_cc&"): // fallthrough case hash("set_hdcc&"):
if (Default::ccNumberRange.containsWithEnd(member.parameters.back())) {
const auto ccValue = readOpcode(member.value, Default::normalizedRange);
if (ccValue)
resources.midiState.ccEvent(0, member.parameters.back(), *ccValue);
}
break;
case hash("label_cc&"): case hash("label_cc&"):
if (Default::ccNumberRange.containsWithEnd(member.parameters.back())) if (Default::ccNumberRange.containsWithEnd(member.parameters.back()))
ccLabels.emplace_back(member.parameters.back(), std::string(member.value)); ccLabels.emplace_back(member.parameters.back(), std::string(member.value));
@ -218,8 +229,6 @@ void sfz::Synth::handleControlOpcodes(const std::vector<Opcode>& members)
if (Default::keyRange.containsWithEnd(member.parameters.back())) if (Default::keyRange.containsWithEnd(member.parameters.back()))
keyLabels.emplace_back(member.parameters.back(), std::string(member.value)); keyLabels.emplace_back(member.parameters.back(), std::string(member.value));
break; break;
case hash("Default_path"):
// fallthrough
case hash("default_path"): case hash("default_path"):
defaultPath = absl::StrReplaceAll(trim(member.value), { { "\\", "/" } }); defaultPath = absl::StrReplaceAll(trim(member.value), { { "\\", "/" } });
DBG("Changing default sample path to " << defaultPath); DBG("Changing default sample path to " << defaultPath);

View file

@ -408,6 +408,24 @@ TEST_CASE("[Files] Set CC applies properly")
REQUIRE(midiState.getCCValue(61) == 122_norm); REQUIRE(midiState.getCCValue(61) == 122_norm);
} }
TEST_CASE("[Files] Set HDCC applies properly")
{
sfz::Synth synth;
const auto& midiState = synth.getResources().midiState;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/set_hdcc.sfz");
REQUIRE(midiState.getCCValue(142) == Approx(0.5678));
REQUIRE(midiState.getCCValue(61) == Approx(0.1234));
}
TEST_CASE("[Files] Set RealCC applies properly")
{
sfz::Synth synth;
const auto& midiState = synth.getResources().midiState;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/set_realcc.sfz");
REQUIRE(midiState.getCCValue(142) == Approx(0.5678));
REQUIRE(midiState.getCCValue(61) == Approx(0.1234));
}
TEST_CASE("[Files] Note and octave offsets") TEST_CASE("[Files] Note and octave offsets")
{ {
sfz::Synth synth; sfz::Synth synth;

View file

@ -161,8 +161,12 @@ TEST_CASE("[Opcode] Derived names")
TEST_CASE("[Opcode] Normalization") TEST_CASE("[Opcode] Normalization")
{ {
REQUIRE(sfz::Opcode("foo_cc7", "").cleanUp(sfz::kOpcodeScopeGeneric).opcode == "foo_oncc7"); // *_ccN
REQUIRE(sfz::Opcode("foo_cc7", "").cleanUp(sfz::kOpcodeScopeRegion).opcode == "foo_oncc7"); REQUIRE(sfz::Opcode("foo_cc7", "").cleanUp(sfz::kOpcodeScopeRegion).opcode == "foo_oncc7");
REQUIRE(sfz::Opcode("foo_cc7", "").cleanUp(sfz::kOpcodeScopeControl).opcode == "foo_cc7");
// <region>
static const std::pair<absl::string_view, absl::string_view> regionSpecific[] = { static const std::pair<absl::string_view, absl::string_view> regionSpecific[] = {
// LFO SFZv1 // LFO SFZv1
@ -202,6 +206,10 @@ TEST_CASE("[Opcode] Normalization")
{"gain_foobar", "volume_foobar"}, {"gain_foobar", "volume_foobar"},
{"tune", "pitch"}, {"tune", "pitch"},
{"tune_foobar", "pitch_foobar"}, {"tune_foobar", "pitch_foobar"},
{"lorealcc24", "lohdcc24"},
{"hirealcc25", "hihdcc25"},
{"on_lohdcc26", "start_lohdcc26"},
{"on_hihdcc27", "start_hihdcc27"},
// SFZv2 aliases // SFZv2 aliases
{"on_hicc22", "start_hicc22"}, {"on_hicc22", "start_hicc22"},
{"on_locc23", "start_locc23"}, {"on_locc23", "start_locc23"},
@ -219,4 +227,22 @@ TEST_CASE("[Opcode] Normalization")
REQUIRE(sfz::Opcode(input, "").cleanUp(sfz::kOpcodeScopeRegion).opcode == expected); REQUIRE(sfz::Opcode(input, "").cleanUp(sfz::kOpcodeScopeRegion).opcode == expected);
REQUIRE(sfz::Opcode(input, "").cleanUp(sfz::kOpcodeScopeGeneric).opcode == input); REQUIRE(sfz::Opcode(input, "").cleanUp(sfz::kOpcodeScopeGeneric).opcode == input);
} }
// <control>
static const std::pair<absl::string_view, absl::string_view> controlSpecific[] = {
// ARIA aliases
{"set_realcc1", "set_hdcc1"},
};
for (auto pair : controlSpecific) {
absl::string_view input = pair.first;
absl::string_view expected = pair.second;
REQUIRE(sfz::Opcode(input, "").cleanUp(sfz::kOpcodeScopeControl).opcode == expected);
REQUIRE(sfz::Opcode(input, "").cleanUp(sfz::kOpcodeScopeGeneric).opcode == input);
}
// case
REQUIRE(sfz::Opcode("SaMpLe", "").cleanUp(sfz::kOpcodeScopeRegion).opcode == "sample");
} }

View file

@ -278,6 +278,34 @@ TEST_CASE("[Region] Parsing opcodes")
REQUIRE(region.ccConditions[127] == sfz::Range<float>(0_norm, 127_norm)); REQUIRE(region.ccConditions[127] == sfz::Range<float>(0_norm, 127_norm));
} }
SECTION("lohdcc, hihdcc")
{
region.parseOpcode({ "lohdcc7", "0.12" });
REQUIRE(region.ccConditions[7].getStart() == Approx(0.12f));
REQUIRE(region.ccConditions[7].getEnd() == 1.0f);
region.parseOpcode({ "lohdcc13", "-1.0" });
REQUIRE(region.ccConditions[13] == sfz::Range<float>(0.0f, 1.0f));
region.parseOpcode({ "hihdcc64", "0.45" });
REQUIRE(region.ccConditions[64].getStart() == 0.0f);
REQUIRE(region.ccConditions[64].getEnd() == Approx(0.45f));
region.parseOpcode({ "hihdcc126", "1.2" });
REQUIRE(region.ccConditions[126] == sfz::Range<float>(0.0f, 1.0f));
}
SECTION("lorealcc, hirealcc")
{
region.parseOpcode({ "lorealcc8", "0.12" });
REQUIRE(region.ccConditions[8].getStart() == Approx(0.12f));
REQUIRE(region.ccConditions[8].getEnd() == 1.0f);
region.parseOpcode({ "lorealcc14", "-1.0" });
REQUIRE(region.ccConditions[14] == sfz::Range<float>(0.0f, 1.0f));
region.parseOpcode({ "hirealcc63", "0.45" });
REQUIRE(region.ccConditions[63].getStart() == 0.0f);
REQUIRE(region.ccConditions[63].getEnd() == Approx(0.45f));
region.parseOpcode({ "hirealcc125", "1.2" });
REQUIRE(region.ccConditions[125] == sfz::Range<float>(0.0f, 1.0f));
}
SECTION("sw_lokey, sw_hikey") SECTION("sw_lokey, sw_hikey")
{ {
REQUIRE(region.keyswitchRange == sfz::Range<uint8_t>(0, 127)); REQUIRE(region.keyswitchRange == sfz::Range<uint8_t>(0, 127));
@ -468,6 +496,21 @@ TEST_CASE("[Region] Parsing opcodes")
REQUIRE(region.ccTriggers[4] == sfz::Range<float>(0_norm, 47_norm)); REQUIRE(region.ccTriggers[4] == sfz::Range<float>(0_norm, 47_norm));
} }
SECTION("on_lohdcc, on_hihdcc")
{
for (int ccIdx = 1; ccIdx < 128; ++ccIdx) {
REQUIRE(!region.ccTriggers.contains(ccIdx));
}
region.parseOpcode({ "on_lohdcc46", "15.9" });
REQUIRE(region.ccTriggers.contains(46));
REQUIRE(region.ccTriggers[46].getStart() == Approx(15.9_norm));
REQUIRE(region.ccTriggers[46].getEnd() == 1.0f);
region.parseOpcode({ "on_hihdcc5", "47.3" });
REQUIRE(region.ccTriggers.contains(5));
REQUIRE(region.ccTriggers[5].getStart() == 0.0f);
REQUIRE(region.ccTriggers[5].getEnd() == Approx(47.3_norm));
}
SECTION("volume") SECTION("volume")
{ {
REQUIRE(region.volume == 0.0f); REQUIRE(region.volume == 0.0f);

View file

@ -0,0 +1,3 @@
<control> set_hdcc61=0.1234
<control> set_hdcc142=0.5678
<region> sample=snare.wav

View file

@ -0,0 +1,3 @@
<control> set_realcc61=0.1234
<control> set_realcc142=0.5678
<region> sample=snare.wav