Simpler power/envelope followerThis one is much cheaper...
This commit is contained in:
parent
66705f54c3
commit
958d9bb5e8
4 changed files with 21 additions and 33 deletions
|
|
@ -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 };
|
||||||
|
|
|
||||||
|
|
@ -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]));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue