Build a set of "effective" keyswitches anduse it instead of sw_lokey/sw_hikey
This commit is contained in:
parent
b3eb8e47ca
commit
c27cb69a83
10 changed files with 271 additions and 200 deletions
|
|
@ -292,11 +292,8 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode)
|
||||||
if (auto value = readOpcode(opcode.value, Default::normalizedRange))
|
if (auto value = readOpcode(opcode.value, Default::normalizedRange))
|
||||||
ccConditions[opcode.parameters.back()].setEnd(*value);
|
ccConditions[opcode.parameters.back()].setEnd(*value);
|
||||||
break;
|
break;
|
||||||
case hash("sw_lokey"):
|
case hash("sw_lokey"): // fallthrough
|
||||||
setRangeStartFromOpcode(opcode, keyswitchRange, Default::keyRange);
|
|
||||||
break;
|
|
||||||
case hash("sw_hikey"):
|
case hash("sw_hikey"):
|
||||||
setRangeEndFromOpcode(opcode, keyswitchRange, Default::keyRange);
|
|
||||||
break;
|
break;
|
||||||
case hash("sw_last"):
|
case hash("sw_last"):
|
||||||
setValueFromOpcode(opcode, keyswitch, Default::keyRange);
|
setValueFromOpcode(opcode, keyswitch, Default::keyRange);
|
||||||
|
|
@ -1591,16 +1588,11 @@ bool sfz::Region::registerNoteOn(int noteNumber, float velocity, float randValue
|
||||||
{
|
{
|
||||||
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
|
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
|
||||||
|
|
||||||
if (keyswitchRange.containsWithEnd(noteNumber)) {
|
if (keyswitchDown && *keyswitchDown == noteNumber)
|
||||||
if (keyswitch)
|
keySwitched = true;
|
||||||
keySwitched = (*keyswitch == noteNumber);
|
|
||||||
|
|
||||||
if (keyswitchDown && *keyswitchDown == noteNumber)
|
if (keyswitchUp && *keyswitchUp == noteNumber)
|
||||||
keySwitched = true;
|
keySwitched = false;
|
||||||
|
|
||||||
if (keyswitchUp && *keyswitchUp == noteNumber)
|
|
||||||
keySwitched = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const bool keyOk = keyRange.containsWithEnd(noteNumber);
|
const bool keyOk = keyRange.containsWithEnd(noteNumber);
|
||||||
if (keyOk) {
|
if (keyOk) {
|
||||||
|
|
@ -1634,13 +1626,11 @@ bool sfz::Region::registerNoteOff(int noteNumber, float velocity, float randValu
|
||||||
{
|
{
|
||||||
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
|
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
|
||||||
|
|
||||||
if (keyswitchRange.containsWithEnd(noteNumber)) {
|
if (keyswitchDown && *keyswitchDown == noteNumber)
|
||||||
if (keyswitchDown && *keyswitchDown == noteNumber)
|
keySwitched = false;
|
||||||
keySwitched = false;
|
|
||||||
|
|
||||||
if (keyswitchUp && *keyswitchUp == noteNumber)
|
if (keyswitchUp && *keyswitchUp == noteNumber)
|
||||||
keySwitched = true;
|
keySwitched = true;
|
||||||
}
|
|
||||||
|
|
||||||
if (!isSwitchedOn())
|
if (!isSwitchedOn())
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -1860,57 +1850,40 @@ float sfz::Region::velocityCurve(float velocity) const noexcept
|
||||||
return gain;
|
return gain;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t offsetAndClamp(uint8_t key, int offset, sfz::Range<uint8_t> range)
|
|
||||||
{
|
|
||||||
const int offsetKey { key + offset };
|
|
||||||
if (offsetKey > std::numeric_limits<uint8_t>::max())
|
|
||||||
return range.getEnd();
|
|
||||||
if (offsetKey < std::numeric_limits<uint8_t>::min())
|
|
||||||
return range.getStart();
|
|
||||||
|
|
||||||
return range.clamp(static_cast<uint8_t>(offsetKey));
|
|
||||||
}
|
|
||||||
|
|
||||||
void sfz::Region::offsetAllKeys(int offset) noexcept
|
void sfz::Region::offsetAllKeys(int offset) noexcept
|
||||||
{
|
{
|
||||||
// Offset key range
|
// Offset key range
|
||||||
if (keyRange != Default::keyRange) {
|
if (keyRange != Default::keyRange) {
|
||||||
const auto start = keyRange.getStart();
|
const auto start = keyRange.getStart();
|
||||||
const auto end = keyRange.getEnd();
|
const auto end = keyRange.getEnd();
|
||||||
keyRange.setStart(offsetAndClamp(start, offset, Default::keyRange));
|
keyRange.setStart(offsetAndClampKey(start, offset, Default::keyRange));
|
||||||
keyRange.setEnd(offsetAndClamp(end, offset, Default::keyRange));
|
keyRange.setEnd(offsetAndClampKey(end, offset, Default::keyRange));
|
||||||
}
|
}
|
||||||
pitchKeycenter = offsetAndClamp(pitchKeycenter, offset, Default::keyRange);
|
pitchKeycenter = offsetAndClampKey(pitchKeycenter, offset, Default::keyRange);
|
||||||
|
|
||||||
// Offset key switches
|
// Offset key switches
|
||||||
if (keyswitchRange != Default::keyRange) {
|
|
||||||
const auto start = keyswitchRange.getStart();
|
|
||||||
const auto end = keyswitchRange.getEnd();
|
|
||||||
keyswitchRange.setStart(offsetAndClamp(start, offset, Default::keyRange));
|
|
||||||
keyswitchRange.setEnd(offsetAndClamp(end, offset, Default::keyRange));
|
|
||||||
}
|
|
||||||
if (keyswitchUp)
|
if (keyswitchUp)
|
||||||
keyswitchUp = offsetAndClamp(*keyswitchUp, offset, Default::keyRange);
|
keyswitchUp = offsetAndClampKey(*keyswitchUp, offset, Default::keyRange);
|
||||||
if (keyswitch)
|
if (keyswitch)
|
||||||
keyswitch = offsetAndClamp(*keyswitch, offset, Default::keyRange);
|
keyswitch = offsetAndClampKey(*keyswitch, offset, Default::keyRange);
|
||||||
if (keyswitchDown)
|
if (keyswitchDown)
|
||||||
keyswitchDown = offsetAndClamp(*keyswitchDown, offset, Default::keyRange);
|
keyswitchDown = offsetAndClampKey(*keyswitchDown, offset, Default::keyRange);
|
||||||
if (previousNote)
|
if (previousNote)
|
||||||
previousNote = offsetAndClamp(*previousNote, offset, Default::keyRange);
|
previousNote = offsetAndClampKey(*previousNote, offset, Default::keyRange);
|
||||||
|
|
||||||
// Offset crossfade ranges
|
// Offset crossfade ranges
|
||||||
if (crossfadeKeyInRange != Default::crossfadeKeyInRange) {
|
if (crossfadeKeyInRange != Default::crossfadeKeyInRange) {
|
||||||
const auto start = crossfadeKeyInRange.getStart();
|
const auto start = crossfadeKeyInRange.getStart();
|
||||||
const auto end = crossfadeKeyInRange.getEnd();
|
const auto end = crossfadeKeyInRange.getEnd();
|
||||||
crossfadeKeyInRange.setStart(offsetAndClamp(start, offset, Default::keyRange));
|
crossfadeKeyInRange.setStart(offsetAndClampKey(start, offset, Default::keyRange));
|
||||||
crossfadeKeyInRange.setEnd(offsetAndClamp(end, offset, Default::keyRange));
|
crossfadeKeyInRange.setEnd(offsetAndClampKey(end, offset, Default::keyRange));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (crossfadeKeyOutRange != Default::crossfadeKeyOutRange) {
|
if (crossfadeKeyOutRange != Default::crossfadeKeyOutRange) {
|
||||||
const auto start = crossfadeKeyOutRange.getStart();
|
const auto start = crossfadeKeyOutRange.getStart();
|
||||||
const auto end = crossfadeKeyOutRange.getEnd();
|
const auto end = crossfadeKeyOutRange.getEnd();
|
||||||
crossfadeKeyOutRange.setStart(offsetAndClamp(start, offset, Default::keyRange));
|
crossfadeKeyOutRange.setStart(offsetAndClampKey(start, offset, Default::keyRange));
|
||||||
crossfadeKeyOutRange.setEnd(offsetAndClamp(end, offset, Default::keyRange));
|
crossfadeKeyOutRange.setEnd(offsetAndClampKey(end, offset, Default::keyRange));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -292,8 +292,6 @@ struct Region {
|
||||||
uint32_t loopStart(Oversampling factor = Oversampling::x1) const noexcept;
|
uint32_t loopStart(Oversampling factor = Oversampling::x1) const noexcept;
|
||||||
uint32_t loopEnd(Oversampling factor = Oversampling::x1) const noexcept;
|
uint32_t loopEnd(Oversampling factor = Oversampling::x1) const noexcept;
|
||||||
|
|
||||||
bool hasKeyswitches() const noexcept { return keyswitchDown || keyswitchUp || keyswitch || previousNote; }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the gain this region contributes into the input of the Nth
|
* @brief Get the gain this region contributes into the input of the Nth
|
||||||
* effect bus
|
* effect bus
|
||||||
|
|
@ -349,7 +347,6 @@ struct Region {
|
||||||
// Region logic: MIDI conditions
|
// Region logic: MIDI conditions
|
||||||
Range<float> bendRange { Default::bendValueRange }; // hibend and lobend
|
Range<float> bendRange { Default::bendValueRange }; // hibend and lobend
|
||||||
CCMap<Range<float>> 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> keyswitch {}; // sw_last
|
||||||
absl::optional<std::string> keyswitchLabel {};
|
absl::optional<std::string> keyswitchLabel {};
|
||||||
absl::optional<uint8_t> keyswitchUp {}; // sw_up
|
absl::optional<uint8_t> keyswitchUp {}; // sw_up
|
||||||
|
|
@ -455,7 +452,7 @@ struct Region {
|
||||||
|
|
||||||
// Started notes
|
// Started notes
|
||||||
std::vector<std::pair<int, float>> delayedReleases;
|
std::vector<std::pair<int, float>> delayedReleases;
|
||||||
private:
|
|
||||||
const MidiState& midiState;
|
const MidiState& midiState;
|
||||||
bool keySwitched { true };
|
bool keySwitched { true };
|
||||||
bool previousKeySwitched { true };
|
bool previousKeySwitched { true };
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,8 @@ struct Resources
|
||||||
absl::optional<StretchTuning> stretch;
|
absl::optional<StretchTuning> stretch;
|
||||||
ModMatrix modMatrix;
|
ModMatrix modMatrix;
|
||||||
|
|
||||||
|
std::vector<uint8_t> keyswitches;
|
||||||
|
|
||||||
void setSampleRate(float samplerate)
|
void setSampleRate(float samplerate)
|
||||||
{
|
{
|
||||||
midiState.setSampleRate(samplerate);
|
midiState.setSampleRate(samplerate);
|
||||||
|
|
@ -54,6 +56,7 @@ struct Resources
|
||||||
logger.clear();
|
logger.clear();
|
||||||
midiState.reset();
|
midiState.reset();
|
||||||
modMatrix.clear();
|
modMatrix.clear();
|
||||||
|
keyswitches.clear();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -177,6 +177,25 @@ constexpr float normalizeBend(float bendValue)
|
||||||
return clamp(bendValue, -8191.0f, 8191.0f) / 8191.0f;
|
return clamp(bendValue, -8191.0f, 8191.0f) / 8191.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Offset a key and clamp it to a reasonable range
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* @param offset
|
||||||
|
* @param range
|
||||||
|
* @return uint8_t
|
||||||
|
*/
|
||||||
|
inline CXX14_CONSTEXPR uint8_t offsetAndClampKey(uint8_t key, int offset, sfz::Range<uint8_t> range)
|
||||||
|
{
|
||||||
|
const int offsetKey { key + offset };
|
||||||
|
if (offsetKey > std::numeric_limits<uint8_t>::max())
|
||||||
|
return range.getEnd();
|
||||||
|
if (offsetKey < std::numeric_limits<uint8_t>::min())
|
||||||
|
return range.getStart();
|
||||||
|
|
||||||
|
return range.clamp(static_cast<uint8_t>(offsetKey));
|
||||||
|
}
|
||||||
|
|
||||||
namespace literals {
|
namespace literals {
|
||||||
inline float operator""_norm(unsigned long long int value)
|
inline float operator""_norm(unsigned long long int value)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -175,6 +175,12 @@ void sfz::Synth::buildRegion(const std::vector<Opcode>& regionOpcodes)
|
||||||
if (octaveOffset != 0 || noteOffset != 0)
|
if (octaveOffset != 0 || noteOffset != 0)
|
||||||
lastRegion->offsetAllKeys(octaveOffset * 12 + noteOffset);
|
lastRegion->offsetAllKeys(octaveOffset * 12 + noteOffset);
|
||||||
|
|
||||||
|
if (lastRegion->keyswitch) {
|
||||||
|
auto it = absl::c_find(resources.keyswitches, *lastRegion->keyswitch);
|
||||||
|
if (it == resources.keyswitches.end())
|
||||||
|
resources.keyswitches.push_back(*lastRegion->keyswitch);
|
||||||
|
}
|
||||||
|
|
||||||
// There was a combination of group= and polyphony= on a region, so set the group polyphony
|
// There was a combination of group= and polyphony= on a region, so set the group polyphony
|
||||||
if (lastRegion->group != Default::group && lastRegion->polyphony != config::maxVoices)
|
if (lastRegion->group != Default::group && lastRegion->polyphony != config::maxVoices)
|
||||||
setGroupPolyphony(lastRegion->group, lastRegion->polyphony);
|
setGroupPolyphony(lastRegion->group, lastRegion->polyphony);
|
||||||
|
|
@ -529,6 +535,7 @@ void sfz::Synth::finalizeSfzLoad()
|
||||||
bool haveFilterEG { false };
|
bool haveFilterEG { false };
|
||||||
|
|
||||||
FlexEGs::clearUnusedCurves();
|
FlexEGs::clearUnusedCurves();
|
||||||
|
absl::c_sort(resources.keyswitches);
|
||||||
|
|
||||||
while (currentRegionIndex < currentRegionCount) {
|
while (currentRegionIndex < currentRegionCount) {
|
||||||
auto region = regions[currentRegionIndex].get();
|
auto region = regions[currentRegionIndex].get();
|
||||||
|
|
@ -595,8 +602,13 @@ void sfz::Synth::finalizeSfzLoad()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (region->keyswitchLabel && region->keyswitch)
|
if (region->keyswitch) {
|
||||||
insertPairUniquely(keyswitchLabels, *region->keyswitch, *region->keyswitchLabel);
|
if (defaultSwitch)
|
||||||
|
region->keySwitched = (*defaultSwitch == *region->keyswitch);
|
||||||
|
|
||||||
|
if (region->keyswitchLabel)
|
||||||
|
insertPairUniquely(keyswitchLabels, *region->keyswitch, *region->keyswitchLabel);
|
||||||
|
}
|
||||||
|
|
||||||
// Some regions had group number but no "group-level" opcodes handled the polyphony
|
// Some regions had group number but no "group-level" opcodes handled the polyphony
|
||||||
while (polyphonyGroups.size() <= region->group) {
|
while (polyphonyGroups.size() <= region->group) {
|
||||||
|
|
@ -605,7 +617,15 @@ void sfz::Synth::finalizeSfzLoad()
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto note = 0; note < 128; note++) {
|
for (auto note = 0; note < 128; note++) {
|
||||||
if (region->keyRange.containsWithEnd(note) || (region->hasKeyswitches() && region->keyswitchRange.containsWithEnd(note)))
|
bool noteIsKeyswitch = (absl::c_binary_search(resources.keyswitches, note));
|
||||||
|
|
||||||
|
if (
|
||||||
|
region->keyRange.containsWithEnd(note)
|
||||||
|
|| (region->keyswitch && noteIsKeyswitch)
|
||||||
|
|| (region->keyswitchDown && *region->keyswitchDown == note)
|
||||||
|
|| (region->keyswitchUp && *region->keyswitchUp == note)
|
||||||
|
|| (region->previousNote && *region->previousNote == note)
|
||||||
|
)
|
||||||
noteActivationLists[note].push_back(region);
|
noteActivationLists[note].push_back(region);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -621,10 +641,6 @@ void sfz::Synth::finalizeSfzLoad()
|
||||||
region->registerCC(cc, resources.midiState.getCCValue(cc));
|
region->registerCC(cc, resources.midiState.getCCValue(cc));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (defaultSwitch) {
|
|
||||||
region->registerNoteOn(*defaultSwitch, 1.0f, 1.0f);
|
|
||||||
region->registerNoteOff(*defaultSwitch, 0.0f, 1.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the default frequencies on equalizers if needed
|
// Set the default frequencies on equalizers if needed
|
||||||
if (region->equalizers.size() > 0
|
if (region->equalizers.size() > 0
|
||||||
|
|
@ -1126,7 +1142,12 @@ void sfz::Synth::noteOnDispatch(int delay, int noteNumber, float velocity) noexc
|
||||||
SisterVoiceRingBuilder ring;
|
SisterVoiceRingBuilder ring;
|
||||||
const TriggerEvent triggerEvent { TriggerEventType::NoteOn, noteNumber, velocity };
|
const TriggerEvent triggerEvent { TriggerEventType::NoteOn, noteNumber, velocity };
|
||||||
|
|
||||||
|
bool noteIsKeyswitch = absl::c_binary_search(resources.keyswitches, noteNumber);
|
||||||
|
|
||||||
for (auto& region : noteActivationLists[noteNumber]) {
|
for (auto& region : noteActivationLists[noteNumber]) {
|
||||||
|
if (noteIsKeyswitch && region->keyswitch)
|
||||||
|
region->keySwitched = (*region->keyswitch == noteNumber);
|
||||||
|
|
||||||
if (region->registerNoteOn(noteNumber, velocity, randValue)) {
|
if (region->registerNoteOn(noteNumber, velocity, randValue)) {
|
||||||
for (auto& voice : voices) {
|
for (auto& voice : voices) {
|
||||||
if (voice->checkOffGroup(region, delay, noteNumber)) {
|
if (voice->checkOffGroup(region, delay, noteNumber)) {
|
||||||
|
|
|
||||||
|
|
@ -356,46 +356,6 @@ TEST_CASE("[Files] Channels (channels_multi.sfz)")
|
||||||
REQUIRE(region->oscillatorEnabled == Region::OscillatorEnabled::Auto);
|
REQUIRE(region->oscillatorEnabled == Region::OscillatorEnabled::Auto);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("[Files] sw_default")
|
|
||||||
{
|
|
||||||
Synth synth;
|
|
||||||
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/sw_default.sfz");
|
|
||||||
REQUIRE( synth.getNumRegions() == 4 );
|
|
||||||
REQUIRE( !synth.getRegionView(0)->isSwitchedOn() );
|
|
||||||
REQUIRE( synth.getRegionView(1)->isSwitchedOn() );
|
|
||||||
REQUIRE( !synth.getRegionView(2)->isSwitchedOn() );
|
|
||||||
REQUIRE( synth.getRegionView(3)->isSwitchedOn() );
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("[Files] sw_default and playing with switches")
|
|
||||||
{
|
|
||||||
Synth synth;
|
|
||||||
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/sw_default.sfz");
|
|
||||||
REQUIRE( synth.getNumRegions() == 4 );
|
|
||||||
REQUIRE( !synth.getRegionView(0)->isSwitchedOn() );
|
|
||||||
REQUIRE( synth.getRegionView(1)->isSwitchedOn() );
|
|
||||||
REQUIRE( !synth.getRegionView(2)->isSwitchedOn() );
|
|
||||||
REQUIRE( synth.getRegionView(3)->isSwitchedOn() );
|
|
||||||
synth.noteOn(0, 41, 64);
|
|
||||||
synth.noteOff(0, 41, 0);
|
|
||||||
REQUIRE( synth.getRegionView(0)->isSwitchedOn() );
|
|
||||||
REQUIRE( !synth.getRegionView(1)->isSwitchedOn() );
|
|
||||||
REQUIRE( synth.getRegionView(2)->isSwitchedOn() );
|
|
||||||
REQUIRE( !synth.getRegionView(3)->isSwitchedOn() );
|
|
||||||
synth.noteOn(0, 42, 64);
|
|
||||||
synth.noteOff(0, 42, 0);
|
|
||||||
REQUIRE( !synth.getRegionView(0)->isSwitchedOn() );
|
|
||||||
REQUIRE( !synth.getRegionView(1)->isSwitchedOn() );
|
|
||||||
REQUIRE( !synth.getRegionView(2)->isSwitchedOn() );
|
|
||||||
REQUIRE( !synth.getRegionView(3)->isSwitchedOn() );
|
|
||||||
synth.noteOn(0, 40, 64);
|
|
||||||
synth.noteOff(0, 40, 64);
|
|
||||||
REQUIRE( !synth.getRegionView(0)->isSwitchedOn() );
|
|
||||||
REQUIRE( synth.getRegionView(1)->isSwitchedOn() );
|
|
||||||
REQUIRE( !synth.getRegionView(2)->isSwitchedOn() );
|
|
||||||
REQUIRE( synth.getRegionView(3)->isSwitchedOn() );
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("[Files] wrong (overlapping) replacement for defines")
|
TEST_CASE("[Files] wrong (overlapping) replacement for defines")
|
||||||
{
|
{
|
||||||
Synth synth;
|
Synth synth;
|
||||||
|
|
@ -491,7 +451,6 @@ TEST_CASE("[Files] Note and octave offsets")
|
||||||
|
|
||||||
REQUIRE(synth.getRegionView(0)->keyRange == Range<uint8_t>(64, 64));
|
REQUIRE(synth.getRegionView(0)->keyRange == Range<uint8_t>(64, 64));
|
||||||
REQUIRE( synth.getRegionView(0)->pitchKeycenter == 64 );
|
REQUIRE( synth.getRegionView(0)->pitchKeycenter == 64 );
|
||||||
REQUIRE(synth.getRegionView(0)->keyswitchRange == Default::keyRange);
|
|
||||||
REQUIRE(synth.getRegionView(0)->crossfadeKeyInRange == Default::crossfadeKeyInRange);
|
REQUIRE(synth.getRegionView(0)->crossfadeKeyInRange == Default::crossfadeKeyInRange);
|
||||||
REQUIRE(synth.getRegionView(0)->crossfadeKeyOutRange == Default::crossfadeKeyOutRange);
|
REQUIRE(synth.getRegionView(0)->crossfadeKeyOutRange == Default::crossfadeKeyOutRange);
|
||||||
|
|
||||||
|
|
@ -504,7 +463,6 @@ TEST_CASE("[Files] Note and octave offsets")
|
||||||
REQUIRE(synth.getRegionView(2)->crossfadeKeyOutRange == Range<uint8_t>(45, 49));
|
REQUIRE(synth.getRegionView(2)->crossfadeKeyOutRange == Range<uint8_t>(45, 49));
|
||||||
|
|
||||||
REQUIRE(synth.getRegionView(3)->keyRange == Range<uint8_t>(62, 62));
|
REQUIRE(synth.getRegionView(3)->keyRange == Range<uint8_t>(62, 62));
|
||||||
REQUIRE(synth.getRegionView(3)->keyswitchRange == Range<uint8_t>(23, 27));
|
|
||||||
REQUIRE( synth.getRegionView(3)->keyswitch );
|
REQUIRE( synth.getRegionView(3)->keyswitch );
|
||||||
REQUIRE( *synth.getRegionView(3)->keyswitch == 24 );
|
REQUIRE( *synth.getRegionView(3)->keyswitch == 24 );
|
||||||
REQUIRE( synth.getRegionView(3)->keyswitchUp );
|
REQUIRE( synth.getRegionView(3)->keyswitchUp );
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||||
|
|
||||||
#include "sfizz/Region.h"
|
#include "sfizz/Region.h"
|
||||||
|
#include "sfizz/Synth.h"
|
||||||
#include "sfizz/SfzHelpers.h"
|
#include "sfizz/SfzHelpers.h"
|
||||||
#include "catch2/catch.hpp"
|
#include "catch2/catch.hpp"
|
||||||
using namespace Catch::literals;
|
using namespace Catch::literals;
|
||||||
|
|
@ -113,83 +114,6 @@ TEST_CASE("Region activation", "Region tests")
|
||||||
REQUIRE(!region.isSwitchedOn());
|
REQUIRE(!region.isSwitchedOn());
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add keyswitches
|
|
||||||
SECTION("Keyswitches: sw_last")
|
|
||||||
{
|
|
||||||
region.parseOpcode({ "sw_last", "40" });
|
|
||||||
REQUIRE(!region.isSwitchedOn());
|
|
||||||
region.registerNoteOn(40, 64_norm, 0.5f);
|
|
||||||
REQUIRE(region.isSwitchedOn());
|
|
||||||
region.registerNoteOff(40, 64_norm, 0.5f);
|
|
||||||
REQUIRE(region.isSwitchedOn());
|
|
||||||
region.registerNoteOn(41, 64_norm, 0.5f);
|
|
||||||
REQUIRE(!region.isSwitchedOn());
|
|
||||||
region.registerNoteOff(41, 0_norm, 0.5f);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("Keyswitches: sw_last with non-default keyswitch range")
|
|
||||||
{
|
|
||||||
region.parseOpcode({ "sw_lokey", "30" });
|
|
||||||
region.parseOpcode({ "sw_hikey", "50" });
|
|
||||||
region.parseOpcode({ "sw_last", "40" });
|
|
||||||
REQUIRE(!region.isSwitchedOn());
|
|
||||||
region.registerNoteOn(60, 64_norm, 0.5f);
|
|
||||||
REQUIRE(!region.isSwitchedOn());
|
|
||||||
region.registerNoteOff(60, 0_norm, 0.5f);
|
|
||||||
REQUIRE(!region.isSwitchedOn());
|
|
||||||
region.registerNoteOn(40, 64_norm, 0.5f);
|
|
||||||
REQUIRE(region.isSwitchedOn());
|
|
||||||
region.registerNoteOff(40, 0_norm, 0.5f);
|
|
||||||
REQUIRE(region.isSwitchedOn());
|
|
||||||
region.registerNoteOn(60, 64_norm, 0.5f);
|
|
||||||
REQUIRE(region.isSwitchedOn());
|
|
||||||
region.registerNoteOff(60, 0_norm, 0.5f);
|
|
||||||
region.registerNoteOn(41, 64_norm, 0.5f);
|
|
||||||
REQUIRE(!region.isSwitchedOn());
|
|
||||||
region.registerNoteOff(41, 0_norm, 0.5f);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("Keyswitches: sw_down with non-default keyswitch range")
|
|
||||||
{
|
|
||||||
region.parseOpcode({ "sw_lokey", "30" });
|
|
||||||
region.parseOpcode({ "sw_hikey", "50" });
|
|
||||||
region.parseOpcode({ "sw_down", "40" });
|
|
||||||
REQUIRE(!region.isSwitchedOn());
|
|
||||||
region.registerNoteOn(60, 64_norm, 0.5f);
|
|
||||||
REQUIRE(!region.isSwitchedOn());
|
|
||||||
region.registerNoteOff(60, 0_norm, 0.5f);
|
|
||||||
REQUIRE(!region.isSwitchedOn());
|
|
||||||
region.registerNoteOn(40, 64_norm, 0.5f);
|
|
||||||
REQUIRE(region.isSwitchedOn());
|
|
||||||
region.registerNoteOff(40, 0_norm, 0.5f);
|
|
||||||
REQUIRE(!region.isSwitchedOn());
|
|
||||||
region.registerNoteOn(60, 64_norm, 0.5f);
|
|
||||||
REQUIRE(!region.isSwitchedOn());
|
|
||||||
region.registerNoteOff(60, 0_norm, 0.5f);
|
|
||||||
region.registerNoteOn(41, 64_norm, 0.5f);
|
|
||||||
REQUIRE(!region.isSwitchedOn());
|
|
||||||
region.registerNoteOff(41, 0_norm, 0.5f);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("Keyswitches: sw_up with non-default keyswitch range")
|
|
||||||
{
|
|
||||||
region.parseOpcode({ "sw_lokey", "30" });
|
|
||||||
region.parseOpcode({ "sw_hikey", "50" });
|
|
||||||
region.parseOpcode({ "sw_up", "40" });
|
|
||||||
REQUIRE(region.isSwitchedOn());
|
|
||||||
region.registerNoteOn(40, 64_norm, 0.5f);
|
|
||||||
REQUIRE(!region.isSwitchedOn());
|
|
||||||
region.registerNoteOff(40, 0_norm, 0.5f);
|
|
||||||
REQUIRE(region.isSwitchedOn());
|
|
||||||
region.registerNoteOn(41, 64_norm, 0.5f);
|
|
||||||
REQUIRE(region.isSwitchedOn());
|
|
||||||
region.registerNoteOn(40, 64_norm, 0.5f);
|
|
||||||
REQUIRE(!region.isSwitchedOn());
|
|
||||||
region.registerNoteOff(40, 0_norm, 0.5f);
|
|
||||||
region.registerNoteOff(41, 0_norm, 0.5f);
|
|
||||||
REQUIRE(region.isSwitchedOn());
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("Keyswitches: sw_previous")
|
SECTION("Keyswitches: sw_previous")
|
||||||
{
|
{
|
||||||
region.parseOpcode({ "sw_previous", "40" });
|
region.parseOpcode({ "sw_previous", "40" });
|
||||||
|
|
@ -273,3 +197,202 @@ TEST_CASE("Region activation", "Region tests")
|
||||||
REQUIRE(!region.isSwitchedOn());
|
REQUIRE(!region.isSwitchedOn());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("[Keyswitches] Normal keyswitch range")
|
||||||
|
{
|
||||||
|
sfz::Synth synth;
|
||||||
|
synth.loadSfzString(fs::current_path() / "tests/TestFiles/keyswitches.sfz", R"(
|
||||||
|
<global> sw_lokey=40 sw_hikey=42 sw_default=40
|
||||||
|
<region> sw_last=40 key=60 sample=*sine
|
||||||
|
<region> sw_last=41 key=62 sample=*saw
|
||||||
|
)");
|
||||||
|
synth.noteOn(0, 60, 64);
|
||||||
|
REQUIRE(synth.getNumActiveVoices(true) == 1);
|
||||||
|
synth.noteOn(0, 62, 64);
|
||||||
|
REQUIRE(synth.getNumActiveVoices(true) == 1);
|
||||||
|
synth.noteOn(0, 41, 64);
|
||||||
|
synth.noteOn(0, 60, 64);
|
||||||
|
REQUIRE(synth.getNumActiveVoices(true) == 1);
|
||||||
|
synth.noteOn(0, 62, 64);
|
||||||
|
REQUIRE(synth.getNumActiveVoices(true) == 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("[Keyswitches] No keyswitch range")
|
||||||
|
{
|
||||||
|
sfz::Synth synth;
|
||||||
|
synth.loadSfzString(fs::current_path() / "tests/TestFiles/keyswitches.sfz", R"(
|
||||||
|
<region> sw_last=40 key=60 sample=*sine
|
||||||
|
<region> sw_last=41 key=62 sample=*saw
|
||||||
|
)");
|
||||||
|
synth.noteOn(0, 60, 64);
|
||||||
|
REQUIRE(synth.getNumActiveVoices(true) == 0);
|
||||||
|
synth.noteOn(0, 62, 64);
|
||||||
|
REQUIRE(synth.getNumActiveVoices(true) == 0);
|
||||||
|
synth.noteOn(0, 40, 64);
|
||||||
|
synth.noteOn(0, 60, 64);
|
||||||
|
REQUIRE(synth.getNumActiveVoices(true) == 1);
|
||||||
|
synth.noteOn(0, 62, 64);
|
||||||
|
REQUIRE(synth.getNumActiveVoices(true) == 1);
|
||||||
|
synth.noteOn(0, 41, 64);
|
||||||
|
synth.noteOn(0, 60, 64);
|
||||||
|
REQUIRE(synth.getNumActiveVoices(true) == 1);
|
||||||
|
synth.noteOn(0, 62, 64);
|
||||||
|
REQUIRE(synth.getNumActiveVoices(true) == 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("[Keyswitches] Out of keyswitch range")
|
||||||
|
{
|
||||||
|
sfz::Synth synth;
|
||||||
|
synth.loadSfzString(fs::current_path() / "tests/TestFiles/keyswitches.sfz", R"(
|
||||||
|
<global> sw_lokey=40 sw_hikey=42 sw_default=40
|
||||||
|
<region> sw_last=40 key=60 sample=*sine
|
||||||
|
<region> sw_last=43 key=62 sample=*saw
|
||||||
|
)");
|
||||||
|
synth.noteOn(0, 60, 64);
|
||||||
|
REQUIRE(synth.getNumActiveVoices(true) == 1);
|
||||||
|
synth.noteOn(0, 62, 64);
|
||||||
|
REQUIRE(synth.getNumActiveVoices(true) == 1);
|
||||||
|
synth.noteOn(0, 43, 64);
|
||||||
|
synth.noteOn(0, 60, 64);
|
||||||
|
REQUIRE(synth.getNumActiveVoices(true) == 1);
|
||||||
|
synth.noteOn(0, 62, 64);
|
||||||
|
REQUIRE(synth.getNumActiveVoices(true) == 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("[Keyswitches] Overlapping key and keyswitch range")
|
||||||
|
{
|
||||||
|
sfz::Synth synth;
|
||||||
|
synth.loadSfzString(fs::current_path() / "tests/TestFiles/keyswitches.sfz", R"(
|
||||||
|
<global> sw_lokey=1 sw_hikey=127 sw_default=40
|
||||||
|
<region> sw_last=40 key=60 sample=*sine
|
||||||
|
<region> sw_last=41 key=62 sample=*saw
|
||||||
|
)");
|
||||||
|
synth.noteOn(0, 60, 64);
|
||||||
|
REQUIRE(synth.getNumActiveVoices(true) == 1);
|
||||||
|
synth.noteOn(0, 62, 64);
|
||||||
|
REQUIRE(synth.getNumActiveVoices(true) == 1);
|
||||||
|
synth.noteOn(0, 41, 64);
|
||||||
|
synth.noteOn(0, 60, 64);
|
||||||
|
REQUIRE(synth.getNumActiveVoices(true) == 1);
|
||||||
|
synth.noteOn(0, 62, 64);
|
||||||
|
REQUIRE(synth.getNumActiveVoices(true) == 2);
|
||||||
|
synth.noteOn(0, 43, 64);
|
||||||
|
synth.noteOn(0, 60, 64);
|
||||||
|
REQUIRE(synth.getNumActiveVoices(true) == 2);
|
||||||
|
synth.noteOn(0, 62, 64);
|
||||||
|
REQUIRE(synth.getNumActiveVoices(true) == 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("[Keyswitches] sw_down, in range")
|
||||||
|
{
|
||||||
|
sfz::Synth synth;
|
||||||
|
synth.loadSfzString(fs::current_path() / "tests/TestFiles/keyswitches.sfz", R"(
|
||||||
|
<global> sw_lokey=1 sw_hikey=127 sw_default=40
|
||||||
|
<region> sw_down=40 key=60 sample=*sine
|
||||||
|
)");
|
||||||
|
synth.noteOn(0, 60, 64);
|
||||||
|
REQUIRE(synth.getNumActiveVoices(true) == 0);
|
||||||
|
synth.noteOn(0, 40, 64);
|
||||||
|
synth.noteOn(0, 60, 64);
|
||||||
|
REQUIRE(synth.getNumActiveVoices(true) == 1);
|
||||||
|
synth.noteOff(0, 40, 64);
|
||||||
|
synth.noteOn(0, 60, 64);
|
||||||
|
REQUIRE(synth.getNumActiveVoices(true) == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("[Keyswitches] sw_down, out of range")
|
||||||
|
{
|
||||||
|
sfz::Synth synth;
|
||||||
|
synth.loadSfzString(fs::current_path() / "tests/TestFiles/keyswitches.sfz", R"(
|
||||||
|
<global> sw_lokey=1 sw_hikey=10 sw_default=40
|
||||||
|
<region> sw_down=40 key=60 sample=*sine
|
||||||
|
)");
|
||||||
|
synth.noteOn(0, 60, 64);
|
||||||
|
REQUIRE(synth.getNumActiveVoices(true) == 0);
|
||||||
|
synth.noteOn(0, 40, 64);
|
||||||
|
synth.noteOn(0, 60, 64);
|
||||||
|
REQUIRE(synth.getNumActiveVoices(true) == 1);
|
||||||
|
synth.noteOff(0, 40, 64);
|
||||||
|
synth.noteOn(0, 60, 64);
|
||||||
|
REQUIRE(synth.getNumActiveVoices(true) == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("[Keyswitches] sw_up, in range")
|
||||||
|
{
|
||||||
|
sfz::Synth synth;
|
||||||
|
synth.loadSfzString(fs::current_path() / "tests/TestFiles/keyswitches.sfz", R"(
|
||||||
|
<global> sw_lokey=1 sw_hikey=127 sw_default=40
|
||||||
|
<region> sw_up=40 key=60 sample=*sine
|
||||||
|
)");
|
||||||
|
synth.noteOn(0, 60, 64);
|
||||||
|
REQUIRE(synth.getNumActiveVoices(true) == 1);
|
||||||
|
synth.noteOn(0, 40, 64);
|
||||||
|
synth.noteOn(0, 60, 64);
|
||||||
|
REQUIRE(synth.getNumActiveVoices(true) == 1);
|
||||||
|
synth.noteOff(0, 40, 64);
|
||||||
|
synth.noteOn(0, 60, 64);
|
||||||
|
REQUIRE(synth.getNumActiveVoices(true) == 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("[Keyswitches] sw_up, out of range")
|
||||||
|
{
|
||||||
|
sfz::Synth synth;
|
||||||
|
synth.loadSfzString(fs::current_path() / "tests/TestFiles/keyswitches.sfz", R"(
|
||||||
|
<global> sw_lokey=1 sw_hikey=127 sw_default=40
|
||||||
|
<region> sw_up=40 key=60 sample=*sine
|
||||||
|
)");
|
||||||
|
synth.noteOn(0, 60, 64);
|
||||||
|
REQUIRE(synth.getNumActiveVoices(true) == 1);
|
||||||
|
synth.noteOn(0, 40, 64);
|
||||||
|
synth.noteOn(0, 60, 64);
|
||||||
|
REQUIRE(synth.getNumActiveVoices(true) == 1);
|
||||||
|
synth.noteOff(0, 40, 64);
|
||||||
|
synth.noteOn(0, 60, 64);
|
||||||
|
REQUIRE(synth.getNumActiveVoices(true) == 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("[Keyswitches] sw_default")
|
||||||
|
{
|
||||||
|
sfz::Synth synth;
|
||||||
|
synth.loadSfzString(fs::current_path() / "tests/TestFiles/sw_default.sfz", R"(
|
||||||
|
<global> sw_lokey=30 sw_hikey=50 sw_default=40
|
||||||
|
<region> sw_last=41 key=51 sample=*sine
|
||||||
|
<region> sw_last=40 key=52 sample=*sine
|
||||||
|
<region> sw_last=41 key=53 sample=*sine
|
||||||
|
<region> sw_last=40 key=54 sample=*sine
|
||||||
|
)");
|
||||||
|
REQUIRE( synth.getNumRegions() == 4 );
|
||||||
|
REQUIRE( !synth.getRegionView(0)->isSwitchedOn() );
|
||||||
|
REQUIRE( synth.getRegionView(1)->isSwitchedOn() );
|
||||||
|
REQUIRE( !synth.getRegionView(2)->isSwitchedOn() );
|
||||||
|
REQUIRE( synth.getRegionView(3)->isSwitchedOn() );
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("[Keyswitches] sw_default and playing with switches")
|
||||||
|
{
|
||||||
|
sfz::Synth synth;
|
||||||
|
synth.loadSfzString(fs::current_path() / "tests/TestFiles/sw_default.sfz", R"(
|
||||||
|
<global> sw_lokey=30 sw_hikey=50 sw_default=40
|
||||||
|
<region> sw_last=41 key=51 sample=*sine
|
||||||
|
<region> sw_last=40 key=52 sample=*sine
|
||||||
|
<region> sw_last=41 key=53 sample=*sine
|
||||||
|
<region> sw_last=40 key=54 sample=*sine
|
||||||
|
)");
|
||||||
|
REQUIRE( synth.getNumRegions() == 4 );
|
||||||
|
REQUIRE( !synth.getRegionView(0)->isSwitchedOn() );
|
||||||
|
REQUIRE( synth.getRegionView(1)->isSwitchedOn() );
|
||||||
|
REQUIRE( !synth.getRegionView(2)->isSwitchedOn() );
|
||||||
|
REQUIRE( synth.getRegionView(3)->isSwitchedOn() );
|
||||||
|
synth.noteOn(0, 41, 64);
|
||||||
|
synth.noteOff(0, 41, 0);
|
||||||
|
REQUIRE( synth.getRegionView(0)->isSwitchedOn() );
|
||||||
|
REQUIRE( !synth.getRegionView(1)->isSwitchedOn() );
|
||||||
|
REQUIRE( synth.getRegionView(2)->isSwitchedOn() );
|
||||||
|
REQUIRE( !synth.getRegionView(3)->isSwitchedOn() );
|
||||||
|
synth.noteOn(0, 40, 64);
|
||||||
|
synth.noteOff(0, 40, 64);
|
||||||
|
REQUIRE( !synth.getRegionView(0)->isSwitchedOn() );
|
||||||
|
REQUIRE( synth.getRegionView(1)->isSwitchedOn() );
|
||||||
|
REQUIRE( !synth.getRegionView(2)->isSwitchedOn() );
|
||||||
|
REQUIRE( synth.getRegionView(3)->isSwitchedOn() );
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -339,23 +339,6 @@ TEST_CASE("[Region] Parsing opcodes")
|
||||||
REQUIRE(region.ccConditions[125] == sfz::Range<float>(0.0f, 1.0f));
|
REQUIRE(region.ccConditions[125] == sfz::Range<float>(0.0f, 1.0f));
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("sw_lokey, sw_hikey")
|
|
||||||
{
|
|
||||||
REQUIRE(region.keyswitchRange == Range<uint8_t>(0, 127));
|
|
||||||
region.parseOpcode({ "sw_lokey", "4" });
|
|
||||||
REQUIRE(region.keyswitchRange == Range<uint8_t>(4, 127));
|
|
||||||
region.parseOpcode({ "sw_lokey", "128" });
|
|
||||||
REQUIRE(region.keyswitchRange == Range<uint8_t>(127, 127));
|
|
||||||
region.parseOpcode({ "sw_lokey", "0" });
|
|
||||||
REQUIRE(region.keyswitchRange == Range<uint8_t>(0, 127));
|
|
||||||
region.parseOpcode({ "sw_hikey", "39" });
|
|
||||||
REQUIRE(region.keyswitchRange == Range<uint8_t>(0, 39));
|
|
||||||
region.parseOpcode({ "sw_hikey", "135" });
|
|
||||||
REQUIRE(region.keyswitchRange == Range<uint8_t>(0, 127));
|
|
||||||
region.parseOpcode({ "sw_hikey", "-1" });
|
|
||||||
REQUIRE(region.keyswitchRange == Range<uint8_t>(0, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("sw_label")
|
SECTION("sw_label")
|
||||||
{
|
{
|
||||||
REQUIRE(!region.keyswitchLabel);
|
REQUIRE(!region.keyswitchLabel);
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@
|
||||||
#include "sfizz/Defaults.h"
|
#include "sfizz/Defaults.h"
|
||||||
#include "sfizz/Region.h"
|
#include "sfizz/Region.h"
|
||||||
#include "sfizz/SfzHelpers.h"
|
#include "sfizz/SfzHelpers.h"
|
||||||
#include "sfizz/MidiState.h"
|
|
||||||
#include "catch2/catch.hpp"
|
#include "catch2/catch.hpp"
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
<global> sw_lokey=30 sw_hikey=50 sw_default=40
|
|
||||||
<region> sw_last=41 key=51 sample=*silence
|
|
||||||
<region> sw_last=40 key=52 sample=*silence
|
|
||||||
<region> sw_last=41 key=53 sample=*silence
|
|
||||||
<region> sw_last=40 key=54 sample=*silence
|
|
||||||
Loading…
Add table
Reference in a new issue