Simpler power/envelope followerThis one is much cheaper...

This commit is contained in:
Paul Ferrand 2020-08-20 02:23:02 +02:00
parent 66705f54c3
commit 958d9bb5e8
4 changed files with 21 additions and 33 deletions

View file

@ -57,7 +57,7 @@ namespace config {
constexpr Oversampling defaultOversamplingFactor { Oversampling::x1 }; constexpr Oversampling defaultOversamplingFactor { Oversampling::x1 };
constexpr float A440 { 440.0 }; constexpr float A440 { 440.0 };
constexpr size_t powerHistoryLength { 16 }; constexpr size_t powerHistoryLength { 16 };
constexpr float filteredEnvelopeCutoff { 5 }; constexpr float powerFollowerFactor { 10 };
constexpr uint16_t numCCs { 512 }; constexpr uint16_t numCCs { 512 };
constexpr int maxCurves { 256 }; constexpr int maxCurves { 256 };
constexpr int chunkSize { 1024 }; constexpr int chunkSize { 1024 };

View file

@ -30,9 +30,6 @@ sfz::Voice::Voice(int voiceNumber, sfz::Resources& resources)
gainSmoother.setSmoothing(config::gainSmoothing, sampleRate); gainSmoother.setSmoothing(config::gainSmoothing, sampleRate);
xfadeSmoother.setSmoothing(config::xfadeSmoothing, sampleRate); xfadeSmoother.setSmoothing(config::xfadeSmoothing, sampleRate);
for (auto & filter : channelEnvelopeFilters)
filter.setGain(vaGain(config::filteredEnvelopeCutoff, sampleRate));
} }
sfz::Voice::~Voice() sfz::Voice::~Voice()
@ -246,20 +243,19 @@ void sfz::Voice::setSampleRate(float sampleRate) noexcept
gainSmoother.setSmoothing(config::gainSmoothing, sampleRate); gainSmoother.setSmoothing(config::gainSmoothing, sampleRate);
xfadeSmoother.setSmoothing(config::xfadeSmoothing, sampleRate); xfadeSmoother.setSmoothing(config::xfadeSmoothing, sampleRate);
for (auto & filter : channelEnvelopeFilters)
filter.setGain(vaGain(config::filteredEnvelopeCutoff, sampleRate));
for (WavetableOscillator& osc : waveOscillators) for (WavetableOscillator& osc : waveOscillators)
osc.init(sampleRate); osc.init(sampleRate);
for (auto& lfo : lfos) for (auto& lfo : lfos)
lfo->setSampleRate(sampleRate); lfo->setSampleRate(sampleRate);
trackingFactor = samplesPerBlock / sampleRate * config::powerFollowerFactor;
} }
void sfz::Voice::setSamplesPerBlock(int samplesPerBlock) noexcept void sfz::Voice::setSamplesPerBlock(int samplesPerBlock) noexcept
{ {
this->samplesPerBlock = samplesPerBlock; this->samplesPerBlock = samplesPerBlock;
this->minEnvelopeDelay = samplesPerBlock / 2; this->trackingFactor = samplesPerBlock / sampleRate * config::powerFollowerFactor;
} }
void sfz::Voice::renderBlock(AudioSpan<float> buffer) noexcept void sfz::Voice::renderBlock(AudioSpan<float> buffer) noexcept
@ -736,10 +732,7 @@ void sfz::Voice::reset() noexcept
floatPositionOffset = 0.0f; floatPositionOffset = 0.0f;
noteIsOff = false; noteIsOff = false;
for (auto& f : channelEnvelopeFilters) for (auto& p : meanChannelPowers)
f.reset();
for (auto& p : smoothedChannelEnvelopes)
p = 0.0f; p = 0.0f;
filters.clear(); filters.clear();
@ -770,9 +763,9 @@ void sfz::Voice::removeVoiceFromRing() noexcept
nextSisterVoice = this; nextSisterVoice = this;
} }
float sfz::Voice::getAverageEnvelope() const noexcept float sfz::Voice::getAveragePower() const noexcept
{ {
return max(smoothedChannelEnvelopes[0], smoothedChannelEnvelopes[1]); return max(meanChannelPowers[0], meanChannelPowers[1]);
} }
bool sfz::Voice::releasedOrFree() const noexcept bool sfz::Voice::releasedOrFree() const noexcept
@ -869,16 +862,14 @@ void sfz::Voice::setupOscillatorUnison()
void sfz::Voice::updateChannelPowers(AudioSpan<float> buffer) void sfz::Voice::updateChannelPowers(AudioSpan<float> buffer)
{ {
assert(smoothedChannelEnvelopes.size() == channelEnvelopeFilters.size());
assert(buffer.getNumChannels() <= channelEnvelopeFilters.size());
if (buffer.getNumFrames() == 0) if (buffer.getNumFrames() == 0)
return; return;
for (unsigned i = 0; i < smoothedChannelEnvelopes.size(); ++i) { const float factor = buffer.getNumFrames() / samplesPerBlock * trackingFactor;
for (unsigned i = 0; i < meanChannelPowers.size(); ++i) {
const auto input = buffer.getConstSpan(i); const auto input = buffer.getConstSpan(i);
for (unsigned s = 0; s < buffer.getNumFrames(); ++s) const float meanPower = sfz::meanSquared(input);
smoothedChannelEnvelopes[i] = meanChannelPowers[i] = meanChannelPowers[i] * (1 - factor) + meanPower * factor;
channelEnvelopeFilters[i].tickLowpass(std::abs(input[s]));
} }
} }

