Voice stealing tweaks

This commit is contained in:
Paul Ferrand 2020-07-18 11:37:29 +02:00
parent 443e89c45d
commit c39a50ee92
4 changed files with 40 additions and 25 deletions

View file

@ -680,6 +680,9 @@ void sfz::Synth::setSampleRate(float sampleRate) noexcept
void sfz::Synth::renderVoiceToOutputs(Voice& voice, AudioSpan<float>& tempSpan) noexcept
{
const Region* region = voice.getRegion();
if (region == nullptr)
return;
voice.renderBlock(tempSpan);
for (size_t i = 0, n = effectBuses.size(); i < n; ++i) {
if (auto& bus = effectBuses[i]) {
@ -936,8 +939,12 @@ void sfz::Synth::noteOnDispatch(int delay, int noteNumber, float velocity) noexc
}
render:
// For some reason we did not find a voice to use.
// This is a degraded case but we'll just drop the note on.
if (selectedVoice == nullptr)
continue;
// Kill voice if necessary, pre-rendering it into the output buffers
ASSERT(selectedVoice);
if (!selectedVoice->isFree()) {
auto tempSpan = resources.bufferPool.getStereoBuffer(samplesPerBlock);
SisterVoiceRing::applyToRing(selectedVoice, [&] (Voice* v) {
@ -1295,22 +1302,22 @@ void sfz::Synth::resetVoices(int numVoices)
voices.clear();
voices.reserve(numVoices);
for (int i = 0; i < numVoices; ++i) {
auto voice = absl::make_unique<Voice>(i, resources);
voice->setStateListener(this);
voices.emplace_back(std::move(voice));
}
voiceViewArray.clear();
voiceViewArray.reserve(numVoices);
regionPolyphonyArray.clear();
regionPolyphonyArray.reserve(numVoices);
for (int i = 0; i < numVoices; ++i) {
auto voice = absl::make_unique<Voice>(i, resources);
voice->setStateListener(this);
voiceViewArray.push_back(voice.get());
voices.emplace_back(std::move(voice));
}
for (auto& voice : voices) {
voice->setSampleRate(this->sampleRate);
voice->setSamplesPerBlock(this->samplesPerBlock);
voiceViewArray.push_back(voice.get());
}
this->numVoices = numVoices;

View file

@ -270,7 +270,8 @@ void sfz::Voice::renderBlock(AudioSpan<float> buffer) noexcept
ASSERT(static_cast<int>(buffer.getNumFrames()) <= samplesPerBlock);
buffer.fill(0.0f);
ASSERT(region != nullptr);
if (region == nullptr)
return;
const auto delay = min(static_cast<size_t>(initialDelay), buffer.getNumFrames());
auto delayed_buffer = buffer.subspan(delay);

View file

@ -494,28 +494,31 @@ private:
inline bool sisterVoices(const Voice* lhs, const Voice* rhs)
{
return lhs->getAge() == rhs->getAge()
&& lhs->getTriggerNumber() == rhs->getTriggerNumber()
&& lhs->getTriggerValue() == rhs->getTriggerValue()
&& lhs->getTriggerType() == rhs->getTriggerType();
if (lhs->getAge() != rhs->getAge())
return false;
if (lhs->getTriggerNumber() != rhs->getTriggerNumber())
return false;
if (lhs->getTriggerValue() != rhs->getTriggerValue())
return false;
if (lhs->getTriggerType() != rhs->getTriggerType())
return false;
return true;
}
inline bool voiceOrdering(const Voice* lhs, const Voice* rhs)
{
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;
return true;
if (lhs->getTriggerType() > rhs->getTriggerType())
return true;

View file

@ -8,7 +8,7 @@ sfz::VoiceStealing::VoiceStealing()
sfz::Voice* sfz::VoiceStealing::steal(absl::Span<sfz::Voice*> voices) noexcept
{
// Start of the voice stealing algorithm
absl::c_sort(voices, voiceOrdering);
absl::c_stable_sort(voices, voiceOrdering);
const auto sumEnvelope = absl::c_accumulate(voices, 0.0f, [](float sum, const Voice* v) {
return sum + v->getAverageEnvelope();
@ -21,9 +21,8 @@ sfz::Voice* sfz::VoiceStealing::steal(absl::Span<sfz::Voice*> voices) noexcept
// This is not perfect because pad-type voices will take a long time to output
// their sound, but it's reasonable for sounds with a quick attack and longer
// release.
const auto ageThreshold = voices.front()->getAge() * config::stealingAgeCoeff;
// This needs to be positive
ASSERT(ageThreshold >= 0);
const auto ageThreshold =
static_cast<int>(voices.front()->getAge() * config::stealingAgeCoeff) + 1;
Voice* returnedVoice = voices.front();
unsigned idx = 0;
@ -49,5 +48,10 @@ sfz::Voice* sfz::VoiceStealing::steal(absl::Span<sfz::Voice*> voices) noexcept
do { idx++; }
while (idx < voices.size() && sisterVoices(ref, voices[idx]));
}
// Guard for future changes: voices with age 0 just started; don't kill those.
if (returnedVoice->getAge() == 0)
return {};
return returnedVoice;
}