Voice stealing tweaks
This commit is contained in:
parent
443e89c45d
commit
c39a50ee92
4 changed files with 40 additions and 25 deletions
|
|
@ -680,6 +680,9 @@ void sfz::Synth::setSampleRate(float sampleRate) noexcept
|
||||||
void sfz::Synth::renderVoiceToOutputs(Voice& voice, AudioSpan<float>& tempSpan) noexcept
|
void sfz::Synth::renderVoiceToOutputs(Voice& voice, AudioSpan<float>& tempSpan) noexcept
|
||||||
{
|
{
|
||||||
const Region* region = voice.getRegion();
|
const Region* region = voice.getRegion();
|
||||||
|
if (region == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
voice.renderBlock(tempSpan);
|
voice.renderBlock(tempSpan);
|
||||||
for (size_t i = 0, n = effectBuses.size(); i < n; ++i) {
|
for (size_t i = 0, n = effectBuses.size(); i < n; ++i) {
|
||||||
if (auto& bus = effectBuses[i]) {
|
if (auto& bus = effectBuses[i]) {
|
||||||
|
|
@ -936,8 +939,12 @@ void sfz::Synth::noteOnDispatch(int delay, int noteNumber, float velocity) noexc
|
||||||
}
|
}
|
||||||
|
|
||||||
render:
|
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
|
// Kill voice if necessary, pre-rendering it into the output buffers
|
||||||
ASSERT(selectedVoice);
|
|
||||||
if (!selectedVoice->isFree()) {
|
if (!selectedVoice->isFree()) {
|
||||||
auto tempSpan = resources.bufferPool.getStereoBuffer(samplesPerBlock);
|
auto tempSpan = resources.bufferPool.getStereoBuffer(samplesPerBlock);
|
||||||
SisterVoiceRing::applyToRing(selectedVoice, [&] (Voice* v) {
|
SisterVoiceRing::applyToRing(selectedVoice, [&] (Voice* v) {
|
||||||
|
|
@ -1295,22 +1302,22 @@ void sfz::Synth::resetVoices(int numVoices)
|
||||||
voices.clear();
|
voices.clear();
|
||||||
voices.reserve(numVoices);
|
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.clear();
|
||||||
voiceViewArray.reserve(numVoices);
|
voiceViewArray.reserve(numVoices);
|
||||||
|
|
||||||
regionPolyphonyArray.clear();
|
regionPolyphonyArray.clear();
|
||||||
regionPolyphonyArray.reserve(numVoices);
|
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) {
|
for (auto& voice : voices) {
|
||||||
voice->setSampleRate(this->sampleRate);
|
voice->setSampleRate(this->sampleRate);
|
||||||
voice->setSamplesPerBlock(this->samplesPerBlock);
|
voice->setSamplesPerBlock(this->samplesPerBlock);
|
||||||
voiceViewArray.push_back(voice.get());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this->numVoices = numVoices;
|
this->numVoices = numVoices;
|
||||||
|
|
|
||||||
|
|
@ -270,7 +270,8 @@ void sfz::Voice::renderBlock(AudioSpan<float> buffer) noexcept
|
||||||
ASSERT(static_cast<int>(buffer.getNumFrames()) <= samplesPerBlock);
|
ASSERT(static_cast<int>(buffer.getNumFrames()) <= samplesPerBlock);
|
||||||
buffer.fill(0.0f);
|
buffer.fill(0.0f);
|
||||||
|
|
||||||
ASSERT(region != nullptr);
|
if (region == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
const auto delay = min(static_cast<size_t>(initialDelay), buffer.getNumFrames());
|
const auto delay = min(static_cast<size_t>(initialDelay), buffer.getNumFrames());
|
||||||
auto delayed_buffer = buffer.subspan(delay);
|
auto delayed_buffer = buffer.subspan(delay);
|
||||||
|
|
|
||||||
|
|
@ -494,28 +494,31 @@ private:
|
||||||
|
|
||||||
inline bool sisterVoices(const Voice* lhs, const Voice* rhs)
|
inline bool sisterVoices(const Voice* lhs, const Voice* rhs)
|
||||||
{
|
{
|
||||||
return lhs->getAge() == rhs->getAge()
|
if (lhs->getAge() != rhs->getAge())
|
||||||
&& lhs->getTriggerNumber() == rhs->getTriggerNumber()
|
return false;
|
||||||
&& lhs->getTriggerValue() == rhs->getTriggerValue()
|
|
||||||
&& lhs->getTriggerType() == rhs->getTriggerType();
|
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)
|
inline bool voiceOrdering(const Voice* lhs, const Voice* rhs)
|
||||||
{
|
{
|
||||||
if (lhs->getAge() > rhs->getAge())
|
if (lhs->getAge() > rhs->getAge())
|
||||||
return true;
|
return true;
|
||||||
if (lhs->getAge() < rhs->getAge())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (lhs->getTriggerNumber() > rhs->getTriggerNumber())
|
|
||||||
return true;
|
|
||||||
if (lhs->getTriggerNumber() < rhs->getTriggerNumber())
|
if (lhs->getTriggerNumber() < rhs->getTriggerNumber())
|
||||||
return false;
|
|
||||||
|
|
||||||
if (lhs->getTriggerValue() > rhs->getTriggerValue())
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (lhs->getTriggerValue() < rhs->getTriggerValue())
|
if (lhs->getTriggerValue() < rhs->getTriggerValue())
|
||||||
return false;
|
return true;
|
||||||
|
|
||||||
if (lhs->getTriggerType() > rhs->getTriggerType())
|
if (lhs->getTriggerType() > rhs->getTriggerType())
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ sfz::VoiceStealing::VoiceStealing()
|
||||||
sfz::Voice* sfz::VoiceStealing::steal(absl::Span<sfz::Voice*> voices) noexcept
|
sfz::Voice* sfz::VoiceStealing::steal(absl::Span<sfz::Voice*> voices) noexcept
|
||||||
{
|
{
|
||||||
// Start of the voice stealing algorithm
|
// 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) {
|
const auto sumEnvelope = absl::c_accumulate(voices, 0.0f, [](float sum, const Voice* v) {
|
||||||
return sum + v->getAverageEnvelope();
|
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
|
// 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
|
// their sound, but it's reasonable for sounds with a quick attack and longer
|
||||||
// release.
|
// release.
|
||||||
const auto ageThreshold = voices.front()->getAge() * config::stealingAgeCoeff;
|
const auto ageThreshold =
|
||||||
// This needs to be positive
|
static_cast<int>(voices.front()->getAge() * config::stealingAgeCoeff) + 1;
|
||||||
ASSERT(ageThreshold >= 0);
|
|
||||||
|
|
||||||
Voice* returnedVoice = voices.front();
|
Voice* returnedVoice = voices.front();
|
||||||
unsigned idx = 0;
|
unsigned idx = 0;
|
||||||
|
|
@ -49,5 +48,10 @@ sfz::Voice* sfz::VoiceStealing::steal(absl::Span<sfz::Voice*> voices) noexcept
|
||||||
do { idx++; }
|
do { idx++; }
|
||||||
while (idx < voices.size() && sisterVoices(ref, voices[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;
|
return returnedVoice;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue