From afc9d72c8968f4069f8b123fe3fb4ea94f79a3b9 Mon Sep 17 00:00:00 2001 From: Paul Ferrand Date: Fri, 21 Aug 2020 11:17:44 +0200 Subject: [PATCH] Further changes to the envelope follower- Apply on a single channel- Track attack and release separately --- src/sfizz/Config.h | 9 +++++---- src/sfizz/Voice.cpp | 32 +++++++++++++++++++++----------- src/sfizz/Voice.h | 5 +++-- src/sfizz/VoiceStealing.cpp | 2 +- 4 files changed, 30 insertions(+), 18 deletions(-) diff --git a/src/sfizz/Config.h b/src/sfizz/Config.h index d3c301ba..fc48aab5 100644 --- a/src/sfizz/Config.h +++ b/src/sfizz/Config.h @@ -57,7 +57,8 @@ namespace config { constexpr Oversampling defaultOversamplingFactor { Oversampling::x1 }; constexpr float A440 { 440.0 }; constexpr size_t powerHistoryLength { 16 }; - constexpr float powerFollowerFactor { 10 }; + constexpr float powerFollowerAttackFactor { 100 }; + constexpr float powerFollowerReleaseFactor { 10 }; constexpr uint16_t numCCs { 512 }; constexpr int maxCurves { 256 }; constexpr int chunkSize { 1024 }; @@ -72,10 +73,10 @@ namespace config { */ constexpr float stealingAgeCoeff { 0.5f }; /** - * @brief The threshold for envelope stealing. - * In percentage of the sum of all envelopes. + * @brief The threshold for power stealing. + * In percentage of the sum of all powers. */ - constexpr float stealingEnvelopeCoeff { 0.5f }; + constexpr float stealingPowerCoeff { 0.5f }; constexpr int filtersPerVoice { 2 }; constexpr int eqsPerVoice { 3 }; constexpr int oscillatorsPerVoice { 9 }; diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index 8b1a0bed..54c5ebc2 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -249,13 +249,13 @@ void sfz::Voice::setSampleRate(float sampleRate) noexcept for (auto& lfo : lfos) lfo->setSampleRate(sampleRate); - trackingFactor = samplesPerBlock / sampleRate * config::powerFollowerFactor; + attackTrackingFactor = config::powerFollowerAttackFactor / sampleRate; + releaseTrackingFactor = config::powerFollowerReleaseFactor / sampleRate; } void sfz::Voice::setSamplesPerBlock(int samplesPerBlock) noexcept { this->samplesPerBlock = samplesPerBlock; - this->trackingFactor = samplesPerBlock / sampleRate * config::powerFollowerFactor; } void sfz::Voice::renderBlock(AudioSpan buffer) noexcept @@ -732,8 +732,7 @@ void sfz::Voice::reset() noexcept floatPositionOffset = 0.0f; noteIsOff = false; - for (auto& p : meanChannelPowers) - p = 0.0f; + meanChannelPower = 0.0f; filters.clear(); equalizers.clear(); @@ -765,7 +764,7 @@ void sfz::Voice::removeVoiceFromRing() noexcept float sfz::Voice::getAveragePower() const noexcept { - return max(meanChannelPowers[0], meanChannelPowers[1]); + return meanChannelPower; } bool sfz::Voice::releasedOrFree() const noexcept @@ -865,12 +864,23 @@ void sfz::Voice::updateChannelPowers(AudioSpan buffer) if (buffer.getNumFrames() == 0) return; - const float factor = buffer.getNumFrames() / samplesPerBlock * trackingFactor; - for (unsigned i = 0; i < meanChannelPowers.size(); ++i) { - const auto input = buffer.getConstSpan(i); - const float meanPower = sfz::meanSquared(input); - meanChannelPowers[i] = meanChannelPowers[i] * (1 - factor) + meanPower * factor; - } + auto tempBuffer = resources.bufferPool.getBuffer(buffer.getNumFrames()); + if (!tempBuffer) + return; + + sfz::copy(buffer.getConstSpan(0), *tempBuffer); + for (unsigned i = 1; i < buffer.getNumChannels(); ++i) + sfz::add(buffer.getConstSpan(i), *tempBuffer); + + const float meanPower = sfz::meanSquared(*tempBuffer); + + const float attackFactor = static_cast(buffer.getNumFrames()) * attackTrackingFactor; + const float releaseFactor = static_cast(buffer.getNumFrames()) * releaseTrackingFactor; + + meanChannelPower = max( + meanChannelPower * (1 - attackFactor) + meanPower * attackFactor, + meanChannelPower * (1 - releaseFactor) + meanPower * releaseFactor + ); } diff --git a/src/sfizz/Voice.h b/src/sfizz/Voice.h index facd3c1d..c2b3d1c4 100644 --- a/src/sfizz/Voice.h +++ b/src/sfizz/Voice.h @@ -485,8 +485,9 @@ private: Smoother xfadeSmoother; void resetSmoothers() noexcept; - float trackingFactor { config::defaultSamplesPerBlock / config::defaultSampleRate * config::powerFollowerFactor }; - std::array meanChannelPowers; + float attackTrackingFactor { config::powerFollowerAttackFactor / config::defaultSampleRate }; + float releaseTrackingFactor { config::powerFollowerReleaseFactor / config::defaultSampleRate }; + float meanChannelPower; LEAK_DETECTOR(Voice); }; diff --git a/src/sfizz/VoiceStealing.cpp b/src/sfizz/VoiceStealing.cpp index 0c935df5..0fa4f1a7 100644 --- a/src/sfizz/VoiceStealing.cpp +++ b/src/sfizz/VoiceStealing.cpp @@ -19,7 +19,7 @@ sfz::Voice* sfz::VoiceStealing::steal(absl::Span voices) noexcept // We are checking the power to try and kill voices with relative low contribution // to the output compared to the rest. const auto powerThreshold = sumPower - / static_cast(voices.size()) * config::stealingEnvelopeCoeff; + / static_cast(voices.size()) * config::stealingPowerCoeff; // 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 // their sound, but it's reasonable for sounds with a quick attack and longer