Merge pull request #233 from jpcima/set-hdcc
Support the opcode set_hdccN
This commit is contained in:
commit
9c93120b83
12 changed files with 1582 additions and 1081 deletions
|
|
@ -42,6 +42,12 @@ enum OpcodeCategory {
|
|||
enum OpcodeScope {
|
||||
//! unknown scope or other
|
||||
kOpcodeScopeGeneric,
|
||||
//! global scope
|
||||
kOpcodeScopeGlobal,
|
||||
//! control scope
|
||||
kOpcodeScopeControl,
|
||||
//! group scope
|
||||
kOpcodeScopeGroup,
|
||||
//! region scope
|
||||
kOpcodeScopeRegion,
|
||||
//! effect scope
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -8,6 +8,7 @@
|
|||
#include "Opcode.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/ascii.h"
|
||||
#include <string>
|
||||
|
||||
namespace sfz {
|
||||
|
|
@ -16,6 +17,9 @@ static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope sc
|
|||
{
|
||||
std::string opcode { rawOpcode };
|
||||
|
||||
// always convert it to lower case
|
||||
absl::AsciiStrToLower(&opcode);
|
||||
|
||||
/*!re2c
|
||||
re2c:flags:posix-captures = 1;
|
||||
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();
|
||||
|
||||
/*!re2c
|
||||
|
||||
(any) "_cc" (number) END {
|
||||
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 */;
|
||||
|
||||
} // scope == kOpcodeScopeRegion
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
if (scope == kOpcodeScopeRegion) {
|
||||
|
|
@ -132,6 +140,10 @@ end_generic:
|
|||
opcode = absl::StrCat("start_", group(1), "cc", group(2));
|
||||
goto end_region;
|
||||
}
|
||||
"on_" ("hi"|"lo") "hdcc" (number) END {
|
||||
opcode = absl::StrCat("start_", group(1), "hdcc", group(2));
|
||||
goto end_region;
|
||||
}
|
||||
|
||||
"fil_" (any) END {
|
||||
opcode = absl::StrCat("fil1_", group(1));
|
||||
|
|
@ -146,6 +158,11 @@ end_generic:
|
|||
goto end_region;
|
||||
}
|
||||
|
||||
("hi"|"lo") "realcc" (number) END {
|
||||
opcode = absl::StrCat(group(1), "hdcc", group(2));
|
||||
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
|
||||
|
||||
return opcode;
|
||||
|
|
|
|||
|
|
@ -102,6 +102,20 @@ public:
|
|||
_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:
|
||||
Type _start { static_cast<Type>(0.0) };
|
||||
Type _end { static_cast<Type>(0.0) };
|
||||
|
|
|
|||
|
|
@ -206,6 +206,18 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode)
|
|||
if (auto value = readOpcode(opcode.value, Default::midi7Range))
|
||||
ccConditions[opcode.parameters.back()].setEnd(normalizeCC(*value));
|
||||
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"):
|
||||
setRangeStartFromOpcode(opcode, keyswitchRange, Default::keyRange);
|
||||
break;
|
||||
|
|
@ -309,6 +321,18 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode)
|
|||
if (auto value = readOpcode(opcode.value, Default::midi7Range))
|
||||
ccTriggers[opcode.parameters.back()].setEnd(normalizeCC(*value));
|
||||
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
|
||||
case hash("volume"): // also gain
|
||||
|
|
|
|||
|
|
@ -194,6 +194,16 @@ namespace literals {
|
|||
|
||||
return normalize7Bits(value);
|
||||
}
|
||||
|
||||
inline float operator""_norm(long double value)
|
||||
{
|
||||
if (value < 0)
|
||||
value = 0;
|
||||
if (value > 127)
|
||||
value = 127;
|
||||
|
||||
return normalize7Bits(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -158,7 +158,9 @@ void sfz::Synth::clear()
|
|||
|
||||
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) {
|
||||
case hash("sw_default"):
|
||||
setValueFromOpcode(member, defaultSwitch, Default::keyRange);
|
||||
|
|
@ -176,7 +178,9 @@ void sfz::Synth::handleGroupOpcodes(const std::vector<Opcode>& members, const st
|
|||
absl::optional<unsigned> groupIdx;
|
||||
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) {
|
||||
case hash("group"):
|
||||
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)
|
||||
{
|
||||
for (auto& member : members) {
|
||||
for (auto& rawMember : members) {
|
||||
const Opcode member = rawMember.cleanUp(kOpcodeScopeControl);
|
||||
|
||||
switch (member.lettersOnlyHash) {
|
||||
case hash("Set_cc&"): // fallthrough
|
||||
case hash("set_cc&"):
|
||||
if (Default::ccNumberRange.containsWithEnd(member.parameters.back())) {
|
||||
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));
|
||||
}
|
||||
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&"):
|
||||
if (Default::ccNumberRange.containsWithEnd(member.parameters.back()))
|
||||
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()))
|
||||
keyLabels.emplace_back(member.parameters.back(), std::string(member.value));
|
||||
break;
|
||||
case hash("Default_path"):
|
||||
// fallthrough
|
||||
case hash("default_path"):
|
||||
defaultPath = absl::StrReplaceAll(trim(member.value), { { "\\", "/" } });
|
||||
DBG("Changing default sample path to " << defaultPath);
|
||||
|
|
|
|||
|
|
@ -408,6 +408,24 @@ TEST_CASE("[Files] Set CC applies properly")
|
|||
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")
|
||||
{
|
||||
sfz::Synth synth;
|
||||
|
|
|
|||
|
|
@ -161,8 +161,12 @@ TEST_CASE("[Opcode] Derived names")
|
|||
|
||||
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::kOpcodeScopeControl).opcode == "foo_cc7");
|
||||
|
||||
// <region>
|
||||
|
||||
static const std::pair<absl::string_view, absl::string_view> regionSpecific[] = {
|
||||
// LFO SFZv1
|
||||
|
|
@ -202,6 +206,10 @@ TEST_CASE("[Opcode] Normalization")
|
|||
{"gain_foobar", "volume_foobar"},
|
||||
{"tune", "pitch"},
|
||||
{"tune_foobar", "pitch_foobar"},
|
||||
{"lorealcc24", "lohdcc24"},
|
||||
{"hirealcc25", "hihdcc25"},
|
||||
{"on_lohdcc26", "start_lohdcc26"},
|
||||
{"on_hihdcc27", "start_hihdcc27"},
|
||||
// SFZv2 aliases
|
||||
{"on_hicc22", "start_hicc22"},
|
||||
{"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::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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -278,6 +278,34 @@ TEST_CASE("[Region] Parsing opcodes")
|
|||
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")
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
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")
|
||||
{
|
||||
REQUIRE(region.volume == 0.0f);
|
||||
|
|
|
|||
3
tests/TestFiles/set_hdcc.sfz
Normal file
3
tests/TestFiles/set_hdcc.sfz
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<control> set_hdcc61=0.1234
|
||||
<control> set_hdcc142=0.5678
|
||||
<region> sample=snare.wav
|
||||
3
tests/TestFiles/set_realcc.sfz
Normal file
3
tests/TestFiles/set_realcc.sfz
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<control> set_realcc61=0.1234
|
||||
<control> set_realcc142=0.5678
|
||||
<region> sample=snare.wav
|
||||
Loading…
Add table
Reference in a new issue