Merge pull request #264 from paulfd/simd-runtime

Revamp the SIMD helpers
This commit is contained in:
JP Cimalando 2020-06-19 21:24:06 +02:00 committed by GitHub
commit 0213a561d0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
70 changed files with 2101 additions and 4137 deletions

View file

@ -21,8 +21,6 @@ The sfizz library also uses in some subprojects:
- [benchmark], licensed under the Apache License 2.0
- [LV2], licensed under the ISC license
- [JACK], licensed under the GNU Lesser General Public License v2.1
- `neon_mathfun.h` and `sse_mathfun.h` by Julien Pommier,
licensed under the zlib license
[Abseil]: https://github.com/abseil/abseil-cpp
[atomic_queue]: https://github.com/max0x7ba/atomic_queue

View file

@ -36,56 +36,64 @@ public:
BENCHMARK_DEFINE_F(AddArray, Value_Scalar)(benchmark::State& state) {
for (auto _ : state)
{
sfz::add<float, false>(1.1f, absl::MakeSpan(output));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::add1, false);
sfz::add1<float>(1.1f, absl::MakeSpan(output));
}
}
BENCHMARK_DEFINE_F(AddArray, Value_SIMD)(benchmark::State& state) {
for (auto _ : state)
{
sfz::add<float, true>(1.1f, absl::MakeSpan(output));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::add1, true);
sfz::add1<float>(1.1f, absl::MakeSpan(output));
}
}
BENCHMARK_DEFINE_F(AddArray, Value_Scalar_Unaligned)(benchmark::State& state) {
for (auto _ : state)
{
sfz::add<float, false>(1.1f, absl::MakeSpan(output).subspan(1));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::add1, false);
sfz::add1<float>(1.1f, absl::MakeSpan(output).subspan(1));
}
}
BENCHMARK_DEFINE_F(AddArray, Value_SIMD_Unaligned)(benchmark::State& state) {
for (auto _ : state)
{
sfz::add<float, true>(1.1f, absl::MakeSpan(output).subspan(1));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::add1, true);
sfz::add1<float>(1.1f, absl::MakeSpan(output).subspan(1));
}
}
BENCHMARK_DEFINE_F(AddArray, Scalar)(benchmark::State& state) {
for (auto _ : state)
{
sfz::add<float, false>(input, absl::MakeSpan(output));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::add, false);
sfz::add<float>(input, absl::MakeSpan(output));
}
}
BENCHMARK_DEFINE_F(AddArray, SIMD)(benchmark::State& state) {
for (auto _ : state)
{
sfz::add<float, true>(input, absl::MakeSpan(output));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::add, true);
sfz::add<float>(input, absl::MakeSpan(output));
}
}
BENCHMARK_DEFINE_F(AddArray, Scalar_Unaligned)(benchmark::State& state) {
for (auto _ : state)
{
sfz::add<float, false>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::add, false);
sfz::add<float>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
}
}
BENCHMARK_DEFINE_F(AddArray, SIMD_Unaligned)(benchmark::State& state) {
for (auto _ : state)
{
sfz::add<float, true>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::add, true);
sfz::add<float>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
}
}

View file

@ -43,14 +43,16 @@ BENCHMARK_DEFINE_F(CopyArray, StdCopy)(benchmark::State& state) {
BENCHMARK_DEFINE_F(CopyArray, Scalar)(benchmark::State& state) {
for (auto _ : state)
{
sfz::copy<float, false>(input, absl::MakeSpan(output));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::copy, false);
sfz::copy<float>(input, absl::MakeSpan(output));
}
}
BENCHMARK_DEFINE_F(CopyArray, SIMD)(benchmark::State& state) {
for (auto _ : state)
{
sfz::copy<float, true>(input, absl::MakeSpan(output));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::copy, true);
sfz::copy<float>(input, absl::MakeSpan(output));
}
}
@ -64,14 +66,16 @@ BENCHMARK_DEFINE_F(CopyArray, StdCopy_Unaligned)(benchmark::State& state) {
BENCHMARK_DEFINE_F(CopyArray, Scalar_Unaligned)(benchmark::State& state) {
for (auto _ : state)
{
sfz::copy<float, false>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::copy, false);
sfz::copy<float>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
}
}
BENCHMARK_DEFINE_F(CopyArray, SIMD_Unaligned)(benchmark::State& state) {
for (auto _ : state)
{
sfz::copy<float, true>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::copy, true);
sfz::copy<float>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
}
}

View file

@ -35,28 +35,32 @@ public:
BENCHMARK_DEFINE_F(CumArray, Sum_Scalar)(benchmark::State& state) {
for (auto _ : state)
{
sfz::cumsum<float, false>(input, absl::MakeSpan(output));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::cumsum, false);
sfz::cumsum<float>(input, absl::MakeSpan(output));
}
}
BENCHMARK_DEFINE_F(CumArray, Sum_SIMD)(benchmark::State& state) {
for (auto _ : state)
{
sfz::cumsum<float, true>(input, absl::MakeSpan(output));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::cumsum, true);
sfz::cumsum<float>(input, absl::MakeSpan(output));
}
}
BENCHMARK_DEFINE_F(CumArray, Sum_Scalar_Unaligned)(benchmark::State& state) {
for (auto _ : state)
{
sfz::cumsum<float, false>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::cumsum, false);
sfz::cumsum<float>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
}
}
BENCHMARK_DEFINE_F(CumArray, Sum_SIMD_Unaligned)(benchmark::State& state) {
for (auto _ : state)
{
sfz::cumsum<float, true>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::cumsum, true);
sfz::cumsum<float>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
}
}

View file

@ -22,7 +22,7 @@ public:
input = std::vector<float>(state.range(0));
output = std::vector<float>(state.range(0));
std::generate(input.begin(), input.end(), [&]() { return dist(gen); });
sfz::cumsum<float, false>(input, absl::MakeSpan(input));
sfz::cumsum<float>(input, absl::MakeSpan(input));
}
void TearDown(const ::benchmark::State& /* state */) {
@ -37,28 +37,32 @@ public:
BENCHMARK_DEFINE_F(DiffArray, Diff_Scalar)(benchmark::State& state) {
for (auto _ : state)
{
sfz::diff<float, false>(input, absl::MakeSpan(output));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::diff, false);
sfz::diff<float>(input, absl::MakeSpan(output));
}
}
BENCHMARK_DEFINE_F(DiffArray, Diff_SIMD)(benchmark::State& state) {
for (auto _ : state)
{
sfz::diff<float, true>(input, absl::MakeSpan(output));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::diff, true);
sfz::diff<float>(input, absl::MakeSpan(output));
}
}
BENCHMARK_DEFINE_F(DiffArray, Diff_Scalar_Unaligned)(benchmark::State& state) {
for (auto _ : state)
{
sfz::diff<float, false>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::diff, false);
sfz::diff<float>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
}
}
BENCHMARK_DEFINE_F(DiffArray, Diff_SIMD_Unaligned)(benchmark::State& state) {
for (auto _ : state)
{
sfz::diff<float, true>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::diff, true);
sfz::diff<float>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
}
}

View file

@ -46,28 +46,32 @@ BENCHMARK_DEFINE_F(Divide, Straight)(benchmark::State& state) {
BENCHMARK_DEFINE_F(Divide, Scalar)(benchmark::State& state) {
for (auto _ : state)
{
sfz::divide<float, false>(input, divisor, absl::MakeSpan(output));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::divide, false);
sfz::divide<float>(input, divisor, absl::MakeSpan(output));
}
}
BENCHMARK_DEFINE_F(Divide, SIMD)(benchmark::State& state) {
for (auto _ : state)
{
sfz::divide<float, true>(input, divisor, absl::MakeSpan(output));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::divide, true);
sfz::divide<float>(input, divisor, absl::MakeSpan(output));
}
}
BENCHMARK_DEFINE_F(Divide, Scalar_Unaligned)(benchmark::State& state) {
for (auto _ : state)
{
sfz::divide<float, false>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(divisor).subspan(1), absl::MakeSpan(output).subspan(1));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::divide, false);
sfz::divide<float>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(divisor).subspan(1), absl::MakeSpan(output).subspan(1));
}
}
BENCHMARK_DEFINE_F(Divide, SIMD_Unaligned)(benchmark::State& state) {
for (auto _ : state)
{
sfz::divide<float, true>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(divisor).subspan(1), absl::MakeSpan(output).subspan(1));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::divide, true);
sfz::divide<float>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(divisor).subspan(1), absl::MakeSpan(output).subspan(1));
}
}

View file

@ -20,7 +20,7 @@ public:
input = std::vector<float>(state.range(0));
output = std::vector<float>(state.range(0));
std::generate(input.begin(), input.end(), [&]() { return dist(gen); });
sfz::cumsum<float, false>(input, absl::MakeSpan(input));
sfz::cumsum<float>(input, absl::MakeSpan(input));
}
void TearDown(const ::benchmark::State& /* state */)

View file

@ -1,70 +0,0 @@
// 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 "SIMDHelpers.h"
#include <benchmark/benchmark.h>
#include "Buffer.h"
#include <algorithm>
#include <random>
#include <numeric>
static void Dummy(benchmark::State& state) {
sfz::Buffer<float> buffer (state.range(0));
std::random_device rd { };
std::mt19937 gen { rd() };
std::uniform_real_distribution<float> dist { 1, 2 };
for (auto _ : state) {
auto fillValue = dist(gen);
benchmark::DoNotOptimize(fillValue);
}
}
static void FillScalar(benchmark::State& state) {
sfz::Buffer<float> buffer (state.range(0));
std::random_device rd { };
std::mt19937 gen { rd() };
std::uniform_real_distribution<float> dist { 1, 2 };
for (auto _ : state) {
sfz::fill<float, false>(absl::MakeSpan(buffer), dist(gen));
}
}
static void FillScalar_unaligned(benchmark::State& state) {
sfz::Buffer<float> buffer (state.range(0));
std::random_device rd { };
std::mt19937 gen { rd() };
std::uniform_real_distribution<float> dist { 1, 2 };
for (auto _ : state) {
sfz::fill<float, false>(absl::MakeSpan(buffer).subspan(1), dist(gen));
}
}
static void FillSIMD(benchmark::State& state) {
sfz::Buffer<float> buffer (state.range(0));
std::random_device rd { };
std::mt19937 gen { rd() };
std::uniform_real_distribution<float> dist { 1, 2 };
for (auto _ : state) {
sfz::fill<float, true>(absl::MakeSpan(buffer), dist(gen));
}
}
static void FillSIMD_unaligned(benchmark::State& state) {
sfz::Buffer<float> buffer (state.range(0));
std::random_device rd { };
std::mt19937 gen { rd() };
std::uniform_real_distribution<float> dist { 1, 2 };
for (auto _ : state) {
sfz::fill<float, true>(absl::MakeSpan(buffer).subspan(1), dist(gen));
}
}
BENCHMARK(Dummy)->RangeMultiplier(4)->Range((1<<2), (1<<12));
BENCHMARK(FillScalar)->RangeMultiplier(4)->Range((1<<2), (1<<12));
BENCHMARK(FillSIMD)->RangeMultiplier(4)->Range((1<<2), (1<<12));
BENCHMARK(FillScalar_unaligned)->RangeMultiplier(4)->Range((1<<2), (1<<12));
BENCHMARK(FillSIMD_unaligned)->RangeMultiplier(4)->Range((1<<2), (1<<12));
BENCHMARK_MAIN();

View file

@ -66,14 +66,16 @@ BENCHMARK_DEFINE_F(GainSingle, Straight)(benchmark::State& state) {
BENCHMARK_DEFINE_F(GainSingle, Scalar)(benchmark::State& state) {
for (auto _ : state)
{
sfz::applyGain<float, false>(gain, input, absl::MakeSpan(output));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::gain1, false);
sfz::applyGain1<float>(gain, input, absl::MakeSpan(output));
}
}
BENCHMARK_DEFINE_F(GainSingle, SIMD)(benchmark::State& state) {
for (auto _ : state)
{
sfz::applyGain<float, true>(gain, input, absl::MakeSpan(output));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::gain1, true);
sfz::applyGain1<float>(gain, input, absl::MakeSpan(output));
}
}
@ -88,28 +90,32 @@ BENCHMARK_DEFINE_F(GainArray, Straight)(benchmark::State& state) {
BENCHMARK_DEFINE_F(GainArray, Scalar)(benchmark::State& state) {
for (auto _ : state)
{
sfz::applyGain<float, false>(gain, input, absl::MakeSpan(output));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::gain, false);
sfz::applyGain<float>(gain, input, absl::MakeSpan(output));
}
}
BENCHMARK_DEFINE_F(GainArray, SIMD)(benchmark::State& state) {
for (auto _ : state)
{
sfz::applyGain<float, true>(gain, input, absl::MakeSpan(output));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::gain, true);
sfz::applyGain<float>(gain, input, absl::MakeSpan(output));
}
}
BENCHMARK_DEFINE_F(GainArray, Scalar_Unaligned)(benchmark::State& state) {
for (auto _ : state)
{
sfz::applyGain<float, false>(absl::MakeSpan(gain).subspan(1), absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::gain, false);
sfz::applyGain<float>(absl::MakeSpan(gain).subspan(1), absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
}
}
BENCHMARK_DEFINE_F(GainArray, SIMD_Unaligned)(benchmark::State& state) {
for (auto _ : state)
{
sfz::applyGain<float, true>(absl::MakeSpan(gain).subspan(1), absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::gain, true);
sfz::applyGain<float>(absl::MakeSpan(gain).subspan(1), absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
}
}

View file

@ -1,74 +0,0 @@
// 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 "SIMDHelpers.h"
#include <benchmark/benchmark.h>
#include <vector>
#include <random>
#include <numeric>
#include <absl/algorithm/container.h>
// In this one we have an array of jumps
constexpr float maxJump { 4 };
class InterpolationCast : public benchmark::Fixture {
public:
void SetUp(const ::benchmark::State& state) {
std::random_device rd { };
std::mt19937 gen { rd() };
std::uniform_real_distribution<float> dist { 0, maxJump };
jumps = std::vector<int>(state.range(0));
coeffs = std::vector<float>(state.range(0));
floatJumps = std::vector<float>(state.range(0));
absl::c_generate(floatJumps, [&]() { return dist(gen); });
}
void TearDown(const ::benchmark::State& /* state */) {
}
std::vector<int> jumps;
std::vector<float> coeffs;
std::vector<float> floatJumps;
};
BENCHMARK_DEFINE_F(InterpolationCast, Scalar)(benchmark::State& state) {
for (auto _ : state)
{
sfz::sfzInterpolationCast<float, false>(floatJumps, absl::MakeSpan(jumps), absl::MakeSpan(coeffs));
}
}
BENCHMARK_DEFINE_F(InterpolationCast, SIMD)(benchmark::State& state) {
for (auto _ : state)
{
sfz::sfzInterpolationCast<float, true>(floatJumps, absl::MakeSpan(jumps), absl::MakeSpan(coeffs));
}
}
BENCHMARK_DEFINE_F(InterpolationCast, Scalar_Unaligned)(benchmark::State& state) {
for (auto _ : state)
{
sfz::sfzInterpolationCast<float, false>(absl::MakeSpan(floatJumps).subspan(1), absl::MakeSpan(jumps).subspan(3), absl::MakeSpan(coeffs).subspan(1));
}
}
BENCHMARK_DEFINE_F(InterpolationCast, SIMD_Unaligned)(benchmark::State& state) {
for (auto _ : state)
{
sfz::sfzInterpolationCast<float, true>(absl::MakeSpan(floatJumps).subspan(1), absl::MakeSpan(jumps).subspan(3), absl::MakeSpan(coeffs).subspan(1));
}
}
// Register the function as a benchmark
BENCHMARK_REGISTER_F(InterpolationCast, Scalar)->RangeMultiplier(2)->Range((2<<6), (2<<12));
BENCHMARK_REGISTER_F(InterpolationCast, SIMD)->RangeMultiplier(2)->Range((2<<6), (2<<12));
BENCHMARK_REGISTER_F(InterpolationCast, Scalar_Unaligned)->RangeMultiplier(2)->Range((2<<6), (2<<12));
BENCHMARK_REGISTER_F(InterpolationCast, SIMD_Unaligned)->RangeMultiplier(2)->Range((2<<6), (2<<12));
BENCHMARK_MAIN();

View file

@ -1,78 +0,0 @@
// 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 <benchmark/benchmark.h>
#include "SIMDHelpers.h"
#include <vector>
#include <random>
#include <numeric>
#include <absl/algorithm/container.h>
// In this one we have an array of indices
constexpr int loopStart { 5 };
constexpr int loopEnd { 1076 };
constexpr float maxJump { 4 };
class LoopingFixture : public benchmark::Fixture {
public:
void SetUp(const ::benchmark::State& state) {
std::random_device rd { };
std::mt19937 gen { rd() };
std::uniform_real_distribution<float> dist { 0, maxJump };
indices = std::vector<int>(state.range(0));
leftCoeffs = std::vector<float>(state.range(0));
rightCoeffs = std::vector<float>(state.range(0));
jumps = std::vector<float>(state.range(0));
absl::c_generate(jumps, [&]() { return dist(gen); });
}
void TearDown(const ::benchmark::State& /* state */) {
}
std::vector<int> indices;
std::vector<float> leftCoeffs;
std::vector<float> rightCoeffs;
std::vector<float> jumps;
};
BENCHMARK_DEFINE_F(LoopingFixture, Scalar)(benchmark::State& state) {
for (auto _ : state)
{
sfz::loopingSFZIndex<float, false>(jumps, absl::MakeSpan(leftCoeffs), absl::MakeSpan(rightCoeffs), absl::MakeSpan(indices), 2.5f, loopEnd, loopStart);
}
}
BENCHMARK_DEFINE_F(LoopingFixture, SIMD)(benchmark::State& state) {
for (auto _ : state)
{
sfz::loopingSFZIndex<float, true>(jumps, absl::MakeSpan(leftCoeffs), absl::MakeSpan(rightCoeffs), absl::MakeSpan(indices), 2.5f, loopEnd, loopStart);
}
}
BENCHMARK_DEFINE_F(LoopingFixture, Scalar_Unaligned)(benchmark::State& state) {
for (auto _ : state)
{
sfz::loopingSFZIndex<float, false>(absl::MakeSpan(jumps).subspan(1), absl::MakeSpan(leftCoeffs).subspan(2), absl::MakeSpan(rightCoeffs).subspan(1), absl::MakeSpan(indices).subspan(3), 2.5f, loopEnd, loopStart);
}
}
BENCHMARK_DEFINE_F(LoopingFixture, SIMD_Unaligned)(benchmark::State& state) {
for (auto _ : state)
{
sfz::loopingSFZIndex<float, true>(absl::MakeSpan(jumps).subspan(1), absl::MakeSpan(leftCoeffs).subspan(2), absl::MakeSpan(rightCoeffs).subspan(1), absl::MakeSpan(indices).subspan(3), 2.5f, loopEnd, loopStart);
}
}
// Register the function as a benchmark
BENCHMARK_REGISTER_F(LoopingFixture, Scalar)->RangeMultiplier(2)->Range((2<<6), (2<<12));
BENCHMARK_REGISTER_F(LoopingFixture, SIMD)->RangeMultiplier(2)->Range((2<<6), (2<<12));
BENCHMARK_REGISTER_F(LoopingFixture, Scalar_Unaligned)->RangeMultiplier(2)->Range((2<<6), (2<<12));
BENCHMARK_REGISTER_F(LoopingFixture, SIMD_Unaligned)->RangeMultiplier(2)->Range((2<<6), (2<<12));
BENCHMARK_MAIN();

View file

@ -45,96 +45,6 @@ BENCHMARK_DEFINE_F(MyFixture, Dummy)
}
}
BENCHMARK_DEFINE_F(MyFixture, ScalarExp)
(benchmark::State& state)
{
for (auto _ : state) {
sfz::exp<float, false>(source, absl::MakeSpan(result));
benchmark::DoNotOptimize(result);
}
}
BENCHMARK_DEFINE_F(MyFixture, SIMDExp)
(benchmark::State& state)
{
for (auto _ : state) {
sfz::exp<float, true>(source, absl::MakeSpan(result));
benchmark::DoNotOptimize(result);
}
}
BENCHMARK_DEFINE_F(MyFixture, ScalarExp_Unaligned)
(benchmark::State& state)
{
for (auto _ : state) {
sfz::exp<float, false>(absl::MakeSpan(source).subspan(1), absl::MakeSpan(result).subspan(1));
benchmark::DoNotOptimize(result);
}
}
BENCHMARK_DEFINE_F(MyFixture, SIMDExp_Unaligned)
(benchmark::State& state)
{
for (auto _ : state) {
sfz::exp<float, true>(absl::MakeSpan(source).subspan(1), absl::MakeSpan(result).subspan(1));
benchmark::DoNotOptimize(result);
}
}
BENCHMARK_DEFINE_F(MyFixture, ScalarLog)
(benchmark::State& state)
{
for (auto _ : state) {
sfz::log<float, false>(source, absl::MakeSpan(result));
benchmark::DoNotOptimize(result);
}
}
BENCHMARK_DEFINE_F(MyFixture, SIMDLog)
(benchmark::State& state)
{
for (auto _ : state) {
sfz::log<float, true>(source, absl::MakeSpan(result));
benchmark::DoNotOptimize(result);
}
}
BENCHMARK_DEFINE_F(MyFixture, ScalarSin)
(benchmark::State& state)
{
for (auto _ : state) {
sfz::sin<float, false>(source, absl::MakeSpan(result));
benchmark::DoNotOptimize(result);
}
}
BENCHMARK_DEFINE_F(MyFixture, SIMDSin)
(benchmark::State& state)
{
for (auto _ : state) {
sfz::sin<float, true>(source, absl::MakeSpan(result));
benchmark::DoNotOptimize(result);
}
}
BENCHMARK_DEFINE_F(MyFixture, ScalarCos)
(benchmark::State& state)
{
for (auto _ : state) {
sfz::cos<float, false>(source, absl::MakeSpan(result));
benchmark::DoNotOptimize(result);
}
}
BENCHMARK_DEFINE_F(MyFixture, SIMDCos)
(benchmark::State& state)
{
for (auto _ : state) {
sfz::cos<float, true>(source, absl::MakeSpan(result));
benchmark::DoNotOptimize(result);
}
}
BENCHMARK_DEFINE_F(MyFixture, ScalarLibmFloorLog2)
(benchmark::State& state)
{
@ -159,16 +69,6 @@ BENCHMARK_DEFINE_F(MyFixture, ScalarFastFloorLog2)
}
BENCHMARK_REGISTER_F(MyFixture, Dummy)->RangeMultiplier(4)->Range(1 << 6, 1 << 10);
BENCHMARK_REGISTER_F(MyFixture, ScalarExp)->RangeMultiplier(4)->Range(1 << 6, 1 << 10);
BENCHMARK_REGISTER_F(MyFixture, SIMDExp)->RangeMultiplier(4)->Range(1 << 6, 1 << 10);
BENCHMARK_REGISTER_F(MyFixture, ScalarExp_Unaligned)->RangeMultiplier(4)->Range(1 << 6, 1 << 10);
BENCHMARK_REGISTER_F(MyFixture, SIMDExp_Unaligned)->RangeMultiplier(4)->Range(1 << 6, 1 << 10);
BENCHMARK_REGISTER_F(MyFixture, ScalarLog)->RangeMultiplier(4)->Range(1 << 6, 1 << 10);
BENCHMARK_REGISTER_F(MyFixture, SIMDLog)->RangeMultiplier(4)->Range(1 << 6, 1 << 10);
BENCHMARK_REGISTER_F(MyFixture, ScalarSin)->RangeMultiplier(4)->Range(1 << 6, 1 << 10);
BENCHMARK_REGISTER_F(MyFixture, SIMDSin)->RangeMultiplier(4)->Range(1 << 6, 1 << 10);
BENCHMARK_REGISTER_F(MyFixture, ScalarCos)->RangeMultiplier(4)->Range(1 << 6, 1 << 10);
BENCHMARK_REGISTER_F(MyFixture, SIMDCos)->RangeMultiplier(4)->Range(1 << 6, 1 << 10);
BENCHMARK_REGISTER_F(MyFixture, ScalarLibmFloorLog2)->RangeMultiplier(4)->Range(1 << 6, 1 << 10);
BENCHMARK_REGISTER_F(MyFixture, ScalarFastFloorLog2)->RangeMultiplier(4)->Range(1 << 6, 1 << 10);

View file

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

View file

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

View file

@ -46,28 +46,32 @@ BENCHMARK_DEFINE_F(MultiplyAdd, Straight)(benchmark::State& state) {
BENCHMARK_DEFINE_F(MultiplyAdd, Scalar)(benchmark::State& state) {
for (auto _ : state)
{
sfz::multiplyAdd<float, false>(gain, input, absl::MakeSpan(output));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyAdd, false);
sfz::multiplyAdd<float>(gain, input, absl::MakeSpan(output));
}
}
BENCHMARK_DEFINE_F(MultiplyAdd, SIMD)(benchmark::State& state) {
for (auto _ : state)
{
sfz::multiplyAdd<float, true>(gain, input, absl::MakeSpan(output));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyAdd, true);
sfz::multiplyAdd<float>(gain, input, absl::MakeSpan(output));
}
}
BENCHMARK_DEFINE_F(MultiplyAdd, Scalar_Unaligned)(benchmark::State& state) {
for (auto _ : state)
{
sfz::multiplyAdd<float, false>(absl::MakeSpan(gain).subspan(1), absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyAdd, false);
sfz::multiplyAdd<float>(absl::MakeSpan(gain).subspan(1), absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
}
}
BENCHMARK_DEFINE_F(MultiplyAdd, SIMD_Unaligned)(benchmark::State& state) {
for (auto _ : state)
{
sfz::multiplyAdd<float, true>(absl::MakeSpan(gain).subspan(1), absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyAdd, true);
sfz::multiplyAdd<float>(absl::MakeSpan(gain).subspan(1), absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
}
}

View file

@ -48,7 +48,8 @@ BENCHMARK_DEFINE_F(MultiplyAddFixedGain, Scalar)
(benchmark::State& state)
{
for (auto _ : state) {
sfz::multiplyAdd<float, false>(gain, input, absl::MakeSpan(output));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyAdd1, false);
sfz::multiplyAdd1<float>(gain, input, absl::MakeSpan(output));
}
}
@ -56,7 +57,8 @@ BENCHMARK_DEFINE_F(MultiplyAddFixedGain, SIMD)
(benchmark::State& state)
{
for (auto _ : state) {
sfz::multiplyAdd<float, true>(gain, input, absl::MakeSpan(output));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyAdd1, true);
sfz::multiplyAdd1<float>(gain, input, absl::MakeSpan(output));
}
}
@ -64,7 +66,8 @@ BENCHMARK_DEFINE_F(MultiplyAddFixedGain, Scalar_Unaligned)
(benchmark::State& state)
{
for (auto _ : state) {
sfz::multiplyAdd<float, false>(gain, absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyAdd1, false);
sfz::multiplyAdd1<float>(gain, absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
}
}
@ -72,7 +75,8 @@ BENCHMARK_DEFINE_F(MultiplyAddFixedGain, SIMD_Unaligned)
(benchmark::State& state)
{
for (auto _ : state) {
sfz::multiplyAdd<float, true>(gain, absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyAdd1, true);
sfz::multiplyAdd1<float>(gain, absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
}
}

View file

@ -1,83 +0,0 @@
// 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 "SIMDHelpers.h"
#include <benchmark/benchmark.h>
#include <random>
#include <numeric>
#include <vector>
#include <cmath>
#include <iostream>
#include "Config.h"
#include "ScopedFTZ.h"
#include "absl/types/span.h"
class PanArray : public benchmark::Fixture {
public:
void SetUp(const ::benchmark::State& state) {
std::random_device rd { };
std::mt19937 gen { rd() };
std::uniform_real_distribution<float> dist { 0.001f, 1.0f };
pan = std::vector<float>(state.range(0));
left = std::vector<float>(state.range(0));
right = std::vector<float>(state.range(0));
std::generate(pan.begin(), pan.end(), [&]() { return dist(gen); });
std::generate(right.begin(), right.end(), [&]() { return dist(gen); });
std::generate(left.begin(), left.end(), [&]() { return dist(gen); });
temp1 = std::vector<float>(state.range(0));
temp2 = std::vector<float>(state.range(0));
span1 = absl::MakeSpan(temp1);
span2 = absl::MakeSpan(temp2);
}
void TearDown(const ::benchmark::State& /* state */) {
}
std::vector<float> pan;
std::vector<float> left;
std::vector<float> right;
std::vector<float> temp1;
std::vector<float> temp2;
absl::Span<float> span1;
absl::Span<float> span2;
};
BENCHMARK_DEFINE_F(PanArray, Scalar)(benchmark::State& state) {
ScopedFTZ ftz;
for (auto _ : state)
{
sfz::pan<float, false>(pan, absl::MakeSpan(left), absl::MakeSpan(right));
}
}
BENCHMARK_DEFINE_F(PanArray, SIMD)(benchmark::State& state) {
ScopedFTZ ftz;
for (auto _ : state)
{
sfz::pan<float, true>(pan, absl::MakeSpan(left), absl::MakeSpan(right));
}
}
BENCHMARK_DEFINE_F(PanArray, BlockOps)(benchmark::State& state) {
ScopedFTZ ftz;
for (auto _ : state)
{
sfz::fill<float>(span2, 1.0f);
sfz::add<float>(span1, span2);
sfz::applyGain<float>(piFour<float>(), span2);
sfz::cos<float>(span2, span1);
sfz::sin<float>(span2, span2);
sfz::applyGain<float>(span1, absl::MakeSpan(left));
sfz::applyGain<float>(span2, absl::MakeSpan(right));
}
}
BENCHMARK_REGISTER_F(PanArray, Scalar)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(PanArray, SIMD)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(PanArray, BlockOps)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_MAIN();

View file

@ -30,7 +30,8 @@ static void LinearScalar(benchmark::State& state) {
for (auto _ : state)
{
auto value = dist(gen);
sfz::linearRamp<float, false>(absl::MakeSpan(output), 0.0f, value);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::linearRamp, false);
sfz::linearRamp<float>(absl::MakeSpan(output), 0.0f, value);
}
}
@ -42,7 +43,8 @@ static void LinearSIMD(benchmark::State& state) {
for (auto _ : state)
{
auto value = dist(gen);
sfz::linearRamp<float, true>(absl::MakeSpan(output), 0.0f, value);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::linearRamp, true);
sfz::linearRamp<float>(absl::MakeSpan(output), 0.0f, value);
}
}
static void LinearScalarUnaligned(benchmark::State& state) {
@ -53,7 +55,8 @@ static void LinearScalarUnaligned(benchmark::State& state) {
for (auto _ : state)
{
auto value = dist(gen);
sfz::linearRamp<float, false>(absl::MakeSpan(output).subspan(1), 0.0f, value);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::linearRamp, false);
sfz::linearRamp<float>(absl::MakeSpan(output).subspan(1), 0.0f, value);
}
}
@ -65,7 +68,8 @@ static void LinearSIMDUnaligned(benchmark::State& state) {
for (auto _ : state)
{
auto value = dist(gen);
sfz::linearRamp<float, true>(absl::MakeSpan(output).subspan(1), 0.0f, value);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::linearRamp, true);
sfz::linearRamp<float>(absl::MakeSpan(output).subspan(1), 0.0f, value);
}
}
@ -77,7 +81,8 @@ static void MulScalar(benchmark::State& state) {
for (auto _ : state)
{
auto value = dist(gen);
sfz::multiplicativeRamp<float, false>(absl::MakeSpan(output), 1.0f, value);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplicativeRamp, false);
sfz::multiplicativeRamp<float>(absl::MakeSpan(output), 1.0f, value);
}
}
@ -89,7 +94,8 @@ static void MulSIMD(benchmark::State& state) {
for (auto _ : state)
{
auto value = dist(gen);
sfz::multiplicativeRamp<float, true>(absl::MakeSpan(output), 1.0f, value);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplicativeRamp, true);
sfz::multiplicativeRamp<float>(absl::MakeSpan(output), 1.0f, value);
}
}
static void MulScalarUnaligned(benchmark::State& state) {
@ -100,7 +106,8 @@ static void MulScalarUnaligned(benchmark::State& state) {
for (auto _ : state)
{
auto value = dist(gen);
sfz::multiplicativeRamp<float, false>(absl::MakeSpan(output).subspan(1), 1.0f, value);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplicativeRamp, false);
sfz::multiplicativeRamp<float>(absl::MakeSpan(output).subspan(1), 1.0f, value);
}
}
@ -112,64 +119,8 @@ static void MulSIMDUnaligned(benchmark::State& state) {
for (auto _ : state)
{
auto value = dist(gen);
sfz::multiplicativeRamp<float, true>(absl::MakeSpan(output).subspan(1), 1.0f, value);
}
}
static void LogDomainScalar(benchmark::State& state) {
sfz::Buffer<float> output(state.range(0));
std::random_device rd { };
std::mt19937 gen { rd() };
std::uniform_real_distribution<float> dist { 1, 2 };
for (auto _ : state)
{
auto value = dist(gen);
sfz::linearRamp<float, false>(absl::MakeSpan(output), 1.0f, value);
sfz::applyGain<float, false>(std::log(2.0f), absl::MakeSpan(output));
sfz::exp<float, false>(output, absl::MakeSpan(output));
}
}
static void LogDomainSIMD(benchmark::State& state) {
sfz::Buffer<float> output(state.range(0));
std::random_device rd { };
std::mt19937 gen { rd() };
std::uniform_real_distribution<float> dist { 1, 2 };
for (auto _ : state)
{
auto value = dist(gen);
sfz::linearRamp<float, true>(absl::MakeSpan(output), 1.0f, value);
sfz::applyGain<float, true>(std::log(2.0f), absl::MakeSpan(output));
sfz::exp<float, true>(output, absl::MakeSpan(output));
}
}
static void LogDomainScalarUnaligned(benchmark::State& state) {
sfz::Buffer<float> output(state.range(0));
std::random_device rd { };
std::mt19937 gen { rd() };
std::uniform_real_distribution<float> dist { 1, 2 };
for (auto _ : state)
{
auto value = dist(gen);
auto outputSpan = absl::MakeSpan(output).subspan(1);
sfz::linearRamp<float, false>(outputSpan, 1.0f, value);
sfz::applyGain<float, false>(std::log(2.0f), outputSpan);
sfz::exp<float, false>(outputSpan, outputSpan);
}
}
static void LogDomainSIMDUnaligned(benchmark::State& state) {
sfz::Buffer<float> output(state.range(0));
std::random_device rd { };
std::mt19937 gen { rd() };
std::uniform_real_distribution<float> dist { 1, 2 };
for (auto _ : state)
{
auto value = dist(gen);
auto outputSpan = absl::MakeSpan(output).subspan(1);
sfz::linearRamp<float, true>(outputSpan, 1.0f, value);
sfz::applyGain<float, true>(std::log(2.0f), outputSpan);
sfz::exp<float, true>(outputSpan, outputSpan);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplicativeRamp, true);
sfz::multiplicativeRamp<float>(absl::MakeSpan(output).subspan(1), 1.0f, value);
}
}
@ -183,8 +134,4 @@ BENCHMARK(MulScalar)->RangeMultiplier(4)->Range((1 << 2), (1 << 12));
BENCHMARK(MulSIMD)->RangeMultiplier(4)->Range((1 << 2), (1 << 12));
BENCHMARK(MulScalarUnaligned)->RangeMultiplier(4)->Range((1 << 2), (1 << 12));
BENCHMARK(MulSIMDUnaligned)->RangeMultiplier(4)->Range((1 << 2), (1 << 12));
BENCHMARK(LogDomainScalar)->RangeMultiplier(4)->Range((1 << 2), (1 << 12));
BENCHMARK(LogDomainSIMD)->RangeMultiplier(4)->Range((1 << 2), (1 << 12));
BENCHMARK(LogDomainScalarUnaligned)->RangeMultiplier(4)->Range((1 << 2), (1 << 12));
BENCHMARK(LogDomainSIMDUnaligned)->RangeMultiplier(4)->Range((1 << 2), (1 << 12));
BENCHMARK_MAIN();

View file

@ -61,7 +61,7 @@ BENCHMARK_DEFINE_F(FileFixture, JustRead)(benchmark::State& state) {
{
sfz::Buffer<float> buffer { numFrames * sndfile.channels() };
sndfile.readf(buffer.data(), numFrames);
sfz::readInterleaved<float>(buffer, output->getSpan(0), output->getSpan(1));
sfz::readInterleaved(buffer, output->getSpan(0), output->getSpan(1));
}
}
@ -75,7 +75,7 @@ BENCHMARK_DEFINE_F(FileFixture, AllocInside)(benchmark::State& state) {
{
sfz::Buffer<float> buffer { chunkSize * sndfile.channels() };
auto read = sndfile.readf(buffer.data(), chunkSize);
sfz::readInterleaved<float>(
sfz::readInterleaved(
absl::MakeSpan(buffer).first(read),
output->getSpan(0).subspan(framesRead),
output->getSpan(1).subspan(framesRead)
@ -95,7 +95,7 @@ BENCHMARK_DEFINE_F(FileFixture, AllocOutside)(benchmark::State& state) {
while(framesRead < numFrames)
{
auto read = sndfile.readf(buffer.data(), chunkSize);
sfz::readInterleaved<float>(
sfz::readInterleaved(
absl::MakeSpan(buffer).first(read),
output->getSpan(0).subspan(framesRead),
output->getSpan(1).subspan(framesRead)
@ -124,7 +124,7 @@ BENCHMARK_DEFINE_F(FileFixture, DrWavChunked)(benchmark::State& state) {
while(framesRead < numFrames)
{
auto read = drwav_read_pcm_frames_f32(&wav, chunkSize, buffer.data());
sfz::readInterleaved<float>(
sfz::readInterleaved(
absl::MakeSpan(buffer).first(read),
output->getSpan(0).subspan(framesRead),
output->getSpan(1).subspan(framesRead)

View file

@ -61,7 +61,7 @@ BENCHMARK_DEFINE_F(FileFixture, SndFileOnce)(benchmark::State& state) {
{
sfz::Buffer<float> buffer { numFrames * sndfile.channels() };
sndfile.readf(buffer.data(), numFrames);
sfz::readInterleaved<float>(buffer, output->getSpan(0), output->getSpan(1));
sfz::readInterleaved(buffer, output->getSpan(0), output->getSpan(1));
}
}
@ -75,7 +75,7 @@ BENCHMARK_DEFINE_F(FileFixture, SndFileChunked)(benchmark::State& state) {
{
sfz::Buffer<float> buffer { chunkSize * sndfile.channels() };
auto read = sndfile.readf(buffer.data(), chunkSize);
sfz::readInterleaved<float>(
sfz::readInterleaved(
absl::MakeSpan(buffer).first(read),
output->getSpan(0).subspan(framesRead),
output->getSpan(1).subspan(framesRead)
@ -104,7 +104,7 @@ BENCHMARK_DEFINE_F(FileFixture, DrWavChunked)(benchmark::State& state) {
while(framesRead < numFrames)
{
auto read = drflac_read_pcm_frames_f32(flac, chunkSize, buffer.data());
sfz::readInterleaved<float>(
sfz::readInterleaved(
absl::MakeSpan(buffer).first(read),
output->getSpan(0).subspan(framesRead),
output->getSpan(1).subspan(framesRead)

View file

@ -18,7 +18,8 @@ static void Scalar(benchmark::State& state) {
std::iota(input.begin(), input.end(), 1.0f);
for (auto _ : state) {
sfz::readInterleaved<float, false>(input, absl::MakeSpan(outputLeft), absl::MakeSpan(outputRight));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::readInterleaved, false);
sfz::readInterleaved(input, absl::MakeSpan(outputLeft), absl::MakeSpan(outputRight));
}
}
@ -29,7 +30,8 @@ static void SSE(benchmark::State& state) {
std::iota(input.begin(), input.end(), 1.0f);
for (auto _ : state) {
sfz::readInterleaved<float, true>(input, absl::MakeSpan(outputLeft), absl::MakeSpan(outputRight));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::readInterleaved, true);
sfz::readInterleaved(input, absl::MakeSpan(outputLeft), absl::MakeSpan(outputRight));
}
}
@ -39,7 +41,12 @@ static void Scalar_Unaligned(benchmark::State& state) {
sfz::Buffer<float> outputRight (state.range(0));
std::iota(input.begin(), input.end(), 1.0f);
for (auto _ : state) {
sfz::readInterleaved<float, false>(absl::MakeSpan(input).subspan(2), absl::MakeSpan(outputLeft), absl::MakeSpan(outputRight));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::readInterleaved, false);
sfz::readInterleaved(
absl::MakeSpan(input).subspan(2),
absl::MakeSpan(outputLeft),
absl::MakeSpan(outputRight)
);
}
}
@ -49,7 +56,12 @@ static void SSE_Unaligned(benchmark::State& state) {
sfz::Buffer<float> outputRight (state.range(0));
std::iota(input.begin(), input.end(), 1.0f);
for (auto _ : state) {
sfz::readInterleaved<float, true>(absl::MakeSpan(input).subspan(2), absl::MakeSpan(outputLeft), absl::MakeSpan(outputRight));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::readInterleaved, true);
sfz::readInterleaved(
absl::MakeSpan(input).subspan(2),
absl::MakeSpan(outputLeft),
absl::MakeSpan(outputRight)
);
}
}
@ -59,7 +71,12 @@ static void Scalar_Unaligned_2(benchmark::State& state) {
sfz::Buffer<float> outputRight (state.range(0));
std::iota(input.begin(), input.end(), 1.0f);
for (auto _ : state) {
sfz::readInterleaved<float, false>(absl::MakeSpan(input).subspan(2), absl::MakeSpan(outputLeft).subspan(1), absl::MakeSpan(outputRight).subspan(3));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::readInterleaved, false);
sfz::readInterleaved(
absl::MakeSpan(input).subspan(2),
absl::MakeSpan(outputLeft).subspan(1),
absl::MakeSpan(outputRight).subspan(3)
);
}
}
@ -69,7 +86,12 @@ static void SSE_Unaligned_2(benchmark::State& state) {
sfz::Buffer<float> outputRight (state.range(0));
std::iota(input.begin(), input.end(), 1.0f);
for (auto _ : state) {
sfz::readInterleaved<float, true>(absl::MakeSpan(input).subspan(2), absl::MakeSpan(outputLeft).subspan(1), absl::MakeSpan(outputRight).subspan(3));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::readInterleaved, true);
sfz::readInterleaved(
absl::MakeSpan(input).subspan(2),
absl::MakeSpan(outputLeft).subspan(1),
absl::MakeSpan(outputRight).subspan(3)
);
}
}

View file

@ -232,7 +232,7 @@ BENCHMARK_DEFINE_F(SndFile, HIIR2X_scalar)(benchmark::State& state)
{
for (auto _ : state) {
auto baseBuffer = absl::make_unique<sfz::AudioBuffer<float>>(numChannels, numFrames);
sfz::readInterleaved<float>(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1));
sfz::readInterleaved(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1));
auto outBuffer = upsample2x<float, false>(*baseBuffer);
benchmark::DoNotOptimize(outBuffer);
}
@ -242,7 +242,7 @@ BENCHMARK_DEFINE_F(SndFile, HIIR4X_scalar)(benchmark::State& state)
{
for (auto _ : state) {
auto baseBuffer = absl::make_unique<sfz::AudioBuffer<float>>(numChannels, numFrames);
sfz::readInterleaved<float>(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1));
sfz::readInterleaved(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1));
auto outBuffer = upsample4x<float, false>(*baseBuffer);
benchmark::DoNotOptimize(outBuffer);
}
@ -252,7 +252,7 @@ BENCHMARK_DEFINE_F(SndFile, HIIR8X_scalar)(benchmark::State& state)
{
for (auto _ : state) {
auto baseBuffer = absl::make_unique<sfz::AudioBuffer<float>>(numChannels, numFrames);
sfz::readInterleaved<float>(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1));
sfz::readInterleaved(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1));
auto outBuffer = upsample8x<float, false>(*baseBuffer);
benchmark::DoNotOptimize(outBuffer);
}
@ -262,7 +262,7 @@ BENCHMARK_DEFINE_F(SndFile, HIIR2X_vector)(benchmark::State& state)
{
for (auto _ : state) {
auto baseBuffer = absl::make_unique<sfz::AudioBuffer<float>>(numChannels, numFrames);
sfz::readInterleaved<float>(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1));
sfz::readInterleaved(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1));
auto outBuffer = upsample2x<float, true>(*baseBuffer);
benchmark::DoNotOptimize(outBuffer);
}
@ -272,7 +272,7 @@ BENCHMARK_DEFINE_F(SndFile, HIIR4X_vector)(benchmark::State& state)
{
for (auto _ : state) {
auto baseBuffer = absl::make_unique<sfz::AudioBuffer<float>>(numChannels, numFrames);
sfz::readInterleaved<float>(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1));
sfz::readInterleaved(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1));
auto outBuffer = upsample4x<float, true>(*baseBuffer);
benchmark::DoNotOptimize(outBuffer);
}
@ -282,7 +282,7 @@ BENCHMARK_DEFINE_F(SndFile, HIIR8X_vector)(benchmark::State& state)
{
for (auto _ : state) {
auto baseBuffer = absl::make_unique<sfz::AudioBuffer<float>>(numChannels, numFrames);
sfz::readInterleaved<float>(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1));
sfz::readInterleaved(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1));
auto outBuffer = upsample8x<float, true>(*baseBuffer);
benchmark::DoNotOptimize(outBuffer);
}
@ -300,7 +300,7 @@ BENCHMARK_DEFINE_F(SndFile, SRC2x_BEST)(benchmark::State& state)
srcData.output_frames = static_cast<long>(2 * numFrames);
src_simple(&srcData, SRC_SINC_BEST_QUALITY, static_cast<int>(numChannels));
auto outBuffer = absl::make_unique<sfz::AudioBuffer<float>>(numChannels, 2 * numFrames);
sfz::readInterleaved<float>(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
sfz::readInterleaved(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
benchmark::DoNotOptimize(outBuffer);
}
}
@ -317,7 +317,7 @@ BENCHMARK_DEFINE_F(SndFile, SRC2x_MEDIUM)(benchmark::State& state)
srcData.output_frames = static_cast<long>(2 * numFrames);
src_simple(&srcData, SRC_SINC_MEDIUM_QUALITY, static_cast<int>(numChannels));
auto outBuffer = absl::make_unique<sfz::AudioBuffer<float>>(numChannels, 2 * numFrames);
sfz::readInterleaved<float>(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
sfz::readInterleaved(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
benchmark::DoNotOptimize(outBuffer);
}
}
@ -334,7 +334,7 @@ BENCHMARK_DEFINE_F(SndFile, SRC2x_FASTEST)(benchmark::State& state)
srcData.output_frames = static_cast<long>(2 * numFrames);
src_simple(&srcData, SRC_SINC_FASTEST, static_cast<int>(numChannels));
auto outBuffer = absl::make_unique<sfz::AudioBuffer<float>>(numChannels, 2 * numFrames);
sfz::readInterleaved<float>(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
sfz::readInterleaved(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
benchmark::DoNotOptimize(outBuffer);
}
}
@ -352,7 +352,7 @@ BENCHMARK_DEFINE_F(SndFile, SRC4x_BEST)(benchmark::State& state)
srcData.output_frames = static_cast<long>(2 * numFrames);
src_simple(&srcData, SRC_SINC_BEST_QUALITY, static_cast<int>(numChannels));
auto outBuffer = absl::make_unique<sfz::AudioBuffer<float>>(numChannels, 2 * numFrames);
sfz::readInterleaved<float>(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
sfz::readInterleaved(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
benchmark::DoNotOptimize(outBuffer);
}
}
@ -369,7 +369,7 @@ BENCHMARK_DEFINE_F(SndFile, SRC4x_MEDIUM)(benchmark::State& state)
srcData.output_frames = static_cast<long>(2 * numFrames);
src_simple(&srcData, SRC_SINC_MEDIUM_QUALITY, static_cast<int>(numChannels));
auto outBuffer = absl::make_unique<sfz::AudioBuffer<float>>(numChannels, 2 * numFrames);
sfz::readInterleaved<float>(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
sfz::readInterleaved(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
benchmark::DoNotOptimize(outBuffer);
}
}
@ -386,7 +386,7 @@ BENCHMARK_DEFINE_F(SndFile, SRC4x_FASTEST)(benchmark::State& state)
srcData.output_frames = static_cast<long>(2 * numFrames);
src_simple(&srcData, SRC_SINC_FASTEST, static_cast<int>(numChannels));
auto outBuffer = absl::make_unique<sfz::AudioBuffer<float>>(numChannels, 2 * numFrames);
sfz::readInterleaved<float>(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
sfz::readInterleaved(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
benchmark::DoNotOptimize(outBuffer);
}
}
@ -403,7 +403,7 @@ BENCHMARK_DEFINE_F(SndFile, SRC8x_BEST)(benchmark::State& state)
srcData.output_frames = static_cast<long>(2 * numFrames);
src_simple(&srcData, SRC_SINC_BEST_QUALITY, static_cast<int>(numChannels));
auto outBuffer = absl::make_unique<sfz::AudioBuffer<float>>(numChannels, 2 * numFrames);
sfz::readInterleaved<float>(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
sfz::readInterleaved(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
benchmark::DoNotOptimize(outBuffer);
}
}
@ -420,7 +420,7 @@ BENCHMARK_DEFINE_F(SndFile, SRC8x_MEDIUM)(benchmark::State& state)
srcData.output_frames = static_cast<long>(2 * numFrames);
src_simple(&srcData, SRC_SINC_MEDIUM_QUALITY, static_cast<int>(numChannels));
auto outBuffer = absl::make_unique<sfz::AudioBuffer<float>>(numChannels, 2 * numFrames);
sfz::readInterleaved<float>(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
sfz::readInterleaved(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
benchmark::DoNotOptimize(outBuffer);
}
}
@ -437,7 +437,7 @@ BENCHMARK_DEFINE_F(SndFile, SRC8x_FASTEST)(benchmark::State& state)
srcData.output_frames = static_cast<long>(2 * numFrames);
src_simple(&srcData, SRC_SINC_FASTEST, static_cast<int>(numChannels));
auto outBuffer = absl::make_unique<sfz::AudioBuffer<float>>(numChannels, 2 * numFrames);
sfz::readInterleaved<float>(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
sfz::readInterleaved(*intermediateBuffer, outBuffer->getSpan(0), outBuffer->getSpan(1));
benchmark::DoNotOptimize(outBuffer);
}
}
@ -446,7 +446,7 @@ BENCHMARK_DEFINE_F(SndFile, HIIR8X_default)(benchmark::State& state)
{
for (auto _ : state) {
auto baseBuffer = absl::make_unique<sfz::AudioBuffer<float>>(numChannels, numFrames);
sfz::readInterleaved<float>(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1));
sfz::readInterleaved(*interleavedBuffer, baseBuffer->getSpan(0), baseBuffer->getSpan(1));
auto outBuffer = upsample8x<float>(*baseBuffer);
benchmark::DoNotOptimize(outBuffer);
}

View file

@ -105,7 +105,7 @@ BENCHMARK_DEFINE_F(FileFixture, NoResampling)(benchmark::State& state) {
{
sfz::Buffer<float> buffer { numFrames * sndfile.channels() };
sndfile.readf(buffer.data(), sndfile.frames());
sfz::readInterleaved<float>(buffer, output->getSpan(0), output->getSpan(1));
sfz::readInterleaved(buffer, output->getSpan(0), output->getSpan(1));
}
}
@ -121,7 +121,7 @@ BENCHMARK_DEFINE_F(FileFixture, ResampleAtOnce)(benchmark::State& state) {
upsampler4x.set_coefs(coeffsStage4x.data());
sndfile.readf(buffer.data(), numFrames);
sfz::readInterleaved<float>(buffer, output->getSpan(0), output->getSpan(1));
sfz::readInterleaved(buffer, output->getSpan(0), output->getSpan(1));
upsampler2x.process_block(temp.data(), output->channelReader(0), static_cast<long>(numFrames));
upsampler4x.process_block(output->channelWriter(0), temp.data(), static_cast<long>(numFrames * 2));
@ -171,7 +171,7 @@ BENCHMARK_DEFINE_F(FileFixture, ResampleInChunks)(benchmark::State& state) {
thisChunkSize * sndfile.channels()
);
sfz::readInterleaved<float>(bufferChunk, leftSpan, rightSpan);
sfz::readInterleaved(bufferChunk, leftSpan, rightSpan);
upsampler2xLeft.process_block(chunkSpan.data(), leftSpan.data(), static_cast<long>(thisChunkSize));
upsampler4xLeft.process_block(output->channelWriter(0) + outputFrameCounter, chunkSpan.data(), static_cast<long>(thisChunkSize * 2));

View file

@ -1,77 +0,0 @@
// 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 "SIMDHelpers.h"
#include <benchmark/benchmark.h>
#include <vector>
#include <random>
#include <numeric>
#include <absl/algorithm/container.h>
// In this one we have an array of indices
constexpr int loopEnd { 1076 };
constexpr float maxJump { 4 };
class SaturatingFixture : public benchmark::Fixture {
public:
void SetUp(const ::benchmark::State& state) {
std::random_device rd { };
std::mt19937 gen { rd() };
std::uniform_real_distribution<float> dist { 0, maxJump };
indices = std::vector<int>(state.range(0));
leftCoeffs = std::vector<float>(state.range(0));
rightCoeffs = std::vector<float>(state.range(0));
jumps = std::vector<float>(state.range(0));
absl::c_generate(jumps, [&]() { return dist(gen); });
}
void TearDown(const ::benchmark::State& /* state */) {
}
std::vector<int> indices;
std::vector<float> leftCoeffs;
std::vector<float> rightCoeffs;
std::vector<float> jumps;
};
BENCHMARK_DEFINE_F(SaturatingFixture, Scalar)(benchmark::State& state) {
for (auto _ : state)
{
sfz::saturatingSFZIndex<float, false>(jumps, absl::MakeSpan(leftCoeffs), absl::MakeSpan(rightCoeffs), absl::MakeSpan(indices), 2.5f, loopEnd);
}
}
BENCHMARK_DEFINE_F(SaturatingFixture, SIMD)(benchmark::State& state) {
for (auto _ : state)
{
sfz::saturatingSFZIndex<float, true>(jumps, absl::MakeSpan(leftCoeffs), absl::MakeSpan(rightCoeffs), absl::MakeSpan(indices), 2.5f, loopEnd);
}
}
BENCHMARK_DEFINE_F(SaturatingFixture, Scalar_Unaligned)(benchmark::State& state) {
for (auto _ : state)
{
sfz::saturatingSFZIndex<float, false>(absl::MakeSpan(jumps).subspan(1), absl::MakeSpan(leftCoeffs).subspan(2), absl::MakeSpan(rightCoeffs).subspan(1), absl::MakeSpan(indices).subspan(3), 2.5f, loopEnd);
}
}
BENCHMARK_DEFINE_F(SaturatingFixture, SIMD_Unaligned)(benchmark::State& state) {
for (auto _ : state)
{
sfz::saturatingSFZIndex<float, true>(absl::MakeSpan(jumps).subspan(1), absl::MakeSpan(leftCoeffs).subspan(2), absl::MakeSpan(rightCoeffs).subspan(1), absl::MakeSpan(indices).subspan(3), 2.5f, loopEnd);
}
}
// Register the function as a benchmark
BENCHMARK_REGISTER_F(SaturatingFixture, Scalar)->RangeMultiplier(2)->Range((2<<6), (2<<12));
BENCHMARK_REGISTER_F(SaturatingFixture, SIMD)->RangeMultiplier(2)->Range((2<<6), (2<<12));
BENCHMARK_REGISTER_F(SaturatingFixture, Scalar_Unaligned)->RangeMultiplier(2)->Range((2<<6), (2<<12));
BENCHMARK_REGISTER_F(SaturatingFixture, SIMD_Unaligned)->RangeMultiplier(2)->Range((2<<6), (2<<12));
BENCHMARK_MAIN();

View file

@ -36,28 +36,32 @@ public:
BENCHMARK_DEFINE_F(SubArray, Scalar)(benchmark::State& state) {
for (auto _ : state)
{
sfz::subtract<float, false>(input, absl::MakeSpan(output));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::subtract, false);
sfz::subtract<float>(input, absl::MakeSpan(output));
}
}
BENCHMARK_DEFINE_F(SubArray, SIMD)(benchmark::State& state) {
for (auto _ : state)
{
sfz::subtract<float, true>(input, absl::MakeSpan(output));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::subtract, true);
sfz::subtract<float>(input, absl::MakeSpan(output));
}
}
BENCHMARK_DEFINE_F(SubArray, Scalar_Unaligned)(benchmark::State& state) {
for (auto _ : state)
{
sfz::subtract<float, false>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::subtract, false);
sfz::subtract<float>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
}
}
BENCHMARK_DEFINE_F(SubArray, SIMD_Unaligned)(benchmark::State& state) {
for (auto _ : state)
{
sfz::subtract<float, true>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::subtract, true);
sfz::subtract<float>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
}
}

View file

@ -1,80 +0,0 @@
// 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 "SIMDHelpers.h"
#include <benchmark/benchmark.h>
#include <random>
#include <numeric>
#include <vector>
#include <cmath>
#include <iostream>
#include "Config.h"
#include "ScopedFTZ.h"
#include "absl/types/span.h"
class WidthPosArray : public benchmark::Fixture {
public:
void SetUp(const ::benchmark::State& state) {
std::random_device rd { };
std::mt19937 gen { rd() };
std::uniform_real_distribution<float> dist { 0.001f, 1.0f };
width = std::vector<float>(state.range(0));
position = std::vector<float>(state.range(0));
left = std::vector<float>(state.range(0));
right = std::vector<float>(state.range(0));
std::generate(width.begin(), width.end(), [&]() { return dist(gen); });
std::generate(position.begin(), position.end(), [&]() { return dist(gen); });
std::generate(right.begin(), right.end(), [&]() { return dist(gen); });
std::generate(left.begin(), left.end(), [&]() { return dist(gen); });
temp1 = std::vector<float>(state.range(0));
temp2 = std::vector<float>(state.range(0));
temp3 = std::vector<float>(state.range(0));
span1 = absl::MakeSpan(temp1);
span2 = absl::MakeSpan(temp2);
span3 = absl::MakeSpan(temp3);
}
void TearDown(const ::benchmark::State& /* state */) {
}
std::vector<float> width;
std::vector<float> position;
std::vector<float> left;
std::vector<float> right;
std::vector<float> temp1;
std::vector<float> temp2;
std::vector<float> temp3;
absl::Span<float> span1;
absl::Span<float> span2;
absl::Span<float> span3;
};
BENCHMARK_DEFINE_F(WidthPosArray, Scalar)(benchmark::State& state) {
ScopedFTZ ftz;
const auto leftBuffer = absl::MakeSpan(left);
const auto rightBuffer = absl::MakeSpan(right);
for (auto _ : state)
{
sfz::width<float, false>(width, leftBuffer, rightBuffer);
sfz::pan<float, false>(position, leftBuffer, rightBuffer);
}
}
BENCHMARK_DEFINE_F(WidthPosArray, SIMD)(benchmark::State& state) {
ScopedFTZ ftz;
const auto leftBuffer = absl::MakeSpan(left);
const auto rightBuffer = absl::MakeSpan(right);
for (auto _ : state)
{
sfz::width<float, true>(width, leftBuffer, rightBuffer);
sfz::pan<float, true>(position, leftBuffer, rightBuffer);
}
}
BENCHMARK_REGISTER_F(WidthPosArray, Scalar)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_REGISTER_F(WidthPosArray, SIMD)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
BENCHMARK_MAIN();

View file

@ -19,7 +19,8 @@ static void Interleaved_Write(benchmark::State& state) {
std::iota(inputRight.begin(), inputRight.end(), 1.0f);
for (auto _ : state) {
sfz::writeInterleaved<float, false>(inputLeft, inputRight, absl::MakeSpan(output));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::writeInterleaved, false);
sfz::writeInterleaved(inputLeft, inputRight, absl::MakeSpan(output));
}
}
@ -30,8 +31,8 @@ static void Interleaved_Write_SSE(benchmark::State& state) {
std::iota(inputLeft.begin(), inputLeft.end(), 1.0f);
std::iota(inputRight.begin(), inputRight.end(), 1.0f);
for (auto _ : state) {
sfz::writeInterleaved<float, true>(inputLeft, inputRight, absl::MakeSpan(output));
benchmark::DoNotOptimize(output);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::writeInterleaved, true);
sfz::writeInterleaved(inputLeft, inputRight, absl::MakeSpan(output));
}
}
@ -42,8 +43,12 @@ static void Unaligned_Interleaved_Write(benchmark::State& state) {
std::iota(inputLeft.begin(), inputLeft.end(), 1.0f);
std::iota(inputRight.begin(), inputRight.end(), 1.0f);
for (auto _ : state) {
sfz::writeInterleaved<float, false>(absl::MakeSpan(inputLeft).subspan(1) , absl::MakeSpan(inputRight).subspan(1), absl::MakeSpan(output).subspan(2));
benchmark::DoNotOptimize(output);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::writeInterleaved, false);
sfz::writeInterleaved(
absl::MakeSpan(inputLeft).subspan(1),
absl::MakeSpan(inputRight).subspan(1),
absl::MakeSpan(output).subspan(2)
);
}
}
@ -54,8 +59,12 @@ static void Unaligned_Interleaved_Write_SSE(benchmark::State& state) {
std::iota(inputLeft.begin(), inputLeft.end(), 1.0f);
std::iota(inputRight.begin(), inputRight.end(), 1.0f);
for (auto _ : state) {
sfz::writeInterleaved<float, true>(absl::MakeSpan(inputLeft).subspan(1) , absl::MakeSpan(inputRight).subspan(1), absl::MakeSpan(output).subspan(2));
benchmark::DoNotOptimize(output);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::writeInterleaved, true);
sfz::writeInterleaved(
absl::MakeSpan(inputLeft).subspan(1),
absl::MakeSpan(inputRight).subspan(1),
absl::MakeSpan(output).subspan(2)
);
}
}
@ -66,8 +75,12 @@ static void Unaligned_Interleaved_Write_2(benchmark::State& state) {
std::iota(inputLeft.begin(), inputLeft.end(), 1.0f);
std::iota(inputRight.begin(), inputRight.end(), 1.0f);
for (auto _ : state) {
sfz::writeInterleaved<float, false>(absl::MakeSpan(inputLeft) , absl::MakeSpan(inputRight).subspan(1), absl::MakeSpan(output).subspan(2));
benchmark::DoNotOptimize(output);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::writeInterleaved, false);
sfz::writeInterleaved(
absl::MakeSpan(inputLeft),
absl::MakeSpan(inputRight).subspan(1),
absl::MakeSpan(output).subspan(2)
);
}
}
@ -78,8 +91,12 @@ static void Unaligned_Interleaved_Write_SSE_2(benchmark::State& state) {
std::iota(inputLeft.begin(), inputLeft.end(), 1.0f);
std::iota(inputRight.begin(), inputRight.end(), 1.0f);
for (auto _ : state) {
sfz::writeInterleaved<float, true>(absl::MakeSpan(inputLeft) , absl::MakeSpan(inputRight).subspan(1), absl::MakeSpan(output).subspan(2));
benchmark::DoNotOptimize(output);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::writeInterleaved, true);
sfz::writeInterleaved(
absl::MakeSpan(inputLeft),
absl::MakeSpan(inputRight).subspan(1),
absl::MakeSpan(output).subspan(2)
);
}
}

View file

@ -18,7 +18,7 @@ if(SAMPLERATE_LIBRARY AND SAMPLERATE_INCLUDE_DIR)
endif()
add_library(bm_simd STATIC ${BENCHMARK_SIMD_SOURCES})
target_link_libraries(bm_simd PRIVATE absl::span)
target_link_libraries(bm_simd PRIVATE absl::span sfizz-cpuid)
target_include_directories(bm_simd PRIVATE ../src/external)
add_library(bm_ftz STATIC ../src/sfizz/ScopedFTZ.cpp)
@ -38,12 +38,9 @@ sfizz_add_benchmark(bm_opf_high_vs_low BM_OPF_high_vs_low.cpp)
sfizz_add_benchmark(bm_clock BM_clock.cpp)
sfizz_add_benchmark(bm_write BM_writeInterleaved.cpp)
sfizz_add_benchmark(bm_read BM_readInterleaved.cpp)
sfizz_add_benchmark(bm_fill BM_fill.cpp)
sfizz_add_benchmark(bm_mathfuns BM_mathfuns.cpp)
sfizz_add_benchmark(bm_gain BM_gain.cpp)
sfizz_add_benchmark(bm_divide BM_divide.cpp)
sfizz_add_benchmark(bm_looping BM_looping.cpp)
sfizz_add_benchmark(bm_saturating BM_saturating.cpp)
sfizz_add_benchmark(bm_ramp BM_ramp.cpp)
sfizz_add_benchmark(bm_ADSR BM_ADSR.cpp)
target_link_libraries(bm_ADSR PRIVATE sfizz::sfizz)
@ -53,13 +50,10 @@ sfizz_add_benchmark(bm_multiplyAdd BM_multiplyAdd.cpp)
sfizz_add_benchmark(bm_multiplyAddFixedGain BM_multiplyAddFixedGain.cpp)
sfizz_add_benchmark(bm_subtract BM_subtract.cpp)
sfizz_add_benchmark(bm_copy BM_copy.cpp)
sfizz_add_benchmark(bm_pan BM_pan.cpp)
sfizz_add_benchmark(bm_mean BM_mean.cpp)
sfizz_add_benchmark(bm_meanSquared BM_meanSquared.cpp)
sfizz_add_benchmark(bm_cumsum BM_cumsum.cpp)
sfizz_add_benchmark(bm_diff BM_diff.cpp)
sfizz_add_benchmark(bm_widthPos BM_widthPos.cpp)
sfizz_add_benchmark(bm_interpolationCast BM_interpolationCast.cpp)
sfizz_add_benchmark(bm_pointerIterationOrOffsets BM_pointerIterationOrOffsets.cpp)
sfizz_add_benchmark(bm_maps BM_maps.cpp)
target_link_libraries(bm_maps PRIVATE absl::flat_hash_map)
@ -71,7 +65,7 @@ target_link_libraries(bm_logger PRIVATE sfizz::sfizz)
if (TARGET sfizz-samplerate)
sfizz_add_benchmark(bm_resample BM_resample.cpp ${BENCHMARK_SIMD_SOURCES})
target_link_libraries(bm_resample PRIVATE sfizz-samplerate sfizz-sndfile)
target_link_libraries(bm_resample PRIVATE sfizz-samplerate sfizz-sndfile sfizz-cpuid)
endif()
sfizz_add_benchmark(bm_envelopes BM_envelopes.cpp)
@ -116,27 +110,21 @@ add_dependencies(sfizz_benchmarks
bm_read
bm_mean
bm_meanSquared
bm_fill
bm_cumsum
bm_diff
bm_interpolationCast
bm_mathfuns
bm_gain
bm_divide
bm_looping
bm_saturating
bm_ramp
bm_ADSR
bm_add
bm_logger
bm_pan
bm_subtract
bm_multiplyAdd
bm_readChunk
bm_resampleChunk
bm_envelopes
bm_wavfile
bm_widthPos
bm_flacfile
bm_filterModulation
bm_filterStereoMono

View file

@ -2,9 +2,9 @@ macro(sfizz_add_simd_sources SOURCES_VAR PREFIX)
# It needs a macro, otherwise the source properties cannot take effect.
list (APPEND ${SOURCES_VAR}
${PREFIX}/sfizz/SIMDSSE.cpp
${PREFIX}/sfizz/SIMDNEON.cpp
${PREFIX}/sfizz/SIMDDummy.cpp)
${PREFIX}/sfizz/SIMDHelpers.cpp
${PREFIX}/sfizz/simd/HelpersSSE.cpp
${PREFIX}/sfizz/simd/HelpersAVX.cpp)
# For CPU-dispatched X86 sources
# Always build them for all X86 targets.
@ -15,6 +15,7 @@ macro(sfizz_add_simd_sources SOURCES_VAR PREFIX)
set_source_files_properties(
${PREFIX}/sfizz/effects/impl/ResonantStringAVX.cpp
${PREFIX}/sfizz/effects/impl/ResonantArrayAVX.cpp
${PREFIX}/sfizz/simd/HelpersAVX.cpp
PROPERTIES COMPILE_FLAGS "-mavx")
endif()
endif()

7
dpf.mk
View file

@ -85,6 +85,7 @@ SFIZZ_SOURCES = \
src/sfizz/OpcodeCleanup.cpp \
src/sfizz/Opcode.cpp \
src/sfizz/Oversampler.cpp \
src/sfizz/Panning.cpp \
src/sfizz/Parser.cpp \
src/sfizz/parser/Parser.cpp \
src/sfizz/parser/ParserPrivate.cpp \
@ -95,9 +96,9 @@ SFIZZ_SOURCES = \
src/sfizz/sfizz_wrapper.cpp \
src/sfizz/SfzFilter.cpp \
src/sfizz/SfzHelpers.cpp \
src/sfizz/SIMDDummy.cpp \
src/sfizz/SIMDNEON.cpp \
src/sfizz/SIMDSSE.cpp \
src/sfizz/SIMDHelpers.cpp \
src/sfizz/simd/HelpersSSE.cpp \
src/sfizz/simd/HelpersAVX.cpp \
src/sfizz/Synth.cpp \
src/sfizz/Tuning.cpp \
src/sfizz/Voice.cpp \

View file

@ -13,10 +13,13 @@ clang-tidy \
src/sfizz/Opcode.cpp \
src/sfizz/Oversampler.cpp \
src/sfizz/Parser.cpp \
src/sfizz/Panning.cpp \
src/sfizz/sfizz.cpp \
src/sfizz/Region.cpp \
src/sfizz/SfzHelpers.cpp \
src/sfizz/SIMDSSE.cpp \
src/sfizz/SIMDHelpers.cpp \
src/sfizz/simd/HelpersSSE.cpp \
src/sfizz/simd/HelpersAVX.cpp \
src/sfizz/Synth.cpp \
src/sfizz/Voice.cpp \
src/sfizz/effects/Eq.cpp \
@ -28,5 +31,6 @@ clang-tidy \
vst/SfizzVstEditor.cpp \
vst/SfizzVstState.cpp \
-- -Iexternal/abseil-cpp -Isrc/external -Isrc/external/pugixml/src \
-Isrc/sfizz -Isrc -Isrc/external/spline \
-Ivst -Ivst/external/VST_SDK/VST3_SDK -Ivst/external/VST_SDK/VST3_SDK/vstgui4 -Ivst/external/ring_buffer -DNDEBUG
-Isrc/sfizz -Isrc -Isrc/external/spline -Isrc/external/cpuid/src \
-Ivst -Ivst/external/VST_SDK/VST3_SDK -Ivst/external/VST_SDK/VST3_SDK/vstgui4 -Ivst/external/ring_buffer \
-DNDEBUG -std=c++17

View file

@ -23,6 +23,7 @@ set (SFIZZ_SOURCES
sfizz/Wavetables.cpp
sfizz/Tuning.cpp
sfizz/RTSemaphore.cpp
sfizz/Panning.cpp
sfizz/Effects.cpp
sfizz/effects/Nothing.cpp
sfizz/effects/Filter.cpp

View file

@ -1,301 +0,0 @@
/* NEON implementation of sin, cos, exp and log
Inspired by Intel Approximate Math library, and based on the
corresponding algorithms of the cephes math library
*/
/* Copyright (C) 2011 Julien Pommier
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
(this is the zlib license)
*/
#include <arm_neon.h>
typedef float32x4_t v4sf; // vector of 4 float
typedef uint32x4_t v4su; // vector of 4 uint32
typedef int32x4_t v4si; // vector of 4 uint32
#define c_inv_mant_mask ~0x7f800000u
#define c_cephes_SQRTHF 0.707106781186547524
#define c_cephes_log_p0 7.0376836292E-2
#define c_cephes_log_p1 - 1.1514610310E-1
#define c_cephes_log_p2 1.1676998740E-1
#define c_cephes_log_p3 - 1.2420140846E-1
#define c_cephes_log_p4 + 1.4249322787E-1
#define c_cephes_log_p5 - 1.6668057665E-1
#define c_cephes_log_p6 + 2.0000714765E-1
#define c_cephes_log_p7 - 2.4999993993E-1
#define c_cephes_log_p8 + 3.3333331174E-1
#define c_cephes_log_q1 -2.12194440e-4
#define c_cephes_log_q2 0.693359375
/* natural logarithm computed for 4 simultaneous float
return NaN for x <= 0
*/
v4sf log_ps(v4sf x) {
v4sf one = vdupq_n_f32(1);
x = vmaxq_f32(x, vdupq_n_f32(0)); /* force flush to zero on denormal values */
v4su invalid_mask = vcleq_f32(x, vdupq_n_f32(0));
v4si ux = vreinterpretq_s32_f32(x);
v4si emm0 = vshrq_n_s32(ux, 23);
/* keep only the fractional part */
ux = vandq_s32(ux, vdupq_n_s32(c_inv_mant_mask));
ux = vorrq_s32(ux, vreinterpretq_s32_f32(vdupq_n_f32(0.5f)));
x = vreinterpretq_f32_s32(ux);
emm0 = vsubq_s32(emm0, vdupq_n_s32(0x7f));
v4sf e = vcvtq_f32_s32(emm0);
e = vaddq_f32(e, one);
/* part2:
if( x < SQRTHF ) {
e -= 1;
x = x + x - 1.0;
} else { x = x - 1.0; }
*/
v4su mask = vcltq_f32(x, vdupq_n_f32(c_cephes_SQRTHF));
v4sf tmp = vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(x), mask));
x = vsubq_f32(x, one);
e = vsubq_f32(e, vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(one), mask)));
x = vaddq_f32(x, tmp);
v4sf z = vmulq_f32(x,x);
v4sf y = vdupq_n_f32(c_cephes_log_p0);
y = vmulq_f32(y, x);
y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p1));
y = vmulq_f32(y, x);
y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p2));
y = vmulq_f32(y, x);
y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p3));
y = vmulq_f32(y, x);
y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p4));
y = vmulq_f32(y, x);
y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p5));
y = vmulq_f32(y, x);
y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p6));
y = vmulq_f32(y, x);
y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p7));
y = vmulq_f32(y, x);
y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p8));
y = vmulq_f32(y, x);
y = vmulq_f32(y, z);
tmp = vmulq_f32(e, vdupq_n_f32(c_cephes_log_q1));
y = vaddq_f32(y, tmp);
tmp = vmulq_f32(z, vdupq_n_f32(0.5f));
y = vsubq_f32(y, tmp);
tmp = vmulq_f32(e, vdupq_n_f32(c_cephes_log_q2));
x = vaddq_f32(x, y);
x = vaddq_f32(x, tmp);
x = vreinterpretq_f32_u32(vorrq_u32(vreinterpretq_u32_f32(x), invalid_mask)); // negative arg will be NAN
return x;
}
#define c_exp_hi 88.3762626647949f
#define c_exp_lo -88.3762626647949f
#define c_cephes_LOG2EF 1.44269504088896341
#define c_cephes_exp_C1 0.693359375
#define c_cephes_exp_C2 -2.12194440e-4
#define c_cephes_exp_p0 1.9875691500E-4
#define c_cephes_exp_p1 1.3981999507E-3
#define c_cephes_exp_p2 8.3334519073E-3
#define c_cephes_exp_p3 4.1665795894E-2
#define c_cephes_exp_p4 1.6666665459E-1
#define c_cephes_exp_p5 5.0000001201E-1
/* exp() computed for 4 float at once */
v4sf exp_ps(v4sf x) {
v4sf tmp, fx;
v4sf one = vdupq_n_f32(1);
x = vminq_f32(x, vdupq_n_f32(c_exp_hi));
x = vmaxq_f32(x, vdupq_n_f32(c_exp_lo));
/* express exp(x) as exp(g + n*log(2)) */
fx = vmlaq_f32(vdupq_n_f32(0.5f), x, vdupq_n_f32(c_cephes_LOG2EF));
/* perform a floorf */
tmp = vcvtq_f32_s32(vcvtq_s32_f32(fx));
/* if greater, substract 1 */
v4su mask = vcgtq_f32(tmp, fx);
mask = vandq_u32(mask, vreinterpretq_u32_f32(one));
fx = vsubq_f32(tmp, vreinterpretq_f32_u32(mask));
tmp = vmulq_f32(fx, vdupq_n_f32(c_cephes_exp_C1));
v4sf z = vmulq_f32(fx, vdupq_n_f32(c_cephes_exp_C2));
x = vsubq_f32(x, tmp);
x = vsubq_f32(x, z);
static const float cephes_exp_p[6] = { c_cephes_exp_p0, c_cephes_exp_p1, c_cephes_exp_p2, c_cephes_exp_p3, c_cephes_exp_p4, c_cephes_exp_p5 };
v4sf y = vld1q_dup_f32(cephes_exp_p+0);
v4sf c1 = vld1q_dup_f32(cephes_exp_p+1);
v4sf c2 = vld1q_dup_f32(cephes_exp_p+2);
v4sf c3 = vld1q_dup_f32(cephes_exp_p+3);
v4sf c4 = vld1q_dup_f32(cephes_exp_p+4);
v4sf c5 = vld1q_dup_f32(cephes_exp_p+5);
y = vmulq_f32(y, x);
z = vmulq_f32(x,x);
y = vaddq_f32(y, c1);
y = vmulq_f32(y, x);
y = vaddq_f32(y, c2);
y = vmulq_f32(y, x);
y = vaddq_f32(y, c3);
y = vmulq_f32(y, x);
y = vaddq_f32(y, c4);
y = vmulq_f32(y, x);
y = vaddq_f32(y, c5);
y = vmulq_f32(y, z);
y = vaddq_f32(y, x);
y = vaddq_f32(y, one);
/* build 2^n */
int32x4_t mm;
mm = vcvtq_s32_f32(fx);
mm = vaddq_s32(mm, vdupq_n_s32(0x7f));
mm = vshlq_n_s32(mm, 23);
v4sf pow2n = vreinterpretq_f32_s32(mm);
y = vmulq_f32(y, pow2n);
return y;
}
#define c_minus_cephes_DP1 -0.78515625
#define c_minus_cephes_DP2 -2.4187564849853515625e-4
#define c_minus_cephes_DP3 -3.77489497744594108e-8
#define c_sincof_p0 -1.9515295891E-4
#define c_sincof_p1 8.3321608736E-3
#define c_sincof_p2 -1.6666654611E-1
#define c_coscof_p0 2.443315711809948E-005
#define c_coscof_p1 -1.388731625493765E-003
#define c_coscof_p2 4.166664568298827E-002
#define c_cephes_FOPI 1.27323954473516 // 4 / M_PI
/* evaluation of 4 sines & cosines at once.
The code is the exact rewriting of the cephes sinf function.
Precision is excellent as long as x < 8192 (I did not bother to
take into account the special handling they have for greater values
-- it does not return garbage for arguments over 8192, though, but
the extra precision is missing).
Note that it is such that sinf((float)M_PI) = 8.74e-8, which is the
surprising but correct result.
Note also that when you compute sin(x), cos(x) is available at
almost no extra price so both sin_ps and cos_ps make use of
sincos_ps..
*/
void sincos_ps(v4sf x, v4sf *ysin, v4sf *ycos) { // any x
v4sf xmm1, xmm2, xmm3, y;
v4su emm2;
v4su sign_mask_sin, sign_mask_cos;
sign_mask_sin = vcltq_f32(x, vdupq_n_f32(0));
x = vabsq_f32(x);
/* scale by 4/Pi */
y = vmulq_f32(x, vdupq_n_f32(c_cephes_FOPI));
/* store the integer part of y in mm0 */
emm2 = vcvtq_u32_f32(y);
/* j=(j+1) & (~1) (see the cephes sources) */
emm2 = vaddq_u32(emm2, vdupq_n_u32(1));
emm2 = vandq_u32(emm2, vdupq_n_u32(~1));
y = vcvtq_f32_u32(emm2);
/* get the polynom selection mask
there is one polynom for 0 <= x <= Pi/4
and another one for Pi/4<x<=Pi/2
Both branches will be computed.
*/
v4su poly_mask = vtstq_u32(emm2, vdupq_n_u32(2));
/* The magic pass: "Extended precision modular arithmetic"
x = ((x - y * DP1) - y * DP2) - y * DP3; */
xmm1 = vmulq_n_f32(y, c_minus_cephes_DP1);
xmm2 = vmulq_n_f32(y, c_minus_cephes_DP2);
xmm3 = vmulq_n_f32(y, c_minus_cephes_DP3);
x = vaddq_f32(x, xmm1);
x = vaddq_f32(x, xmm2);
x = vaddq_f32(x, xmm3);
sign_mask_sin = veorq_u32(sign_mask_sin, vtstq_u32(emm2, vdupq_n_u32(4)));
sign_mask_cos = vtstq_u32(vsubq_u32(emm2, vdupq_n_u32(2)), vdupq_n_u32(4));
/* Evaluate the first polynom (0 <= x <= Pi/4) in y1,
and the second polynom (Pi/4 <= x <= 0) in y2 */
v4sf z = vmulq_f32(x,x);
v4sf y1, y2;
y1 = vmulq_n_f32(z, c_coscof_p0);
y2 = vmulq_n_f32(z, c_sincof_p0);
y1 = vaddq_f32(y1, vdupq_n_f32(c_coscof_p1));
y2 = vaddq_f32(y2, vdupq_n_f32(c_sincof_p1));
y1 = vmulq_f32(y1, z);
y2 = vmulq_f32(y2, z);
y1 = vaddq_f32(y1, vdupq_n_f32(c_coscof_p2));
y2 = vaddq_f32(y2, vdupq_n_f32(c_sincof_p2));
y1 = vmulq_f32(y1, z);
y2 = vmulq_f32(y2, z);
y1 = vmulq_f32(y1, z);
y2 = vmulq_f32(y2, x);
y1 = vsubq_f32(y1, vmulq_f32(z, vdupq_n_f32(0.5f)));
y2 = vaddq_f32(y2, x);
y1 = vaddq_f32(y1, vdupq_n_f32(1));
/* select the correct result from the two polynoms */
v4sf ys = vbslq_f32(poly_mask, y1, y2);
v4sf yc = vbslq_f32(poly_mask, y2, y1);
*ysin = vbslq_f32(sign_mask_sin, vnegq_f32(ys), ys);
*ycos = vbslq_f32(sign_mask_cos, yc, vnegq_f32(yc));
}
v4sf sin_ps(v4sf x) {
v4sf ysin, ycos;
sincos_ps(x, &ysin, &ycos);
return ysin;
}
v4sf cos_ps(v4sf x) {
v4sf ysin, ycos;
sincos_ps(x, &ysin, &ycos);
return ycos;
}

View file

@ -1,713 +0,0 @@
/* SIMD (SSE1+MMX or SSE2) implementation of sin, cos, exp and log
Inspired by Intel Approximate Math library, and based on the
corresponding algorithms of the cephes math library
The default is to use the SSE1 version. If you define USE_SSE2 the
the SSE2 intrinsics will be used in place of the MMX intrinsics. Do
not expect any significant performance improvement with SSE2.
*/
/* Copyright (C) 2007 Julien Pommier
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
(this is the zlib license)
*/
#include <xmmintrin.h>
/* yes I know, the top of this file is quite ugly */
#ifdef _MSC_VER /* visual c++ */
# define ALIGN16_BEG __declspec(align(16))
# define ALIGN16_END
#else /* gcc or icc */
# define ALIGN16_BEG
# define ALIGN16_END __attribute__((aligned(16)))
#endif
#define USE_SSE2
/* __m128 is ugly to write */
typedef __m128 v4sf; // vector of 4 float (sse1)
#ifdef USE_SSE2
# include <emmintrin.h>
typedef __m128i v4si; // vector of 4 int (sse2)
#else
typedef __m64 v2si; // vector of 2 int (mmx)
#endif
/* declare some SSE constants -- why can't I figure a better way to do that? */
#define _PS_CONST(Name, Val) \
static const ALIGN16_BEG float _ps_##Name[4] ALIGN16_END = { Val, Val, Val, Val }
#define _PI32_CONST(Name, Val) \
static const ALIGN16_BEG int _pi32_##Name[4] ALIGN16_END = { Val, Val, Val, Val }
#define _PS_CONST_TYPE(Name, Type, Val) \
static const ALIGN16_BEG Type _ps_##Name[4] ALIGN16_END = { Val, Val, Val, Val }
_PS_CONST(1 , 1.0f);
_PS_CONST(0p5, 0.5f);
/* the smallest non denormalized float number */
_PS_CONST_TYPE(min_norm_pos, int, 0x00800000);
_PS_CONST_TYPE(mant_mask, int, 0x7f800000);
_PS_CONST_TYPE(inv_mant_mask, int, ~0x7f800000);
_PS_CONST_TYPE(sign_mask, int, (int)0x80000000);
_PS_CONST_TYPE(inv_sign_mask, int, ~0x80000000);
_PI32_CONST(1, 1);
_PI32_CONST(inv1, ~1);
_PI32_CONST(2, 2);
_PI32_CONST(4, 4);
_PI32_CONST(0x7f, 0x7f);
_PS_CONST(cephes_SQRTHF, 0.707106781186547524f);
_PS_CONST(cephes_log_p0, 7.0376836292E-2f);
_PS_CONST(cephes_log_p1, - 1.1514610310E-1f);
_PS_CONST(cephes_log_p2, 1.1676998740E-1f);
_PS_CONST(cephes_log_p3, - 1.2420140846E-1f);
_PS_CONST(cephes_log_p4, + 1.4249322787E-1f);
_PS_CONST(cephes_log_p5, - 1.6668057665E-1f);
_PS_CONST(cephes_log_p6, + 2.0000714765E-1f);
_PS_CONST(cephes_log_p7, - 2.4999993993E-1f);
_PS_CONST(cephes_log_p8, + 3.3333331174E-1f);
_PS_CONST(cephes_log_q1, -2.12194440e-4f);
_PS_CONST(cephes_log_q2, 0.693359375f);
#ifndef USE_SSE2
typedef union xmm_mm_union {
__m128 xmm;
__m64 mm[2];
} xmm_mm_union;
#define COPY_XMM_TO_MM(xmm_, mm0_, mm1_) { \
xmm_mm_union u; u.xmm = xmm_; \
mm0_ = u.mm[0]; \
mm1_ = u.mm[1]; \
}
#define COPY_MM_TO_XMM(mm0_, mm1_, xmm_) { \
xmm_mm_union u; u.mm[0]=mm0_; u.mm[1]=mm1_; xmm_ = u.xmm; \
}
#endif // USE_SSE2
/* natural logarithm computed for 4 simultaneous float
return NaN for x <= 0
*/
v4sf log_ps(v4sf x) {
#ifdef USE_SSE2
v4si emm0;
#else
v2si mm0, mm1;
#endif
v4sf one = *(v4sf*)_ps_1;
v4sf invalid_mask = _mm_cmple_ps(x, _mm_setzero_ps());
x = _mm_max_ps(x, *(v4sf*)_ps_min_norm_pos); /* cut off denormalized stuff */
#ifndef USE_SSE2
/* part 1: x = frexpf(x, &e); */
COPY_XMM_TO_MM(x, mm0, mm1);
mm0 = _mm_srli_pi32(mm0, 23);
mm1 = _mm_srli_pi32(mm1, 23);
#else
emm0 = _mm_srli_epi32(_mm_castps_si128(x), 23);
#endif
/* keep only the fractional part */
x = _mm_and_ps(x, *(v4sf*)_ps_inv_mant_mask);
x = _mm_or_ps(x, *(v4sf*)_ps_0p5);
#ifndef USE_SSE2
/* now e=mm0:mm1 contain the really base-2 exponent */
mm0 = _mm_sub_pi32(mm0, *(v2si*)_pi32_0x7f);
mm1 = _mm_sub_pi32(mm1, *(v2si*)_pi32_0x7f);
v4sf e = _mm_cvtpi32x2_ps(mm0, mm1);
_mm_empty(); /* bye bye mmx */
#else
emm0 = _mm_sub_epi32(emm0, *(v4si*)_pi32_0x7f);
v4sf e = _mm_cvtepi32_ps(emm0);
#endif
e = _mm_add_ps(e, one);
/* part2:
if( x < SQRTHF ) {
e -= 1;
x = x + x - 1.0;
} else { x = x - 1.0; }
*/
v4sf mask = _mm_cmplt_ps(x, *(v4sf*)_ps_cephes_SQRTHF);
v4sf tmp = _mm_and_ps(x, mask);
x = _mm_sub_ps(x, one);
e = _mm_sub_ps(e, _mm_and_ps(one, mask));
x = _mm_add_ps(x, tmp);
v4sf z = _mm_mul_ps(x,x);
v4sf y = *(v4sf*)_ps_cephes_log_p0;
y = _mm_mul_ps(y, x);
y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p1);
y = _mm_mul_ps(y, x);
y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p2);
y = _mm_mul_ps(y, x);
y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p3);
y = _mm_mul_ps(y, x);
y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p4);
y = _mm_mul_ps(y, x);
y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p5);
y = _mm_mul_ps(y, x);
y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p6);
y = _mm_mul_ps(y, x);
y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p7);
y = _mm_mul_ps(y, x);
y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p8);
y = _mm_mul_ps(y, x);
y = _mm_mul_ps(y, z);
tmp = _mm_mul_ps(e, *(v4sf*)_ps_cephes_log_q1);
y = _mm_add_ps(y, tmp);
tmp = _mm_mul_ps(z, *(v4sf*)_ps_0p5);
y = _mm_sub_ps(y, tmp);
tmp = _mm_mul_ps(e, *(v4sf*)_ps_cephes_log_q2);
x = _mm_add_ps(x, y);
x = _mm_add_ps(x, tmp);
x = _mm_or_ps(x, invalid_mask); // negative arg will be NAN
return x;
}
_PS_CONST(exp_hi, 88.3762626647949f);
_PS_CONST(exp_lo, -88.3762626647949f);
_PS_CONST(cephes_LOG2EF, 1.44269504088896341f);
_PS_CONST(cephes_exp_C1, 0.693359375f);
_PS_CONST(cephes_exp_C2, -2.12194440e-4f);
_PS_CONST(cephes_exp_p0, 1.9875691500E-4f);
_PS_CONST(cephes_exp_p1, 1.3981999507E-3f);
_PS_CONST(cephes_exp_p2, 8.3334519073E-3f);
_PS_CONST(cephes_exp_p3, 4.1665795894E-2f);
_PS_CONST(cephes_exp_p4, 1.6666665459E-1f);
_PS_CONST(cephes_exp_p5, 5.0000001201E-1f);
v4sf exp_ps(v4sf x) {
v4sf tmp = _mm_setzero_ps(), fx;
#ifdef USE_SSE2
v4si emm0;
#else
v2si mm0, mm1;
#endif
v4sf one = *(v4sf*)_ps_1;
x = _mm_min_ps(x, *(v4sf*)_ps_exp_hi);
x = _mm_max_ps(x, *(v4sf*)_ps_exp_lo);
/* express exp(x) as exp(g + n*log(2)) */
fx = _mm_mul_ps(x, *(v4sf*)_ps_cephes_LOG2EF);
fx = _mm_add_ps(fx, *(v4sf*)_ps_0p5);
/* how to perform a floorf with SSE: just below */
#ifndef USE_SSE2
/* step 1 : cast to int */
tmp = _mm_movehl_ps(tmp, fx);
mm0 = _mm_cvttps_pi32(fx);
mm1 = _mm_cvttps_pi32(tmp);
/* step 2 : cast back to float */
tmp = _mm_cvtpi32x2_ps(mm0, mm1);
#else
emm0 = _mm_cvttps_epi32(fx);
tmp = _mm_cvtepi32_ps(emm0);
#endif
/* if greater, substract 1 */
v4sf mask = _mm_cmpgt_ps(tmp, fx);
mask = _mm_and_ps(mask, one);
fx = _mm_sub_ps(tmp, mask);
tmp = _mm_mul_ps(fx, *(v4sf*)_ps_cephes_exp_C1);
v4sf z = _mm_mul_ps(fx, *(v4sf*)_ps_cephes_exp_C2);
x = _mm_sub_ps(x, tmp);
x = _mm_sub_ps(x, z);
z = _mm_mul_ps(x,x);
v4sf y = *(v4sf*)_ps_cephes_exp_p0;
y = _mm_mul_ps(y, x);
y = _mm_add_ps(y, *(v4sf*)_ps_cephes_exp_p1);
y = _mm_mul_ps(y, x);
y = _mm_add_ps(y, *(v4sf*)_ps_cephes_exp_p2);
y = _mm_mul_ps(y, x);
y = _mm_add_ps(y, *(v4sf*)_ps_cephes_exp_p3);
y = _mm_mul_ps(y, x);
y = _mm_add_ps(y, *(v4sf*)_ps_cephes_exp_p4);
y = _mm_mul_ps(y, x);
y = _mm_add_ps(y, *(v4sf*)_ps_cephes_exp_p5);
y = _mm_mul_ps(y, z);
y = _mm_add_ps(y, x);
y = _mm_add_ps(y, one);
/* build 2^n */
#ifndef USE_SSE2
z = _mm_movehl_ps(z, fx);
mm0 = _mm_cvttps_pi32(fx);
mm1 = _mm_cvttps_pi32(z);
mm0 = _mm_add_pi32(mm0, *(v2si*)_pi32_0x7f);
mm1 = _mm_add_pi32(mm1, *(v2si*)_pi32_0x7f);
mm0 = _mm_slli_pi32(mm0, 23);
mm1 = _mm_slli_pi32(mm1, 23);
v4sf pow2n;
COPY_MM_TO_XMM(mm0, mm1, pow2n);
_mm_empty();
#else
emm0 = _mm_cvttps_epi32(fx);
emm0 = _mm_add_epi32(emm0, *(v4si*)_pi32_0x7f);
emm0 = _mm_slli_epi32(emm0, 23);
v4sf pow2n = _mm_castsi128_ps(emm0);
#endif
y = _mm_mul_ps(y, pow2n);
return y;
}
_PS_CONST(minus_cephes_DP1, -0.78515625f);
_PS_CONST(minus_cephes_DP2, -2.4187564849853515625e-4f);
_PS_CONST(minus_cephes_DP3, -3.77489497744594108e-8f);
_PS_CONST(sincof_p0, -1.9515295891E-4f);
_PS_CONST(sincof_p1, 8.3321608736E-3f);
_PS_CONST(sincof_p2, -1.6666654611E-1f);
_PS_CONST(coscof_p0, 2.443315711809948E-005f);
_PS_CONST(coscof_p1, -1.388731625493765E-003f);
_PS_CONST(coscof_p2, 4.166664568298827E-002f);
_PS_CONST(cephes_FOPI, 1.27323954473516f); // 4 / M_PI
/* evaluation of 4 sines at onces, using only SSE1+MMX intrinsics so
it runs also on old athlons XPs and the pentium III of your grand
mother.
The code is the exact rewriting of the cephes sinf function.
Precision is excellent as long as x < 8192 (I did not bother to
take into account the special handling they have for greater values
-- it does not return garbage for arguments over 8192, though, but
the extra precision is missing).
Note that it is such that sinf((float)M_PI) = 8.74e-8, which is the
surprising but correct result.
Performance is also surprisingly good, 1.33 times faster than the
macos vsinf SSE2 function, and 1.5 times faster than the
__vrs4_sinf of amd's ACML (which is only available in 64 bits). Not
too bad for an SSE1 function (with no special tuning) !
However the latter libraries probably have a much better handling of NaN,
Inf, denormalized and other special arguments..
On my core 1 duo, the execution of this function takes approximately 95 cycles.
From what I have observed on the experiments with Intel AMath lib, switching to an
SSE2 version would improve the perf by only 10%.
Since it is based on SSE intrinsics, it has to be compiled at -O2 to
deliver full speed.
*/
v4sf sin_ps(v4sf x) { // any x
v4sf xmm1, xmm2 = _mm_setzero_ps(), xmm3, sign_bit, y;
#ifdef USE_SSE2
v4si emm0, emm2;
#else
v2si mm0, mm1, mm2, mm3;
#endif
sign_bit = x;
/* take the absolute value */
x = _mm_and_ps(x, *(v4sf*)_ps_inv_sign_mask);
/* extract the sign bit (upper one) */
sign_bit = _mm_and_ps(sign_bit, *(v4sf*)_ps_sign_mask);
/* scale by 4/Pi */
y = _mm_mul_ps(x, *(v4sf*)_ps_cephes_FOPI);
#ifdef USE_SSE2
/* store the integer part of y in mm0 */
emm2 = _mm_cvttps_epi32(y);
/* j=(j+1) & (~1) (see the cephes sources) */
emm2 = _mm_add_epi32(emm2, *(v4si*)_pi32_1);
emm2 = _mm_and_si128(emm2, *(v4si*)_pi32_inv1);
y = _mm_cvtepi32_ps(emm2);
/* get the swap sign flag */
emm0 = _mm_and_si128(emm2, *(v4si*)_pi32_4);
emm0 = _mm_slli_epi32(emm0, 29);
/* get the polynom selection mask
there is one polynom for 0 <= x <= Pi/4
and another one for Pi/4<x<=Pi/2
Both branches will be computed.
*/
emm2 = _mm_and_si128(emm2, *(v4si*)_pi32_2);
emm2 = _mm_cmpeq_epi32(emm2, _mm_setzero_si128());
v4sf swap_sign_bit = _mm_castsi128_ps(emm0);
v4sf poly_mask = _mm_castsi128_ps(emm2);
sign_bit = _mm_xor_ps(sign_bit, swap_sign_bit);
#else
/* store the integer part of y in mm0:mm1 */
xmm2 = _mm_movehl_ps(xmm2, y);
mm2 = _mm_cvttps_pi32(y);
mm3 = _mm_cvttps_pi32(xmm2);
/* j=(j+1) & (~1) (see the cephes sources) */
mm2 = _mm_add_pi32(mm2, *(v2si*)_pi32_1);
mm3 = _mm_add_pi32(mm3, *(v2si*)_pi32_1);
mm2 = _mm_and_si64(mm2, *(v2si*)_pi32_inv1);
mm3 = _mm_and_si64(mm3, *(v2si*)_pi32_inv1);
y = _mm_cvtpi32x2_ps(mm2, mm3);
/* get the swap sign flag */
mm0 = _mm_and_si64(mm2, *(v2si*)_pi32_4);
mm1 = _mm_and_si64(mm3, *(v2si*)_pi32_4);
mm0 = _mm_slli_pi32(mm0, 29);
mm1 = _mm_slli_pi32(mm1, 29);
/* get the polynom selection mask */
mm2 = _mm_and_si64(mm2, *(v2si*)_pi32_2);
mm3 = _mm_and_si64(mm3, *(v2si*)_pi32_2);
mm2 = _mm_cmpeq_pi32(mm2, _mm_setzero_si64());
mm3 = _mm_cmpeq_pi32(mm3, _mm_setzero_si64());
v4sf swap_sign_bit, poly_mask;
COPY_MM_TO_XMM(mm0, mm1, swap_sign_bit);
COPY_MM_TO_XMM(mm2, mm3, poly_mask);
sign_bit = _mm_xor_ps(sign_bit, swap_sign_bit);
_mm_empty(); /* good-bye mmx */
#endif
/* The magic pass: "Extended precision modular arithmetic"
x = ((x - y * DP1) - y * DP2) - y * DP3; */
xmm1 = *(v4sf*)_ps_minus_cephes_DP1;
xmm2 = *(v4sf*)_ps_minus_cephes_DP2;
xmm3 = *(v4sf*)_ps_minus_cephes_DP3;
xmm1 = _mm_mul_ps(y, xmm1);
xmm2 = _mm_mul_ps(y, xmm2);
xmm3 = _mm_mul_ps(y, xmm3);
x = _mm_add_ps(x, xmm1);
x = _mm_add_ps(x, xmm2);
x = _mm_add_ps(x, xmm3);
/* Evaluate the first polynom (0 <= x <= Pi/4) */
y = *(v4sf*)_ps_coscof_p0;
v4sf z = _mm_mul_ps(x,x);
y = _mm_mul_ps(y, z);
y = _mm_add_ps(y, *(v4sf*)_ps_coscof_p1);
y = _mm_mul_ps(y, z);
y = _mm_add_ps(y, *(v4sf*)_ps_coscof_p2);
y = _mm_mul_ps(y, z);
y = _mm_mul_ps(y, z);
v4sf tmp = _mm_mul_ps(z, *(v4sf*)_ps_0p5);
y = _mm_sub_ps(y, tmp);
y = _mm_add_ps(y, *(v4sf*)_ps_1);
/* Evaluate the second polynom (Pi/4 <= x <= 0) */
v4sf y2 = *(v4sf*)_ps_sincof_p0;
y2 = _mm_mul_ps(y2, z);
y2 = _mm_add_ps(y2, *(v4sf*)_ps_sincof_p1);
y2 = _mm_mul_ps(y2, z);
y2 = _mm_add_ps(y2, *(v4sf*)_ps_sincof_p2);
y2 = _mm_mul_ps(y2, z);
y2 = _mm_mul_ps(y2, x);
y2 = _mm_add_ps(y2, x);
/* select the correct result from the two polynoms */
xmm3 = poly_mask;
y2 = _mm_and_ps(xmm3, y2); //, xmm3);
y = _mm_andnot_ps(xmm3, y);
y = _mm_add_ps(y,y2);
/* update the sign */
y = _mm_xor_ps(y, sign_bit);
return y;
}
/* almost the same as sin_ps */
v4sf cos_ps(v4sf x) { // any x
v4sf xmm1, xmm2 = _mm_setzero_ps(), xmm3, y;
#ifdef USE_SSE2
v4si emm0, emm2;
#else
v2si mm0, mm1, mm2, mm3;
#endif
/* take the absolute value */
x = _mm_and_ps(x, *(v4sf*)_ps_inv_sign_mask);
/* scale by 4/Pi */
y = _mm_mul_ps(x, *(v4sf*)_ps_cephes_FOPI);
#ifdef USE_SSE2
/* store the integer part of y in mm0 */
emm2 = _mm_cvttps_epi32(y);
/* j=(j+1) & (~1) (see the cephes sources) */
emm2 = _mm_add_epi32(emm2, *(v4si*)_pi32_1);
emm2 = _mm_and_si128(emm2, *(v4si*)_pi32_inv1);
y = _mm_cvtepi32_ps(emm2);
emm2 = _mm_sub_epi32(emm2, *(v4si*)_pi32_2);
/* get the swap sign flag */
emm0 = _mm_andnot_si128(emm2, *(v4si*)_pi32_4);
emm0 = _mm_slli_epi32(emm0, 29);
/* get the polynom selection mask */
emm2 = _mm_and_si128(emm2, *(v4si*)_pi32_2);
emm2 = _mm_cmpeq_epi32(emm2, _mm_setzero_si128());
v4sf sign_bit = _mm_castsi128_ps(emm0);
v4sf poly_mask = _mm_castsi128_ps(emm2);
#else
/* store the integer part of y in mm0:mm1 */
xmm2 = _mm_movehl_ps(xmm2, y);
mm2 = _mm_cvttps_pi32(y);
mm3 = _mm_cvttps_pi32(xmm2);
/* j=(j+1) & (~1) (see the cephes sources) */
mm2 = _mm_add_pi32(mm2, *(v2si*)_pi32_1);
mm3 = _mm_add_pi32(mm3, *(v2si*)_pi32_1);
mm2 = _mm_and_si64(mm2, *(v2si*)_pi32_inv1);
mm3 = _mm_and_si64(mm3, *(v2si*)_pi32_inv1);
y = _mm_cvtpi32x2_ps(mm2, mm3);
mm2 = _mm_sub_pi32(mm2, *(v2si*)_pi32_2);
mm3 = _mm_sub_pi32(mm3, *(v2si*)_pi32_2);
/* get the swap sign flag in mm0:mm1 and the
polynom selection mask in mm2:mm3 */
mm0 = _mm_andnot_si64(mm2, *(v2si*)_pi32_4);
mm1 = _mm_andnot_si64(mm3, *(v2si*)_pi32_4);
mm0 = _mm_slli_pi32(mm0, 29);
mm1 = _mm_slli_pi32(mm1, 29);
mm2 = _mm_and_si64(mm2, *(v2si*)_pi32_2);
mm3 = _mm_and_si64(mm3, *(v2si*)_pi32_2);
mm2 = _mm_cmpeq_pi32(mm2, _mm_setzero_si64());
mm3 = _mm_cmpeq_pi32(mm3, _mm_setzero_si64());
v4sf sign_bit, poly_mask;
COPY_MM_TO_XMM(mm0, mm1, sign_bit);
COPY_MM_TO_XMM(mm2, mm3, poly_mask);
_mm_empty(); /* good-bye mmx */
#endif
/* The magic pass: "Extended precision modular arithmetic"
x = ((x - y * DP1) - y * DP2) - y * DP3; */
xmm1 = *(v4sf*)_ps_minus_cephes_DP1;
xmm2 = *(v4sf*)_ps_minus_cephes_DP2;
xmm3 = *(v4sf*)_ps_minus_cephes_DP3;
xmm1 = _mm_mul_ps(y, xmm1);
xmm2 = _mm_mul_ps(y, xmm2);
xmm3 = _mm_mul_ps(y, xmm3);
x = _mm_add_ps(x, xmm1);
x = _mm_add_ps(x, xmm2);
x = _mm_add_ps(x, xmm3);
/* Evaluate the first polynom (0 <= x <= Pi/4) */
y = *(v4sf*)_ps_coscof_p0;
v4sf z = _mm_mul_ps(x,x);
y = _mm_mul_ps(y, z);
y = _mm_add_ps(y, *(v4sf*)_ps_coscof_p1);
y = _mm_mul_ps(y, z);
y = _mm_add_ps(y, *(v4sf*)_ps_coscof_p2);
y = _mm_mul_ps(y, z);
y = _mm_mul_ps(y, z);
v4sf tmp = _mm_mul_ps(z, *(v4sf*)_ps_0p5);
y = _mm_sub_ps(y, tmp);
y = _mm_add_ps(y, *(v4sf*)_ps_1);
/* Evaluate the second polynom (Pi/4 <= x <= 0) */
v4sf y2 = *(v4sf*)_ps_sincof_p0;
y2 = _mm_mul_ps(y2, z);
y2 = _mm_add_ps(y2, *(v4sf*)_ps_sincof_p1);
y2 = _mm_mul_ps(y2, z);
y2 = _mm_add_ps(y2, *(v4sf*)_ps_sincof_p2);
y2 = _mm_mul_ps(y2, z);
y2 = _mm_mul_ps(y2, x);
y2 = _mm_add_ps(y2, x);
/* select the correct result from the two polynoms */
xmm3 = poly_mask;
y2 = _mm_and_ps(xmm3, y2); //, xmm3);
y = _mm_andnot_ps(xmm3, y);
y = _mm_add_ps(y,y2);
/* update the sign */
y = _mm_xor_ps(y, sign_bit);
return y;
}
/* since sin_ps and cos_ps are almost identical, sincos_ps could replace both of them..
it is almost as fast, and gives you a free cosine with your sine */
void sincos_ps(v4sf x, v4sf *s, v4sf *c) {
v4sf xmm1, xmm2, xmm3 = _mm_setzero_ps(), sign_bit_sin, y;
#ifdef USE_SSE2
v4si emm0, emm2, emm4;
#else
v2si mm0, mm1, mm2, mm3, mm4, mm5;
#endif
sign_bit_sin = x;
/* take the absolute value */
x = _mm_and_ps(x, *(v4sf*)_ps_inv_sign_mask);
/* extract the sign bit (upper one) */
sign_bit_sin = _mm_and_ps(sign_bit_sin, *(v4sf*)_ps_sign_mask);
/* scale by 4/Pi */
y = _mm_mul_ps(x, *(v4sf*)_ps_cephes_FOPI);
#ifdef USE_SSE2
/* store the integer part of y in emm2 */
emm2 = _mm_cvttps_epi32(y);
/* j=(j+1) & (~1) (see the cephes sources) */
emm2 = _mm_add_epi32(emm2, *(v4si*)_pi32_1);
emm2 = _mm_and_si128(emm2, *(v4si*)_pi32_inv1);
y = _mm_cvtepi32_ps(emm2);
emm4 = emm2;
/* get the swap sign flag for the sine */
emm0 = _mm_and_si128(emm2, *(v4si*)_pi32_4);
emm0 = _mm_slli_epi32(emm0, 29);
v4sf swap_sign_bit_sin = _mm_castsi128_ps(emm0);
/* get the polynom selection mask for the sine*/
emm2 = _mm_and_si128(emm2, *(v4si*)_pi32_2);
emm2 = _mm_cmpeq_epi32(emm2, _mm_setzero_si128());
v4sf poly_mask = _mm_castsi128_ps(emm2);
#else
/* store the integer part of y in mm2:mm3 */
xmm3 = _mm_movehl_ps(xmm3, y);
mm2 = _mm_cvttps_pi32(y);
mm3 = _mm_cvttps_pi32(xmm3);
/* j=(j+1) & (~1) (see the cephes sources) */
mm2 = _mm_add_pi32(mm2, *(v2si*)_pi32_1);
mm3 = _mm_add_pi32(mm3, *(v2si*)_pi32_1);
mm2 = _mm_and_si64(mm2, *(v2si*)_pi32_inv1);
mm3 = _mm_and_si64(mm3, *(v2si*)_pi32_inv1);
y = _mm_cvtpi32x2_ps(mm2, mm3);
mm4 = mm2;
mm5 = mm3;
/* get the swap sign flag for the sine */
mm0 = _mm_and_si64(mm2, *(v2si*)_pi32_4);
mm1 = _mm_and_si64(mm3, *(v2si*)_pi32_4);
mm0 = _mm_slli_pi32(mm0, 29);
mm1 = _mm_slli_pi32(mm1, 29);
v4sf swap_sign_bit_sin;
COPY_MM_TO_XMM(mm0, mm1, swap_sign_bit_sin);
/* get the polynom selection mask for the sine */
mm2 = _mm_and_si64(mm2, *(v2si*)_pi32_2);
mm3 = _mm_and_si64(mm3, *(v2si*)_pi32_2);
mm2 = _mm_cmpeq_pi32(mm2, _mm_setzero_si64());
mm3 = _mm_cmpeq_pi32(mm3, _mm_setzero_si64());
v4sf poly_mask;
COPY_MM_TO_XMM(mm2, mm3, poly_mask);
#endif
/* The magic pass: "Extended precision modular arithmetic"
x = ((x - y * DP1) - y * DP2) - y * DP3; */
xmm1 = *(v4sf*)_ps_minus_cephes_DP1;
xmm2 = *(v4sf*)_ps_minus_cephes_DP2;
xmm3 = *(v4sf*)_ps_minus_cephes_DP3;
xmm1 = _mm_mul_ps(y, xmm1);
xmm2 = _mm_mul_ps(y, xmm2);
xmm3 = _mm_mul_ps(y, xmm3);
x = _mm_add_ps(x, xmm1);
x = _mm_add_ps(x, xmm2);
x = _mm_add_ps(x, xmm3);
#ifdef USE_SSE2
emm4 = _mm_sub_epi32(emm4, *(v4si*)_pi32_2);
emm4 = _mm_andnot_si128(emm4, *(v4si*)_pi32_4);
emm4 = _mm_slli_epi32(emm4, 29);
v4sf sign_bit_cos = _mm_castsi128_ps(emm4);
#else
/* get the sign flag for the cosine */
mm4 = _mm_sub_pi32(mm4, *(v2si*)_pi32_2);
mm5 = _mm_sub_pi32(mm5, *(v2si*)_pi32_2);
mm4 = _mm_andnot_si64(mm4, *(v2si*)_pi32_4);
mm5 = _mm_andnot_si64(mm5, *(v2si*)_pi32_4);
mm4 = _mm_slli_pi32(mm4, 29);
mm5 = _mm_slli_pi32(mm5, 29);
v4sf sign_bit_cos;
COPY_MM_TO_XMM(mm4, mm5, sign_bit_cos);
_mm_empty(); /* good-bye mmx */
#endif
sign_bit_sin = _mm_xor_ps(sign_bit_sin, swap_sign_bit_sin);
/* Evaluate the first polynom (0 <= x <= Pi/4) */
v4sf z = _mm_mul_ps(x,x);
y = *(v4sf*)_ps_coscof_p0;
y = _mm_mul_ps(y, z);
y = _mm_add_ps(y, *(v4sf*)_ps_coscof_p1);
y = _mm_mul_ps(y, z);
y = _mm_add_ps(y, *(v4sf*)_ps_coscof_p2);
y = _mm_mul_ps(y, z);
y = _mm_mul_ps(y, z);
v4sf tmp = _mm_mul_ps(z, *(v4sf*)_ps_0p5);
y = _mm_sub_ps(y, tmp);
y = _mm_add_ps(y, *(v4sf*)_ps_1);
/* Evaluate the second polynom (Pi/4 <= x <= 0) */
v4sf y2 = *(v4sf*)_ps_sincof_p0;
y2 = _mm_mul_ps(y2, z);
y2 = _mm_add_ps(y2, *(v4sf*)_ps_sincof_p1);
y2 = _mm_mul_ps(y2, z);
y2 = _mm_add_ps(y2, *(v4sf*)_ps_sincof_p2);
y2 = _mm_mul_ps(y2, z);
y2 = _mm_mul_ps(y2, x);
y2 = _mm_add_ps(y2, x);
/* select the correct result from the two polynoms */
xmm3 = poly_mask;
v4sf ysin2 = _mm_and_ps(xmm3, y2);
v4sf ysin1 = _mm_andnot_ps(xmm3, y);
y2 = _mm_sub_ps(y2,ysin2);
y = _mm_sub_ps(y, ysin1);
xmm1 = _mm_add_ps(ysin1,ysin2);
xmm2 = _mm_add_ps(y,y2);
/* update the sign */
*s = _mm_xor_ps(xmm1, sign_bit_sin);
*c = _mm_xor_ps(xmm2, sign_bit_cos);
}

View file

@ -26,8 +26,8 @@ namespace sfz
* @tparam MaxChannels the maximum number of channels in the buffer
* @tparam Alignment the alignment for the buffers
*/
template <class Type, size_t MaxChannels = sfz::config::numChannels,
unsigned int Alignment = SIMDConfig::defaultAlignment,
template <class Type, size_t MaxChannels = config::numChannels,
unsigned int Alignment = config::defaultAlignment,
size_t PaddingLeft_ = 0, size_t PaddingRight_ = 0>
class AudioBuffer {
public:

View file

@ -279,7 +279,7 @@ public:
{
static_assert(!std::is_const<Type>::value, "Can't allow mutating operations on const AudioSpans");
for (size_t i = 0; i < numChannels; ++i)
sfz::fill<Type>(getSpan(i), value);
sfz::fill(getSpan(i), value);
}
/**
@ -303,7 +303,7 @@ public:
{
static_assert(!std::is_const<Type>::value, "Can't allow mutating operations on const AudioSpans");
for (size_t i = 0; i < numChannels; ++i)
sfz::applyGain<Type>(gain, getSpan(i));
sfz::applyGain1<Type>(gain, getSpan(i));
}
/**

View file

@ -119,9 +119,9 @@ private:
*
* @tparam Type The buffer type
* @tparam Alignment the required alignment in bytes (defaults to
* SIMDConfig::defaultAlignment)
* config::defaultAlignment)
*/
template <class Type, unsigned int Alignment = SIMDConfig::defaultAlignment>
template <class Type, unsigned int Alignment = config::defaultAlignment>
class Buffer {
public:
using value_type = typename std::remove_cv<Type>::type;

View file

@ -58,6 +58,7 @@ namespace config {
constexpr uint16_t numCCs { 512 };
constexpr int maxCurves { 256 };
constexpr int chunkSize { 1024 };
constexpr unsigned int defaultAlignment { 16 };
constexpr int filtersInPool { maxVoices * 2 };
constexpr int excessFileFrames { 8 };
/**
@ -96,29 +97,4 @@ namespace config {
static constexpr double amplitudeSquare = 0.515;
} // namespace config
// Enable or disable SIMD accelerators by default
namespace SIMDConfig {
constexpr unsigned int defaultAlignment { 16 };
constexpr bool writeInterleaved { true };
constexpr bool readInterleaved { true };
constexpr bool fill { true };
constexpr bool gain { false };
constexpr bool divide { false };
constexpr bool mathfuns { false };
constexpr bool loopingSFZIndex { true };
constexpr bool saturatingSFZIndex { true };
constexpr bool linearRamp { false };
constexpr bool multiplicativeRamp { true };
constexpr bool add { false };
constexpr bool subtract { false };
constexpr bool multiplyAdd { false };
constexpr bool copy { false };
constexpr bool pan { false };
constexpr bool cumsum { true };
constexpr bool diff { false };
constexpr bool sfzInterpolationCast { true };
constexpr bool mean { false };
constexpr bool meanSquared { false };
constexpr bool upsampling { true };
}
} // namespace sfz

View file

@ -46,7 +46,7 @@
std::cerr << "Check failed at " << __FILE__ << ":" << __LINE__ << '\n'; \
} while (0)
#define CHECK(expression) \
#define SFIZZ_CHECK(expression) \
do { \
if (!(expression)) { \
std::cerr << "Check failed: " << #expression << '\n'; \
@ -59,7 +59,7 @@
#define ASSERTFALSE do {} while (0)
#define ASSERT(expression) do {} while (0)
#define CHECKFALSE do {} while (0)
#define CHECK(expression) do {} while (0)
#define SFIZZ_CHECK(expression) do {} while (0)
#endif

View file

@ -105,7 +105,7 @@ void EffectBus::addToInputs(const float* const addInput[], float addGain, unsign
for (unsigned c = 0; c < EffectChannels; ++c) {
absl::Span<const float> addIn { addInput[c], nframes };
sfz::multiplyAdd(addGain, addIn, _inputs.getSpan(c).first(nframes));
sfz::multiplyAdd1(addGain, addIn, _inputs.getSpan(c).first(nframes));
}
}
@ -154,8 +154,8 @@ void EffectBus::mixOutputsTo(float* const mainOutput[], float* const mixOutput[]
for (unsigned c = 0; c < EffectChannels; ++c) {
auto fxOut = _outputs.getConstSpan(c).first(nframes);
sfz::multiplyAdd(gainToMain, fxOut, absl::Span<float>(mainOutput[c], nframes));
sfz::multiplyAdd(gainToMix, fxOut, absl::Span<float>(mixOutput[c], nframes));
sfz::multiplyAdd1(gainToMain, fxOut, absl::Span<float>(mainOutput[c], nframes));
sfz::multiplyAdd1(gainToMix, fxOut, absl::Span<float>(mixOutput[c], nframes));
}
}

View file

@ -60,7 +60,7 @@ void readBaseFile(SndfileHandle& sndFile, sfz::FileAudioBuffer& output, uint32_t
output.clear();
sfz::Buffer<float> tempReadBuffer { 2 * numFrames };
sndFile.readf(tempReadBuffer.data(), numFrames);
sfz::readInterleaved<float>(tempReadBuffer, output.getSpan(0), output.getSpan(1));
sfz::readInterleaved(tempReadBuffer, output.getSpan(0), output.getSpan(1));
}
if (reverse) {
@ -87,7 +87,6 @@ std::unique_ptr<sfz::FileAudioBuffer> readFromFile(SndfileHandle& sndFile, uint3
return outputBuffer;
}
template <class T>
void streamFromFile(SndfileHandle& sndFile, uint32_t numFrames, sfz::Oversampling factor, bool reverse, sfz::FileAudioBuffer& output, std::atomic<size_t>* filledFrames = nullptr)
{
if (factor == sfz::Oversampling::x1) {
@ -400,7 +399,7 @@ void sfz::FilePool::loadingThread() noexcept
continue;
}
const auto frames = static_cast<uint32_t>(sndFile.frames());
streamFromFile<float>(sndFile, frames, oversamplingFactor, promise->fileId.isReverse(), promise->fileData, &promise->availableFrames);
streamFromFile(sndFile, frames, oversamplingFactor, promise->fileId.isReverse(), promise->fileData, &promise->availableFrames);
promise->dataStatus = FilePromise::DataStatus::Ready;
const auto loadDuration = std::chrono::high_resolution_clock::now() - loadStartTime;
logger.logFileTime(waitDuration, loadDuration, frames, promise->fileId.filename());

View file

@ -43,7 +43,7 @@
#include <mutex>
namespace sfz {
using FileAudioBuffer = AudioBuffer<float, 2, SIMDConfig::defaultAlignment,
using FileAudioBuffer = AudioBuffer<float, 2, config::defaultAlignment,
sfz::config::excessFileFrames, sfz::config::excessFileFrames>;
using FileAudioBufferPtr = std::shared_ptr<FileAudioBuffer>;

View file

@ -35,7 +35,7 @@ public:
void resize(size_t size)
{
buffer.resize(size);
fill<ValueType>(absl::MakeSpan(buffer), 0.0);
fill(absl::MakeSpan(buffer), ValueType { 0 });
index = 0;
validMean = false;
}

View file

@ -9,6 +9,7 @@
* @brief Contains math helper functions and math constants
*/
#pragma once
#include "Debug.h"
#include "Config.h"
#include "Macros.h"
#include "SIMDConfig.h"
@ -253,6 +254,20 @@ constexpr Type sqrtTwo() { return static_cast<Type>(1.41421356237309504880168872
template <class Type>
constexpr Type sqrtTwoInv() { return static_cast<Type>(0.707106781186547524400844362104849039284835937688474036588); };
/**
* @brief lround for positive values
* This optimizes a bit better by ignoring the negative code path
*
* @tparam T
* @param value
* @return constexpr long int
*/
template<class T, absl::enable_if_t<std::is_floating_point<T>::value, int> = 0 >
constexpr long int lroundPositive(T value)
{
return static_cast<int>(0.5f + value); // NOLINT
}
/**
@brief A fraction which is parameterized by integer type
*/
@ -476,7 +491,7 @@ constexpr bool checkSpanSizes(const absl::Span<T>& span1, Others... others)
return _checkSpanSizes(span1.size(), others...);
}
#define CHECK_SPAN_SIZES(...) CHECK(checkSpanSizes(__VA_ARGS__))
#define CHECK_SPAN_SIZES(...) SFIZZ_CHECK(checkSpanSizes(__VA_ARGS__))
class ScopedRoundingMode {

View file

@ -74,7 +74,7 @@ void linearEnvelope(const EventVector& events, absl::Span<float> envelope, F&& l
lastValue = linearRamp<float>(envelope.subspan(lastDelay, length), lastValue, step);
lastDelay += length;
}
fill<float>(envelope.subspan(lastDelay), lastValue);
fill(envelope.subspan(lastDelay), lastValue);
}
template <class F>
@ -100,7 +100,7 @@ void linearEnvelope(const EventVector& events, absl::Span<float> envelope, F&& l
const auto length = min(events[i].delay, maxDelay) - lastDelay;
if (difference < step) {
fill<float>(envelope.subspan(lastDelay, length), lastValue);
fill(envelope.subspan(lastDelay, length), lastValue);
lastValue = nextValue;
lastDelay += length;
continue;
@ -109,12 +109,12 @@ void linearEnvelope(const EventVector& events, absl::Span<float> envelope, F&& l
const auto numSteps = static_cast<int>(difference / step);
const auto stepLength = static_cast<int>(length / numSteps);
for (int i = 0; i < numSteps; ++i) {
fill<float>(envelope.subspan(lastDelay, stepLength), lastValue);
fill(envelope.subspan(lastDelay, stepLength), lastValue);
lastValue += lastValue <= nextValue ? step : -step;
lastDelay += stepLength;
}
}
fill<float>(envelope.subspan(lastDelay), lastValue);
fill(envelope.subspan(lastDelay), lastValue);
}
template <class F>
@ -137,7 +137,7 @@ void multiplicativeEnvelope(const EventVector& events, absl::Span<float> envelop
lastValue = nextValue;
lastDelay += length;
}
fill<float>(envelope.subspan(lastDelay), lastValue);
fill(envelope.subspan(lastDelay), lastValue);
}
template <class F, bool Round = false>
@ -170,7 +170,7 @@ void multiplicativeEnvelope(const EventVector& events, absl::Span<float> envelop
const auto difference = nextValue > lastValue ? nextValue / lastValue : lastValue / nextValue;
if (difference < step) {
fill<float>(envelope.subspan(lastDelay, length), lastValue);
fill(envelope.subspan(lastDelay, length), lastValue);
lastValue = nextValue;
lastDelay += length;
continue;
@ -179,12 +179,12 @@ void multiplicativeEnvelope(const EventVector& events, absl::Span<float> envelop
const auto numSteps = std::round(std::log(difference) / logStep);
const auto stepLength = static_cast<int>(length / numSteps);
for (int i = 0; i < static_cast<int>(numSteps); ++i) {
fill<float>(envelope.subspan(lastDelay, stepLength), lastValue);
fill(envelope.subspan(lastDelay, stepLength), lastValue);
lastValue = nextValue > lastValue ? lastValue * step : lastValue / step;
lastDelay += stepLength;
}
}
fill<float>(envelope.subspan(lastDelay), lastValue);
fill(envelope.subspan(lastDelay), lastValue);
}
template <class F>

59
src/sfizz/Panning.cpp Normal file
View file

@ -0,0 +1,59 @@
#include "Panning.h"
#include <array>
#include <cmath>
namespace sfz
{
// Number of elements in the table, odd for equal volume at center
constexpr int panSize = 4095;
// Table of pan values for the left channel, extra element for safety
static const auto panData = []()
{
std::array<float, panSize + 1> pan;
int i = 0;
for (; i < panSize; ++i)
pan[i] = std::cos(i * (piTwo<double>() / (panSize - 1)));
for (; i < static_cast<int>(pan.size()); ++i)
pan[i] = pan[panSize - 1];
return pan;
}();
float panLookup(float pan)
{
// reduce range, round to nearest
int index = lroundPositive(pan * (panSize - 1));
return panData[index];
}
void pan(const float* panEnvelope, float* leftBuffer, float* rightBuffer, unsigned size) noexcept
{
const auto sentinel = panEnvelope + size;
while (panEnvelope < sentinel) {
auto p =(*panEnvelope + 1.0f) * 0.5f;
p = clamp(p, 0.0f, 1.0f);
*leftBuffer *= panLookup(p);
*rightBuffer *= panLookup(1 - p);
incrementAll(panEnvelope, leftBuffer, rightBuffer);
}
}
void width(const float* widthEnvelope, float* leftBuffer, float* rightBuffer, unsigned size) noexcept
{
const auto sentinel = widthEnvelope + size;
while (widthEnvelope < sentinel) {
float w = (*widthEnvelope + 1.0f) * 0.5f;
w = clamp(w, 0.0f, 1.0f);
const auto coeff1 = panLookup(w);
const auto coeff2 = panLookup(1 - w);
const auto l = *leftBuffer;
const auto r = *rightBuffer;
*leftBuffer = l * coeff2 + r * coeff1;
*rightBuffer = l * coeff1 + r * coeff2;
incrementAll(widthEnvelope, leftBuffer, rightBuffer);
}
}
}

47
src/sfizz/Panning.h Normal file
View file

@ -0,0 +1,47 @@
#pragma once
#include "absl/types/span.h"
#include "MathHelpers.h"
namespace sfz
{
/**
* @brief Lookup a value from the pan table
*
* @param pan
* @return float
*/
float panLookup(float pan);
/**
* @brief Pans a mono signal left or right
*
* @param panEnvelope
* @param leftBuffer
* @param rightBuffer
* @param size
*/
void pan(const float* panEnvelope, float* leftBuffer, float* rightBuffer, unsigned size) noexcept;
inline void pan(absl::Span<const float> panEnvelope, absl::Span<float> leftBuffer, absl::Span<float> rightBuffer) noexcept
{
CHECK_SPAN_SIZES(panEnvelope, leftBuffer, rightBuffer);
pan(panEnvelope.data(), leftBuffer.data(), rightBuffer.data(), minSpanSize(panEnvelope, leftBuffer, rightBuffer));
}
/**
* @brief Controls the width of a stereo signal, setting it to mono when width = 0 and inverting the channels
* when width = -1. Width = 1 has no effect.
*
* @param widthEnvelope
* @param leftBuffer
* @param rightBuffer
* @param size
*/
void width(const float* widthEnvelope, float* leftBuffer, float* rightBuffer, unsigned size) noexcept;
inline void width(absl::Span<const float> widthEnvelope, absl::Span<float> leftBuffer, absl::Span<float> rightBuffer) noexcept
{
CHECK_SPAN_SIZES(widthEnvelope, leftBuffer, rightBuffer);
width(widthEnvelope.data(), leftBuffer.data(), rightBuffer.data(), minSpanSize(widthEnvelope, leftBuffer, rightBuffer));
}
}

View file

@ -13,19 +13,27 @@
- SFIZZ_HAVE_SSE
- SFIZZ_HAVE_SSE2
- SFIZZ_HAVE_AVX
- SFIZZ_HAVE_NEON
*/
#if defined(__GNUC__)
# if defined(__SSE2__)
# if defined(__AVX__)
# define SFIZZ_DETECT_SSE 1
# define SFIZZ_DETECT_SSE2 1
# define SFIZZ_DETECT_AVX 1
# elif defined(__SSE2__)
# define SFIZZ_DETECT_SSE 1
# define SFIZZ_DETECT_SSE2 1
# define SFIZZ_DETECT_AVX 0
# elif defined(__SSE__)
# define SFIZZ_DETECT_SSE 1
# define SFIZZ_DETECT_SSE2 0
# define SFIZZ_DETECT_AVX 0
# else
# define SFIZZ_DETECT_SSE 0
# define SFIZZ_DETECT_SSE2 0
# define SFIZZ_DETECT_AVX 0
# endif
# if defined(__ARM_NEON__)
# define SFIZZ_DETECT_NEON 1
@ -33,15 +41,22 @@
# define SFIZZ_DETECT_NEON 0
# endif
#elif defined(_MSC_VER)
# if defined(_M_AMD64) || defined(_M_X64)
# if defined(__AVX__)
# define SFIZZ_DETECT_SSE 1
# define SFIZZ_DETECT_SSE2 1
# define SFIZZ_DETECT_AVX 1
# elif defined(_M_AMD64) || defined(_M_X64)
# define SFIZZ_DETECT_SSE 1
# define SFIZZ_DETECT_SSE2 1
# define SFIZZ_DETECT_AVX 0
# elif _M_IX86_FP == 2
# define SFIZZ_DETECT_SSE 1
# define SFIZZ_DETECT_SSE2 1
# define SFIZZ_DETECT_AVX 0
# elif _M_IX86_FP == 1
# define SFIZZ_DETECT_SSE 1
# define SFIZZ_DETECT_SSE2 0
# define SFIZZ_DETECT_AVX 0
# endif
// TODO: how to check for NEON on MSVC ARM?
#endif
@ -60,6 +75,13 @@
# define SFIZZ_HAVE_SSE2 0
# endif
#endif
#ifndef SFIZZ_HAVE_AVX
# ifdef SFIZZ_DETECT_AVX
# define SFIZZ_HAVE_AVX SFIZZ_DETECT_AVX
# else
# define SFIZZ_HAVE_AVX 0
# endif
#endif
#ifndef SFIZZ_HAVE_NEON
# ifdef SFIZZ_DETECT_NEON
# define SFIZZ_HAVE_NEON SFIZZ_DETECT_NEON

View file

@ -1,177 +0,0 @@
// 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 "SIMDConfig.h"
#if !(SFIZZ_HAVE_SSE2 || SFIZZ_HAVE_NEON)
#include "SIMDHelpers.h"
template <>
void sfz::readInterleaved<float, true>(absl::Span<const float> input, absl::Span<float> outputLeft, absl::Span<float> outputRight) noexcept
{
readInterleaved<float, false>(input, outputLeft, outputRight);
}
template <>
void sfz::writeInterleaved<float, true>(absl::Span<const float> inputLeft, absl::Span<const float> inputRight, absl::Span<float> output) noexcept
{
writeInterleaved<float, false>(inputLeft, inputRight, output);
}
template <>
void sfz::fill<float, true>(absl::Span<float> output, float value) noexcept
{
fill<float, false>(output, value);
}
template <>
void sfz::exp<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept
{
exp<float, false>(input, output);
}
template <>
void sfz::log<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept
{
log<float, false>(input, output);
}
template <>
void sfz::sin<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept
{
sin<float, false>(input, output);
}
template <>
void sfz::cos<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept
{
cos<float, false>(input, output);
}
template <>
void sfz::applyGain<float, true>(float gain, absl::Span<const float> input, absl::Span<float> output) noexcept
{
applyGain<float, false>(gain, input, output);
}
template <>
void sfz::applyGain<float, true>(absl::Span<const float> gain, absl::Span<const float> input, absl::Span<float> output) noexcept
{
applyGain<float, false>(gain, input, output);
}
template <>
void sfz::divide<float, true>(absl::Span<const float> input, absl::Span<const float> divisor, absl::Span<float> output) noexcept
{
divide<float, false>(input, divisor, output);
}
template <>
void sfz::multiplyAdd<float, true>(absl::Span<const float> gain, absl::Span<const float> input, absl::Span<float> output) noexcept
{
multiplyAdd<float, false>(gain, input, output);
}
template <>
void sfz::multiplyAdd<float, true>(const float gain, absl::Span<const float> input, absl::Span<float> output) noexcept
{
multiplyAdd<float, false>(gain, input, output);
}
template <>
float sfz::loopingSFZIndex<float, true>(absl::Span<const float> jumps, absl::Span<float> leftCoeff, absl::Span<float> rightCoeff, absl::Span<int> indices, float floatIndex, float loopEnd, float loopStart) noexcept
{
return loopingSFZIndex<float, false>(jumps, leftCoeff, rightCoeff, indices, floatIndex, loopEnd, loopStart);
}
template <>
float sfz::saturatingSFZIndex<float, true>(absl::Span<const float> jumps, absl::Span<float> leftCoeff, absl::Span<float> rightCoeff, absl::Span<int> indices, float floatIndex, float loopEnd) noexcept
{
return saturatingSFZIndex<float, false>(jumps, leftCoeff, rightCoeff, indices, floatIndex, loopEnd);
}
template <>
float sfz::linearRamp<float, true>(absl::Span<float> output, float start, float step) noexcept
{
return linearRamp<float, false>(output, start, step);
}
template <>
float sfz::multiplicativeRamp<float, true>(absl::Span<float> output, float start, float step) noexcept
{
return multiplicativeRamp<float, false>(output, start, step);
}
template <>
void sfz::add<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept
{
add<float, false>(input, output);
}
template <>
void sfz::add<float, true>(float value, absl::Span<float> output) noexcept
{
add<float, false>(value, output);
}
template <>
void sfz::subtract<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept
{
subtract<float, false>(input, output);
}
template <>
void sfz::subtract<float, true>(const float value, absl::Span<float> output) noexcept
{
subtract<float, false>(value, output);
}
template <>
void sfz::copy<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept
{
copy<float, false>(input, output);
}
template <>
void sfz::pan<float, true>(absl::Span<const float> panEnvelope, absl::Span<float> leftBuffer, absl::Span<float> rightBuffer) noexcept
{
pan<float, false>(panEnvelope, leftBuffer, rightBuffer);
}
template <>
float sfz::mean<float, true>(absl::Span<const float> vector) noexcept
{
return mean<float, false>(vector);
}
template <>
float sfz::meanSquared<float, true>(absl::Span<const float> vector) noexcept
{
return meanSquared<float, false>(vector);
}
template <>
void sfz::cumsum<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept
{
cumsum<float, false>(input, output);
}
template<>
void sfz::sfzInterpolationCast<float, true>(absl::Span<const float> floatJumps, absl::Span<int> jumps, absl::Span<float> coeffs) noexcept
{
sfzInterpolationCast<float, false>(floatJumps, jumps, coeffs);
}
template <>
void sfz::diff<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept
{
diff<float, false>(input, output);
}
#endif // !(SFIZZ_HAVE_SSE2 || SFIZZ_HAVE_NEON)

304
src/sfizz/SIMDHelpers.cpp Normal file
View file

@ -0,0 +1,304 @@
// 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 "SIMDHelpers.h"
#include "SIMDConfig.h"
#include "Debug.h"
#include "simd/HelpersSSE.h"
#include "simd/HelpersAVX.h"
#include "cpuid/cpuinfo.hpp"
#include <array>
#include <mutex>
namespace sfz {
template <class T>
struct SIMDDispatch {
constexpr SIMDDispatch() = default;
void resetStatus();
bool getStatus(SIMDOps op) const;
void setStatus(SIMDOps op, bool enable);
decltype(&writeInterleavedScalar<T>) writeInterleaved = &writeInterleavedScalar<T>;
decltype(&readInterleavedScalar<T>) readInterleaved = &readInterleavedScalar<T>;
decltype(&gainScalar<T>) gain = &gainScalar<T>;
decltype(&gain1Scalar<T>) gain1 = &gain1Scalar<T>;
decltype(&divideScalar<T>) divide = &divideScalar<T>;
decltype(&multiplyAddScalar<T>) multiplyAdd = &multiplyAddScalar<T>;
decltype(&multiplyAdd1Scalar<T>) multiplyAdd1 = &multiplyAdd1Scalar<T>;
decltype(&linearRampScalar<T>) linearRamp = &linearRampScalar<T>;
decltype(&multiplicativeRampScalar<T>) multiplicativeRamp = &multiplicativeRampScalar<T>;
decltype(&addScalar<T>) add = &addScalar<T>;
decltype(&add1Scalar<T>) add1 = &add1Scalar<T>;
decltype(&subtractScalar<T>) subtract = &subtractScalar<T>;
decltype(&subtract1Scalar<T>) subtract1 = &subtract1Scalar<T>;
decltype(&copyScalar<T>) copy = &copyScalar<T>;
decltype(&cumsumScalar<T>) cumsum = &cumsumScalar<T>;
decltype(&diffScalar<T>) diff = &diffScalar<T>;
decltype(&meanScalar<T>) mean = &meanScalar<T>;
decltype(&meanSquaredScalar<T>) meanSquared = &meanSquaredScalar<T>;
private:
std::array<bool, static_cast<unsigned>(SIMDOps::_sentinel)> simdStatus;
cpuid::cpuinfo info;
};
template <>
bool SIMDDispatch<float>::getStatus(SIMDOps op) const
{
const unsigned index = static_cast<unsigned>(op);
ASSERT(index < simdStatus.size());
return simdStatus[index];
}
template <>
void SIMDDispatch<float>::setStatus(SIMDOps op, bool enable)
{
const unsigned index = static_cast<unsigned>(op);
ASSERT(index < simdStatus.size());
simdStatus[index] = enable;
if (!enable) {
#define SIMD_OP(opname) case SIMDOps::opname : (opname) = opname ## Scalar<float>; return;
switch (op) {
default: break;
SIMD_OP(writeInterleaved)
SIMD_OP(readInterleaved)
SIMD_OP(gain)
SIMD_OP(gain1)
SIMD_OP(divide)
SIMD_OP(linearRamp)
SIMD_OP(multiplicativeRamp)
SIMD_OP(add)
SIMD_OP(add1)
SIMD_OP(subtract)
SIMD_OP(subtract1)
SIMD_OP(multiplyAdd)
SIMD_OP(multiplyAdd1)
SIMD_OP(copy)
SIMD_OP(cumsum)
SIMD_OP(diff)
SIMD_OP(mean)
SIMD_OP(meanSquared)
}
#undef SIMD_OP
}
#if SFIZZ_CPU_FAMILY_X86_64 || SFIZZ_CPU_FAMILY_I386
#define SIMD_OP(opname) case SIMDOps::opname : (opname) = opname ## AVX; return;
if (info.has_avx()) {
switch (op) {
default: break;
}
}
#undef SIMD_OP
#define SIMD_OP(opname) case SIMDOps::opname : (opname) = opname ## SSE; return;
if (info.has_sse()) {
switch (op) {
default: break;
SIMD_OP(writeInterleaved)
SIMD_OP(readInterleaved)
SIMD_OP(gain)
SIMD_OP(gain1)
SIMD_OP(divide)
SIMD_OP(linearRamp)
SIMD_OP(multiplicativeRamp)
SIMD_OP(add)
SIMD_OP(add1)
SIMD_OP(subtract)
SIMD_OP(subtract1)
SIMD_OP(multiplyAdd)
SIMD_OP(multiplyAdd1)
SIMD_OP(copy)
SIMD_OP(cumsum)
SIMD_OP(diff)
SIMD_OP(mean)
SIMD_OP(meanSquared)
}
}
#undef SIMD_OP
#endif // SFIZZ_CPU_FAMILY_X86_64 || SFIZZ_CPU_FAMILY_I386
#if SFIZZ_CPU_FAMILY_AARCH64 || SFIZZ_CPU_FAMILY_ARM
#define SIMD_OP(opname) case SIMDOps::opname : (opname) = opname ## NEON; return;
if (info.has_neon()) {
switch (op) {
default: break;
}
}
#undef SIMD_OP
#endif // SFIZZ_CPU_FAMILY_AARCH64 || SFIZZ_CPU_FAMILY_ARM
}
template <>
void SIMDDispatch<float>::resetStatus()
{
setStatus(SIMDOps::writeInterleaved, false);
setStatus(SIMDOps::readInterleaved, false);
setStatus(SIMDOps::fill, true);
setStatus(SIMDOps::gain, true);
setStatus(SIMDOps::gain1, true);
setStatus(SIMDOps::divide, false);
setStatus(SIMDOps::linearRamp, false);
setStatus(SIMDOps::multiplicativeRamp, true);
setStatus(SIMDOps::add, false);
setStatus(SIMDOps::add1, false);
setStatus(SIMDOps::subtract, false);
setStatus(SIMDOps::subtract1, false);
setStatus(SIMDOps::multiplyAdd, false);
setStatus(SIMDOps::multiplyAdd1, false);
setStatus(SIMDOps::copy, false);
setStatus(SIMDOps::cumsum, true);
setStatus(SIMDOps::diff, false);
setStatus(SIMDOps::sfzInterpolationCast, true);
setStatus(SIMDOps::mean, false);
setStatus(SIMDOps::meanSquared, false);
setStatus(SIMDOps::upsampling, true);
}
///
template<class T>
static SIMDDispatch<T>& simdDispatch()
{
static SIMDDispatch<T> dispatch;
return dispatch;
}
template<>
void resetSIMDOpStatus<float>()
{
simdDispatch<float>().resetStatus();
}
template<>
void setSIMDOpStatus<float>(SIMDOps op, bool status)
{
simdDispatch<float>().setStatus(op, status);
}
template<>
bool getSIMDOpStatus<float>(SIMDOps op)
{
return simdDispatch<float>().getStatus(op);
}
void initializeSIMDDispatchers()
{
simdDispatch<float>().resetStatus();
}
///
void readInterleaved(const float* input, float* outputLeft, float* outputRight, unsigned inputSize) noexcept
{
return simdDispatch<float>().readInterleaved(input, outputLeft, outputRight, inputSize);
}
void writeInterleaved(const float* inputLeft, const float* inputRight, float* output, unsigned outputSize) noexcept
{
return simdDispatch<float>().writeInterleaved(inputLeft, inputRight, output, outputSize);
}
template <>
void applyGain1<float>(float gain, const float* input, float* output, unsigned size) noexcept
{
return simdDispatch<float>().gain1(gain, input, output, size);
}
template <>
void applyGain<float>(const float* gain, const float* input, float* output, unsigned size) noexcept
{
return simdDispatch<float>().gain(gain, input, output, size);
}
template <>
void divide<float>(const float* input, const float* divisor, float* output, unsigned size) noexcept
{
return simdDispatch<float>().divide(input, divisor, output, size);
}
template <>
void multiplyAdd<float>(const float* gain, const float* input, float* output, unsigned size) noexcept
{
return simdDispatch<float>().multiplyAdd(gain, input, output, size);
}
template <>
void multiplyAdd1<float>(float gain, const float* input, float* output, unsigned size) noexcept
{
return simdDispatch<float>().multiplyAdd1(gain, input, output, size);
}
template <>
float linearRamp<float>(float* output, float start, float step, unsigned size) noexcept
{
return simdDispatch<float>().linearRamp(output, start, step, size);
}
template <>
float multiplicativeRamp<float>(float* output, float start, float step, unsigned size) noexcept
{
return simdDispatch<float>().multiplicativeRamp(output, start, step, size);
}
template <>
void add<float>(const float* input, float* output, unsigned size) noexcept
{
return simdDispatch<float>().add(input, output, size);
}
template <>
void add1<float>(float value, float* output, unsigned size) noexcept
{
return simdDispatch<float>().add1(value, output, size);
}
template <>
void subtract<float>(const float* input, float* output, unsigned size) noexcept
{
return simdDispatch<float>().subtract(input, output, size);
}
template <>
void subtract1<float>(float value, float* output, unsigned size) noexcept
{
return simdDispatch<float>().subtract1(value, output, size);
}
template <>
void copy<float>(const float* input, float* output, unsigned size) noexcept
{
return simdDispatch<float>().copy(input, output, size);
}
template <>
float mean<float>(const float* vector, unsigned size) noexcept
{
return simdDispatch<float>().mean(vector, size);
}
template <>
float meanSquared<float>(const float* vector, unsigned size) noexcept
{
return simdDispatch<float>().meanSquared(vector, size);
}
template <>
void cumsum<float>(const float* input, float* output, unsigned size) noexcept
{
return simdDispatch<float>().cumsum(input, output, size);
}
template <>
void diff<float>(const float* input, float* output, unsigned size) noexcept
{
return simdDispatch<float>().diff(input, output, size);
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,241 +0,0 @@
// Copyright (c) 2019, Paul Ferrand
// All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "SIMDConfig.h"
#if SFIZZ_HAVE_NEON
#include "SIMDHelpers.h"
#include <arm_neon.h>
using Type = float;
constexpr uintptr_t TypeAlignment { 4 };
constexpr uintptr_t ByteAlignment { TypeAlignment * sizeof(Type) };
constexpr uintptr_t ByteAlignmentMask { ByteAlignment - 1 };
float* nextAligned(const float* ptr)
{
return reinterpret_cast<float*>((reinterpret_cast<uintptr_t>(ptr) + ByteAlignmentMask) & (~ByteAlignmentMask));
}
float* prevAligned(const float* ptr)
{
return reinterpret_cast<float*>(reinterpret_cast<uintptr_t>(ptr) & (~ByteAlignmentMask));
}
bool unaligned(const float* ptr)
{
return (reinterpret_cast<uintptr_t>(ptr) & ByteAlignmentMask) != 0;
}
template<class... Args>
bool unaligned(const float* ptr1, Args... rest)
{
return unaligned(ptr1) || unaligned(rest...);
}
template <>
void sfz::readInterleaved<float, true>(absl::Span<const float> input, absl::Span<float> outputLeft, absl::Span<float> outputRight) noexcept
{
// The size of the outputs is not big enough for the input...
ASSERT(outputLeft.size() >= input.size() / 2);
ASSERT(outputRight.size() >= input.size() / 2);
// Input is too small
ASSERT(input.size() > 1);
auto* in = input.begin();
auto* lOut = outputLeft.begin();
auto* rOut = outputRight.begin();
const auto size = std::min(input.size(), std::min(outputLeft.size() * 2, outputRight.size() * 2));
const auto* lastAligned = prevAligned(input.begin() + size - TypeAlignment);
while (unaligned(in, lOut, rOut) && in < lastAligned)
_internals::snippetRead<float>(in, lOut, rOut);
while (in < lastAligned) {
auto reg = vld2q_f32(in);
vst1q_f32(lOut, reg.val[0]);
vst1q_f32(rOut, reg.val[1]);
// *lOut = reg.val[0];
// *rOut = reg.val[1];
incrementAll<TypeAlignment>(in, in, lOut, rOut);
}
while (in < input.end() - 1)
_internals::snippetRead<float>(in, lOut, rOut);
}
template <>
void sfz::writeInterleaved<float, true>(absl::Span<const float> inputLeft, absl::Span<const float> inputRight, absl::Span<float> output) noexcept
{
writeInterleaved<float, false>(inputLeft, inputRight, output);
}
template <>
void sfz::fill<float, true>(absl::Span<float> output, float value) noexcept
{
fill<float, false>(output, value);
}
template <>
void sfz::exp<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept
{
exp<float, false>(input, output);
}
template <>
void sfz::log<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept
{
log<float, false>(input, output);
}
template <>
void sfz::sin<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept
{
sin<float, false>(input, output);
}
template <>
void sfz::cos<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept
{
cos<float, false>(input, output);
}
template <>
void sfz::applyGain<float, true>(float gain, absl::Span<const float> input, absl::Span<float> output) noexcept
{
applyGain<float, false>(gain, input, output);
}
template <>
void sfz::applyGain<float, true>(absl::Span<const float> gain, absl::Span<const float> input, absl::Span<float> output) noexcept
{
applyGain<float, false>(gain, input, output);
}
template <>
void sfz::divide<float, true>(absl::Span<const float> input, absl::Span<const float> divisor, absl::Span<float> output) noexcept
{
divide<float, false>(input, divisor, output);
}
template <>
void sfz::multiplyAdd<float, true>(absl::Span<const float> gain, absl::Span<const float> input, absl::Span<float> output) noexcept
{
multiplyAdd<float, false>(gain, input, output);
}
template <>
float sfz::loopingSFZIndex<float, true>(absl::Span<const float> jumps, absl::Span<float> leftCoeff, absl::Span<float> rightCoeff, absl::Span<int> indices, float floatIndex, float loopEnd, float loopStart) noexcept
{
return loopingSFZIndex<float, false>(jumps, leftCoeff, rightCoeff, indices, floatIndex, loopEnd, loopStart);
}
template <>
float sfz::saturatingSFZIndex<float, true>(absl::Span<const float> jumps, absl::Span<float> leftCoeff, absl::Span<float> rightCoeff, absl::Span<int> indices, float floatIndex, float loopEnd) noexcept
{
return saturatingSFZIndex<float, false>(jumps, leftCoeff, rightCoeff, indices, floatIndex, loopEnd);
}
template <>
float sfz::linearRamp<float, true>(absl::Span<float> output, float start, float step) noexcept
{
return linearRamp<float, false>(output, start, step);
}
template <>
float sfz::multiplicativeRamp<float, true>(absl::Span<float> output, float start, float step) noexcept
{
return multiplicativeRamp<float, false>(output, start, step);
}
template <>
void sfz::add<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept
{
add<float, false>(input, output);
}
template <>
void sfz::add<float, true>(float value, absl::Span<float> output) noexcept
{
add<float, false>(value, output);
}
template <>
void sfz::subtract<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept
{
subtract<float, false>(input, output);
}
template <>
void sfz::subtract<float, true>(const float value, absl::Span<float> output) noexcept
{
subtract<float, false>(value, output);
}
template <>
void sfz::copy<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept
{
copy<float, false>(input, output);
}
template <>
void sfz::pan<float, true>(absl::Span<const float> panEnvelope, absl::Span<float> leftBuffer, absl::Span<float> rightBuffer) noexcept
{
pan<float, false>(panEnvelope, leftBuffer, rightBuffer);
}
template <>
float sfz::mean<float, true>(absl::Span<const float> vector) noexcept
{
return mean<float, false>(vector);
}
template <>
float sfz::meanSquared<float, true>(absl::Span<const float> vector) noexcept
{
return meanSquared<float, false>(vector);
}
template <>
void sfz::cumsum<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept
{
cumsum<float, false>(input, output);
}
template<>
void sfz::sfzInterpolationCast<float, true>(absl::Span<const float> floatJumps, absl::Span<int> jumps, absl::Span<float> coeffs) noexcept
{
sfzInterpolationCast<float, false>(floatJumps, jumps, coeffs);
}
template <>
void sfz::diff<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept
{
diff<float, false>(input, output);
}
#endif // SFIZZ_HAVE_NEON

View file

@ -1,849 +0,0 @@
// 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 "SIMDConfig.h"
#if SFIZZ_HAVE_SSE2
#include "SIMDHelpers.h"
#include <array>
#include <xmmintrin.h>
#include <emmintrin.h>
#include "mathfuns/sse_mathfun.h"
using Type = float;
constexpr uintptr_t TypeAlignment { 4 };
constexpr uintptr_t ByteAlignment { TypeAlignment * sizeof(Type) };
constexpr uintptr_t ByteAlignmentMask { ByteAlignment - 1 };
struct AlignmentSentinels {
float* nextAligned;
float* lastAligned;
};
float* nextAligned(const float* ptr)
{
return reinterpret_cast<float*>((reinterpret_cast<uintptr_t>(ptr) + ByteAlignmentMask) & (~ByteAlignmentMask));
}
float* prevAligned(const float* ptr)
{
return reinterpret_cast<float*>(reinterpret_cast<uintptr_t>(ptr) & (~ByteAlignmentMask));
}
bool unaligned(const float* ptr)
{
return (reinterpret_cast<uintptr_t>(ptr) & ByteAlignmentMask) != 0;
}
template<class... Args>
bool unaligned(const float* ptr1, Args... rest)
{
return unaligned(ptr1) || unaligned(rest...);
}
template <>
void sfz::readInterleaved<float, true>(absl::Span<const float> input, absl::Span<float> outputLeft, absl::Span<float> outputRight) noexcept
{
// The size of the outputs is not big enough for the input...
CHECK(outputLeft.size() >= input.size() / 2);
CHECK(outputRight.size() >= input.size() / 2);
// Input is too small
CHECK(input.size() > 1);
auto* in = input.begin();
auto* lOut = outputLeft.begin();
auto* rOut = outputRight.begin();
const auto size = std::min(input.size(), std::min(outputLeft.size() * 2, outputRight.size() * 2));
const auto* lastAligned = prevAligned(input.begin() + size - TypeAlignment);
while (unaligned(in, lOut, rOut) && in < lastAligned)
_internals::snippetRead<float>(in, lOut, rOut);
while (in < lastAligned) {
auto register0 = _mm_load_ps(in);
in += TypeAlignment;
auto register1 = _mm_load_ps(in);
in += TypeAlignment;
auto register2 = register0;
// register 2 holds the copy of register 0 that is going to get erased by the first operation
// Remember that the bit mask reads from the end; 10 00 10 00 means
// "take 0 from a, take 2 from a, take 0 from b, take 2 from b"
register0 = _mm_shuffle_ps(register0, register1, 0b10001000);
register1 = _mm_shuffle_ps(register2, register1, 0b11011101);
_mm_store_ps(lOut, register0);
_mm_store_ps(rOut, register1);
lOut += TypeAlignment;
rOut += TypeAlignment;
}
while (in < input.end() - 1)
_internals::snippetRead<float>(in, lOut, rOut);
}
template <>
void sfz::writeInterleaved<float, true>(absl::Span<const float> inputLeft, absl::Span<const float> inputRight, absl::Span<float> output) noexcept
{
// The size of the output is not big enough for the inputs...
CHECK(inputLeft.size() <= output.size() / 2);
CHECK(inputRight.size() <= output.size() / 2);
auto* lIn = inputLeft.begin();
auto* rIn = inputRight.begin();
auto* out = output.begin();
const auto size = std::min(output.size(), std::min(inputLeft.size(), inputRight.size()) * 2);
const auto* lastAligned = prevAligned(output.begin() + size - TypeAlignment);
while (unaligned(out, rIn, lIn) && out < lastAligned)
_internals::snippetWrite<float>(out, lIn, rIn);
while (out < lastAligned) {
const auto lInRegister = _mm_load_ps(lIn);
const auto rInRegister = _mm_load_ps(rIn);
const auto outRegister1 = _mm_unpacklo_ps(lInRegister, rInRegister);
_mm_store_ps(out, outRegister1);
out += TypeAlignment;
const auto outRegister2 = _mm_unpackhi_ps(lInRegister, rInRegister);
_mm_store_ps(out, outRegister2);
out += TypeAlignment;
lIn += TypeAlignment;
rIn += TypeAlignment;
}
while (out < output.end() - 1)
_internals::snippetWrite<float>(out, lIn, rIn);
}
template <>
void sfz::fill<float, true>(absl::Span<float> output, float value) noexcept
{
const auto mmValue = _mm_set_ps1(value);
auto* out = output.begin();
const auto* lastAligned = prevAligned(output.end());
while (unaligned(out) && out < lastAligned)
*out++ = value;
while (out < lastAligned) // we should only need to test a single channel
{
_mm_store_ps(out, mmValue);
out += TypeAlignment;
}
while (out < output.end())
*out++ = value;
}
template <>
void sfz::exp<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept
{
CHECK(output.size() >= input.size());
auto* in = input.begin();
auto* out = output.begin();
auto* sentinel = in + std::min(input.size(), output.size());
const auto* lastAligned = prevAligned(sentinel);
while (unaligned(in, out) && in < lastAligned)
*out++ = std::exp(*in++);
while (in < lastAligned) {
_mm_store_ps(out, exp_ps(_mm_load_ps(in)));
incrementAll<TypeAlignment>(out, in);
}
while (in < sentinel)
*out++ = std::exp(*in++);
}
template <>
void sfz::cos<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept
{
CHECK(output.size() >= input.size());
auto* in = input.begin();
auto* out = output.begin();
auto* sentinel = in + std::min(input.size(), output.size());
const auto* lastAligned = prevAligned(sentinel);
while (unaligned(in, out) && in < lastAligned)
*out++ = std::exp(*in++);
while (in < lastAligned) {
_mm_store_ps(out, cos_ps(_mm_load_ps(in)));
incrementAll<TypeAlignment>(out, in);
}
while (in < sentinel)
*out++ = std::exp(*in++);
}
template <>
void sfz::log<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept
{
CHECK(output.size() >= input.size());
auto* in = input.begin();
auto* out = output.begin();
auto* sentinel = in + std::min(input.size(), output.size());
const auto* lastAligned = prevAligned(sentinel);
while (unaligned(in, out) && in < lastAligned)
*out++ = std::exp(*in++);
while (in < lastAligned) {
_mm_store_ps(out, log_ps(_mm_load_ps(in)));
incrementAll<TypeAlignment>(out, in);
}
while (in < sentinel)
*out++ = std::exp(*in++);
}
template <>
void sfz::sin<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept
{
CHECK(output.size() >= input.size());
auto* in = input.begin();
auto* out = output.begin();
auto* sentinel = in + std::min(input.size(), output.size());
const auto* lastAligned = prevAligned(sentinel);
while (unaligned(in, out) && in < lastAligned)
*out++ = std::exp(*in++);
while (in < lastAligned) {
_mm_store_ps(out, sin_ps(_mm_load_ps(in)));
incrementAll<TypeAlignment>(out, in);
}
while (in < sentinel)
*out++ = std::exp(*in++);
}
template <>
void sfz::applyGain<float, true>(float gain, absl::Span<const float> input, absl::Span<float> output) noexcept
{
auto* in = input.begin();
auto* out = output.begin();
const auto size = std::min(output.size(), input.size());
const auto* lastAligned = prevAligned(output.begin() + size);
const auto mmGain = _mm_set_ps1(gain);
while (unaligned(out, in) && out < lastAligned)
*out++ = gain * (*in++);
while (out < lastAligned) {
_mm_store_ps(out, _mm_mul_ps(mmGain, _mm_load_ps(in)));
incrementAll<TypeAlignment>(out, in);
}
while (out < output.end())
*out++ = gain * (*in++);
}
template <>
void sfz::applyGain<float, true>(absl::Span<const float> gain, absl::Span<const float> input, absl::Span<float> output) noexcept
{
auto* in = input.begin();
auto* out = output.begin();
auto* g = gain.begin();
const auto size = std::min(output.size(), std::min(input.size(), gain.size()));
const auto* lastAligned = prevAligned(output.begin() + size);
while (unaligned(out, in, g) && out < lastAligned)
_internals::snippetGainSpan<float>(g, in, out);
while (out < lastAligned) {
_mm_store_ps(out, _mm_mul_ps(_mm_load_ps(g), _mm_load_ps(in)));
incrementAll<TypeAlignment>(g, in, out);
}
while (out < output.end())
_internals::snippetGainSpan<float>(g, in, out);
}
template <>
void sfz::divide<float, true>(absl::Span<const float> input, absl::Span<const float> divisor, absl::Span<float> output) noexcept
{
auto* in = input.begin();
auto* out = output.begin();
auto* div = divisor.begin();
const auto size = std::min(output.size(), std::min(input.size(), divisor.size()));
const auto* lastAligned = prevAligned(output.begin() + size);
while (unaligned(out, in, div) && out < lastAligned)
_internals::snippetDivSpan<float>(in, div, out);
while (out < lastAligned) {
_mm_store_ps(out, _mm_div_ps(_mm_load_ps(in), _mm_load_ps(div)));
incrementAll<TypeAlignment>(in, div, out);
}
while (out < output.end())
_internals::snippetDivSpan<float>(in, div, out);
}
template <>
void sfz::multiplyAdd<float, true>(absl::Span<const float> gain, absl::Span<const float> input, absl::Span<float> output) noexcept
{
auto* in = input.begin();
auto* out = output.begin();
auto* g = gain.begin();
const auto size = std::min(output.size(), std::min(input.size(), gain.size()));
const auto* lastAligned = prevAligned(output.begin() + size);
while (unaligned(out, in, g) && out < lastAligned)
_internals::snippetMultiplyAdd<float>(g, in, out);
while (out < lastAligned) {
auto mmOut = _mm_load_ps(out);
mmOut = _mm_add_ps(_mm_mul_ps(_mm_load_ps(g), _mm_load_ps(in)), mmOut);
_mm_store_ps(out, mmOut);
incrementAll<TypeAlignment>(g, in, out);
}
while (out < output.end())
_internals::snippetMultiplyAdd<float>(g, in, out);
}
template <>
void sfz::multiplyAdd<float, true>(const float gain, absl::Span<const float> input, absl::Span<float> output) noexcept
{
auto* in = input.begin();
auto* out = output.begin();
const auto size = std::min(output.size(), input.size());
const auto* lastAligned = prevAligned(output.begin() + size);
while (unaligned(out, in) && out < lastAligned)
_internals::snippetMultiplyAdd<float>(gain, in, out);
auto mmGain = _mm_set1_ps(gain);
while (out < lastAligned) {
auto mmOut = _mm_load_ps(out);
mmOut = _mm_add_ps(_mm_mul_ps(mmGain, _mm_load_ps(in)), mmOut);
_mm_store_ps(out, mmOut);
incrementAll<TypeAlignment>(in, out);
}
while (out < output.end())
_internals::snippetMultiplyAdd<float>(gain, in, out);
}
template <>
float sfz::loopingSFZIndex<float, true>(absl::Span<const float> jumps,
absl::Span<float> leftCoeffs,
absl::Span<float> rightCoeffs,
absl::Span<int> indices,
float floatIndex,
float loopEnd,
float loopStart) noexcept
{
CHECK(indices.size() >= jumps.size());
CHECK(indices.size() == leftCoeffs.size());
CHECK(indices.size() == rightCoeffs.size());
auto index = indices.data();
auto leftCoeff = leftCoeffs.data();
auto rightCoeff = rightCoeffs.data();
auto jump = jumps.data();
const auto size = min(jumps.size(), indices.size(), leftCoeffs.size(), rightCoeffs.size());
const auto* sentinel = jumps.begin() + size;
const auto* alignedEnd = prevAligned(sentinel);
while (unaligned(reinterpret_cast<float*>(index), leftCoeff, rightCoeff, jump) && jump < alignedEnd)
_internals::snippetLoopingIndex<float>(jump, leftCoeff, rightCoeff, index, floatIndex, loopEnd, loopStart);
auto mmFloatIndex = _mm_set_ps1(floatIndex);
const auto mmJumpBack = _mm_set1_ps(loopEnd - loopStart);
const auto mmLoopEnd = _mm_set1_ps(loopEnd);
while (jump < alignedEnd) {
auto mmOffset = _mm_load_ps(jump);
mmOffset = _mm_add_ps(mmOffset, _mm_castsi128_ps(_mm_slli_si128(_mm_castps_si128(mmOffset), 4)));
mmOffset = _mm_add_ps(mmOffset, _mm_shuffle_ps(_mm_setzero_ps(), mmOffset, 0x40));
mmFloatIndex = _mm_add_ps(mmFloatIndex, mmOffset);
const auto mmCompared = _mm_cmpge_ps(mmFloatIndex, mmLoopEnd);
auto mmLoopBack = _mm_sub_ps(mmFloatIndex, mmJumpBack);
mmLoopBack = _mm_and_ps(mmCompared, mmLoopBack);
mmFloatIndex = _mm_andnot_ps(mmCompared, mmFloatIndex);
mmFloatIndex = _mm_add_ps(mmFloatIndex, mmLoopBack);
auto mmIndices = _mm_cvtps_epi32(_mm_sub_ps(mmFloatIndex, _mm_set_ps1(0.4999999552965164184570312f)));
_mm_store_si128(reinterpret_cast<__m128i*>(index), mmIndices);
auto mmRight = _mm_sub_ps(mmFloatIndex, _mm_cvtepi32_ps(mmIndices));
auto mmLeft = _mm_sub_ps(_mm_set_ps1(1.0f), mmRight);
_mm_store_ps(leftCoeff, mmLeft);
_mm_store_ps(rightCoeff, mmRight);
mmFloatIndex = _mm_shuffle_ps(mmFloatIndex, mmFloatIndex, _MM_SHUFFLE(3, 3, 3, 3));
// floatingIndex = _mm_cvtss_f32(_mm_shuffle_ps(mmFloatIndex, mmFloatIndex, _MM_SHUFFLE(0, 0, 0, 3)));;
// floatingIndex = *(index + 3) + *(rightCoeff + 3);
incrementAll<TypeAlignment>(index, jump, leftCoeff, rightCoeff);
}
floatIndex = _mm_cvtss_f32(mmFloatIndex);
while (jump < sentinel)
_internals::snippetLoopingIndex<float>(jump, leftCoeff, rightCoeff, index, floatIndex, loopEnd, loopStart);
return floatIndex;
}
template <>
float sfz::saturatingSFZIndex<float, true>(absl::Span<const float> jumps,
absl::Span<float> leftCoeffs,
absl::Span<float> rightCoeffs,
absl::Span<int> indices,
float floatIndex,
float loopEnd) noexcept
{
CHECK(indices.size() >= jumps.size());
CHECK(indices.size() == leftCoeffs.size());
CHECK(indices.size() == rightCoeffs.size());
auto index = indices.data();
auto leftCoeff = leftCoeffs.data();
auto rightCoeff = rightCoeffs.data();
auto jump = jumps.data();
const auto size = min(jumps.size(), indices.size(), leftCoeffs.size(), rightCoeffs.size());
const auto* sentinel = jumps.begin() + size;
const auto* alignedEnd = prevAligned(sentinel);
while (unaligned(reinterpret_cast<float*>(index), leftCoeff, rightCoeff, jump) && jump < alignedEnd)
_internals::snippetSaturatingIndex<float>(jump, leftCoeff, rightCoeff, index, floatIndex, loopEnd);
auto mmFloatIndex = _mm_set_ps1(floatIndex);
const auto mmLoopEnd = _mm_set1_ps(loopEnd);
const auto mmSaturated = _mm_sub_ps(mmLoopEnd, _mm_set_ps1(0.000001f));
while (jump < alignedEnd) {
auto mmOffset = _mm_load_ps(jump);
mmOffset = _mm_add_ps(mmOffset, _mm_castsi128_ps(_mm_slli_si128(_mm_castps_si128(mmOffset), 4)));
mmOffset = _mm_add_ps(mmOffset, _mm_shuffle_ps(_mm_setzero_ps(), mmOffset, 0x40));
mmFloatIndex = _mm_add_ps(mmFloatIndex, mmOffset);
const auto mmCompared = _mm_cmplt_ps(mmFloatIndex, mmLoopEnd);
mmFloatIndex = _mm_add_ps(_mm_and_ps(mmCompared, mmFloatIndex), _mm_andnot_ps(mmCompared, mmSaturated));
auto mmIndices = _mm_cvtps_epi32(_mm_sub_ps(mmFloatIndex, _mm_set_ps1(0.4999999552965164184570312f)));
_mm_store_si128(reinterpret_cast<__m128i*>(index), mmIndices);
auto mmRight = _mm_sub_ps(mmFloatIndex, _mm_cvtepi32_ps(mmIndices));
auto mmLeft = _mm_sub_ps(_mm_set_ps1(1.0f), mmRight);
_mm_store_ps(leftCoeff, mmLeft);
_mm_store_ps(rightCoeff, mmRight);
mmFloatIndex = _mm_shuffle_ps(mmFloatIndex, mmFloatIndex, _MM_SHUFFLE(3, 3, 3, 3));
// floatingIndex = _mm_cvtss_f32(_mm_shuffle_ps(mmFloatIndex, mmFloatIndex, _MM_SHUFFLE(0, 0, 0, 3)));;
// floatingIndex = *(index + 3) + *(rightCoeff + 3);
incrementAll<TypeAlignment>(index, jump, leftCoeff, rightCoeff);
}
floatIndex = _mm_cvtss_f32(mmFloatIndex);
while (jump < sentinel)
_internals::snippetSaturatingIndex<float>(jump, leftCoeff, rightCoeff, index, floatIndex, loopEnd);
return floatIndex;
}
template <>
float sfz::linearRamp<float, true>(absl::Span<float> output, float value, float step) noexcept
{
auto* out = output.begin();
const auto* lastAligned = prevAligned(output.end());
while (unaligned(out) && out < lastAligned)
_internals::snippetRampLinear<float>(out, value, step);
auto mmValue = _mm_set1_ps(value - step);
auto mmStep = _mm_set_ps(step + step + step + step, step + step + step, step + step, step);
while (out < lastAligned) {
mmValue = _mm_add_ps(mmValue, mmStep);
_mm_store_ps(out, mmValue);
mmValue = _mm_shuffle_ps(mmValue, mmValue, _MM_SHUFFLE(3, 3, 3, 3));
out += TypeAlignment;
}
value = _mm_cvtss_f32(mmValue) + step;
while (out < output.end())
_internals::snippetRampLinear<float>(out, value, step);
return value;
}
template <>
float sfz::multiplicativeRamp<float, true>(absl::Span<float> output, float value, float step) noexcept
{
auto* out = output.begin();
const auto* lastAligned = prevAligned(output.end());
while (unaligned(out) && out < lastAligned)
_internals::snippetRampMultiplicative<float>(out, value, step);
auto mmValue = _mm_set1_ps(value / step);
auto mmStep = _mm_set_ps(step * step * step * step, step * step * step, step * step, step);
while (out < lastAligned) {
mmValue = _mm_mul_ps(mmValue, mmStep);
_mm_store_ps(out, mmValue);
mmValue = _mm_shuffle_ps(mmValue, mmValue, _MM_SHUFFLE(3, 3, 3, 3));
out += TypeAlignment;
}
value = _mm_cvtss_f32(mmValue) * step;
while (out < output.end())
_internals::snippetRampMultiplicative<float>(out, value, step);
return value;
}
template <>
void sfz::add<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept
{
CHECK(output.size() >= input.size());
auto* in = input.begin();
auto* out = output.begin();
auto* sentinel = out + min(input.size(), output.size());
const auto* lastAligned = prevAligned(sentinel);
while (unaligned(in, out) && out < lastAligned)
_internals::snippetAdd<float>(in, out);
while (out < lastAligned) {
_mm_store_ps(out, _mm_add_ps(_mm_load_ps(in), _mm_load_ps(out)));
incrementAll<TypeAlignment>(in, out);
}
while (out < sentinel)
_internals::snippetAdd<float>(in, out);
}
template <>
void sfz::add<float, true>(float value, absl::Span<float> output) noexcept
{
auto* out = output.begin();
auto* sentinel = output.end();
const auto* lastAligned = prevAligned(sentinel);
while (unaligned(out) && out < lastAligned)
_internals::snippetAdd<float>(value, out);
auto mmValue = _mm_set_ps1(value);
while (out < lastAligned) {
_mm_store_ps(out, _mm_add_ps(mmValue, _mm_load_ps(out)));
out += TypeAlignment;
}
while (out < sentinel)
_internals::snippetAdd<float>(value, out);
}
template <>
void sfz::subtract<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept
{
CHECK(output.size() >= input.size());
auto* in = input.begin();
auto* out = output.begin();
auto* sentinel = out + min(input.size(), output.size());
const auto* lastAligned = prevAligned(sentinel);
while (unaligned(in, out) && out < lastAligned)
_internals::snippetSubtract<float>(in, out);
while (out < lastAligned) {
_mm_store_ps(out, _mm_sub_ps(_mm_load_ps(out), _mm_load_ps(in)));
incrementAll<TypeAlignment>(in, out);
}
while (out < sentinel)
_internals::snippetSubtract<float>(in, out);
}
template <>
void sfz::subtract<float, true>(const float value, absl::Span<float> output) noexcept
{
auto* out = output.begin();
auto* sentinel = output.end();
const auto* lastAligned = prevAligned(sentinel);
while (unaligned(out) && out < lastAligned)
_internals::snippetSubtract<float>(value, out);
auto mmValue = _mm_set_ps1(value);
while (out < lastAligned) {
_mm_store_ps(out, _mm_sub_ps(_mm_load_ps(out), mmValue));
out += TypeAlignment;
}
while (out < sentinel)
_internals::snippetSubtract<float>(value, out);
}
template <>
void sfz::copy<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept
{
CHECK(output.size() >= input.size());
auto* in = input.begin();
auto* out = output.begin();
auto* sentinel = out + min(input.size(), output.size());
const auto* lastAligned = prevAligned(sentinel);
while (unaligned(in, out) && out < lastAligned)
_internals::snippetCopy<float>(in, out);
while (out < lastAligned) {
_mm_store_ps(out, _mm_load_ps(in));
incrementAll<TypeAlignment>(in, out);
}
while (out < sentinel)
_internals::snippetCopy<float>(in, out);
}
template <>
void sfz::pan<float, true>(absl::Span<const float> panEnvelope, absl::Span<float> leftBuffer, absl::Span<float> rightBuffer) noexcept
{
CHECK(leftBuffer.size() >= panEnvelope.size());
CHECK(rightBuffer.size() >= panEnvelope.size());
auto* pan = panEnvelope.begin();
auto* left = leftBuffer.begin();
auto* right = rightBuffer.begin();
auto* sentinel = pan + min(panEnvelope.size(), leftBuffer.size(), rightBuffer.size());
const auto* lastAligned = prevAligned(sentinel);
while (unaligned(pan, left, right) && pan < lastAligned) {
_internals::snippetPan(*pan, *left, *right);
incrementAll(pan, left, right);
}
const auto mmOne = _mm_set_ps1(1.0f);
const auto mmPiFour = _mm_set_ps1(piFour<float>());
__m128 mmCos;
__m128 mmSin;
while (pan < lastAligned) {
auto mmPan = _mm_load_ps(pan);
mmPan = _mm_add_ps(mmOne, mmPan);
mmPan = _mm_mul_ps(mmPan, mmPiFour);
sincos_ps(mmPan, &mmSin, &mmCos);
auto mmLeft = _mm_mul_ps(mmCos, _mm_load_ps(left));
auto mmRight = _mm_mul_ps(mmSin, _mm_load_ps(right));
_mm_store_ps(left, mmLeft);
_mm_store_ps(right, mmRight);
incrementAll<TypeAlignment>(pan, left, right);
}
while (pan < sentinel){
_internals::snippetPan(*pan, *left, *right);
incrementAll(pan, left, right);
}
}
template <>
void sfz::width<float, true>(absl::Span<const float> widthEnvelope, absl::Span<float> leftBuffer, absl::Span<float> rightBuffer) noexcept
{
CHECK(leftBuffer.size() >= widthEnvelope.size());
CHECK(rightBuffer.size() >= widthEnvelope.size());
auto* width = widthEnvelope.begin();
auto* left = leftBuffer.begin();
auto* right = rightBuffer.begin();
auto* sentinel = width + min(widthEnvelope.size(), leftBuffer.size(), rightBuffer.size());
const auto* lastAligned = prevAligned(sentinel);
while (unaligned(width, left, right) && width < lastAligned) {
_internals::snippetWidth(*width, *left, *right);
incrementAll(width, left, right);
}
const auto mmPiFour = _mm_set_ps1(piFour<float>());
__m128 mmCos;
__m128 mmSin;
while (width < lastAligned) {
auto mmWidth = _mm_load_ps(width);
mmWidth = _mm_mul_ps(mmWidth, mmPiFour);
sincos_ps(mmWidth, &mmSin, &mmCos);
auto mmCosPlusSine = _mm_add_ps(mmCos, mmSin);
auto mmCosMinusSine = _mm_sub_ps(mmCos, mmSin);
auto mmLeft = _mm_load_ps(left);
auto mmRight = _mm_load_ps(right);
auto mmTemp = _mm_mul_ps(mmCosMinusSine, mmRight);
mmRight = _mm_add_ps(_mm_mul_ps(mmCosMinusSine, mmLeft), _mm_mul_ps(mmCosPlusSine, mmRight));
mmLeft = _mm_add_ps(_mm_mul_ps(mmCosPlusSine, mmLeft), mmTemp);
_mm_store_ps(left, mmLeft);
_mm_store_ps(right, mmRight);
incrementAll<TypeAlignment>(width, left, right);
}
while (width < sentinel){
_internals::snippetWidth(*width, *left, *right);
incrementAll(width, left, right);
}
}
template <>
float sfz::mean<float, true>(absl::Span<const float> vector) noexcept
{
float result { 0.0 };
if (vector.size() == 0)
return result;
auto* value = vector.begin();
auto* sentinel = vector.end();
const auto* lastAligned = prevAligned(sentinel);
while (unaligned(value) && value < lastAligned)
result += *value++;
auto mmSums = _mm_setzero_ps();
while (value < lastAligned) {
mmSums = _mm_add_ps(mmSums, _mm_load_ps(value));
value += TypeAlignment;
}
std::array<float, 4> sseResult;
_mm_store_ps(sseResult.data(), mmSums);
for (auto sseValue : sseResult)
result += sseValue;
while (value < sentinel)
result += *value++;
return result / static_cast<float>(vector.size());
}
template <>
float sfz::meanSquared<float, true>(absl::Span<const float> vector) noexcept
{
float result { 0.0 };
if (vector.size() == 0)
return result;
auto* value = vector.begin();
auto* sentinel = vector.end();
const auto* lastAligned = prevAligned(sentinel);
while (unaligned(value) && value < lastAligned) {
result += (*value) * (*value);
value++;
}
auto mmSums = _mm_setzero_ps();
while (value < lastAligned) {
const auto mmValues = _mm_load_ps(value);
mmSums = _mm_add_ps(mmSums, _mm_mul_ps(mmValues, mmValues));
value += TypeAlignment;
}
std::array<float, 4> sseResult;
_mm_store_ps(sseResult.data(), mmSums);
for (auto sseValue : sseResult)
result += sseValue;
while (value < sentinel) {
result += (*value) * (*value);
value++;
}
return result / static_cast<float>(vector.size());
}
template <>
void sfz::cumsum<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept
{
CHECK(output.size() >= input.size());
if (input.size() == 0)
return;
auto out = output.data();
auto in = input.data();
const auto sentinel = in + std::min(input.size(), output.size());
const auto lastAligned = prevAligned(sentinel);
*out++ = *in++;
while (unaligned(in, out) && in < lastAligned)
_internals::snippetCumsum(in, out);
auto mmOutput = _mm_set_ps1(*(out - 1));
while (in < lastAligned) {
auto mmOffset = _mm_load_ps(in);
mmOffset = _mm_add_ps(mmOffset, _mm_castsi128_ps(_mm_slli_si128(_mm_castps_si128(mmOffset), 4)));
mmOffset = _mm_add_ps(mmOffset, _mm_shuffle_ps(_mm_setzero_ps(), mmOffset, _MM_SHUFFLE(1, 0, 0, 0)));
mmOutput = _mm_add_ps(mmOutput, mmOffset);
_mm_store_ps(out, mmOutput);
mmOutput = _mm_shuffle_ps(mmOutput, mmOutput, _MM_SHUFFLE(3, 3, 3, 3));
incrementAll<TypeAlignment>(in, out);
}
while (in < sentinel)
_internals::snippetCumsum(in, out);
}
template <>
void sfz::sfzInterpolationCast<float, true>(absl::Span<const float> floatJumps, absl::Span<int> jumps, absl::Span<float> coeffs) noexcept
{
sfz::sfzInterpolationCast<float, false>(floatJumps, jumps, coeffs);
// CHECK(jumps.size() >= floatJumps.size());
// CHECK(jumps.size() == coeffs.size());
// auto floatJump = floatJumps.data();
// auto jump = jumps.data();
// auto coeff = coeffs.data();
// const auto sentinel = floatJump + min(floatJumps.size(), jumps.size(), coeffs.size());
// const auto lastAligned = prevAligned(sentinel);
// while (unaligned(floatJump, reinterpret_cast<float*>(jump), coeff) && floatJump < lastAligned)
// _internals::snippetSFZInterpolationCast(floatJump, jump, coeff);
// while (floatJump < lastAligned) {
// auto mmFloatJumps = _mm_load_ps(floatJump);
// auto mmIndices = _mm_cvtps_epi32(_mm_sub_ps(mmFloatJumps, _mm_set_ps1(0.4999999552965164184570312f)));
// _mm_store_si128(reinterpret_cast<__m128i*>(jump), mmIndices);
// auto mmCoeff = _mm_sub_ps(mmFloatJumps, _mm_cvtepi32_ps(mmIndices));
// _mm_store_ps(coeff, mmCoeff);
// incrementAll<TypeAlignment>(floatJump, jump, coeff);
// }
// while(floatJump < sentinel)
// _internals::snippetSFZInterpolationCast(floatJump, jump, coeff);
}
template <>
void sfz::diff<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept
{
CHECK(output.size() >= input.size());
if (input.size() == 0)
return;
auto out = output.data();
auto in = input.data();
const auto sentinel = in + std::min(input.size(), output.size());
const auto lastAligned = prevAligned(sentinel);
*out++ = *in++;
while (unaligned(in, out) && in < lastAligned)
_internals::snippetDiff(in, out);
auto mmBase = _mm_set_ps1(*(in - 1));
while (in < lastAligned) {
auto mmOutput = _mm_load_ps(in);
auto mmNextBase = _mm_shuffle_ps(mmOutput, mmOutput, _MM_SHUFFLE(3, 3, 3, 3));
mmOutput = _mm_sub_ps(mmOutput, mmBase);
mmBase = mmNextBase;
mmOutput = _mm_sub_ps(mmOutput, _mm_castsi128_ps(_mm_slli_si128(_mm_castps_si128(mmOutput), 4)));
_mm_store_ps(out, mmOutput);
incrementAll<TypeAlignment>(in, out);
}
while (in < sentinel)
_internals::snippetDiff(in, out);
}
#endif // SFIZZ_HAVE_SSE2

View file

@ -7,7 +7,9 @@
#include "SfzHelpers.h"
#include "StringViewHelpers.h"
absl::optional<uint8_t> sfz::readNoteValue(const absl::string_view& value)
namespace sfz{
absl::optional<uint8_t> readNoteValue(const absl::string_view& value)
{
switch(hash(value))
{
@ -153,7 +155,7 @@ absl::optional<uint8_t> sfz::readNoteValue(const absl::string_view& value)
}
}
bool sfz::findHeader(absl::string_view& source, absl::string_view& header, absl::string_view& members)
bool findHeader(absl::string_view& source, absl::string_view& header, absl::string_view& members)
{
auto openHeader = source.find("<");
if (openHeader == absl::string_view::npos)
@ -176,7 +178,7 @@ bool sfz::findHeader(absl::string_view& source, absl::string_view& header, absl:
return true;
}
bool sfz::findOpcode(absl::string_view& source, absl::string_view& opcode, absl::string_view& value)
bool findOpcode(absl::string_view& source, absl::string_view& opcode, absl::string_view& value)
{
auto opcodeEnd = source.find("=");
if (opcodeEnd == absl::string_view::npos)
@ -203,7 +205,7 @@ bool sfz::findOpcode(absl::string_view& source, absl::string_view& opcode, absl:
}
bool sfz::findDefine(absl::string_view line, absl::string_view& variable, absl::string_view& value)
bool findDefine(absl::string_view line, absl::string_view& variable, absl::string_view& value)
{
const auto defPosition = line.find("#define");
if (defPosition == absl::string_view::npos)
@ -229,7 +231,7 @@ bool sfz::findDefine(absl::string_view line, absl::string_view& variable, absl::
return true;
}
bool sfz::findInclude(absl::string_view line, std::string& path)
bool findInclude(absl::string_view line, std::string& path)
{
const auto defPosition = line.find("#include");
if (defPosition == absl::string_view::npos)
@ -246,3 +248,5 @@ bool sfz::findInclude(absl::string_view line, std::string& path)
path = std::string(line.substr(pathStart + 1, pathEnd - pathStart - 1));
return true;
}
}

View file

@ -24,6 +24,7 @@
sfz::Synth::Synth()
: Synth(config::numVoices)
{
initializeSIMDDispatchers();
}
sfz::Synth::Synth(int numVoices)
@ -524,7 +525,7 @@ float sfz::Synth::getTuningFrequency() const
void sfz::Synth::loadStretchTuningByRatio(float ratio)
{
CHECK(ratio >= 0.0f && ratio <= 1.0f);
SFIZZ_CHECK(ratio >= 0.0f && ratio <= 1.0f);
ratio = clamp(ratio, 0.0f, 1.0f);
if (ratio > 0.0f)
@ -747,8 +748,8 @@ void sfz::Synth::renderBlock(AudioSpan<float> buffer) noexcept
ASSERT(!hasNanInf(buffer.getConstSpan(0)));
ASSERT(!hasNanInf(buffer.getConstSpan(1)));
CHECK(isReasonableAudio(buffer.getConstSpan(0)));
CHECK(isReasonableAudio(buffer.getConstSpan(1)));
SFIZZ_CHECK(isReasonableAudio(buffer.getConstSpan(0)));
SFIZZ_CHECK(isReasonableAudio(buffer.getConstSpan(1)));
}
void sfz::Synth::noteOn(int delay, int noteNumber, uint8_t velocity) noexcept
@ -1129,14 +1130,14 @@ int sfz::Synth::getSampleQuality(ProcessMode mode)
case ProcessFreewheeling:
return resources.synthConfig.freeWheelingSampleQuality;
default:
CHECK(false);
SFIZZ_CHECK(false);
return 0;
}
}
void sfz::Synth::setSampleQuality(ProcessMode mode, int quality)
{
CHECK(quality >= 1 && quality <= 10);
SFIZZ_CHECK(quality >= 1 && quality <= 10);
quality = clamp(quality, 1, 10);
switch (mode) {
@ -1147,7 +1148,7 @@ void sfz::Synth::setSampleQuality(ProcessMode mode, int quality)
resources.synthConfig.freeWheelingSampleQuality = quality;
break;
default:
CHECK(false);
SFIZZ_CHECK(false);
break;
}
}

View file

@ -10,6 +10,7 @@
#include "ModifierHelpers.h"
#include "MathHelpers.h"
#include "SIMDHelpers.h"
#include "Panning.h"
#include "SfzHelpers.h"
#include "Interpolators.h"
#include "absl/algorithm/container.h"
@ -263,8 +264,8 @@ void sfz::Voice::renderBlock(AudioSpan<float> buffer) noexcept
#if 0
ASSERT(!hasNanInf(buffer.getConstSpan(0)));
ASSERT(!hasNanInf(buffer.getConstSpan(1)));
CHECK(isReasonableAudio(buffer.getConstSpan(0)));
CHECK(isReasonableAudio(buffer.getConstSpan(1)));
SFIZZ_CHECK(isReasonableAudio(buffer.getConstSpan(0)));
SFIZZ_CHECK(isReasonableAudio(buffer.getConstSpan(1)));
#endif
}
@ -281,7 +282,7 @@ void sfz::Voice::amplitudeEnvelope(absl::Span<float> modulationSpan) noexcept
egEnvelope.getBlock(modulationSpan);
// Amplitude envelope
applyGain<float>(baseGain, modulationSpan);
applyGain1<float>(baseGain, modulationSpan);
for (const auto& mod : region->amplitudeCC) {
linearModifier(resources, *tempSpan, mod, normalizePercents<float>);
applyGain<float>(*tempSpan, modulationSpan);
@ -304,7 +305,7 @@ void sfz::Voice::amplitudeEnvelope(absl::Span<float> modulationSpan) noexcept
}
// Volume envelope
applyGain<float>(db2mag(baseVolumedB), modulationSpan);
applyGain1<float>(db2mag(baseVolumedB), modulationSpan);
for (const auto& mod : region->volumeCC) {
multiplicativeModifier(resources, *tempSpan, mod, [](float x) {
return db2mag(x);
@ -358,12 +359,12 @@ void sfz::Voice::panStageMono(AudioSpan<float> buffer) noexcept
copy<float>(leftBuffer, rightBuffer);
// Apply panning
fill<float>(*modulationSpan, region->pan);
fill(*modulationSpan, region->pan);
for (const auto& mod : region->panCC) {
linearModifier(resources, *tempSpan, mod, normalizePercents<float>);
add<float>(*tempSpan, *modulationSpan);
}
pan<float>(*modulationSpan, leftBuffer, rightBuffer);
pan(*modulationSpan, leftBuffer, rightBuffer);
}
void sfz::Voice::panStageStereo(AudioSpan<float> buffer) noexcept
@ -379,27 +380,27 @@ void sfz::Voice::panStageStereo(AudioSpan<float> buffer) noexcept
return;
// Apply panning
fill<float>(*modulationSpan, region->pan);
fill(*modulationSpan, region->pan);
for (const auto& mod : region->panCC) {
linearModifier(resources, *tempSpan, mod, normalizePercents<float>);
add<float>(*tempSpan, *modulationSpan);
}
pan<float>(*modulationSpan, leftBuffer, rightBuffer);
pan(*modulationSpan, leftBuffer, rightBuffer);
// Apply the width/position process
fill<float>(*modulationSpan, region->width);
fill(*modulationSpan, region->width);
for (const auto& mod : region->widthCC) {
linearModifier(resources, *tempSpan, mod, normalizePercents<float>);
add<float>(*tempSpan, *modulationSpan);
}
width<float>(*modulationSpan, leftBuffer, rightBuffer);
width(*modulationSpan, leftBuffer, rightBuffer);
fill<float>(*modulationSpan, region->position);
fill(*modulationSpan, region->position);
for (const auto& mod : region->positionCC) {
linearModifier(resources, *tempSpan, mod, normalizePercents<float>);
add<float>(*tempSpan, *modulationSpan);
}
pan<float>(*modulationSpan, leftBuffer, rightBuffer);
pan(*modulationSpan, leftBuffer, rightBuffer);
}
void sfz::Voice::filterStageMono(AudioSpan<float> buffer) noexcept
@ -457,7 +458,7 @@ void sfz::Voice::fillWithData(AudioSpan<float> buffer) noexcept
if (!jumps || !bends || !indices || !coeffs)
return;
fill<float>(*jumps, pitchRatio * speedRatio);
fill(*jumps, pitchRatio * speedRatio);
const auto events = resources.midiState.getPitchEvents();
const auto bendLambda = [this](float bend) {
@ -479,7 +480,7 @@ void sfz::Voice::fillWithData(AudioSpan<float> buffer) noexcept
jumps->front() += floatPositionOffset;
cumsum<float>(*jumps, *jumps);
sfzInterpolationCast<float>(*jumps, *indices, *coeffs);
add<int>(sourcePosition, *indices);
add1<int>(sourcePosition, *indices);
if (region->shouldLoop() && region->loopEnd(currentPromise->oversamplingFactor) <= source.getNumFrames()) {
const auto loopEnd = static_cast<int>(region->loopEnd(currentPromise->oversamplingFactor));
@ -487,7 +488,7 @@ void sfz::Voice::fillWithData(AudioSpan<float> buffer) noexcept
for (auto* index = indices->begin(); index < indices->end(); ++index) {
if (*index > loopEnd) {
const auto remainingElements = static_cast<size_t>(std::distance(index, indices->end()));
subtract<int>(offset, { index, remainingElements });
subtract1<int>(offset, { index, remainingElements });
}
}
} else {
@ -541,8 +542,8 @@ void sfz::Voice::fillWithData(AudioSpan<float> buffer) noexcept
#if 0
ASSERT(!hasNanInf(buffer.getConstSpan(0)));
ASSERT(!hasNanInf(buffer.getConstSpan(1)));
CHECK(isReasonableAudio(buffer.getConstSpan(0)));
CHECK(isReasonableAudio(buffer.getConstSpan(1)));
SFIZZ_CHECK(isReasonableAudio(buffer.getConstSpan(0)));
SFIZZ_CHECK(isReasonableAudio(buffer.getConstSpan(1)));
#endif
}
@ -588,7 +589,7 @@ void sfz::Voice::fillWithGenerator(AudioSpan<float> buffer) noexcept
return;
float keycenterFrequency = midiNoteFrequency(region->pitchKeycenter);
fill<float>(*frequencies, pitchRatio * keycenterFrequency);
fill(*frequencies, pitchRatio * keycenterFrequency);
const auto events = resources.midiState.getPitchEvents();
const auto bendLambda = [this](float bend) {
@ -623,8 +624,8 @@ void sfz::Voice::fillWithGenerator(AudioSpan<float> buffer) noexcept
for (unsigned i = 0, n = waveUnisonSize; i < n; ++i) {
WavetableOscillator& osc = waveOscillators[i];
osc.processModulated(frequencies->data(), waveDetuneRatio[i], tempSpan->data(), numFrames);
sfz::multiplyAdd<float>(waveLeftGain[i], *tempSpan, leftSpan);
sfz::multiplyAdd<float>(waveRightGain[i], *tempSpan, rightSpan);
multiplyAdd1<float>(waveLeftGain[i], *tempSpan, leftSpan);
multiplyAdd1<float>(waveRightGain[i], *tempSpan, rightSpan);
}
}
}
@ -632,8 +633,8 @@ void sfz::Voice::fillWithGenerator(AudioSpan<float> buffer) noexcept
#if 0
ASSERT(!hasNanInf(buffer.getConstSpan(0)));
ASSERT(!hasNanInf(buffer.getConstSpan(1)));
CHECK(isReasonableAudio(buffer.getConstSpan(0)));
CHECK(isReasonableAudio(buffer.getConstSpan(1)));
SFIZZ_CHECK(isReasonableAudio(buffer.getConstSpan(0)));
SFIZZ_CHECK(isReasonableAudio(buffer.getConstSpan(1)));
#endif
}

View file

@ -103,8 +103,8 @@ namespace fx {
// mix down the stereo signal to create the resonator excitation source
absl::Span<float> resInput = _tempBuffer.getSpan(0).first(nframes);
sfz::applyGain<float>(M_SQRT1_2, inputL, resInput);
sfz::multiplyAdd<float>(M_SQRT1_2, inputR, resInput);
sfz::applyGain1<float>(M_SQRT1_2, inputL, resInput);
sfz::multiplyAdd1<float>(M_SQRT1_2, inputR, resInput);
// generate the strings summed into a common buffer
absl::Span<float> resOutput = _tempBuffer.getSpan(1).first(nframes);

View file

@ -16,7 +16,7 @@
#include "Width.h"
#include "Opcode.h"
#include "SIMDHelpers.h"
#include "Panning.h"
#include "absl/memory/memory.h"
namespace sfz {
@ -53,8 +53,8 @@ namespace fx {
const float r = input2[i];
const float w = clamp((widths[i] + 100.0f) * 0.005f, 0.0f, 1.0f);
const float coeff1 = _internals::panLookup(w);
const float coeff2 = _internals::panLookup(1.0f - w);
const float coeff1 = panLookup(w);
const float coeff2 = panLookup(1.0f - w);
output1[i] = l * coeff2 + r * coeff1;
output2[i] = l * coeff1 + r * coeff2;

34
src/sfizz/simd/Common.h Normal file
View file

@ -0,0 +1,34 @@
// 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 <stdint.h>
constexpr uintptr_t ByteAlignmentMask(unsigned N) { return N - 1; }
template<unsigned N, class T>
T* nextAligned(const T* ptr)
{
return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(ptr) + ByteAlignmentMask(N) & (~ByteAlignmentMask(N)));
}
template<unsigned N, class T>
T* prevAligned(const T* ptr)
{
return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(ptr) & (~ByteAlignmentMask(N)));
}
template<unsigned N, class T>
bool unaligned(const T* ptr)
{
return (reinterpret_cast<uintptr_t>(ptr) & ByteAlignmentMask(N) )!= 0;
}
template<unsigned N, class T, class... Args>
bool unaligned(const T* ptr1, Args... rest)
{
return unaligned<N>(ptr1) || unaligned<N>(rest...);
}

View file

@ -0,0 +1,56 @@
// 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 "HelpersAVX.h"
#include "../SIMDConfig.h"
#include "../MathHelpers.h"
#include "Common.h"
#if SFIZZ_HAVE_AVX
#include <immintrin.h>
using Type = float;
constexpr unsigned TypeAlignment = 8;
constexpr unsigned ByteAlignment = TypeAlignment * sizeof(Type);
#endif
void gain1AVX(float gain, const float* input, float* output, unsigned size) noexcept
{
const auto sentinel = output + size;
#if SFIZZ_HAVE_AVX
const auto* lastAligned = prevAligned<ByteAlignment>(sentinel);
const auto mmGain = _mm256_set1_ps(gain);
while (unaligned<ByteAlignment>(input, output) && output < lastAligned)
*output++ = gain * (*input++);
while (output < lastAligned) {
_mm256_store_ps(output, _mm256_mul_ps(mmGain, _mm256_load_ps(input)));
incrementAll<TypeAlignment>(input, output);
}
#endif
while (output < sentinel)
*output++ = gain * (*input++);
}
void gainAVX(const float* gain, const float* input, float* output, unsigned size) noexcept
{
const auto sentinel = output + size;
#if SFIZZ_HAVE_AVX
const auto* lastAligned = prevAligned<ByteAlignment>(sentinel);
while (unaligned<ByteAlignment>(input, output) && output < lastAligned)
*output++ = (*gain++) * (*input++);
while (output < lastAligned) {
_mm256_store_ps(output, _mm256_mul_ps(_mm256_load_ps(gain), _mm256_load_ps(input)));
incrementAll<TypeAlignment>(input, output);
}
#endif
while (output < sentinel)
*output++ = (*gain++) * (*input++);
}

View file

@ -0,0 +1,10 @@
// 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
void gain1AVX(float gain, const float* input, float* output, unsigned size) noexcept;
void gainAVX(const float* gain, const float* input, float* output, unsigned size) noexcept;

View file

@ -0,0 +1,474 @@
// 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 "HelpersSSE.h"
#include "../SIMDConfig.h"
#include "../MathHelpers.h"
#include "Common.h"
#include <array>
#if SFIZZ_HAVE_SSE2
#include <immintrin.h>
using Type = float;
constexpr unsigned TypeAlignment = 4;
constexpr unsigned ByteAlignment = TypeAlignment * sizeof(Type);
#endif
void readInterleavedSSE(const float* input, float* outputLeft, float* outputRight, unsigned inputSize) noexcept
{
const auto sentinel = input + inputSize - 1;
#if SFIZZ_HAVE_SSE2
const auto* lastAligned = prevAligned<ByteAlignment>(input + inputSize - TypeAlignment);
while (unaligned<ByteAlignment>(input, outputLeft, outputRight) && input < lastAligned) {
*outputLeft++ = *input++;
*outputRight++ = *input++;
}
while (input < lastAligned) {
auto register0 = _mm_load_ps(input);
auto register1 = _mm_load_ps(input + TypeAlignment);
auto register2 = register0;
// register 2 holds the copy of register 0 that is going to get erased by the first operation
// Remember that the bit mask reads from the end; 10 00 10 00 means
// "take 0 from a, take 2 from a, take 0 from b, take 2 from b"
register0 = _mm_shuffle_ps(register0, register1, 0b10001000);
register1 = _mm_shuffle_ps(register2, register1, 0b11011101);
_mm_store_ps(outputLeft, register0);
_mm_store_ps(outputRight, register1);
incrementAll<TypeAlignment>(input, input, outputLeft, outputRight);
}
// NEON wip
// auto reg = vld2q_f32(in);
// vst1q_f32(lOut, reg.val[0]);
// vst1q_f32(rOut, reg.val[1]);
#endif
while (input < sentinel) {
*outputLeft++ = *input++;
*outputRight++ = *input++;
}
}
void writeInterleavedSSE(const float* inputLeft, const float* inputRight, float* output, unsigned outputSize) noexcept
{
const auto sentinel = output + outputSize - 1;
#if SFIZZ_HAVE_SSE2
const auto* lastAligned = prevAligned<ByteAlignment>(output + outputSize - TypeAlignment);
while (unaligned<ByteAlignment>(output, inputRight, inputLeft) && output < lastAligned) {
*output++ = *inputLeft++;
*output++ = *inputRight++;
}
while (output < lastAligned) {
const auto lInRegister = _mm_load_ps(inputLeft);
const auto rInRegister = _mm_load_ps(inputRight);
const auto outRegister1 = _mm_unpacklo_ps(lInRegister, rInRegister);
_mm_store_ps(output, outRegister1);
const auto outRegister2 = _mm_unpackhi_ps(lInRegister, rInRegister);
_mm_store_ps(output + 4, outRegister2);
incrementAll<TypeAlignment>(output, output, inputLeft, inputRight);
}
#endif
while (output < sentinel) {
*output++ = *inputLeft++;
*output++ = *inputRight++;
}
}
void gain1SSE(float gain, const float* input, float* output, unsigned size) noexcept
{
const auto sentinel = output + size;
#if SFIZZ_HAVE_SSE2
const auto* lastAligned = prevAligned<ByteAlignment>(sentinel);
const auto mmGain = _mm_set1_ps(gain);
while (unaligned<ByteAlignment>(input, output) && output < lastAligned)
*output++ = gain * (*input++);
while (output < lastAligned) {
_mm_store_ps(output, _mm_mul_ps(mmGain, _mm_load_ps(input)));
incrementAll<TypeAlignment>(input, output);
}
#endif
while (output < sentinel)
*output++ = gain * (*input++);
}
void gainSSE(const float* gain, const float* input, float* output, unsigned size) noexcept
{
const auto sentinel = output + size;
#if SFIZZ_HAVE_SSE2
const auto* lastAligned = prevAligned<ByteAlignment>(sentinel);
while (unaligned<ByteAlignment>(input, output) && output < lastAligned)
*output++ = (*gain++) * (*input++);
while (output < lastAligned) {
_mm_store_ps(output, _mm_mul_ps(_mm_load_ps(gain), _mm_load_ps(input)));
incrementAll<TypeAlignment>(gain, input, output);
}
#endif
while (output < sentinel)
*output++ = (*gain++) * (*input++);
}
void divideSSE(const float* input, const float* divisor, float* output, unsigned size) noexcept
{
const auto sentinel = output + size;
#if SFIZZ_HAVE_SSE2
const auto* lastAligned = prevAligned<ByteAlignment>(sentinel);
while (unaligned<ByteAlignment>(input, output) && output < lastAligned)
*output++ = (*input++) / (*divisor++);
while (output < lastAligned) {
_mm_store_ps(output, _mm_div_ps(_mm_load_ps(input), _mm_load_ps(divisor)));
incrementAll<TypeAlignment>(divisor, input, output);
}
#endif
while (output < sentinel)
*output++ = (*input++) / (*divisor++);
}
void multiplyAddSSE(const float* gain, const float* input, float* output, unsigned size) noexcept
{
const auto sentinel = output + size;
#if SFIZZ_HAVE_SSE2
const auto* lastAligned = prevAligned<ByteAlignment>(sentinel);
while (unaligned<ByteAlignment>(input, output) && output < lastAligned)
*output++ += (*gain++) * (*input++);
while (output < lastAligned) {
auto mmOut = _mm_load_ps(output);
mmOut = _mm_add_ps(_mm_mul_ps(_mm_load_ps(gain), _mm_load_ps(input)), mmOut);
_mm_store_ps(output, mmOut);
incrementAll<TypeAlignment>(gain, input, output);
}
#endif
while (output < sentinel)
*output++ += (*gain++) * (*input++);
}
void multiplyAdd1SSE(float gain, const float* input, float* output, unsigned size) noexcept
{
const auto sentinel = output + size;
#if SFIZZ_HAVE_SSE2
const auto* lastAligned = prevAligned<ByteAlignment>(sentinel);
while (unaligned<ByteAlignment>(input, output) && output < lastAligned)
*output++ += gain * (*input++);
auto mmGain = _mm_set1_ps(gain);
while (output < lastAligned) {
auto mmOut = _mm_load_ps(output);
mmOut = _mm_add_ps(_mm_mul_ps(mmGain, _mm_load_ps(input)), mmOut);
_mm_store_ps(output, mmOut);
incrementAll<TypeAlignment>(input, output);
}
#endif
while (output < sentinel)
*output++ += gain * (*input++);
}
float linearRampSSE(float* output, float start, float step, unsigned size) noexcept
{
const auto sentinel = output + size;
#if SFIZZ_HAVE_SSE2
const auto* lastAligned = prevAligned<ByteAlignment>(sentinel);
while (unaligned<ByteAlignment>(output) && output < lastAligned) {
*output++ = start;
start += step;
}
auto mmStart = _mm_set1_ps(start - step);
auto mmStep = _mm_set_ps(step + step + step + step, step + step + step, step + step, step);
while (output < lastAligned) {
mmStart = _mm_add_ps(mmStart, mmStep);
_mm_store_ps(output, mmStart);
mmStart = _mm_shuffle_ps(mmStart, mmStart, _MM_SHUFFLE(3, 3, 3, 3));
incrementAll<TypeAlignment>(output);
}
start = _mm_cvtss_f32(mmStart) + step;
#endif
while (output < sentinel) {
*output++ = start;
start += step;
}
return start;
}
float multiplicativeRampSSE(float* output, float start, float step, unsigned size) noexcept
{
const auto sentinel = output + size;
#if SFIZZ_HAVE_SSE2
const auto* lastAligned = prevAligned<ByteAlignment>(sentinel);
while (unaligned<ByteAlignment>(output) && output < lastAligned) {
*output++ = start;
start *= step;
}
auto mmStart = _mm_set1_ps(start / step);
auto mmStep = _mm_set_ps(step * step * step * step, step * step * step, step * step, step);
while (output < lastAligned) {
mmStart = _mm_mul_ps(mmStart, mmStep);
_mm_store_ps(output, mmStart);
mmStart = _mm_shuffle_ps(mmStart, mmStart, _MM_SHUFFLE(3, 3, 3, 3));
incrementAll<TypeAlignment>(output);
}
start = _mm_cvtss_f32(mmStart) * step;
#endif
while (output < sentinel) {
*output++ = start;
start *= step;
}
return start;
}
void addSSE(const float* input, float* output, unsigned size) noexcept
{
const auto sentinel = output + size;
#if SFIZZ_HAVE_SSE2
const auto* lastAligned = prevAligned<ByteAlignment>(sentinel);
while (unaligned<ByteAlignment>(input, output) && output < lastAligned)
*output++ += *input++;
while (output < lastAligned) {
_mm_store_ps(output, _mm_add_ps(_mm_load_ps(output), _mm_load_ps(input)));
incrementAll<TypeAlignment>(input, output);
}
#endif
while (output < sentinel)
*output++ += *input++;
}
void add1SSE(float value, float* output, unsigned size) noexcept
{
const auto sentinel = output + size;
#if SFIZZ_HAVE_SSE2
const auto* lastAligned = prevAligned<ByteAlignment>(sentinel);
while (unaligned<ByteAlignment>(output) && output < lastAligned)
*output++ += value;
const auto mmValue = _mm_set1_ps(value);
while (output < lastAligned) {
_mm_store_ps(output, _mm_add_ps(_mm_load_ps(output), mmValue));
incrementAll<TypeAlignment>(output);
}
#endif
while (output < sentinel)
*output++ += value;
}
void subtractSSE(const float* input, float* output, unsigned size) noexcept
{
const auto sentinel = output + size;
#if SFIZZ_HAVE_SSE2
const auto* lastAligned = prevAligned<ByteAlignment>(sentinel);
while (unaligned<ByteAlignment>(input, output) && output < lastAligned)
*output++ -= *input++;
while (output < lastAligned) {
_mm_store_ps(output, _mm_sub_ps(_mm_load_ps(output), _mm_load_ps(input)));
incrementAll<TypeAlignment>(input, output);
}
#endif
while (output < sentinel)
*output++ -= *input++;
}
void subtract1SSE(float value, float* output, unsigned size) noexcept
{
const auto sentinel = output + size;
#if SFIZZ_HAVE_SSE2
const auto* lastAligned = prevAligned<ByteAlignment>(sentinel);
while (unaligned<ByteAlignment>(output) && output < lastAligned)
*output++ -= value;
const auto mmValue = _mm_set1_ps(value);
while (output < lastAligned) {
_mm_store_ps(output, _mm_sub_ps(_mm_load_ps(output), mmValue));
incrementAll<TypeAlignment>(output);
}
#endif
while (output < sentinel)
*output++ -= value;
}
void copySSE(const float* input, float* output, unsigned size) noexcept
{
// The sentinel is the input here
const auto sentinel = input + size;
#if SFIZZ_HAVE_SSE2
const auto* lastAligned = prevAligned<ByteAlignment>(sentinel);
while (unaligned<ByteAlignment>(input, output) && input < lastAligned)
*output++ = *input++;
while (input < lastAligned) {
_mm_store_ps(output, _mm_load_ps(input));
incrementAll<TypeAlignment>(input, output);
}
#endif
std::copy(input, sentinel, output);
}
float meanSSE(const float* vector, unsigned size) noexcept
{
const auto sentinel = vector + size;
float result { 0.0f };
if (size == 0)
return result;
#if SFIZZ_HAVE_SSE2
const auto* lastAligned = prevAligned<ByteAlignment>(sentinel);
while (unaligned<ByteAlignment>(vector) && vector < lastAligned)
result += *vector++;
auto mmSums = _mm_setzero_ps();
while (vector < lastAligned) {
mmSums = _mm_add_ps(mmSums, _mm_load_ps(vector));
incrementAll<TypeAlignment>(vector);
}
std::array<float, 4> sseResult;
_mm_store_ps(sseResult.data(), mmSums);
for (auto sseValue : sseResult)
result += sseValue;
#endif
while (vector < sentinel)
result += *vector++;
return result / static_cast<float>(size);
}
float meanSquaredSSE(const float* vector, unsigned size) noexcept
{
const auto sentinel = vector + size;
float result { 0.0f };
if (size == 0)
return result;
#if SFIZZ_HAVE_SSE2
const auto* lastAligned = prevAligned<ByteAlignment>(sentinel);
while (unaligned<ByteAlignment>(vector) && vector < lastAligned) {
result += (*vector) * (*vector);
vector++;
}
auto mmSums = _mm_setzero_ps();
while (vector < lastAligned) {
const auto mmValues = _mm_load_ps(vector);
mmSums = _mm_add_ps(mmSums, _mm_mul_ps(mmValues, mmValues));
incrementAll<TypeAlignment>(vector);
}
std::array<float, 4> sseResult;
_mm_store_ps(sseResult.data(), mmSums);
for (auto sseValue : sseResult)
result += sseValue;
#endif
while (vector < sentinel) {
result += (*vector) * (*vector);
vector++;
}
return result / static_cast<float>(size);
}
void cumsumSSE(const float* input, float* output, unsigned size) noexcept
{
if (size == 0)
return;
const auto sentinel = output + size;
*output++ = *input++;
#if SFIZZ_HAVE_SSE2
const auto* lastAligned = prevAligned<ByteAlignment>(sentinel);
while (unaligned<ByteAlignment>(input, output) && output < lastAligned) {
*output = *(output - 1) + *input;
incrementAll(input, output);
}
auto mmOutput = _mm_set_ps1(*(output - 1));
while (output < lastAligned) {
auto mmOffset = _mm_load_ps(input);
mmOffset = _mm_add_ps(mmOffset, _mm_castsi128_ps(_mm_slli_si128(_mm_castps_si128(mmOffset), 4)));
mmOffset = _mm_add_ps(mmOffset, _mm_shuffle_ps(_mm_setzero_ps(), mmOffset, _MM_SHUFFLE(1, 0, 0, 0)));
mmOutput = _mm_add_ps(mmOutput, mmOffset);
_mm_store_ps(output, mmOutput);
mmOutput = _mm_shuffle_ps(mmOutput, mmOutput, _MM_SHUFFLE(3, 3, 3, 3));
incrementAll<TypeAlignment>(input, output);
}
#endif
while (output < sentinel) {
*output = *(output - 1) + *input;
incrementAll(input, output);
}
}
void diffSSE(const float* input, float* output, unsigned size) noexcept
{
if (size == 0)
return;
const auto sentinel = output + size;
*output++ = *input++;
#if SFIZZ_HAVE_SSE2
const auto* lastAligned = prevAligned<ByteAlignment>(sentinel);
while (unaligned<ByteAlignment>(input, output) && output < lastAligned) {
*output = *input - *(input - 1);
incrementAll(input, output);
}
auto mmBase = _mm_set_ps1(*(input - 1));
while (output < lastAligned) {
auto mmOutput = _mm_load_ps(input);
auto mmNextBase = _mm_shuffle_ps(mmOutput, mmOutput, _MM_SHUFFLE(3, 3, 3, 3));
mmOutput = _mm_sub_ps(mmOutput, mmBase);
mmBase = mmNextBase;
mmOutput = _mm_sub_ps(mmOutput, _mm_castsi128_ps(_mm_slli_si128(_mm_castps_si128(mmOutput), 4)));
_mm_store_ps(output, mmOutput);
incrementAll<TypeAlignment>(input, output);
}
#endif
while (output < sentinel) {
*output = *input - *(input - 1);
incrementAll(input, output);
}
}

View file

@ -0,0 +1,27 @@
// 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
/* These are the SSE versions of the SIMDHelpers */
void readInterleavedSSE(const float* input, float* outputLeft, float* outputRight, unsigned inputSize) noexcept;
void writeInterleavedSSE(const float* inputLeft, const float* inputRight, float* output, unsigned outputSize) noexcept;
void gainSSE(const float* gain, const float* input, float* output, unsigned size) noexcept;
void gain1SSE(float gain, const float* input, float* output, unsigned size) noexcept;
void divideSSE(const float* input, const float* divisor, float* output, unsigned size) noexcept;
void multiplyAddSSE(const float* gain, const float* input, float* output, unsigned size) noexcept;
void multiplyAdd1SSE(float gain, const float* input, float* output, unsigned size) noexcept;
float linearRampSSE(float* output, float start, float step, unsigned size) noexcept;
float multiplicativeRampSSE(float* output, float start, float step, unsigned size) noexcept;
void addSSE(const float* input, float* output, unsigned size) noexcept;
void add1SSE(float value, float* output, unsigned size) noexcept;
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;
void cumsumSSE(const float* input, float* output, unsigned size) noexcept;
void diffSSE(const float* input, float* output, unsigned size) noexcept;

View file

@ -0,0 +1,188 @@
// 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 <algorithm>
template<class T>
inline void readInterleavedScalar(const T* input, T* outputLeft, T* outputRight, unsigned inputSize) noexcept
{
const auto sentinel = input + inputSize - 1;
while (input < sentinel) {
*outputLeft++ = *input++;
*outputRight++ = *input++;
}
}
template<class T>
inline void writeInterleavedScalar(const T* inputLeft, const T* inputRight, T* output, unsigned outputSize) noexcept
{
const auto sentinel = output + outputSize - 1;
while (output < sentinel) {
*output++ = *inputLeft++;
*output++ = *inputRight++;
}
}
template<class T>
inline void gain1Scalar(T gain, const T* input, T* output, unsigned size) noexcept
{
const auto sentinel = output + size;
while (output < sentinel)
*output++ = gain * (*input++);
}
template<class T>
inline void gainScalar(const T* gain, const T* input, T* output, unsigned size) noexcept
{
const auto sentinel = output + size;
while (output < sentinel)
*output++ = (*gain++) * (*input++);
}
template <class T>
inline void divideScalar(const T* input, const T* divisor, T* output, unsigned size) noexcept
{
const auto sentinel = output + size;
while (output < sentinel)
*output++ = (*input++) / (*divisor++);
}
template <class T>
inline void multiplyAddScalar(const T* gain, const T* input, T* output, unsigned size) noexcept
{
const auto sentinel = output + size;
while (output < sentinel)
*output++ += (*gain++) * (*input++);
}
template <class T>
inline void multiplyAdd1Scalar(T gain, const T* input, T* output, unsigned size) noexcept
{
const auto sentinel = output + size;
while (output < sentinel)
*output++ += gain * (*input++);
}
template <class T>
T linearRampScalar(T* output, T start, T step, unsigned size) noexcept
{
const auto sentinel = output + size;
while (output < sentinel) {
*output++ = start;
start += step;
}
return start;
}
template <class T>
T multiplicativeRampScalar(T* output, T start, T step, unsigned size) noexcept
{
const auto sentinel = output + size;
while (output < sentinel) {
*output++ = start;
start *= step;
}
return start;
}
template <class T>
inline void addScalar(const T* input, T* output, unsigned size) noexcept
{
const auto sentinel = output + size;
while (output < sentinel)
*output++ += *input++;
}
template <class T>
inline void add1Scalar(T value, T* output, unsigned size) noexcept
{
const auto sentinel = output + size;
while (output < sentinel)
*output++ += value;
}
template <class T>
inline void subtractScalar(const T* input, T* output, unsigned size) noexcept
{
const auto sentinel = output + size;
while (output < sentinel)
*output++ -= *input++;
}
template <class T>
inline void subtract1Scalar(T value, T* output, unsigned size) noexcept
{
const auto sentinel = output + size;
while (output < sentinel)
*output++ -= value;
}
template <class T>
void copyScalar(const T* input, T* output, unsigned size) noexcept
{
std::copy(input, input + size, output);
}
template <class T>
T meanScalar(const T* vector, unsigned size) noexcept
{
T result{ 0.0 };
if (size == 0)
return result;
const auto sentinel = vector + size;
while (vector < sentinel)
result += *vector++;
return result / static_cast<T>(size);
}
template <class T>
T meanSquaredScalar(const T* vector, unsigned size) noexcept
{
T result{ 0.0 };
if (size == 0)
return result;
const auto sentinel = vector + size;
while (vector < sentinel) {
result += (*vector) * (*vector);
vector++;
}
return result / static_cast<T>(size);
}
template <class T>
void cumsumScalar(const T* input, T* output, unsigned size) noexcept
{
if (size == 0)
return;
const auto sentinel = output + size;
*output++ = *input++;
while (output < sentinel) {
*output = *(output - 1) + *input;
incrementAll(input, output);
}
}
template <class T>
void diffScalar(const T* input, T* output, unsigned size) noexcept
{
if (size == 0)
return;
const auto sentinel = output + size;
*output++ = *input++;
while (output < sentinel) {
*output = *input - *(input - 1);
incrementAll(input, output);
}
}

View file

@ -41,8 +41,8 @@ template <class Type>
void checkBoundaries(sfz::Buffer<Type>& buffer, int expectedSize)
{
REQUIRE((int)buffer.size() == expectedSize);
REQUIRE(((size_t)buffer.data() & (sfz::SIMDConfig::defaultAlignment - 1)) == 0);
REQUIRE(((size_t)buffer.alignedEnd() & (sfz::SIMDConfig::defaultAlignment - 1)) == 0);
REQUIRE(((size_t)buffer.data() & (sfz::config::defaultAlignment - 1)) == 0);
REQUIRE(((size_t)buffer.alignedEnd() & (sfz::config::defaultAlignment - 1)) == 0);
REQUIRE(std::distance(buffer.begin(), buffer.end()) == expectedSize);
REQUIRE(std::distance(buffer.begin(), buffer.alignedEnd()) >= expectedSize);
}

View file

@ -4,7 +4,7 @@
// 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 "sfizz/SIMDHelpers.h"
#include "sfizz/Panning.h"
#include "ui_DemoStereo.h"
#include <QApplication>
#include <QMainWindow>
@ -160,8 +160,8 @@ int DemoApp::processAudio(jack_nframes_t nframes, void *cbdata)
std::fill(positionEnvelope.begin(), positionEnvelope.end(), self->fPan * 0.01f);
using namespace sfz;
width<float>(widthEnvelope, leftBuffer, rightBuffer);
pan<float>(positionEnvelope, leftBuffer, rightBuffer);
width(widthEnvelope, leftBuffer, rightBuffer);
pan(positionEnvelope, leftBuffer, rightBuffer);
return 0;
}

View file

@ -68,7 +68,7 @@ int main(int argc, char** argv)
sfz::Buffer<float> buffer { numFrames * 2 };
sfz::Buffer<float> right { numFrames };
sndfile.readf(buffer.data(), numFrames * 2 );
sfz::readInterleaved<float>(buffer, absl::MakeSpan(left), absl::MakeSpan(right));
sfz::readInterleaved(buffer, absl::MakeSpan(left), absl::MakeSpan(right));
} else if (sndfile.channels() == 1) {
sndfile.readf(left.data(), numFrames);
} else {

View file

@ -71,7 +71,7 @@ int main(int argc, char** argv)
sfz::Buffer<float> buffer { numFrames * 2 };
sfz::Buffer<float> right { numFrames };
sndfile.readf(buffer.data(), numFrames * 2 );
sfz::readInterleaved<float>(buffer, absl::MakeSpan(left), absl::MakeSpan(right));
sfz::readInterleaved(buffer, absl::MakeSpan(left), absl::MakeSpan(right));
} else if (sndfile.channels() == 1) {
sndfile.readf(left.data(), numFrames);
} else {

View file

@ -1,2 +1,10 @@
#define CATCH_CONFIG_MAIN
#include "catch2/catch.hpp"
#include "sfizz/SIMDHelpers.h"
#define CATCH_CONFIG_RUNNER
#include "catch2/catch.hpp"
int main(int argc, char* argv[])
{
int result = Catch::Session().run(argc, argv);
return result;
}

View file

@ -5,6 +5,7 @@
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
#include "sfizz/SIMDHelpers.h"
#include "sfizz/Panning.h"
#include "catch2/catch.hpp"
#include <absl/algorithm/container.h>
#include <absl/types/span.h>
@ -48,81 +49,14 @@ inline bool approxEqual(absl::Span<const Type> lhs, absl::Span<const Type> rhs,
return true;
}
TEST_CASE("[Helpers] fill() - Manual buffer")
{
std::vector<float> buffer(5);
std::vector<float> expected { fillValue, fillValue, fillValue, fillValue, fillValue };
sfz::fill<float, false>(absl::MakeSpan(buffer), fillValue);
REQUIRE(buffer == expected);
}
TEST_CASE("[Helpers] fill() - Small buffer")
{
std::vector<float> buffer(smallBufferSize);
std::vector<float> expected(smallBufferSize);
std::fill(expected.begin(), expected.end(), fillValue);
sfz::fill<float, false>(absl::MakeSpan(buffer), fillValue);
REQUIRE(buffer == expected);
}
TEST_CASE("[Helpers] fill() - Big buffer")
{
std::vector<float> buffer(bigBufferSize);
std::vector<float> expected(bigBufferSize);
std::fill(expected.begin(), expected.end(), fillValue);
sfz::fill<float, false>(absl::MakeSpan(buffer), fillValue);
REQUIRE(buffer == expected);
}
TEST_CASE("[Helpers] fill() - Small buffer -- SIMD")
{
std::vector<float> buffer(smallBufferSize);
std::vector<float> expected(smallBufferSize);
std::fill(expected.begin(), expected.end(), fillValue);
sfz::fill<float, true>(absl::MakeSpan(buffer), fillValue);
REQUIRE(buffer == expected);
}
TEST_CASE("[Helpers] fill() - Big buffer -- SIMD")
{
std::vector<float> buffer(bigBufferSize);
std::vector<float> expected(bigBufferSize);
std::fill(expected.begin(), expected.end(), fillValue);
sfz::fill<float, true>(absl::MakeSpan(buffer), fillValue);
REQUIRE(buffer == expected);
}
TEST_CASE("[Helpers] fill() - Small buffer -- doubles")
{
std::vector<double> buffer(smallBufferSize);
std::vector<double> expected(smallBufferSize);
std::fill(expected.begin(), expected.end(), fillValue);
sfz::fill<double, false>(absl::MakeSpan(buffer), fillValue);
REQUIRE(buffer == expected);
}
TEST_CASE("[Helpers] fill() - Big buffer -- doubles")
{
std::vector<double> buffer(bigBufferSize);
std::vector<double> expected(bigBufferSize);
std::fill(expected.begin(), expected.end(), fillValue);
sfz::fill<double, false>(absl::MakeSpan(buffer), fillValue);
REQUIRE(buffer == expected);
}
TEST_CASE("[Helpers] Interleaved read")
{
std::array<float, 16> input { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f };
std::array<float, 16> expected { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f };
std::array<float, 8> leftOutput;
std::array<float, 8> rightOutput;
sfz::readInterleaved<float, false>(input, absl::MakeSpan(leftOutput), absl::MakeSpan(rightOutput));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::readInterleaved, false);
sfz::readInterleaved(input, absl::MakeSpan(leftOutput), absl::MakeSpan(rightOutput));
std::array<float, 16> real;
auto realIdx = 0;
@ -139,7 +73,8 @@ TEST_CASE("[Helpers] Interleaved read unaligned end")
std::array<float, 20> expected { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f, 18.0f, 19.0f };
std::array<float, 10> leftOutput;
std::array<float, 10> rightOutput;
sfz::readInterleaved<float, false>(input, absl::MakeSpan(leftOutput), absl::MakeSpan(rightOutput));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::readInterleaved, false);
sfz::readInterleaved(input, absl::MakeSpan(leftOutput), absl::MakeSpan(rightOutput));
std::array<float, 20> real;
auto realIdx = 0;
@ -156,7 +91,8 @@ TEST_CASE("[Helpers] Small interleaved read unaligned end")
std::array<float, 6> expected { 0.0f, 1.0f, 2.0f, 10.0f, 11.0f, 12.0f };
std::array<float, 3> leftOutput;
std::array<float, 3> rightOutput;
sfz::readInterleaved<float, false>(input, absl::MakeSpan(leftOutput), absl::MakeSpan(rightOutput));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::readInterleaved, false);
sfz::readInterleaved(input, absl::MakeSpan(leftOutput), absl::MakeSpan(rightOutput));
std::array<float, 6> real;
auto realIdx = 0;
@ -173,7 +109,8 @@ TEST_CASE("[Helpers] Interleaved read -- SIMD")
std::array<float, 16> expected = { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f };
std::array<float, 8> leftOutput;
std::array<float, 8> rightOutput;
sfz::readInterleaved<float, true>(input, absl::MakeSpan(leftOutput), absl::MakeSpan(rightOutput));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::readInterleaved, true);
sfz::readInterleaved(input, absl::MakeSpan(leftOutput), absl::MakeSpan(rightOutput));
std::array<float, 16> real;
auto realIdx = 0;
@ -190,7 +127,8 @@ TEST_CASE("[Helpers] Interleaved read unaligned end -- SIMD")
std::array<float, 20> expected = { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f, 18.0f, 19.0f };
std::array<float, 10> leftOutput;
std::array<float, 10> rightOutput;
sfz::readInterleaved<float, true>(input, absl::MakeSpan(leftOutput), absl::MakeSpan(rightOutput));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::readInterleaved, true);
sfz::readInterleaved(input, absl::MakeSpan(leftOutput), absl::MakeSpan(rightOutput));
std::array<float, 20> real;
auto realIdx = 0;
@ -207,7 +145,8 @@ TEST_CASE("[Helpers] Small interleaved read unaligned end -- SIMD")
std::array<float, 6> expected { 0.0f, 1.0f, 2.0f, 10.0f, 11.0f, 12.0f };
std::array<float, 3> leftOutput;
std::array<float, 3> rightOutput;
sfz::readInterleaved<float, true>(input, absl::MakeSpan(leftOutput), absl::MakeSpan(rightOutput));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::readInterleaved, true);
sfz::readInterleaved(input, absl::MakeSpan(leftOutput), absl::MakeSpan(rightOutput));
std::array<float, 6> real;
auto realIdx = 0;
@ -226,8 +165,10 @@ TEST_CASE("[Helpers] Interleaved read SIMD vs Scalar")
std::array<float, medBufferSize> leftOutputSIMD;
std::array<float, medBufferSize> rightOutputSIMD;
std::iota(input.begin(), input.end(), 0.0f);
sfz::readInterleaved<float, false>(input, absl::MakeSpan(leftOutputScalar), absl::MakeSpan(rightOutputScalar));
sfz::readInterleaved<float, true>(input, absl::MakeSpan(leftOutputSIMD), absl::MakeSpan(rightOutputSIMD));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::readInterleaved, false);
sfz::readInterleaved(input, absl::MakeSpan(leftOutputScalar), absl::MakeSpan(rightOutputScalar));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::readInterleaved, true);
sfz::readInterleaved(input, absl::MakeSpan(leftOutputSIMD), absl::MakeSpan(rightOutputSIMD));
REQUIRE(leftOutputScalar == leftOutputSIMD);
REQUIRE(rightOutputScalar == rightOutputSIMD);
}
@ -247,7 +188,8 @@ TEST_CASE("[Helpers] Interleaved write")
std::array<float, 8> rightInput { 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f };
std::array<float, 16> output;
std::array<float, 16> expected { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f };
sfz::writeInterleaved<float, false>(leftInput, rightInput, absl::MakeSpan(output));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::writeInterleaved, false);
sfz::writeInterleaved(leftInput, rightInput, absl::MakeSpan(output));
REQUIRE(output == expected);
}
@ -257,7 +199,8 @@ TEST_CASE("[Helpers] Interleaved write unaligned end")
std::array<float, 10> rightInput { 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f, 18.0f, 19.0f };
std::array<float, 20> output;
std::array<float, 20> expected { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f, 8.0f, 18.0f, 9.0f, 19.0f };
sfz::writeInterleaved<float, false>(leftInput, rightInput, absl::MakeSpan(output));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::writeInterleaved, false);
sfz::writeInterleaved(leftInput, rightInput, absl::MakeSpan(output));
REQUIRE(output == expected);
}
@ -267,7 +210,8 @@ TEST_CASE("[Helpers] Small interleaved write unaligned end")
std::array<float, 3> rightInput { 10.0f, 11.0f, 12.0f };
std::array<float, 6> output;
std::array<float, 6> expected { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f };
sfz::writeInterleaved<float, false>(leftInput, rightInput, absl::MakeSpan(output));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::writeInterleaved, false);
sfz::writeInterleaved(leftInput, rightInput, absl::MakeSpan(output));
REQUIRE(output == expected);
}
@ -286,7 +230,8 @@ TEST_CASE("[Helpers] Interleaved write -- SIMD")
std::array<float, 8> rightInput { 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f };
std::array<float, 16> output;
std::array<float, 16> expected { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f };
sfz::writeInterleaved<float, true>(leftInput, rightInput, absl::MakeSpan(output));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::writeInterleaved, true);
sfz::writeInterleaved(leftInput, rightInput, absl::MakeSpan(output));
REQUIRE(output == expected);
}
@ -296,7 +241,7 @@ TEST_CASE("[Helpers] Interleaved write unaligned end -- SIMD")
std::array<float, 10> rightInput { 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f, 18.0f, 19.0f };
std::array<float, 20> output;
std::array<float, 20> expected = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f, 8.0f, 18.0f, 9.0f, 19.0f };
sfz::writeInterleaved<float, true>(leftInput, rightInput, absl::MakeSpan(output));
sfz::writeInterleaved(leftInput, rightInput, absl::MakeSpan(output));
REQUIRE(output == expected);
}
@ -306,7 +251,8 @@ TEST_CASE("[Helpers] Small interleaved write unaligned end -- SIMD")
std::array<float, 3> rightInput { 10.0f, 11.0f, 12.0f };
std::array<float, 6> output;
std::array<float, 6> expected { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f };
sfz::writeInterleaved<float, true>(leftInput, rightInput, absl::MakeSpan(output));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::writeInterleaved, true);
sfz::writeInterleaved(leftInput, rightInput, absl::MakeSpan(output));
REQUIRE(output == expected);
}
@ -318,180 +264,107 @@ TEST_CASE("[Helpers] Interleaved write SIMD vs Scalar")
std::array<float, medBufferSize * 2> outputSIMD;
std::iota(leftInput.begin(), leftInput.end(), 0.0f);
std::iota(rightInput.begin(), rightInput.end(), static_cast<float>(medBufferSize));
sfz::writeInterleaved<float, false>(leftInput, rightInput, absl::MakeSpan(outputScalar));
sfz::writeInterleaved<float, true>(leftInput, rightInput, absl::MakeSpan(outputSIMD));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::writeInterleaved, false);
sfz::writeInterleaved(leftInput, rightInput, absl::MakeSpan(outputScalar));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::writeInterleaved, true);
sfz::writeInterleaved(leftInput, rightInput, absl::MakeSpan(outputSIMD));
REQUIRE(outputScalar == outputSIMD);
}
TEST_CASE("[Helpers] Gain, single")
{
std::array<float, 5> input { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f };
std::array<float, 5> output { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
std::array<float, 5> expected { fillValue, fillValue, fillValue, fillValue, fillValue };
sfz::applyGain<float, false>(fillValue, input, absl::MakeSpan(output));
REQUIRE(output == expected);
std::array<float, 65> input;
std::array<float, 65> expected;
absl::c_fill(input, 1.0f);
absl::c_fill(expected, fillValue);
SECTION("Scalar")
{
std::array<float, 65> output;
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::gain1, false);
sfz::applyGain1<float>(fillValue, input, absl::MakeSpan(output));
REQUIRE(output == expected);
}
SECTION("SIMD")
{
std::array<float, 65> output;
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::gain1, true);
sfz::applyGain1<float>(fillValue, input, absl::MakeSpan(output));
REQUIRE(output == expected);
}
}
TEST_CASE("[Helpers] Gain, single and inplace")
{
std::array<float, 5> buffer { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f };
std::array<float, 5> expected { fillValue, fillValue, fillValue, fillValue, fillValue };
sfz::applyGain<float, false>(fillValue, buffer, absl::MakeSpan(buffer));
REQUIRE(buffer == expected);
std::array<float, 65> expected;
std::array<float, 65> buffer;
absl::c_fill(expected, fillValue);
SECTION("Scalar")
{
absl::c_fill(buffer, 1.0f);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::gain1, false);
sfz::applyGain1<float>(fillValue, buffer, absl::MakeSpan(buffer));
REQUIRE(buffer == expected);
}
SECTION("SIMD")
{
absl::c_fill(buffer, 1.0f);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::gain1, false);
sfz::applyGain1<float>(fillValue, buffer, absl::MakeSpan(buffer));
REQUIRE(buffer == expected);
}
}
TEST_CASE("[Helpers] Gain, spans")
{
std::array<float, 5> input { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f };
std::array<float, 5> gain { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
std::array<float, 5> output { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
std::array<float, 5> expected { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
sfz::applyGain<float, false>(gain, input, absl::MakeSpan(output));
REQUIRE(output == expected);
std::array<float, 65> input;
std::array<float, 65> gain;
std::array<float, 65> expected;
absl::c_fill(input, 1.0f);
absl::c_iota(gain, 1.0f);
absl::c_iota(expected, 1.0f);
SECTION("Scalar")
{
std::array<float, 65> output;
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::gain, false);
sfz::applyGain<float>(gain, input, absl::MakeSpan(output));
REQUIRE(output == expected);
}
SECTION("SIMD")
{
std::array<float, 65> output;
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::gain, true);
sfz::applyGain<float>(gain, input, absl::MakeSpan(output));
REQUIRE(output == expected);
}
}
TEST_CASE("[Helpers] Gain, spans and inplace")
{
std::array<float, 5> buffer { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f };
std::array<float, 5> gain { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
std::array<float, 5> expected { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
sfz::applyGain<float, false>(gain, buffer, absl::MakeSpan(buffer));
REQUIRE(buffer == expected);
}
std::array<float, 65> buffer;
std::array<float, 65> gain;
std::array<float, 65> expected;
absl::c_iota(gain, 1.0f);
absl::c_iota(expected, 1.0f);
TEST_CASE("[Helpers] Gain, single (SIMD)")
{
std::array<float, 5> input { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f };
std::array<float, 5> output { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
std::array<float, 5> expected { fillValue, fillValue, fillValue, fillValue, fillValue };
sfz::applyGain<float, true>(fillValue, input, absl::MakeSpan(output));
REQUIRE(output == expected);
}
SECTION("Scalar")
{
absl::c_fill(buffer, 1.0f);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::gain, false);
sfz::applyGain<float>(gain, buffer, absl::MakeSpan(buffer));
REQUIRE(buffer == expected);
}
TEST_CASE("[Helpers] Gain, single and inplace (SIMD)")
{
std::array<float, 5> buffer { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f };
std::array<float, 5> expected { fillValue, fillValue, fillValue, fillValue, fillValue };
sfz::applyGain<float, true>(fillValue, buffer, absl::MakeSpan(buffer));
REQUIRE(buffer == expected);
}
TEST_CASE("[Helpers] Gain, spans (SIMD)")
{
std::array<float, 5> input { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f };
std::array<float, 5> gain { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
std::array<float, 5> output { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
std::array<float, 5> expected { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
sfz::applyGain<float, true>(gain, input, absl::MakeSpan(output));
REQUIRE(output == expected);
}
TEST_CASE("[Helpers] Gain, spans and inplace (SIMD)")
{
std::array<float, 5> buffer { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f };
std::array<float, 5> gain { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
std::array<float, 5> expected { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
sfz::applyGain<float, true>(gain, buffer, absl::MakeSpan(buffer));
REQUIRE(buffer == expected);
}
TEST_CASE("[Helpers] SFZ looping index")
{
std::array<float, 6> jumps { 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f }; // 1.1 2.3 3.6 5.0f 6.5 8.1
std::array<int, 6> indices;
std::array<float, 6> leftCoeffs;
std::array<float, 6> rightCoeffs;
std::array<int, 6> expectedIndices { 2, 3, 4, 1, 2, 4 };
std::array<float, 6> expectedLeft { 0.9f, 0.7f, 0.4f, 1.0f, 0.5f, 0.9f };
std::array<float, 6> expectedRight { 0.1f, 0.3f, 0.6f, 0.0f, 0.5f, 0.1f };
sfz::loopingSFZIndex<float, false>(jumps, absl::MakeSpan(leftCoeffs), absl::MakeSpan(rightCoeffs), absl::MakeSpan(indices), 1.0f, 6, 1);
REQUIRE(indices == expectedIndices);
REQUIRE(approxEqual<float>(leftCoeffs, expectedLeft));
REQUIRE(approxEqual<float>(rightCoeffs, expectedRight));
}
TEST_CASE("[Helpers] SFZ looping index (SIMD)")
{
std::array<float, 6> jumps { 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f }; // 1.1 2.3 3.6 5.0f 6.5 8.1
std::array<int, 6> indices;
std::array<float, 6> leftCoeffs;
std::array<float, 6> rightCoeffs;
std::array<int, 6> expectedIndices { 2, 3, 4, 1, 2, 4 };
std::array<float, 6> expectedLeft { 0.9f, 0.7f, 0.4f, 1.0f, 0.5f, 0.9f };
std::array<float, 6> expectedRight { 0.1f, 0.3f, 0.6f, 0.0f, 0.5f, 0.1f };
sfz::loopingSFZIndex<float, true>(jumps, absl::MakeSpan(leftCoeffs), absl::MakeSpan(rightCoeffs), absl::MakeSpan(indices), 1.0f, 6, 1);
REQUIRE(indices == expectedIndices);
REQUIRE(approxEqual<float>(leftCoeffs, expectedLeft));
REQUIRE(approxEqual<float>(rightCoeffs, expectedRight));
}
// TEST_CASE("[Helpers] SFZ looping index (SIMD vs Scalar)")
// {
// std::vector<float> jumps(bigBufferSize);
// absl::c_fill(jumps, fillValue);
// std::vector<int> indices(bigBufferSize);
// std::vector<float> leftCoeffs(bigBufferSize);
// std::vector<float> rightCoeffs(bigBufferSize);
// std::vector<int> indicesSIMD(bigBufferSize);
// std::vector<float> leftCoeffsSIMD(bigBufferSize);
// std::vector<float> rightCoeffsSIMD(bigBufferSize);
// sfz::loopingSFZIndex<float, false>(jumps, absl::MakeSpan(leftCoeffs), absl::MakeSpan(rightCoeffs), absl::MakeSpan(indices), 1.0f, medBufferSize, 1);
// sfz::loopingSFZIndex<float, true>(jumps, absl::MakeSpan(leftCoeffsSIMD), absl::MakeSpan(rightCoeffsSIMD), absl::MakeSpan(indicesSIMD), 1.0f, medBufferSize, 1);
// for (int i = 0; i < bigBufferSize; ++i)
// REQUIRE( ((static_cast<float>(indices[i]) + rightCoeffs[i] == Approx(static_cast<float>(indicesSIMD[i]) + rightCoeffsSIMD[i]).margin(1e-2))
// || (static_cast<float>(indices[i]) + rightCoeffs[i] == Approx(static_cast<float>(indicesSIMD[i]) + rightCoeffsSIMD[i] - static_cast<float>(medBufferSize)).margin(2e-2))) );
// }
TEST_CASE("[Helpers] SFZ saturating index")
{
std::array<float, 6> jumps { 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f }; // 1.1 2.3 3.6 5.0f 6.5 8.1
std::array<int, 6> indices;
std::array<float, 6> leftCoeffs;
std::array<float, 6> rightCoeffs;
std::array<int, 6> expectedIndices { 2, 3, 4, 5, 5, 5 };
std::array<float, 6> expectedLeft { 0.9f, 0.7f, 0.4f, 0.0f, 0.0f, 0.0f };
std::array<float, 6> expectedRight { 0.1f, 0.3f, 0.6f, 1.0f, 1.0f, 1.0f };
sfz::saturatingSFZIndex<float, false>(jumps, absl::MakeSpan(leftCoeffs), absl::MakeSpan(rightCoeffs), absl::MakeSpan(indices), 1.0f, 6);
REQUIRE(indices == expectedIndices);
REQUIRE(approxEqual<float>(leftCoeffs, expectedLeft));
REQUIRE(approxEqual<float>(rightCoeffs, expectedRight));
}
TEST_CASE("[Helpers] SFZ saturating index (SIMD)")
{
std::array<float, 6> jumps { 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f }; // 1.1 2.3 3.6 5.0f 6.5 8.1
std::array<int, 6> indices;
std::array<float, 6> leftCoeffs;
std::array<float, 6> rightCoeffs;
std::array<int, 6> expectedIndices { 2, 3, 4, 5, 5, 5 };
std::array<float, 6> expectedLeft { 0.9f, 0.7f, 0.4f, 0.0f, 0.0f, 0.0f };
std::array<float, 6> expectedRight { 0.1f, 0.3f, 0.6f, 1.0f, 1.0f, 1.0f };
sfz::saturatingSFZIndex<float, true>(jumps, absl::MakeSpan(leftCoeffs), absl::MakeSpan(rightCoeffs), absl::MakeSpan(indices), 1.0f, 6);
REQUIRE(indices == expectedIndices);
REQUIRE(approxEqualMargin<float>(leftCoeffs, expectedLeft));
REQUIRE(approxEqualMargin<float>(rightCoeffs, expectedRight));
}
TEST_CASE("[Helpers] SFZ saturating index (SIMD vs Scalar)")
{
std::vector<float> jumps(medBufferSize);
absl::c_fill(jumps, fillValue);
std::vector<int> indices(medBufferSize);
std::vector<float> leftCoeffs(medBufferSize);
std::vector<float> rightCoeffs(medBufferSize);
std::vector<int> indicesSIMD(medBufferSize);
std::vector<float> leftCoeffsSIMD(medBufferSize);
std::vector<float> rightCoeffsSIMD(medBufferSize);
sfz::saturatingSFZIndex<float, false>(jumps, absl::MakeSpan(leftCoeffs), absl::MakeSpan(rightCoeffs), absl::MakeSpan(indices), 1.0f, 78);
sfz::saturatingSFZIndex<float, true>(jumps, absl::MakeSpan(leftCoeffsSIMD), absl::MakeSpan(rightCoeffsSIMD), absl::MakeSpan(indicesSIMD), 1.0f, 78);
for (int i = 0; i < medBufferSize; ++i)
REQUIRE( static_cast<float>(indices[i]) + rightCoeffs[i] == Approx(static_cast<float>(indicesSIMD[i]) + rightCoeffsSIMD[i]));
SECTION("SIMD")
{
absl::c_fill(buffer, 1.0f);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::gain, false);
sfz::applyGain<float>(gain, buffer, absl::MakeSpan(buffer));
REQUIRE(buffer == expected);
}
}
TEST_CASE("[Helpers] Linear Ramp")
@ -500,7 +373,8 @@ TEST_CASE("[Helpers] Linear Ramp")
const float v { fillValue };
std::array<float, 6> output;
std::array<float, 6> expected { start, start + v, start + v + v, start + v + v + v, start + v + v + v + v, start + v + v + v + v + v };
sfz::linearRamp<float, false>(absl::MakeSpan(output), start, v);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::linearRamp, false);
sfz::linearRamp<float>(absl::MakeSpan(output), start, v);
REQUIRE(output == expected);
}
@ -510,7 +384,8 @@ TEST_CASE("[Helpers] Linear Ramp (SIMD)")
const float v { fillValue };
std::array<float, 6> output;
std::array<float, 6> expected { start, start + v, start + v + v, start + v + v + v, start + v + v + v + v, start + v + v + v + v + v };
sfz::linearRamp<float, true>(absl::MakeSpan(output), start, v);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::linearRamp, true);
sfz::linearRamp<float>(absl::MakeSpan(output), start, v);
REQUIRE(approxEqual<float>(output, expected));
}
@ -519,8 +394,10 @@ TEST_CASE("[Helpers] Linear Ramp (SIMD vs scalar)")
const float start { 0.0f };
std::vector<float> outputScalar(bigBufferSize);
std::vector<float> outputSIMD(bigBufferSize);
sfz::linearRamp<float, false>(absl::MakeSpan(outputScalar), start, fillValue);
sfz::linearRamp<float, true>(absl::MakeSpan(outputSIMD), start, fillValue);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::linearRamp, false);
sfz::linearRamp<float>(absl::MakeSpan(outputScalar), start, fillValue);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::linearRamp, true);
sfz::linearRamp<float>(absl::MakeSpan(outputSIMD), start, fillValue);
REQUIRE(approxEqual<float>(outputScalar, outputSIMD));
}
@ -529,8 +406,10 @@ TEST_CASE("[Helpers] Linear Ramp unaligned (SIMD vs scalar)")
const float start { 0.0f };
std::vector<float> outputScalar(bigBufferSize);
std::vector<float> outputSIMD(bigBufferSize);
sfz::linearRamp<float, false>(absl::MakeSpan(outputScalar).subspan(1), start, fillValue);
sfz::linearRamp<float, true>(absl::MakeSpan(outputSIMD).subspan(1), start, fillValue);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::linearRamp, false);
sfz::linearRamp<float>(absl::MakeSpan(outputScalar).subspan(1), start, fillValue);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::linearRamp, true);
sfz::linearRamp<float>(absl::MakeSpan(outputSIMD).subspan(1), start, fillValue);
REQUIRE(approxEqual<float>(outputScalar, outputSIMD));
}
@ -540,7 +419,8 @@ TEST_CASE("[Helpers] Multiplicative Ramp")
const float v { fillValue };
std::array<float, 6> output;
std::array<float, 6> expected { start, start * v, start * v * v, start * v * v * v, start * v * v * v * v, start * v * v * v * v * v };
sfz::multiplicativeRamp<float, false>(absl::MakeSpan(output), start, v);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplicativeRamp, false);
sfz::multiplicativeRamp<float>(absl::MakeSpan(output), start, v);
REQUIRE(approxEqual<float>(output, expected));
}
@ -550,7 +430,8 @@ TEST_CASE("[Helpers] Multiplicative Ramp (SIMD)")
const float v { fillValue };
std::array<float, 6> output;
std::array<float, 6> expected { start, start * v, start * v * v, start * v * v * v, start * v * v * v * v, start * v * v * v * v * v };
sfz::multiplicativeRamp<float, true>(absl::MakeSpan(output), start, v);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplicativeRamp, true);
sfz::multiplicativeRamp<float>(absl::MakeSpan(output), start, v);
REQUIRE(approxEqual<float>(output, expected));
}
@ -559,8 +440,10 @@ TEST_CASE("[Helpers] Multiplicative Ramp (SIMD vs scalar)")
const float start { 1.0f };
std::vector<float> outputScalar(bigBufferSize);
std::vector<float> outputSIMD(bigBufferSize);
sfz::multiplicativeRamp<float, false>(absl::MakeSpan(outputScalar), start, fillValue);
sfz::multiplicativeRamp<float, true>(absl::MakeSpan(outputSIMD), start, fillValue);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplicativeRamp, false);
sfz::multiplicativeRamp<float>(absl::MakeSpan(outputScalar), start, fillValue);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplicativeRamp, true);
sfz::multiplicativeRamp<float>(absl::MakeSpan(outputSIMD), start, fillValue);
REQUIRE(approxEqual<float>(outputScalar, outputSIMD));
}
@ -569,8 +452,10 @@ TEST_CASE("[Helpers] Multiplicative Ramp unaligned (SIMD vs scalar)")
const float start { 1.0f };
std::vector<float> outputScalar(bigBufferSize);
std::vector<float> outputSIMD(bigBufferSize);
sfz::multiplicativeRamp<float, false>(absl::MakeSpan(outputScalar).subspan(1), start, fillValue);
sfz::multiplicativeRamp<float, true>(absl::MakeSpan(outputSIMD).subspan(1), start, fillValue);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplicativeRamp, false);
sfz::multiplicativeRamp<float>(absl::MakeSpan(outputScalar).subspan(1), start, fillValue);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplicativeRamp, true);
sfz::multiplicativeRamp<float>(absl::MakeSpan(outputSIMD).subspan(1), start, fillValue);
REQUIRE(approxEqual<float>(outputScalar, outputSIMD));
}
@ -579,7 +464,8 @@ TEST_CASE("[Helpers] Add")
std::array<float, 5> input { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
std::array<float, 5> output { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f };
std::array<float, 5> expected { 2.0f, 3.0f, 4.0f, 5.0f, 6.0f };
sfz::add<float, false>(input, absl::MakeSpan(output));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::add, false);
sfz::add<float>(input, absl::MakeSpan(output));
REQUIRE(output == expected);
}
@ -588,7 +474,8 @@ TEST_CASE("[Helpers] Add (SIMD)")
std::array<float, 5> input { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
std::array<float, 5> output { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f };
std::array<float, 5> expected { 2.0f, 3.0f, 4.0f, 5.0f, 6.0f };
sfz::add<float, true>(input, absl::MakeSpan(output));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::add, true);
sfz::add<float>(input, absl::MakeSpan(output));
REQUIRE(output == expected);
}
@ -601,18 +488,32 @@ TEST_CASE("[Helpers] Add (SIMD vs scalar)")
absl::c_fill(outputScalar, 0.0f);
absl::c_fill(outputSIMD, 0.0f);
sfz::add<float, false>(input, absl::MakeSpan(outputScalar));
sfz::add<float, true>(input, absl::MakeSpan(outputSIMD));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::add, false);
sfz::add<float>(input, absl::MakeSpan(outputScalar));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::add, true);
sfz::add<float>(input, absl::MakeSpan(outputSIMD));
REQUIRE(approxEqual<float>(outputScalar, outputSIMD));
}
TEST_CASE("[Helpers] MultiplyAdd (Scalar)")
{
std::array<float, 5> gain { 0.0f, 0.1f, 0.2f, 0.3f, 0.4f };
std::array<float, 5> input { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
std::array<float, 5> output { 5.0f, 4.0f, 3.0f, 2.0f, 1.0f };
std::array<float, 5> expected { 5.0f, 4.2f, 3.6f, 3.2f, 3.0f };
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyAdd, false);
sfz::multiplyAdd<float>(gain, input, absl::MakeSpan(output));
REQUIRE(output == expected);
}
TEST_CASE("[Helpers] MultiplyAdd (SIMD)")
{
std::array<float, 5> gain { 0.0f, 0.1f, 0.2f, 0.3f, 0.4f };
std::array<float, 5> input { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
std::array<float, 5> output { 5.0f, 4.0f, 3.0f, 2.0f, 1.0f };
std::array<float, 5> expected { 5.0f, 4.2f, 3.6f, 3.2f, 3.0f };
sfz::multiplyAdd<float, true>(gain, input, absl::MakeSpan(output));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyAdd, true);
sfz::multiplyAdd<float>(gain, input, absl::MakeSpan(output));
REQUIRE(output == expected);
}
@ -627,18 +528,32 @@ TEST_CASE("[Helpers] MultiplyAdd (SIMD vs scalar)")
absl::c_iota(outputScalar, 0.0f);
absl::c_iota(outputSIMD, 0.0f);
sfz::multiplyAdd<float, false>(gain, input, absl::MakeSpan(outputScalar));
sfz::multiplyAdd<float, true>(gain, input, absl::MakeSpan(outputSIMD));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyAdd, false);
sfz::multiplyAdd<float>(gain, input, absl::MakeSpan(outputScalar));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyAdd, true);
sfz::multiplyAdd<float>(gain, input, absl::MakeSpan(outputSIMD));
REQUIRE(approxEqual<float>(outputScalar, outputSIMD));
}
TEST_CASE("[Helpers] MultiplyAdd fixed gain (Scalar)")
{
float gain = 0.3f;
std::array<float, 5> input { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
std::array<float, 5> output { 5.0f, 4.0f, 3.0f, 2.0f, 1.0f };
std::array<float, 5> expected { 5.3f, 4.6f, 3.9f, 3.2f, 2.5f };
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyAdd1, false);
sfz::multiplyAdd1<float>(gain, input, absl::MakeSpan(output));
REQUIRE(output == expected);
}
TEST_CASE("[Helpers] MultiplyAdd fixed gain (SIMD)")
{
float gain = 0.3f;
std::array<float, 5> input { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
std::array<float, 5> output { 5.0f, 4.0f, 3.0f, 2.0f, 1.0f };
std::array<float, 5> expected { 5.3f, 4.6f, 3.9f, 3.2f, 2.5f };
sfz::multiplyAdd<float, true>(gain, input, absl::MakeSpan(output));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyAdd1, true);
sfz::multiplyAdd1<float>(gain, input, absl::MakeSpan(output));
REQUIRE(output == expected);
}
@ -652,8 +567,10 @@ TEST_CASE("[Helpers] MultiplyAdd fixed gain (SIMD vs scalar)")
absl::c_iota(outputScalar, 0.0f);
absl::c_iota(outputSIMD, 0.0f);
sfz::multiplyAdd<float, false>(gain, input, absl::MakeSpan(outputScalar));
sfz::multiplyAdd<float, true>(gain, input, absl::MakeSpan(outputSIMD));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyAdd1, false);
sfz::multiplyAdd1<float>(gain, input, absl::MakeSpan(outputScalar));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::multiplyAdd1, true);
sfz::multiplyAdd1<float>(gain, input, absl::MakeSpan(outputSIMD));
REQUIRE(approxEqual<float>(outputScalar, outputSIMD));
}
@ -662,7 +579,7 @@ TEST_CASE("[Helpers] Subtract")
std::array<float, 5> input { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
std::array<float, 5> output { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f };
std::array<float, 5> expected { 0.0f, -1.0f, -2.0f, -3.0f, -4.0f };
sfz::subtract<float, false>(input, absl::MakeSpan(output));
sfz::subtract<float>(input, absl::MakeSpan(output));
REQUIRE(output == expected);
}
@ -670,7 +587,8 @@ TEST_CASE("[Helpers] Subtract 2")
{
std::array<float, 5> output { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
std::array<float, 5> expected { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f };
sfz::subtract<float, false>(1.0f, absl::MakeSpan(output));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::subtract1, false);
sfz::subtract1<float>(1.0f, absl::MakeSpan(output));
REQUIRE(output == expected);
}
@ -680,7 +598,8 @@ TEST_CASE("[Helpers] Subtract (SIMD)")
std::array<float, 5> input { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
std::array<float, 5> output { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f };
std::array<float, 5> expected { 0.0f, -1.0f, -2.0f, -3.0f, -4.0f };
sfz::subtract<float, true>(input, absl::MakeSpan(output));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::subtract, true);
sfz::subtract<float>(input, absl::MakeSpan(output));
REQUIRE(output == expected);
}
@ -693,8 +612,10 @@ TEST_CASE("[Helpers] Subtract (SIMD vs scalar)")
absl::c_fill(outputScalar, 0.0f);
absl::c_fill(outputSIMD, 0.0f);
sfz::subtract<float, false>(input, absl::MakeSpan(outputScalar));
sfz::subtract<float, true>(input, absl::MakeSpan(outputSIMD));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::subtract, false);
sfz::subtract<float>(input, absl::MakeSpan(outputScalar));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::subtract, true);
sfz::subtract<float>(input, absl::MakeSpan(outputSIMD));
REQUIRE(approxEqual<float>(outputScalar, outputSIMD));
}
@ -705,8 +626,10 @@ TEST_CASE("[Helpers] Subtract 2 (SIMD vs scalar)")
absl::c_iota(outputScalar, 0.0f);
absl::c_iota(outputSIMD, 0.0f);
sfz::subtract<float, false>(1.2f, absl::MakeSpan(outputScalar));
sfz::subtract<float, true>(1.2f, absl::MakeSpan(outputSIMD));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::subtract1, false);
sfz::subtract1<float>(1.2f, absl::MakeSpan(outputScalar));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::subtract1, true);
sfz::subtract1<float>(1.2f, absl::MakeSpan(outputSIMD));
REQUIRE(approxEqual<float>(outputScalar, outputSIMD));
}
@ -714,7 +637,8 @@ TEST_CASE("[Helpers] copy")
{
std::array<float, 5> input { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
std::array<float, 5> output { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f };
sfz::copy<float, false>(input, absl::MakeSpan(output));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::copy, false);
sfz::copy<float>(input, absl::MakeSpan(output));
REQUIRE(output == input);
}
@ -722,7 +646,8 @@ TEST_CASE("[Helpers] copy (SIMD)")
{
std::array<float, 5> input { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
std::array<float, 5> output { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f };
sfz::copy<float, true>(input, absl::MakeSpan(output));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::copy, true);
sfz::copy<float>(input, absl::MakeSpan(output));
REQUIRE(output == input);
}
@ -735,37 +660,51 @@ TEST_CASE("[Helpers] copy (SIMD vs scalar)")
absl::c_fill(outputScalar, 0.0f);
absl::c_fill(outputSIMD, 0.0f);
sfz::add<float, false>(input, absl::MakeSpan(outputScalar));
sfz::add<float, true>(input, absl::MakeSpan(outputSIMD));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::copy, false);
sfz::copy<float>(input, absl::MakeSpan(outputScalar));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::copy, true);
sfz::copy<float>(input, absl::MakeSpan(outputSIMD));
REQUIRE(approxEqual<float>(outputScalar, outputSIMD));
}
TEST_CASE("[Helpers] Mean")
{
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 };
REQUIRE(sfz::mean<float, false>(input) == 5.5f);
REQUIRE(sfz::mean<float, true>(input) == 5.5f);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::mean, false);
REQUIRE(sfz::mean<float>(input) == 5.5f);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::mean, true);
REQUIRE(sfz::mean<float>(input) == 5.5f);
}
TEST_CASE("[Helpers] Mean (SIMD vs scalar)")
{
std::vector<float> input(bigBufferSize);
absl::c_iota(input, 0.0f);
REQUIRE(sfz::mean<float, false>(input) == Approx(sfz::mean<float, true>(input)).margin(0.001));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::mean, false);
auto scalarResult = sfz::mean<float>(input);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::mean, true);
auto simdResult = sfz::mean<float>(input);
REQUIRE( scalarResult == Approx(simdResult).margin(1e-3) );
}
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 };
REQUIRE(sfz::meanSquared<float, false>(input) == 38.5f);
REQUIRE(sfz::meanSquared<float, true>(input) == 38.5f);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::meanSquared, false);
REQUIRE(sfz::meanSquared<float>(input) == 38.5f);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::meanSquared, true);
REQUIRE(sfz::meanSquared<float>(input) == 38.5f);
}
TEST_CASE("[Helpers] Mean Squared (SIMD vs scalar)")
{
std::vector<float> input(medBufferSize);
absl::c_iota(input, 0.0f);
REQUIRE(sfz::meanSquared<float, false>(input) == sfz::meanSquared<float, true>(input));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::meanSquared, false);
auto scalarResult = sfz::meanSquared<float>(input);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::meanSquared, true);
auto simdResult = sfz::meanSquared<float>(input);
REQUIRE( scalarResult == Approx(simdResult).margin(1e-3) );
}
TEST_CASE("[Helpers] Cumulative sum")
@ -773,7 +712,8 @@ TEST_CASE("[Helpers] Cumulative sum")
std::array<float, 6> input { 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f }; // 1.1 2.3 3.6 5.0f 6.5 8.1
std::array<float, 6> output;
std::array<float, 6> expected { 1.1f, 2.3f, 3.6f, 5.0f, 6.5f, 8.1f };
sfz::cumsum<float, false>(input, absl::MakeSpan(output));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::cumsum, false);
sfz::cumsum<float>(input, absl::MakeSpan(output));
REQUIRE(approxEqual<float>(output, expected));
}
@ -782,9 +722,12 @@ TEST_CASE("[Helpers] Cumulative sum (SIMD vs Scalar)")
std::vector<float> input(bigBufferSize);
std::vector<float> outputScalar(bigBufferSize);
std::vector<float> outputSIMD(bigBufferSize);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::linearRamp, true);
sfz::linearRamp<float>(absl::MakeSpan(input), 0.0f, 0.1f);
sfz::cumsum<float, false>(input, absl::MakeSpan(outputScalar));
sfz::cumsum<float, true>(input, absl::MakeSpan(outputSIMD));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::cumsum, false);
sfz::cumsum<float>(input, absl::MakeSpan(outputScalar));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::cumsum, true);
sfz::cumsum<float>(input, absl::MakeSpan(outputSIMD));
REQUIRE(approxEqual<float>(outputScalar, outputSIMD));
}
@ -793,7 +736,8 @@ TEST_CASE("[Helpers] Diff")
std::array<float, 6> input { 1.1f, 2.3f, 3.6f, 5.0f, 6.5f, 8.1f };
std::array<float, 6> output;
std::array<float, 6> expected { 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f };
sfz::diff<float, false>(input, absl::MakeSpan(output));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::diff, false);
sfz::diff<float>(input, absl::MakeSpan(output));
REQUIRE(approxEqual<float>(output, expected));
}
@ -802,9 +746,12 @@ TEST_CASE("[Helpers] Diff (SIMD vs Scalar)")
std::vector<float> input(bigBufferSize);
std::vector<float> outputScalar(bigBufferSize);
std::vector<float> outputSIMD(bigBufferSize);
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::linearRamp, true);
sfz::linearRamp<float>(absl::MakeSpan(input), 0.0f, 0.1f);
sfz::diff<float, false>(input, absl::MakeSpan(outputScalar));
sfz::diff<float, true>(input, absl::MakeSpan(outputSIMD));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::diff, false);
sfz::diff<float>(input, absl::MakeSpan(outputScalar));
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::diff, true);
sfz::diff<float>(input, absl::MakeSpan(outputSIMD));
REQUIRE(approxEqual<float>(outputScalar, outputSIMD));
}
@ -817,21 +764,21 @@ TEST_CASE("[Helpers] Pan Scalar")
SECTION("Pan = 0")
{
std::array<float, 1> pan { 0.0f };
sfz::pan<float, false>(pan, left, right);
sfz::pan(pan, left, right);
REQUIRE(left[0] == Approx(0.70711f).margin(0.001f));
REQUIRE(right[0] == Approx(0.70711f).margin(0.001f));
}
SECTION("Pan = 1")
{
std::array<float, 1> pan { 1.0f };
sfz::pan<float, false>(pan, left, right);
sfz::pan(pan, left, right);
REQUIRE(left[0] == Approx(0.0f).margin(0.001f));
REQUIRE(right[0] == Approx(1.0f).margin(0.001f));
}
SECTION("Pan = -1")
{
std::array<float, 1> pan { -1.0f };
sfz::pan<float, false>(pan, left, right);
sfz::pan(pan, left, right);
REQUIRE(left[0] == Approx(1.0f).margin(0.001f));
REQUIRE(right[0] == Approx(0.0f).margin(0.001f));
}
@ -846,21 +793,21 @@ TEST_CASE("[Helpers] Width Scalar")
SECTION("width = 1")
{
std::array<float, 1> width { 1.0f };
sfz::width<float, false>(width, left, right);
sfz::width(width, left, right);
REQUIRE(left[0] == Approx(1.0f).margin(0.001f));
REQUIRE(right[0] == Approx(1.0f).margin(0.001f));
}
SECTION("width = 0")
{
std::array<float, 1> width { 0.0f };
sfz::width<float, false>(width, left, right);
sfz::width(width, left, right);
REQUIRE(left[0] == Approx(1.414f).margin(0.001f));
REQUIRE(right[0] == Approx(1.414f).margin(0.001f));
}
SECTION("width = -1")
{
std::array<float, 1> width { -1.0f };
sfz::width<float, false>(width, left, right);
sfz::width(width, left, right);
REQUIRE(left[0] == Approx(1.0f).margin(0.001f));
REQUIRE(right[0] == Approx(1.0f).margin(0.001f));
}