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
|
namespace sfz
|
||||||
{
|
{
|
||||||
inline std::array<std::chrono::steady_clock::time_point, 128> noteOnTimes { };
|
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();
|
noteOnTimes[noteNumber] = std::chrono::steady_clock::now();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline float getNoteDuration(int noteNumber)
|
inline float getNoteDuration(int noteNumber)
|
||||||
|
|
@ -20,4 +23,12 @@ namespace sfz
|
||||||
|
|
||||||
return 0.0f;
|
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
|
else
|
||||||
sequenceSwitched = false;
|
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) {
|
||||||
if (*previousNote == noteNumber)
|
if (*previousNote == noteNumber)
|
||||||
previousKeySwitched = true;
|
previousKeySwitched = true;
|
||||||
|
|
@ -568,9 +564,10 @@ bool sfz::Region::registerNoteOff(int channel, int noteNumber, uint8_t velocity
|
||||||
if (!isSwitchedOn())
|
if (!isSwitchedOn())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
const bool velOk = velocityRange.containsWithEnd(velocity);
|
||||||
const bool randOk = randRange.contains(randValue);
|
const bool randOk = randRange.contains(randValue);
|
||||||
const bool releaseTrigger = (trigger == SfzTrigger::release || trigger == SfzTrigger::release_key);
|
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
|
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);
|
baseGain *= crossfadeOut(crossfadeKeyOutRange, noteNumber, crossfadeKeyCurve);
|
||||||
|
|
||||||
// Amplitude velocity tracking
|
// Amplitude velocity tracking
|
||||||
if (trigger == SfzTrigger::release_key)
|
baseGain *= velocityCurve(velocity);
|
||||||
baseGain *= velocityCurve(lastNoteVelocities[noteNumber]);
|
|
||||||
else
|
|
||||||
baseGain *= velocityCurve(velocity);
|
|
||||||
|
|
||||||
// Crossfades related to velocity
|
// Crossfades related to velocity
|
||||||
baseGain *= crossfadeIn(crossfadeVelInRange, velocity, crossfadeVelCurve);
|
baseGain *= crossfadeIn(crossfadeVelInRange, velocity, crossfadeVelCurve);
|
||||||
|
|
|
||||||
|
|
@ -108,7 +108,6 @@ struct Region {
|
||||||
|
|
||||||
// Region logic: triggers
|
// Region logic: triggers
|
||||||
SfzTrigger trigger { Default::trigger }; // trigger
|
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
|
CCMap<Range<uint8_t>> ccTriggers { Default::ccTriggerValueRange }; // on_loccN on_hiccN
|
||||||
|
|
||||||
// Performance parameters: amplifier
|
// 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 < 128);
|
||||||
ASSERT(noteNumber >= 0);
|
ASSERT(noteNumber >= 0);
|
||||||
|
|
||||||
setNoteOnTime(noteNumber);
|
sfz::noteOn(noteNumber, velocity);
|
||||||
auto randValue = randNoteDistribution(Random::randomGenerator);
|
auto randValue = randNoteDistribution(Random::randomGenerator);
|
||||||
|
|
||||||
for (auto& region : noteActivationLists[noteNumber]) {
|
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 < 128);
|
||||||
ASSERT(noteNumber >= 0);
|
ASSERT(noteNumber >= 0);
|
||||||
|
|
||||||
|
auto replacedVelocity = velocity == 0 ? sfz::getNoteVelocity(noteNumber) : velocity;
|
||||||
auto randValue = randNoteDistribution(Random::randomGenerator);
|
auto randValue = randNoteDistribution(Random::randomGenerator);
|
||||||
for (auto& voice : voices)
|
for (auto& voice : voices)
|
||||||
voice->registerNoteOff(delay, channel, noteNumber, velocity);
|
voice->registerNoteOff(delay, channel, noteNumber, replacedVelocity);
|
||||||
|
|
||||||
for (auto& region : noteActivationLists[noteNumber]) {
|
for (auto& region : noteActivationLists[noteNumber]) {
|
||||||
if (region->registerNoteOff(channel, noteNumber, velocity, randValue)) {
|
if (region->registerNoteOff(channel, noteNumber, replacedVelocity, randValue)) {
|
||||||
auto voice = findFreeVoice();
|
auto voice = findFreeVoice();
|
||||||
if (voice == nullptr)
|
if (voice == nullptr)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
voice->startVoice(region, delay, channel, noteNumber, velocity, Voice::TriggerType::NoteOff);
|
voice->startVoice(region, delay, channel, noteNumber, replacedVelocity, Voice::TriggerType::NoteOff);
|
||||||
if (!region->isGenerator()) {
|
if (!region->isGenerator()) {
|
||||||
voice->expectFileData(fileTicket);
|
voice->expectFileData(fileTicket);
|
||||||
filePool.enqueueLoading(voice, region->sample, region->trueSampleEnd(), fileTicket++);
|
filePool.enqueueLoading(voice, region->sample, region->trueSampleEnd(), fileTicket++);
|
||||||
|
|
|
||||||
|
|
@ -269,15 +269,15 @@ TEST_CASE("[Region] rt_decay")
|
||||||
region.parseOpcode({ "sample", "*sine" });
|
region.parseOpcode({ "sample", "*sine" });
|
||||||
region.parseOpcode({ "trigger", "release" });
|
region.parseOpcode({ "trigger", "release" });
|
||||||
region.parseOpcode({ "rt_decay", "10" });
|
region.parseOpcode({ "rt_decay", "10" });
|
||||||
sfz::setNoteOnTime(64);
|
sfz::noteOn(64, 64);
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume - 1.0f).margin(0.1) );
|
REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume - 1.0f).margin(0.1) );
|
||||||
region.parseOpcode({ "rt_decay", "20" });
|
region.parseOpcode({ "rt_decay", "20" });
|
||||||
sfz::setNoteOnTime(64);
|
sfz::noteOn(64, 64);
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume - 2.0f).margin(0.1) );
|
REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume - 2.0f).margin(0.1) );
|
||||||
region.parseOpcode({ "trigger", "attack" });
|
region.parseOpcode({ "trigger", "attack" });
|
||||||
sfz::setNoteOnTime(64);
|
sfz::noteOn(64, 64);
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume).margin(0.1) );
|
REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume).margin(0.1) );
|
||||||
}
|
}
|
||||||
Loading…
Add table
Reference in a new issue