View file

@ -261,7 +261,7 @@ public:
* *
* @return float * @return float
*/ */
float getAverageEnvelope() const noexcept; float getAveragePower() const noexcept;
/** /**
* @brief Get the position of the voice in the source, in samples * @brief Get the position of the voice in the source, in samples
* *
@ -450,7 +450,6 @@ private:
FilePromisePtr currentPromise { nullptr }; FilePromisePtr currentPromise { nullptr };
int samplesPerBlock { config::defaultSamplesPerBlock }; int samplesPerBlock { config::defaultSamplesPerBlock };
int minEnvelopeDelay { config::defaultSamplesPerBlock / 2 };
float sampleRate { config::defaultSampleRate }; float sampleRate { config::defaultSampleRate };
Resources& resources; Resources& resources;
@ -486,10 +485,8 @@ private:
Smoother xfadeSmoother; Smoother xfadeSmoother;
void resetSmoothers() noexcept; void resetSmoothers() noexcept;
std::array<OnePoleFilter<float>, 2> channelEnvelopeFilters; float trackingFactor { config::defaultSamplesPerBlock / config::defaultSampleRate * config::powerFollowerFactor };
std::array<float, 2> smoothedChannelEnvelopes; std::array<float, 2> meanChannelPowers;
HistoricalBuffer<float> powerHistory { config::powerHistoryLength };
LEAK_DETECTOR(Voice); LEAK_DETECTOR(Voice);
}; };

View file

@ -13,12 +13,12 @@ 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_stable_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 sumPower = absl::c_accumulate(voices, 0.0f, [](float sum, const Voice* v) {
return sum + v->getAverageEnvelope(); return sum + v->getAveragePower();
}); });
// We are checking the envelope to try and kill voices with relative low contribution // We are checking the power to try and kill voices with relative low contribution
// to the output compared to the rest. // to the output compared to the rest.
const auto envThreshold = sumEnvelope const auto powerThreshold = sumPower
/ static_cast<float>(voices.size()) * config::stealingEnvelopeCoeff; / static_cast<float>(voices.size()) * config::stealingEnvelopeCoeff;
// We are checking the age so that voices have the time to build up attack // We are checking the age so that voices have the time to build up attack
// 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
@ -37,12 +37,12 @@ sfz::Voice* sfz::VoiceStealing::steal(absl::Span<sfz::Voice*> voices) noexcept
break; break;
} }
float maxEnvelope { 0.0f }; float maxPower { 0.0f };
SisterVoiceRing::applyToRing(ref, [&](Voice* v) { SisterVoiceRing::applyToRing(ref, [&](Voice* v) {
maxEnvelope = max(maxEnvelope, v->getAverageEnvelope()); maxPower = max(maxPower, v->getAveragePower());
}); });
if (maxEnvelope < envThreshold) { if (maxPower < powerThreshold) {
returnedVoice = ref; returnedVoice = ref;
break; break;
} }