Velocity tracking and replacements in note ons and offs are now done at the synth/global level. Velocity is always replaced and checked, since some libraries use "release" along with velocity ranges

This commit is contained in:
paulfd 2019-09-16 13:17:18 +02:00
parent 61d1f1963c
commit f41c36d241
5 changed files with 24 additions and 19 deletions

View file

@ -4,10 +4,13 @@
namespace sfz
{
inline std::array<std::chrono::steady_clock::time_point, 128> noteOnTimes { };
inline void setNoteOnTime(int noteNumber)
inline std::array<uint8_t, 128> lastNoteVelocities { };
inline void noteOn(int noteNumber, uint8_t velocity)
{
if (noteNumber >= 0 && noteNumber < 128)
if (noteNumber >= 0 && noteNumber < 128) {
lastNoteVelocities[noteNumber] = velocity;
noteOnTimes[noteNumber] = std::chrono::steady_clock::now();
}
}
inline float getNoteDuration(int noteNumber)
@ -20,4 +23,12 @@ namespace sfz
return 0.0f;
}
inline uint8_t getNoteVelocity(int noteNumber)
{
if (noteNumber >= 0 && noteNumber < 128)
return lastNoteVelocities[noteNumber];
return 0;
}
}

View file

@ -518,10 +518,6 @@ bool sfz::Region::registerNoteOn(int channel, int noteNumber, uint8_t velocity,
else
sequenceSwitched = false;
// Velocity memory for release_key and for sw_vel=previous
if (trigger == SfzTrigger::release_key || velocityOverride == SfzVelocityOverride::previous)
lastNoteVelocities[noteNumber] = velocity;
if (previousNote) {
if (*previousNote == noteNumber)
previousKeySwitched = true;
@ -568,9 +564,10 @@ bool sfz::Region::registerNoteOff(int channel, int noteNumber, uint8_t velocity
if (!isSwitchedOn())
return false;
const bool velOk = velocityRange.containsWithEnd(velocity);
const bool randOk = randRange.contains(randValue);
const bool releaseTrigger = (trigger == SfzTrigger::release || trigger == SfzTrigger::release_key);
return keyOk && chanOk && randOk && releaseTrigger;
return keyOk && velOk && chanOk && randOk && releaseTrigger;
}
bool sfz::Region::registerCC(int channel, int ccNumber, uint8_t ccValue) noexcept
@ -721,10 +718,7 @@ float sfz::Region::getNoteGain(int noteNumber, uint8_t velocity) noexcept
baseGain *= crossfadeOut(crossfadeKeyOutRange, noteNumber, crossfadeKeyCurve);
// Amplitude velocity tracking
if (trigger == SfzTrigger::release_key)
baseGain *= velocityCurve(lastNoteVelocities[noteNumber]);
else
baseGain *= velocityCurve(velocity);
baseGain *= velocityCurve(velocity);
// Crossfades related to velocity
baseGain *= crossfadeIn(crossfadeVelInRange, velocity, crossfadeVelCurve);

View file

@ -108,7 +108,6 @@ struct Region {
// Region logic: triggers
SfzTrigger trigger { Default::trigger }; // trigger
std::array<uint8_t, 128> lastNoteVelocities; // Keeps the velocities of the previous note-ons if the region has the trigger release_key
CCMap<Range<uint8_t>> ccTriggers { Default::ccTriggerValueRange }; // on_loccN on_hiccN
// Performance parameters: amplifier

View file

@ -313,7 +313,7 @@ void sfz::Synth::noteOn(int delay, int channel, int noteNumber, uint8_t velocity
ASSERT(noteNumber < 128);
ASSERT(noteNumber >= 0);
setNoteOnTime(noteNumber);
sfz::noteOn(noteNumber, velocity);
auto randValue = randNoteDistribution(Random::randomGenerator);
for (auto& region : noteActivationLists[noteNumber]) {
@ -341,17 +341,18 @@ void sfz::Synth::noteOff(int delay, int channel, int noteNumber, uint8_t velocit
ASSERT(noteNumber < 128);
ASSERT(noteNumber >= 0);
auto replacedVelocity = velocity == 0 ? sfz::getNoteVelocity(noteNumber) : velocity;
auto randValue = randNoteDistribution(Random::randomGenerator);
for (auto& voice : voices)
voice->registerNoteOff(delay, channel, noteNumber, velocity);
voice->registerNoteOff(delay, channel, noteNumber, replacedVelocity);
for (auto& region : noteActivationLists[noteNumber]) {
if (region->registerNoteOff(channel, noteNumber, velocity, randValue)) {
if (region->registerNoteOff(channel, noteNumber, replacedVelocity, randValue)) {
auto voice = findFreeVoice();
if (voice == nullptr)
continue;
voice->startVoice(region, delay, channel, noteNumber, velocity, Voice::TriggerType::NoteOff);
voice->startVoice(region, delay, channel, noteNumber, replacedVelocity, Voice::TriggerType::NoteOff);
if (!region->isGenerator()) {
voice->expectFileData(fileTicket);
filePool.enqueueLoading(voice, region->sample, region->trueSampleEnd(), fileTicket++);

View file

@ -269,15 +269,15 @@ TEST_CASE("[Region] rt_decay")
region.parseOpcode({ "sample", "*sine" });
region.parseOpcode({ "trigger", "release" });
region.parseOpcode({ "rt_decay", "10" });
sfz::setNoteOnTime(64);
sfz::noteOn(64, 64);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume - 1.0f).margin(0.1) );
region.parseOpcode({ "rt_decay", "20" });
sfz::setNoteOnTime(64);
sfz::noteOn(64, 64);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume - 2.0f).margin(0.1) );
region.parseOpcode({ "trigger", "attack" });
sfz::setNoteOnTime(64);
sfz::noteOn(64, 64);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume).margin(0.1) );
}