Merge pull request #384 from paulfd/simpler-envelope-follower

Simpler power/envelope follower
This commit is contained in:
Paul Ferrand 2020-08-27 17:03:18 +02:00 committed by GitHub
commit 81169b8209
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 335 additions and 72 deletions

View file

@ -34,7 +34,7 @@ BENCHMARK_DEFINE_F(MeanSquaredArray, Scalar)
(benchmark::State& state)
{
for (auto _ : state) {
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::meanSquared, false);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::sumSquares, false);
auto result = sfz::meanSquared<float>(input);
benchmark::DoNotOptimize(result);
}
@ -44,7 +44,7 @@ BENCHMARK_DEFINE_F(MeanSquaredArray, SIMD)
(benchmark::State& state)
{
for (auto _ : state) {
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::meanSquared, true);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::sumSquares, true);
auto result = sfz::meanSquared<float>(input);
benchmark::DoNotOptimize(result);
}
@ -54,7 +54,7 @@ BENCHMARK_DEFINE_F(MeanSquaredArray, Scalar_Unaligned)
(benchmark::State& state)
{
for (auto _ : state) {
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::meanSquared, false);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::sumSquares, false);
auto result = sfz::meanSquared<float>(absl::MakeSpan(input).subspan(1));
benchmark::DoNotOptimize(result);
}
@ -64,7 +64,7 @@ BENCHMARK_DEFINE_F(MeanSquaredArray, SIMD_Unaligned)
(benchmark::State& state)
{
for (auto _ : state) {
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::meanSquared, true);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::sumSquares, true);
auto result = sfz::meanSquared<float>(absl::MakeSpan(input).subspan(1));
benchmark::DoNotOptimize(result);
}

View file

@ -0,0 +1,125 @@
// 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 "AudioBuffer.h"
#include "Config.h"
#include <benchmark/benchmark.h>
#include <cmath>
class PowerFollowerFixture : public benchmark::Fixture {
public:
PowerFollowerFixture()
{
inputSignal_ = sfz::AudioBuffer<float>(2, numFrames);
auto leftSignal = inputSignal_.getSpan(0);
auto rightSignal = inputSignal_.getSpan(1);
float phase = 0;
for (size_t i = 0; i < numFrames; ++i) {
constexpr float k2pi = 2.0 * M_PI;
leftSignal[i] = std::sin(k2pi * phase);
rightSignal[i] = std::cos(k2pi * phase);
phase += 440.0f / sfz::config::defaultSampleRate;
phase -= static_cast<int>(phase);
}
}
void SetUp(const ::benchmark::State& state)
{
auto blockSize = static_cast<size_t>(state.range(0));
follower_.setSampleRate(sfz::config::defaultSampleRate);
follower_.setSamplesPerBlock(blockSize);
follower_.clear();
//
refFollower_.init(sfz::config::defaultSampleRate);
refFollower_.clear();
}
void TearDown(const ::benchmark::State& /* state */)
{
}
static constexpr size_t numFrames = 65536;
sfz::PowerFollower follower_;
sfz::AudioBuffer<float> inputSignal_;
//
struct ReferenceFollower {
/*
import("stdfaust.lib");
process = (_, _) : + : an.amp_follower_ud(att, rel) with { att = 5e-3; rel = 200e-3; };
*/
void init(float sampleRate)
{
fConst0 = std::min<float>(192000.0f, std::max<float>(1.0f, float(sampleRate)));
fConst1 = std::exp((0.0f - (200.0f / fConst0)));
fConst2 = (1.0f - fConst1);
fConst3 = std::exp((0.0f - (5.0f / fConst0)));
fConst4 = (1.0f - fConst3);
}
void clear()
{
for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) {
fRec1[l0] = 0.0f;
}
for (int l1 = 0; (l1 < 2); l1 = (l1 + 1)) {
fRec0[l1] = 0.0f;
}
}
float process(float input0, float input1)
{
float fTemp0 = std::fabs((float(input0) + float(input1)));
fRec1[0] = std::max<float>(fTemp0, ((fConst3 * fRec1[1]) + (fConst4 * fTemp0)));
fRec0[0] = ((fConst1 * fRec0[1]) + (fConst2 * fRec1[0]));
float output = fRec0[0];
fRec1[1] = fRec1[0];
fRec0[1] = fRec0[0];
return output;
}
float fConst0;
float fConst1;
float fConst2;
float fConst3;
float fConst4;
float fRec1[2];
float fRec0[2];
};
ReferenceFollower refFollower_;
};
constexpr size_t PowerFollowerFixture::numFrames;
BENCHMARK_DEFINE_F(PowerFollowerFixture, ReferenceFollower) (benchmark::State& state)
{
sfz::AudioSpan<float> inputSignal(inputSignal_);
auto input0 = inputSignal.getConstSpan(0);
auto input1 = inputSignal.getConstSpan(1);
for (auto _ : state) {
auto& follower = refFollower_;
float output = 0;
for (size_t i = 0, n = inputSignal.getNumFrames(); i < n; ++i)
output = follower.process(input0[i], input1[i]);
benchmark::DoNotOptimize(output);
}
}
BENCHMARK_DEFINE_F(PowerFollowerFixture, Follower) (benchmark::State& state)
{
sfz::AudioSpan<float> inputSignal(inputSignal_);
for (auto _ : state) {
auto& follower = follower_;
auto blockSize = static_cast<size_t>(state.range(0));
for (size_t i = 0; i < numFrames; i += blockSize)
follower.process(inputSignal.subspan(i, blockSize));
}
}
BENCHMARK_REGISTER_F(PowerFollowerFixture, ReferenceFollower)->Range(1, 1);
BENCHMARK_REGISTER_F(PowerFollowerFixture, Follower)->RangeMultiplier(2)->Range(1 << 5, 1 << 12);

View file

@ -66,6 +66,8 @@ sfizz_add_benchmark(bm_logger BM_logger.cpp)
target_link_libraries(bm_logger PRIVATE sfizz::sfizz)
sfizz_add_benchmark(bm_smoothers BM_smoothers.cpp)
target_link_libraries(bm_smoothers PRIVATE sfizz::sfizz)
sfizz_add_benchmark(bm_powerFollower BM_powerFollower.cpp)
target_link_libraries(bm_powerFollower PRIVATE sfizz::sfizz)
if (TARGET sfizz-samplerate)
sfizz_add_benchmark(bm_resample BM_resample.cpp ${BENCHMARK_SIMD_SOURCES})

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

@ -57,7 +57,9 @@ namespace config {
constexpr Oversampling defaultOversamplingFactor { Oversampling::x1 };
constexpr float A440 { 440.0 };
constexpr size_t powerHistoryLength { 16 };
constexpr float filteredEnvelopeCutoff { 5 };
constexpr size_t powerFollowerStep { 512 };
constexpr float powerFollowerAttackTime { 5e-3f };
constexpr float powerFollowerReleaseTime { 200e-3f };
constexpr uint16_t numCCs { 512 };
constexpr int maxCurves { 256 };
constexpr int chunkSize { 1024 };
@ -72,10 +74,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 };

View file

@ -0,0 +1,98 @@
// 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;
}
}
void PowerFollower::process(AudioSpan<float> buffer) noexcept
{
size_t numFrames = buffer.getNumFrames();
if (numFrames == 0)
return;
///
constexpr size_t step = config::powerFollowerStep;
float currentPower = currentPower_;
float currentSum = currentSum_;
size_t currentCount = currentCount_;
const float attackFactor = attackTrackingFactor_;
const float releaseFactor = releaseTrackingFactor_;
///
size_t index = 0;
while (index < numFrames) {
size_t blockSize = std::min(step - currentCount, numFrames - index);
absl::Span<float> tempBuffer(tempBuffer_.get(), blockSize);
copy(buffer.getConstSpan(0).subspan(index, blockSize), tempBuffer);
for (unsigned i = 1, n = buffer.getNumChannels(); i < n; ++i)
add(buffer.getConstSpan(i).subspan(index, blockSize), tempBuffer);
currentSum += sumSquares<float>(tempBuffer);
currentCount += blockSize;
if (currentCount == step) {
const float meanPower = currentSum / step;
currentPower = max(
currentPower * attackFactor + meanPower * (1 - attackFactor),
currentPower * releaseFactor + meanPower * (1 - releaseFactor));
currentSum = 0;
currentCount = 0;
}
index += blockSize;
}
///
currentPower_ = currentPower;
currentSum_ = currentSum;
currentCount_ = currentCount;
}
void PowerFollower::clear() noexcept
{
currentPower_ = 0;
currentSum_ = 0;
currentCount_ = 0;
}
void PowerFollower::updateTrackingFactor() noexcept
{
// Protect the envelope follower against blowups
attackTrackingFactor_ = std::exp(-1.0f / ((config::powerFollowerAttackTime / config::powerFollowerStep) * sampleRate_));
releaseTrackingFactor_ = std::exp(-1.0f / ((config::powerFollowerReleaseTime / config::powerFollowerStep) * sampleRate_));
}
} // namespace sfz

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

@ -0,0 +1,39 @@
// 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 currentPower_; }
private:
void updateTrackingFactor() noexcept;
private:
float sampleRate_ {};
unsigned samplesPerBlock_ {};
std::unique_ptr<float[]> tempBuffer_;
float attackTrackingFactor_ {};
float releaseTrackingFactor_ {};
float currentPower_ {};
float currentSum_ = 0;
size_t currentCount_ = 0;
};
} // namespace sfz

View file

@ -39,7 +39,7 @@ struct SIMDDispatch {
decltype(&cumsumScalar<T>) cumsum = &cumsumScalar<T>;
decltype(&diffScalar<T>) diff = &diffScalar<T>;
decltype(&meanScalar<T>) mean = &meanScalar<T>;
decltype(&meanSquaredScalar<T>) meanSquared = &meanSquaredScalar<T>;
decltype(&sumSquaresScalar<T>) sumSquares = &sumSquaresScalar<T>;
decltype(&clampAllScalar<T>) clampAll = &clampAllScalar<T>;
decltype(&allWithinScalar<T>) allWithin = &allWithinScalar<T>;
@ -85,7 +85,7 @@ void SIMDDispatch<float>::setStatus(SIMDOps op, bool enable)
SIMD_OP(cumsum)
SIMD_OP(diff)
SIMD_OP(mean)
SIMD_OP(meanSquared)
SIMD_OP(sumSquares)
SIMD_OP(clampAll)
SIMD_OP(allWithin)
}
@ -122,7 +122,7 @@ void SIMDDispatch<float>::setStatus(SIMDOps op, bool enable)
SIMD_OP(cumsum)
SIMD_OP(diff)
SIMD_OP(mean)
SIMD_OP(meanSquared)
SIMD_OP(sumSquares)
SIMD_OP(clampAll)
SIMD_OP(allWithin)
}
@ -163,7 +163,7 @@ void SIMDDispatch<float>::resetStatus()
setStatus(SIMDOps::diff, false);
setStatus(SIMDOps::sfzInterpolationCast, true);
setStatus(SIMDOps::mean, false);
setStatus(SIMDOps::meanSquared, false);
setStatus(SIMDOps::sumSquares, false);
setStatus(SIMDOps::upsampling, true);
setStatus(SIMDOps::clampAll, false);
setStatus(SIMDOps::allWithin, true);
@ -292,9 +292,9 @@ float mean<float>(const float* vector, unsigned size) noexcept
}
template <>
float meanSquared<float>(const float* vector, unsigned size) noexcept
float sumSquares<float>(const float* vector, unsigned size) noexcept
{
return simdDispatch<float>().meanSquared(vector, size);
return simdDispatch<float>().sumSquares(vector, size);
}
template <>

View file

@ -57,7 +57,7 @@ enum class SIMDOps {
diff,
sfzInterpolationCast,
mean,
meanSquared,
sumSquares,
upsampling,
clampAll,
allWithin,
@ -515,7 +515,7 @@ T mean(absl::Span<const T> vector) noexcept
}
/**
* @brief Computes the mean squared of a span
* @brief Computes the sum of squares of a span
*
* @tparam T the underlying type
* @tparam SIMD use the SIMD version or the scalar version
@ -523,13 +523,33 @@ T mean(absl::Span<const T> vector) noexcept
* @return T
*/
template <class T>
T meanSquared(const T* vector, unsigned size) noexcept
T sumSquares(const T* vector, unsigned size) noexcept
{
meanSquaredScalar(vector, size);
return sumSquaresScalar(vector, size);
}
template <>
float meanSquared<float>(const float* vector, unsigned size) noexcept;
float sumSquares<float>(const float* vector, unsigned size) noexcept;
template <class T>
T sumSquares(absl::Span<const T> vector) noexcept
{
return sumSquares(vector.data(), vector.size());
}
/**
* @brief Computes the mean squared of a span
*
* @tparam T the underlying type
* @param vector
* @return T
*/
template <class T>
T meanSquared(const T* vector, unsigned size) noexcept
{
T sum = sumSquares(vector, size);
return sum / size;
}
template <class T>
T meanSquared(absl::Span<const T> vector) noexcept

View file

@ -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()
@ -252,20 +249,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);
powerFollower.setSampleRate(sampleRate);
}
void sfz::Voice::setSamplesPerBlock(int samplesPerBlock) noexcept
{
this->samplesPerBlock = samplesPerBlock;
this->minEnvelopeDelay = samplesPerBlock / 2;
powerFollower.setSamplesPerBlock(samplesPerBlock);
}
void sfz::Voice::renderBlock(AudioSpan<float> buffer) noexcept
@ -301,7 +297,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) {
@ -739,11 +735,7 @@ void sfz::Voice::reset() noexcept
floatPositionOffset = 0.0f;
noteIsOff = false;
for (auto& f : channelEnvelopeFilters)
f.reset();
for (auto& p : smoothedChannelEnvelopes)
p = 0.0f;
powerFollower.clear();
filters.clear();
equalizers.clear();
@ -773,9 +765,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 powerFollower.getAveragePower();
}
bool sfz::Voice::releasedOrFree() const noexcept
@ -870,22 +862,6 @@ void sfz::Voice::setupOscillatorUnison()
#endif
}
void sfz::Voice::updateChannelPowers(AudioSpan<float> 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 auto input = buffer.getConstSpan(i);
for (unsigned s = 0; s < buffer.getNumFrames(); ++s)
smoothedChannelEnvelopes[i] =
channelEnvelopeFilters[i].tickLowpass(std::abs(input[s]));
}
}
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>
@ -261,7 +262,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 +451,6 @@ private:
FilePromisePtr currentPromise { nullptr };
int samplesPerBlock { config::defaultSamplesPerBlock };
int minEnvelopeDelay { config::defaultSamplesPerBlock / 2 };
float sampleRate { config::defaultSampleRate };
Resources& resources;
@ -486,10 +486,8 @@ private:
Smoother xfadeSmoother;
void resetSmoothers() noexcept;
std::array<OnePoleFilter<float>, 2> channelEnvelopeFilters;
std::array<float, 2> smoothedChannelEnvelopes;
PowerFollower powerFollower;
HistoricalBuffer<float> powerHistory { config::powerHistoryLength };
LEAK_DETECTOR(Voice);
};

View file

@ -13,13 +13,13 @@ sfz::Voice* sfz::VoiceStealing::steal(absl::Span<sfz::Voice*> 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
/ static_cast<float>(voices.size()) * config::stealingEnvelopeCoeff;
const auto powerThreshold = sumPower
/ static_cast<float>(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
@ -37,12 +37,12 @@ sfz::Voice* sfz::VoiceStealing::steal(absl::Span<sfz::Voice*> 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;
}

View file

@ -370,7 +370,7 @@ float meanSSE(const float* vector, unsigned size) noexcept
return result / static_cast<float>(size);
}
float meanSquaredSSE(const float* vector, unsigned size) noexcept
float sumSquaresSSE(const float* vector, unsigned size) noexcept
{
const auto sentinel = vector + size;
@ -404,7 +404,7 @@ float meanSquaredSSE(const float* vector, unsigned size) noexcept
vector++;
}
return result / static_cast<float>(size);
return result;
}
void cumsumSSE(const float* input, float* output, unsigned size) noexcept

View file

@ -22,7 +22,7 @@ void subtractSSE(const float* input, float* output, unsigned size) noexcept;
void subtract1SSE(float value, float* output, unsigned size) noexcept;
void copySSE(const float* input, float* output, unsigned size) noexcept;
float meanSSE(const float* vector, unsigned size) noexcept;
float meanSquaredSSE(const float* vector, unsigned size) noexcept;
float sumSquaresSSE(const float* vector, unsigned size) noexcept;
void cumsumSSE(const float* input, float* output, unsigned size) noexcept;
void diffSSE(const float* input, float* output, unsigned size) noexcept;
void clampAllSSE(float* input, float low, float high, unsigned size) noexcept;

View file

@ -142,7 +142,7 @@ T meanScalar(const T* vector, unsigned size) noexcept
}
template <class T>
T meanSquaredScalar(const T* vector, unsigned size) noexcept
T sumSquaresScalar(const T* vector, unsigned size) noexcept
{
T result{ 0.0 };
if (size == 0)
@ -154,7 +154,7 @@ T meanSquaredScalar(const T* vector, unsigned size) noexcept
vector++;
}
return result / static_cast<T>(size);
return result;
}
template <class T>

View file

@ -690,9 +690,9 @@ TEST_CASE("[Helpers] Mean (SIMD vs scalar)")
TEST_CASE("[Helpers] Mean Squared")
{
std::array<float, 10> input { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f };
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::meanSquared, false);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::sumSquares, false);
REQUIRE(sfz::meanSquared<float>(input) == 38.5f);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::meanSquared, true);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::sumSquares, true);
REQUIRE(sfz::meanSquared<float>(input) == 38.5f);
}
@ -700,9 +700,9 @@ TEST_CASE("[Helpers] Mean Squared (SIMD vs scalar)")
{
std::vector<float> input(medBufferSize);
absl::c_iota(input, 0.0f);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::meanSquared, false);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::sumSquares, false);
auto scalarResult = sfz::meanSquared<float>(input);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::meanSquared, true);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::sumSquares, true);
auto simdResult = sfz::meanSquared<float>(input);
REQUIRE( scalarResult == Approx(simdResult).margin(1e-3) );
}