Normalize CCs internally in Synth and region
This commit is contained in:
parent
0e1e65135e
commit
601126db0d
6 changed files with 82 additions and 44 deletions
|
|
@ -68,14 +68,13 @@ namespace Default
|
|||
|
||||
// Region logic: key mapping
|
||||
constexpr Range<uint8_t> keyRange { 0, 127 };
|
||||
constexpr Range<float> velocityRange { 0.0f, 1.0f };
|
||||
constexpr auto velocityRange = normalizedRange;
|
||||
|
||||
// Region logic: MIDI conditions
|
||||
constexpr Range<uint8_t> channelRange { 1, 16 };
|
||||
constexpr Range<uint8_t> midiChannelRange { 0, 15 };
|
||||
constexpr Range<uint16_t> ccNumberRange { 0, config::numCCs };
|
||||
constexpr Range<uint8_t> ccValueRange { 0, 127 };
|
||||
constexpr uint8_t cc { 0 };
|
||||
constexpr auto ccValueRange = normalizedRange;
|
||||
constexpr Range<int> bendRange { -8192, 8192 };
|
||||
constexpr int bend { 0 };
|
||||
constexpr SfzVelocityOverride velocityOverride { SfzVelocityOverride::current };
|
||||
|
|
@ -92,7 +91,7 @@ namespace Default
|
|||
|
||||
// Region logic: Triggers
|
||||
constexpr SfzTrigger trigger { SfzTrigger::attack };
|
||||
constexpr Range<uint8_t> ccTriggerValueRange{ 0, 127 };
|
||||
constexpr Range<float> ccTriggerValueRange = normalizedRange;
|
||||
|
||||
// Performance parameters: amplifier
|
||||
constexpr float globalVolume { -7.35f };
|
||||
|
|
@ -123,8 +122,8 @@ namespace Default
|
|||
constexpr Range<uint8_t> crossfadeKeyOutRange { 127, 127 };
|
||||
constexpr Range<float> crossfadeVelInRange { 0.0f, 0.0f };
|
||||
constexpr Range<float> crossfadeVelOutRange { 1.0f, 1.0f };
|
||||
constexpr Range<uint8_t> crossfadeCCInRange { 0, 0 };
|
||||
constexpr Range<uint8_t> crossfadeCCOutRange { 127, 127 };
|
||||
constexpr Range<float> crossfadeCCInRange { 0.0f, 0.0f };
|
||||
constexpr Range<float> crossfadeCCOutRange { 1.0f, 1.0f };
|
||||
constexpr SfzCrossfadeCurve crossfadeKeyCurve { SfzCrossfadeCurve::power };
|
||||
constexpr SfzCrossfadeCurve crossfadeVelCurve { SfzCrossfadeCurve::power };
|
||||
constexpr SfzCrossfadeCurve crossfadeCCCurve { SfzCrossfadeCurve::power };
|
||||
|
|
|
|||
|
|
@ -155,10 +155,16 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
|
|||
setRangeEndFromOpcode(opcode, bendRange, Default::bendRange);
|
||||
break;
|
||||
case hash("locc&"):
|
||||
setRangeStartFromOpcode(opcode, ccConditions[opcode.parameters.back()], Default::ccValueRange);
|
||||
if (opcode.parameters.back() > config::numCCs)
|
||||
return false;
|
||||
if (auto value = readOpcode(opcode.value, Default::midi7Range))
|
||||
ccConditions[opcode.parameters.back()].setStart(normalizeCC(*value));
|
||||
break;
|
||||
case hash("hicc&"):
|
||||
setRangeEndFromOpcode(opcode, ccConditions[opcode.parameters.back()], Default::ccValueRange);
|
||||
if (opcode.parameters.back() > config::numCCs)
|
||||
return false;
|
||||
if (auto value = readOpcode(opcode.value, Default::midi7Range))
|
||||
ccConditions[opcode.parameters.back()].setEnd(normalizeCC(*value));
|
||||
break;
|
||||
case hash("sw_lokey"):
|
||||
setRangeStartFromOpcode(opcode, keyswitchRange, Default::keyRange);
|
||||
|
|
@ -250,11 +256,17 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
|
|||
break;
|
||||
case hash("on_locc&"): // fallthrough
|
||||
case hash("start_locc&"):
|
||||
setRangeStartFromOpcode(opcode, ccTriggers[opcode.parameters.back()], Default::ccTriggerValueRange);
|
||||
if (opcode.parameters.back() > config::numCCs)
|
||||
return false;
|
||||
if (auto value = readOpcode(opcode.value, Default::midi7Range))
|
||||
ccTriggers[opcode.parameters.back()].setStart(normalizeCC(*value));
|
||||
break;
|
||||
case hash("on_hicc&"): // fallthrough
|
||||
case hash("start_hicc&"):
|
||||
setRangeEndFromOpcode(opcode, ccTriggers[opcode.parameters.back()], Default::ccTriggerValueRange);
|
||||
if (opcode.parameters.back() > config::numCCs)
|
||||
return false;
|
||||
if (auto value = readOpcode(opcode.value, Default::midi7Range))
|
||||
ccTriggers[opcode.parameters.back()].setEnd(normalizeCC(*value));
|
||||
break;
|
||||
|
||||
// Performance parameters: amplifier
|
||||
|
|
@ -366,16 +378,28 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
|
|||
}
|
||||
break;
|
||||
case hash("xfin_locc&"):
|
||||
setRangeStartFromOpcode(opcode, crossfadeCCInRange[opcode.parameters.back()], Default::ccValueRange);
|
||||
if (opcode.parameters.back() > config::numCCs)
|
||||
return false;
|
||||
if (auto value = readOpcode(opcode.value, Default::midi7Range))
|
||||
crossfadeCCInRange[opcode.parameters.back()].setStart(normalizeCC(*value));
|
||||
break;
|
||||
case hash("xfin_hicc&"):
|
||||
setRangeEndFromOpcode(opcode, crossfadeCCInRange[opcode.parameters.back()], Default::ccValueRange);
|
||||
if (opcode.parameters.back() > config::numCCs)
|
||||
return false;
|
||||
if (auto value = readOpcode(opcode.value, Default::midi7Range))
|
||||
crossfadeCCInRange[opcode.parameters.back()].setEnd(normalizeCC(*value));
|
||||
break;
|
||||
case hash("xfout_locc&"):
|
||||
setRangeStartFromOpcode(opcode, crossfadeCCOutRange[opcode.parameters.back()], Default::ccValueRange);
|
||||
if (opcode.parameters.back() > config::numCCs)
|
||||
return false;
|
||||
if (auto value = readOpcode(opcode.value, Default::midi7Range))
|
||||
crossfadeCCOutRange[opcode.parameters.back()].setStart(normalizeCC(*value));
|
||||
break;
|
||||
case hash("xfout_hicc&"):
|
||||
setRangeEndFromOpcode(opcode, crossfadeCCOutRange[opcode.parameters.back()], Default::ccValueRange);
|
||||
if (opcode.parameters.back() > config::numCCs)
|
||||
return false;
|
||||
if (auto value = readOpcode(opcode.value, Default::midi7Range))
|
||||
crossfadeCCOutRange[opcode.parameters.back()].setEnd(normalizeCC(*value));
|
||||
break;
|
||||
case hash("xf_cccurve"):
|
||||
switch (hash(opcode.value)) {
|
||||
|
|
@ -872,6 +896,11 @@ bool sfz::Region::registerNoteOffNormalized(int noteNumber, float velocity, floa
|
|||
}
|
||||
|
||||
bool sfz::Region::registerCC(int ccNumber, uint8_t ccValue) noexcept
|
||||
{
|
||||
return registerCCNormalized(ccNumber, normalizeCC(ccValue));
|
||||
}
|
||||
|
||||
bool sfz::Region::registerCCNormalized(int ccNumber, float ccValue) noexcept
|
||||
{
|
||||
if (ccConditions.getWithDefault(ccNumber).containsWithEnd(ccValue))
|
||||
ccSwitched.set(ccNumber, true);
|
||||
|
|
@ -1063,13 +1092,13 @@ float sfz::Region::getCrossfadeGain() const noexcept
|
|||
|
||||
// Crossfades due to CC states
|
||||
for (const auto& valuePair : crossfadeCCInRange) {
|
||||
const auto ccValue = midiState.getCCValue(valuePair.cc);
|
||||
const auto ccValue = midiState.getCCValueNormalized(valuePair.cc);
|
||||
const auto crossfadeRange = valuePair.value;
|
||||
gain *= crossfadeIn(crossfadeRange, ccValue, crossfadeCCCurve);
|
||||
}
|
||||
|
||||
for (const auto& valuePair : crossfadeCCOutRange) {
|
||||
const auto ccValue = midiState.getCCValue(valuePair.cc);
|
||||
const auto ccValue = midiState.getCCValueNormalized(valuePair.cc);
|
||||
const auto crossfadeRange = valuePair.value;
|
||||
gain *= crossfadeOut(crossfadeRange, ccValue, crossfadeCCCurve);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -132,6 +132,16 @@ struct Region {
|
|||
* @return false
|
||||
*/
|
||||
bool registerCC(int ccNumber, uint8_t ccValue) noexcept;
|
||||
/**
|
||||
* @brief Register a new CC event. The region may be switched on or off using CCs so
|
||||
* this function checks if it indeeds need to activate or not.
|
||||
*
|
||||
* @param ccNumber
|
||||
* @param ccValue
|
||||
* @return true if the region should trigger on this event
|
||||
* @return false
|
||||
*/
|
||||
bool registerCCNormalized(int ccNumber, float ccValue) noexcept;
|
||||
/**
|
||||
* @brief Register a new pitch wheel event.
|
||||
*
|
||||
|
|
@ -295,7 +305,7 @@ struct Region {
|
|||
|
||||
// Region logic: MIDI conditions
|
||||
Range<int> bendRange { Default::bendRange }; // hibend and lobend
|
||||
CCMap<Range<uint8_t>> ccConditions { Default::ccValueRange };
|
||||
CCMap<Range<float>> ccConditions { Default::ccValueRange };
|
||||
Range<uint8_t> keyswitchRange { Default::keyRange }; // sw_hikey and sw_lokey
|
||||
absl::optional<uint8_t> keyswitch {}; // sw_last
|
||||
absl::optional<uint8_t> keyswitchUp {}; // sw_up
|
||||
|
|
@ -314,7 +324,7 @@ struct Region {
|
|||
|
||||
// Region logic: triggers
|
||||
SfzTrigger trigger { Default::trigger }; // trigger
|
||||
CCMap<Range<uint8_t>> ccTriggers { Default::ccTriggerValueRange }; // on_loccN on_hiccN
|
||||
CCMap<Range<float>> ccTriggers { Default::ccTriggerValueRange }; // on_loccN on_hiccN
|
||||
|
||||
// Performance parameters: amplifier
|
||||
float volume { Default::volume }; // volume
|
||||
|
|
@ -339,8 +349,8 @@ struct Region {
|
|||
SfzCrossfadeCurve crossfadeKeyCurve { Default::crossfadeKeyCurve };
|
||||
SfzCrossfadeCurve crossfadeVelCurve { Default::crossfadeVelCurve };
|
||||
SfzCrossfadeCurve crossfadeCCCurve { Default::crossfadeCCCurve };
|
||||
CCMap<Range<uint8_t>> crossfadeCCInRange { Default::crossfadeCCInRange }; // xfin_loccN xfin_hiccN
|
||||
CCMap<Range<uint8_t>> crossfadeCCOutRange { Default::crossfadeCCOutRange }; // xfout_loccN xfout_hiccN
|
||||
CCMap<Range<float>> crossfadeCCInRange { Default::crossfadeCCInRange }; // xfin_loccN xfin_hiccN
|
||||
CCMap<Range<float>> crossfadeCCOutRange { Default::crossfadeCCOutRange }; // xfout_loccN xfout_hiccN
|
||||
float rtDecay { Default::rtDecay }; // rt_decay
|
||||
|
||||
// Filters and EQs
|
||||
|
|
|
|||
|
|
@ -181,7 +181,7 @@ void sfz::Synth::handleControlOpcodes(const std::vector<Opcode>& members)
|
|||
case hash("Set_cc&"): // fallthrough
|
||||
case hash("set_cc&"):
|
||||
if (Default::ccNumberRange.containsWithEnd(member.parameters.back())) {
|
||||
const auto ccValue = readOpcode(member.value, Default::ccValueRange).value_or(0);
|
||||
const auto ccValue = readOpcode(member.value, Default::midi7Range).value_or(0);
|
||||
resources.midiState.ccEvent(0, member.parameters.back(), ccValue);
|
||||
}
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -236,7 +236,7 @@ TEST_CASE("[Files] Pizz basic")
|
|||
REQUIRE(synth.getRegionView(i)->keyRange == sfz::Range<uint8_t>(12, 22));
|
||||
REQUIRE(synth.getRegionView(i)->velocityRange == sfz::Range<float>(97_norm, 127_norm));
|
||||
REQUIRE(synth.getRegionView(i)->pitchKeycenter == 21);
|
||||
REQUIRE(synth.getRegionView(i)->ccConditions.getWithDefault(107) == sfz::Range<uint8_t>(0, 13));
|
||||
REQUIRE(synth.getRegionView(i)->ccConditions.getWithDefault(107) == sfz::Range<float>(0_norm, 13_norm));
|
||||
}
|
||||
REQUIRE(synth.getRegionView(0)->randRange == sfz::Range<float>(0, 0.25));
|
||||
REQUIRE(synth.getRegionView(1)->randRange == sfz::Range<float>(0.25, 0.5));
|
||||
|
|
|
|||
|
|
@ -240,16 +240,16 @@ TEST_CASE("[Region] Parsing opcodes")
|
|||
|
||||
SECTION("locc, hicc")
|
||||
{
|
||||
REQUIRE(region.ccConditions.getWithDefault(0) == sfz::Range<uint8_t>(0, 127));
|
||||
REQUIRE(region.ccConditions[127] == sfz::Range<uint8_t>(0, 127));
|
||||
REQUIRE(region.ccConditions.getWithDefault(0) == sfz::Range<float>(0_norm, 127_norm));
|
||||
REQUIRE(region.ccConditions[127] == sfz::Range<float>(0_norm, 127_norm));
|
||||
region.parseOpcode({ "locc6", "4" });
|
||||
REQUIRE(region.ccConditions[6] == sfz::Range<uint8_t>(4, 127));
|
||||
REQUIRE(region.ccConditions[6] == sfz::Range<float>(4_norm, 127_norm));
|
||||
region.parseOpcode({ "locc12", "-128" });
|
||||
REQUIRE(region.ccConditions[12] == sfz::Range<uint8_t>(0, 127));
|
||||
REQUIRE(region.ccConditions[12] == sfz::Range<float>(0_norm, 127_norm));
|
||||
region.parseOpcode({ "hicc65", "39" });
|
||||
REQUIRE(region.ccConditions[65] == sfz::Range<uint8_t>(0, 39));
|
||||
REQUIRE(region.ccConditions[65] == sfz::Range<float>(0_norm, 39_norm));
|
||||
region.parseOpcode({ "hicc127", "135" });
|
||||
REQUIRE(region.ccConditions[127] == sfz::Range<uint8_t>(0, 127));
|
||||
REQUIRE(region.ccConditions[127] == sfz::Range<float>(0_norm, 127_norm));
|
||||
}
|
||||
|
||||
SECTION("sw_lokey, sw_hikey")
|
||||
|
|
@ -427,10 +427,10 @@ TEST_CASE("[Region] Parsing opcodes")
|
|||
}
|
||||
region.parseOpcode({ "on_locc45", "15" });
|
||||
REQUIRE(region.ccTriggers.contains(45));
|
||||
REQUIRE(region.ccTriggers[45] == sfz::Range<uint8_t>(15, 127));
|
||||
REQUIRE(region.ccTriggers[45] == sfz::Range<float>(15_norm, 127_norm));
|
||||
region.parseOpcode({ "on_hicc4", "47" });
|
||||
REQUIRE(region.ccTriggers.contains(45));
|
||||
REQUIRE(region.ccTriggers[4] == sfz::Range<uint8_t>(0, 47));
|
||||
REQUIRE(region.ccTriggers[4] == sfz::Range<float>(0_norm, 47_norm));
|
||||
}
|
||||
|
||||
SECTION("volume")
|
||||
|
|
@ -650,38 +650,38 @@ TEST_CASE("[Region] Parsing opcodes")
|
|||
{
|
||||
REQUIRE(!region.crossfadeCCInRange.contains(4));
|
||||
region.parseOpcode({ "xfin_locc4", "4" });
|
||||
REQUIRE(region.crossfadeCCInRange[4] == sfz::Range<uint8_t>(4, 4));
|
||||
REQUIRE(region.crossfadeCCInRange[4] == sfz::Range<float>(4_norm, 4_norm));
|
||||
region.parseOpcode({ "xfin_locc4", "128" });
|
||||
REQUIRE(region.crossfadeCCInRange[4] == sfz::Range<uint8_t>(127, 127));
|
||||
REQUIRE(region.crossfadeCCInRange[4] == sfz::Range<float>(127_norm, 127_norm));
|
||||
region.parseOpcode({ "xfin_locc4", "59" });
|
||||
REQUIRE(region.crossfadeCCInRange[4] == sfz::Range<uint8_t>(59, 127));
|
||||
REQUIRE(region.crossfadeCCInRange[4] == sfz::Range<float>(59_norm, 127_norm));
|
||||
region.parseOpcode({ "xfin_hicc4", "59" });
|
||||
REQUIRE(region.crossfadeCCInRange[4] == sfz::Range<uint8_t>(59, 59));
|
||||
REQUIRE(region.crossfadeCCInRange[4] == sfz::Range<float>(59_norm, 59_norm));
|
||||
region.parseOpcode({ "xfin_hicc4", "128" });
|
||||
REQUIRE(region.crossfadeCCInRange[4] == sfz::Range<uint8_t>(59, 127));
|
||||
REQUIRE(region.crossfadeCCInRange[4] == sfz::Range<float>(59_norm, 127_norm));
|
||||
region.parseOpcode({ "xfin_hicc4", "0" });
|
||||
REQUIRE(region.crossfadeCCInRange[4] == sfz::Range<uint8_t>(0, 0));
|
||||
REQUIRE(region.crossfadeCCInRange[4] == sfz::Range<float>(0_norm, 0_norm));
|
||||
region.parseOpcode({ "xfin_hicc4", "-1" });
|
||||
REQUIRE(region.crossfadeCCInRange[4] == sfz::Range<uint8_t>(0, 0));
|
||||
REQUIRE(region.crossfadeCCInRange[4] == sfz::Range<float>(0_norm, 0_norm));
|
||||
}
|
||||
|
||||
SECTION("xfout_locc, xfout_hicc")
|
||||
{
|
||||
REQUIRE(!region.crossfadeCCOutRange.contains(4));
|
||||
region.parseOpcode({ "xfout_locc4", "4" });
|
||||
REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range<uint8_t>(4, 127));
|
||||
REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range<float>(4_norm, 127_norm));
|
||||
region.parseOpcode({ "xfout_locc4", "128" });
|
||||
REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range<uint8_t>(127, 127));
|
||||
REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range<float>(127_norm, 127_norm));
|
||||
region.parseOpcode({ "xfout_locc4", "59" });
|
||||
REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range<uint8_t>(59, 127));
|
||||
REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range<float>(59_norm, 127_norm));
|
||||
region.parseOpcode({ "xfout_hicc4", "59" });
|
||||
REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range<uint8_t>(59, 59));
|
||||
REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range<float>(59_norm, 59_norm));
|
||||
region.parseOpcode({ "xfout_hicc4", "128" });
|
||||
REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range<uint8_t>(59, 127));
|
||||
REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range<float>(59_norm, 127_norm));
|
||||
region.parseOpcode({ "xfout_hicc4", "0" });
|
||||
REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range<uint8_t>(0, 0));
|
||||
REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range<float>(0_norm, 0_norm));
|
||||
region.parseOpcode({ "xfout_hicc4", "-1" });
|
||||
REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range<uint8_t>(0, 0));
|
||||
REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range<float>(0_norm, 0_norm));
|
||||
}
|
||||
|
||||
SECTION("xf_keycurve")
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue