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:
parent
61d1f1963c
commit
f41c36d241
5 changed files with 24 additions and 19 deletions
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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++);
|
||||
|
|
|
|||
|
|
@ -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) );
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue