diff --git a/src/sfizz/Config.h b/src/sfizz/Config.h index 2956dae6..d3c301ba 100644 --- a/src/sfizz/Config.h +++ b/src/sfizz/Config.h @@ -57,7 +57,7 @@ namespace config { constexpr Oversampling defaultOversamplingFactor { Oversampling::x1 }; constexpr float A440 { 440.0 }; constexpr size_t powerHistoryLength { 16 }; - constexpr float filteredEnvelopeCutoff { 5 }; + constexpr float powerFollowerFactor { 10 }; constexpr uint16_t numCCs { 512 }; constexpr int maxCurves { 256 }; constexpr int chunkSize { 1024 }; diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index d7517a3d..8b1a0bed 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -30,9 +30,6 @@ sfz::Voice::Voice(int voiceNumber, sfz::Resources& resources) gainSmoother.setSmoothing(config::gainSmoothing, sampleRate); xfadeSmoother.setSmoothing(config::xfadeSmoothing, sampleRate); - - for (auto & filter : channelEnvelopeFilters) - filter.setGain(vaGain(config::filteredEnvelopeCutoff, sampleRate)); } sfz::Voice::~Voice() @@ -246,20 +243,19 @@ void sfz::Voice::setSampleRate(float sampleRate) noexcept gainSmoother.setSmoothing(config::gainSmoothing, sampleRate); xfadeSmoother.setSmoothing(config::xfadeSmoothing, sampleRate); - for (auto & filter : channelEnvelopeFilters) - filter.setGain(vaGain(config::filteredEnvelopeCutoff, sampleRate)); - for (WavetableOscillator& osc : waveOscillators) osc.init(sampleRate); for (auto& lfo : lfos) lfo->setSampleRate(sampleRate); + + trackingFactor = samplesPerBlock / sampleRate * config::powerFollowerFactor; } void sfz::Voice::setSamplesPerBlock(int samplesPerBlock) noexcept { this->samplesPerBlock = samplesPerBlock; - this->minEnvelopeDelay = samplesPerBlock / 2; + this->trackingFactor = samplesPerBlock / sampleRate * config::powerFollowerFactor; } void sfz::Voice::renderBlock(AudioSpan buffer) noexcept @@ -736,10 +732,7 @@ void sfz::Voice::reset() noexcept floatPositionOffset = 0.0f; noteIsOff = false; - for (auto& f : channelEnvelopeFilters) - f.reset(); - - for (auto& p : smoothedChannelEnvelopes) + for (auto& p : meanChannelPowers) p = 0.0f; filters.clear(); @@ -770,9 +763,9 @@ void sfz::Voice::removeVoiceFromRing() noexcept 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 @@ -869,16 +862,14 @@ void sfz::Voice::setupOscillatorUnison() void sfz::Voice::updateChannelPowers(AudioSpan buffer) { - assert(smoothedChannelEnvelopes.size() == channelEnvelopeFilters.size()); - assert(buffer.getNumChannels() <= channelEnvelopeFilters.size()); if (buffer.getNumFrames() == 0) 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); - for (unsigned s = 0; s < buffer.getNumFrames(); ++s) - smoothedChannelEnvelopes[i] = - channelEnvelopeFilters[i].tickLowpass(std::abs(input[s])); + const float meanPower = sfz::meanSquared(input); + meanChannelPowers[i] = meanChannelPowers[i] * (1 - factor) + meanPower * factor; } } diff --git a/src/sfizz/Voice.h b/src/sfizz/Voice.h index 5b72e799..facd3c1d 100644 --- a/src/sfizz/Voice.h +++ b/src/sfizz/Voice.h @@ -261,7 +261,7 @@ public: * * @return float */ - float getAverageEnvelope() const noexcept; + float getAveragePower() const noexcept; /** * @brief Get the position of the voice in the source, in samples * @@ -450,7 +450,6 @@ private: FilePromisePtr currentPromise { nullptr }; int samplesPerBlock { config::defaultSamplesPerBlock }; - int minEnvelopeDelay { config::defaultSamplesPerBlock / 2 }; float sampleRate { config::defaultSampleRate }; Resources& resources; @@ -486,10 +485,8 @@ private: Smoother xfadeSmoother; void resetSmoothers() noexcept; - std::array, 2> channelEnvelopeFilters; - std::array smoothedChannelEnvelopes; - - HistoricalBuffer powerHistory { config::powerHistoryLength }; + float trackingFactor { config::defaultSamplesPerBlock / config::defaultSampleRate * config::powerFollowerFactor }; + std::array meanChannelPowers; LEAK_DETECTOR(Voice); }; diff --git a/src/sfizz/VoiceStealing.cpp b/src/sfizz/VoiceStealing.cpp index 49e1b2d0..0c935df5 100644 --- a/src/sfizz/VoiceStealing.cpp +++ b/src/sfizz/VoiceStealing.cpp @@ -13,12 +13,12 @@ sfz::Voice* sfz::VoiceStealing::steal(absl::Span voices) noexcept // Start of the voice stealing algorithm absl::c_stable_sort(voices, voiceOrdering); - const auto sumEnvelope = absl::c_accumulate(voices, 0.0f, [](float sum, const Voice* v) { - return sum + v->getAverageEnvelope(); + const auto sumPower = absl::c_accumulate(voices, 0.0f, [](float sum, const Voice* v) { + 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. - const auto envThreshold = sumEnvelope + const auto powerThreshold = sumPower / static_cast(voices.size()) * config::stealingEnvelopeCoeff; // 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 @@ -37,12 +37,12 @@ sfz::Voice* sfz::VoiceStealing::steal(absl::Span voices) noexcept break; } - float maxEnvelope { 0.0f }; + float maxPower { 0.0f }; SisterVoiceRing::applyToRing(ref, [&](Voice* v) { - maxEnvelope = max(maxEnvelope, v->getAverageEnvelope()); + maxPower = max(maxPower, v->getAveragePower()); }); - if (maxEnvelope < envThreshold) { + if (maxPower < powerThreshold) { returnedVoice = ref; break; }