Move the power follower to its own file

This commit is contained in:
Jean Pierre Cimalando 2020-08-27 04:56:56 +02:00
parent dd8134277c
commit cb144ad42f
6 changed files with 123 additions and 47 deletions

1
dpf.mk
View file

@ -103,6 +103,7 @@ SFIZZ_SOURCES = \
src/sfizz/Parser.cpp \
src/sfizz/parser/Parser.cpp \
src/sfizz/parser/ParserPrivate.cpp \
src/sfizz/PowerFollower.cpp \
src/sfizz/Region.cpp \
src/sfizz/RTSemaphore.cpp \
src/sfizz/ScopedFTZ.cpp \

View file

@ -81,6 +81,7 @@ set (SFIZZ_HEADERS
sfizz/Oversampler.h
sfizz/Panning.h
sfizz/PolyphonyGroup.h
sfizz/PowerFollower.h
sfizz/railsback/2-1.h
sfizz/railsback/4-1.h
sfizz/railsback/4-2.h
@ -138,6 +139,7 @@ set (SFIZZ_SOURCES
sfizz/Effects.cpp
sfizz/LFO.cpp
sfizz/LFODescription.cpp
sfizz/PowerFollower.cpp
sfizz/modulations/ModId.cpp
sfizz/modulations/ModKey.cpp
sfizz/modulations/ModKeyHash.cpp

View file

@ -0,0 +1,75 @@
// SPDX-License-Identifier: BSD-2-Clause
// This code is part of the sfizz library and is licensed under a BSD 2-clause
// license. You should have receive a LICENSE.md file along with the code.
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
#include "PowerFollower.h"
#include "Defaults.h"
#include "SIMDHelpers.h"
#include <absl/types/span.h>
namespace sfz {
PowerFollower::PowerFollower()
: sampleRate_(config::defaultSampleRate),
samplesPerBlock_(config::defaultSamplesPerBlock),
tempBuffer_(new float[config::defaultSamplesPerBlock])
{
updateTrackingFactor();
}
void PowerFollower::setSampleRate(float sampleRate) noexcept
{
if (sampleRate_ != sampleRate) {
sampleRate_ = sampleRate;
updateTrackingFactor();
}
}
void PowerFollower::setSamplesPerBlock(unsigned samplesPerBlock)
{
if (samplesPerBlock_ != samplesPerBlock) {
tempBuffer_.reset(new float[samplesPerBlock]);
samplesPerBlock_ = samplesPerBlock;
updateTrackingFactor();
}
}
void PowerFollower::process(AudioSpan<float> buffer) noexcept
{
size_t numFrames = buffer.getNumFrames();
if (numFrames == 0)
return;
absl::Span<float> tempBuffer(tempBuffer_.get(), numFrames);
copy(buffer.getConstSpan(0), tempBuffer);
for (unsigned i = 1; i < buffer.getNumChannels(); ++i)
add(buffer.getConstSpan(i), tempBuffer);
const float meanPower = meanSquared<float>(tempBuffer);
const float attackFactor = static_cast<float>(buffer.getNumFrames()) * attackTrackingFactor_;
const float releaseFactor = static_cast<float>(buffer.getNumFrames()) * releaseTrackingFactor_;
meanChannelPower_ = max(
meanChannelPower_ * (1 - attackFactor) + meanPower * attackFactor,
meanChannelPower_ * (1 - releaseFactor) + meanPower * releaseFactor
);
}
void PowerFollower::clear() noexcept
{
meanChannelPower_ = 0;
}
void PowerFollower::updateTrackingFactor() noexcept
{
// Protect the envelope follower against blowups
const auto maxTrackingFactor = sampleRate_ / samplesPerBlock_;
attackTrackingFactor_ = min(config::powerFollowerAttackFactor, maxTrackingFactor) / sampleRate_;
releaseTrackingFactor_ = min(config::powerFollowerReleaseFactor, maxTrackingFactor) / sampleRate_;
}
} // namespace sfz

37
src/sfizz/PowerFollower.h Normal file
View file

@ -0,0 +1,37 @@
// SPDX-License-Identifier: BSD-2-Clause
// This code is part of the sfizz library and is licensed under a BSD 2-clause
// license. You should have receive a LICENSE.md file along with the code.
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
#pragma once
#include "AudioSpan.h"
#include <memory>
namespace sfz {
class PowerFollower {
public:
PowerFollower();
void setSampleRate(float sampleRate) noexcept;
void setSamplesPerBlock(unsigned samplesPerBlock);
void process(AudioSpan<float> buffer) noexcept;
void clear() noexcept;
float getAveragePower() const noexcept { return meanChannelPower_; }
private:
void updateTrackingFactor() noexcept;
private:
float sampleRate_ {};
unsigned samplesPerBlock_ {};
std::unique_ptr<float[]> tempBuffer_;
float attackTrackingFactor_ {};
float releaseTrackingFactor_ {};
float meanChannelPower_ {};
};
} // namespace sfz

View file

@ -237,14 +237,6 @@ void sfz::Voice::registerTempo(int delay, float secondsPerQuarter) noexcept
UNUSED(secondsPerQuarter);
}
void sfz::Voice::updateTrackingFactor() noexcept
{
// Protect the envelope follower against blowups
const auto maxTrackingFactor = sampleRate / samplesPerBlock;
attackTrackingFactor = min(config::powerFollowerAttackFactor, maxTrackingFactor) / sampleRate;
releaseTrackingFactor = min(config::powerFollowerReleaseFactor, maxTrackingFactor) / sampleRate;
}
void sfz::Voice::setSampleRate(float sampleRate) noexcept
{
this->sampleRate = sampleRate;
@ -257,13 +249,13 @@ void sfz::Voice::setSampleRate(float sampleRate) noexcept
for (auto& lfo : lfos)
lfo->setSampleRate(sampleRate);
updateTrackingFactor();
powerFollower.setSampleRate(sampleRate);
}
void sfz::Voice::setSamplesPerBlock(int samplesPerBlock) noexcept
{
this->samplesPerBlock = samplesPerBlock;
updateTrackingFactor();
powerFollower.setSamplesPerBlock(samplesPerBlock);
}
void sfz::Voice::renderBlock(AudioSpan<float> buffer) noexcept
@ -299,7 +291,7 @@ void sfz::Voice::renderBlock(AudioSpan<float> buffer) noexcept
if (!egEnvelope.isSmoothing())
switchState(State::cleanMeUp);
updateChannelPowers(buffer);
powerFollower.process(buffer);
age += buffer.getNumFrames();
if (triggerDelay) {
@ -740,7 +732,7 @@ void sfz::Voice::reset() noexcept
floatPositionOffset = 0.0f;
noteIsOff = false;
meanChannelPower = 0.0f;
powerFollower.clear();
filters.clear();
equalizers.clear();
@ -772,7 +764,7 @@ void sfz::Voice::removeVoiceFromRing() noexcept
float sfz::Voice::getAveragePower() const noexcept
{
return meanChannelPower;
return powerFollower.getAveragePower();
}
bool sfz::Voice::releasedOrFree() const noexcept
@ -867,31 +859,6 @@ void sfz::Voice::setupOscillatorUnison()
#endif
}
void sfz::Voice::updateChannelPowers(AudioSpan<float> buffer)
{
if (buffer.getNumFrames() == 0)
return;
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<float>(*tempBuffer);
const float attackFactor = static_cast<float>(buffer.getNumFrames()) * attackTrackingFactor;
const float releaseFactor = static_cast<float>(buffer.getNumFrames()) * releaseTrackingFactor;
meanChannelPower = max(
meanChannelPower * (1 - attackFactor) + meanPower * attackFactor,
meanChannelPower * (1 - releaseFactor) + meanPower * releaseFactor
);
}
void sfz::Voice::switchState(State s)
{
if (s != state) {

View file

@ -15,6 +15,7 @@
#include "AudioSpan.h"
#include "LeakDetector.h"
#include "OnePoleFilter.h"
#include "PowerFollower.h"
#include "NumericId.h"
#include "absl/types/span.h"
#include <memory>
@ -485,15 +486,8 @@ private:
Smoother xfadeSmoother;
void resetSmoothers() noexcept;
/**
* @brief Update and clamp the tracking factors to ensure the power
* follower does not blow up.
*
*/
void updateTrackingFactor() noexcept;
float attackTrackingFactor { config::powerFollowerAttackFactor / config::defaultSampleRate };
float releaseTrackingFactor { config::powerFollowerReleaseFactor / config::defaultSampleRate };
float meanChannelPower;
PowerFollower powerFollower;
LEAK_DETECTOR(Voice);
};