Consider all sister voices when killing a note

This commit is contained in:
Paul Ferrand 2020-05-07 18:24:49 +02:00
parent 81ee1f2e59
commit cd455c435e

View file

@ -389,15 +389,14 @@ bool sfz::Synth::loadSfzFile(const fs::path& file)
// TODO: adjust with LFO targets // TODO: adjust with LFO targets
const auto maxOffset = [region]() { const auto maxOffset = [region]() {
uint64_t sumOffsetCC = region->offset + region->offsetRandom; uint64_t sumOffsetCC = region->offset + region->offsetRandom;
for (const auto& offsets: region->offsetCC) for (const auto& offsets : region->offsetCC)
sumOffsetCC += offsets.data; sumOffsetCC += offsets.data;
return Default::offsetCCRange.clamp(sumOffsetCC); return Default::offsetCCRange.clamp(sumOffsetCC);
}(); }();
if (!resources.filePool.preloadFile(region->sampleId, maxOffset)) if (!resources.filePool.preloadFile(region->sampleId, maxOffset))
removeCurrentRegion(); removeCurrentRegion();
} } else if (region->oscillator && !region->isGenerator()) {
else if (region->oscillator && !region->isGenerator()) {
if (!resources.filePool.checkSampleId(region->sampleId)) { if (!resources.filePool.checkSampleId(region->sampleId)) {
removeCurrentRegion(); removeCurrentRegion();
continue; continue;
@ -412,7 +411,6 @@ bool sfz::Synth::loadSfzFile(const fs::path& file)
if (region->keyswitchLabel && region->keyswitch) if (region->keyswitchLabel && region->keyswitch)
keyswitchLabels.push_back({ *region->keyswitch, *region->keyswitchLabel }); keyswitchLabels.push_back({ *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 (groupMaxPolyphony.size() <= region->group) while (groupMaxPolyphony.size() <= region->group)
groupMaxPolyphony.push_back(config::maxVoices); groupMaxPolyphony.push_back(config::maxVoices);
@ -473,26 +471,6 @@ bool sfz::Synth::loadSfzFile(const fs::path& file)
return true; return true;
} }
unsigned sfz::Synth::killSisterVoices(const Voice* voiceToKill) noexcept
{
const auto age = voiceToKill->getAge();
const auto type = voiceToKill->getTriggerType();
const auto number = voiceToKill->getTriggerNumber();
const auto value = voiceToKill->getTriggerValue();
unsigned killedVoices = 0;
for (auto & voice : voiceViewArray) {
if (voice->getAge() == age
&& voice->getTriggerType() == type
&& voice->getTriggerNumber() == number
&& voice->getTriggerValue() == value) {
killedVoices++;
voice->reset();
}
}
return killedVoices;
}
sfz::Voice* sfz::Synth::findFreeVoice() noexcept sfz::Voice* sfz::Synth::findFreeVoice() noexcept
{ {
auto freeVoice = absl::c_find_if(voices, [](const std::unique_ptr<Voice>& voice) { auto freeVoice = absl::c_find_if(voices, [](const std::unique_ptr<Voice>& voice) {
@ -503,34 +481,69 @@ sfz::Voice* sfz::Synth::findFreeVoice() noexcept
// Find voices that can be stolen // Find voices that can be stolen
absl::c_sort(voiceViewArray, [](Voice* lhs, Voice* rhs) { absl::c_sort(voiceViewArray, [](Voice* lhs, Voice* rhs) {
return lhs->getAge() > rhs->getAge(); if (lhs->getAge() > rhs->getAge())
return true;
if (lhs->getAge() < rhs->getAge())
return false;
if (lhs->getTriggerNumber() > rhs->getTriggerNumber())
return true;
if (lhs->getTriggerNumber() < rhs->getTriggerNumber())
return false;
if (lhs->getTriggerValue() > rhs->getTriggerValue())
return true;
if (lhs->getTriggerValue() < rhs->getTriggerValue())
return false;
if (lhs->getTriggerType() > rhs->getTriggerType())
return true;
return false;
}); });
const auto sumEnvelope = absl::c_accumulate(voiceViewArray, 0.0f, [] (float sum, const Voice* v) { const auto sumEnvelope = absl::c_accumulate(voiceViewArray, 0.0f, [](float sum, const Voice* v) {
return sum + v->getAverageEnvelope(); return sum + v->getAverageEnvelope();
}); });
const auto envThreshold = sumEnvelope / static_cast<float>(voiceViewArray.size()) * 0.25f; const auto envThreshold = sumEnvelope / static_cast<float>(voiceViewArray.size()) * 0.5f;
const auto ageThreshold = voiceViewArray.front()->getAge() * 0.5f; const auto ageThreshold = voiceViewArray.front()->getAge() * 0.5f;
Voice* returnedVoice = voiceViewArray.front(); Voice* returnedVoice = voiceViewArray.front();
for (auto & voice : voiceViewArray) { unsigned idx = 0;
if (voice->getAge() < ageThreshold) { while (idx < voiceViewArray.size()) {
// std::cout << "Went too far, picking the oldest note..." << '\n'; const auto refIdx = idx;
const auto ref = voiceViewArray[idx];
idx++;
if (ref->getAge() < ageThreshold) {
unsigned killIdx = 1;
while (killIdx < voiceViewArray.size()
&& sisterVoices(returnedVoice, voiceViewArray[killIdx])) {
voiceViewArray[killIdx]->reset();
killIdx++;
}
// std::cout << "Went too far, picking the oldest voice and killing "
// << killIdx << " voices" << '\n';
returnedVoice->reset();
break; break;
} }
if (voice->getAverageEnvelope() < envThreshold) {
// std::cout << "Found a better candidate!" << '\n'; float sumEnvelope = ref->getAverageEnvelope();
returnedVoice = voice; while (idx < voiceViewArray.size() && sisterVoices(ref, voiceViewArray[idx])) {
sumEnvelope += voiceViewArray[idx]->getAverageEnvelope();
idx++;
}
if (sumEnvelope < envThreshold) {
returnedVoice = ref;
// std::cout << "Killing " << idx - refIdx << " voices" << '\n';
for (unsigned j = refIdx; j < idx; j++)
voiceViewArray[j]->reset();
break; break;
} }
} }
const auto killedVoices = killSisterVoices(returnedVoice);
UNUSED(killedVoices); // only in debug
assert(killedVoices > 0);
assert(returnedVoice->isFree()); assert(returnedVoice->isFree());
// std::cout << "Killed " << killedVoices << " voices" << '\n';
return returnedVoice; return returnedVoice;
} }
@ -599,7 +612,6 @@ void sfz::Synth::renderBlock(AudioSpan<float> buffer) noexcept
if (!lock.owns_lock()) if (!lock.owns_lock())
return; return;
size_t numFrames = buffer.getNumFrames(); size_t numFrames = buffer.getNumFrames();
auto tempSpan = resources.bufferPool.getStereoBuffer(numFrames); auto tempSpan = resources.bufferPool.getStereoBuffer(numFrames);
auto tempMixSpan = resources.bufferPool.getStereoBuffer(numFrames); auto tempMixSpan = resources.bufferPool.getStereoBuffer(numFrames);
@ -1107,7 +1119,7 @@ void sfz::Synth::resetAllControllers(int delay) noexcept
fs::file_time_type sfz::Synth::checkModificationTime() fs::file_time_type sfz::Synth::checkModificationTime()
{ {
auto returnedTime = modificationTime; auto returnedTime = modificationTime;
for (const auto& file: parser.getIncludedFiles()) { for (const auto& file : parser.getIncludedFiles()) {
std::error_code ec; std::error_code ec;
const auto fileTime = fs::last_write_time(file, ec); const auto fileTime = fs::last_write_time(file, ec);
if (!ec && returnedTime < fileTime) if (!ec && returnedTime < fileTime)