From d120c2bc9a5ecac373ef61ca72339936f894d75e Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Tue, 14 Apr 2020 17:59:12 +0200 Subject: [PATCH] Use rint, rounding modes, and corrected tests --- src/sfizz/MathHelpers.h | 92 ++++++++++++++++++++++++------------- src/sfizz/ModifierHelpers.h | 14 ++---- tests/EventEnvelopesT.cpp | 44 +++++++++--------- 3 files changed, 87 insertions(+), 63 deletions(-) diff --git a/src/sfizz/MathHelpers.h b/src/sfizz/MathHelpers.h index 9fa734f1..a70446ca 100644 --- a/src/sfizz/MathHelpers.h +++ b/src/sfizz/MathHelpers.h @@ -15,26 +15,27 @@ #include #include #include +#include -template +template constexpr T max(T op1, T op2) { return op1 > op2 ? op1 : op2; } -template +template constexpr T max(T op1, Args... rest) { return max(op1, max(rest...)); } -template +template constexpr T min(T op1, T op2) { return op1 > op2 ? op2 : op1; } -template +template constexpr T min(T op1, Args... rest) { return min(op1, min(rest...)); @@ -46,7 +47,7 @@ constexpr T min(T op1, Args... rest) * @param op * @return T */ -template +template constexpr T power2(T in) { return in * in; @@ -111,8 +112,8 @@ constexpr Type mag2db(Type in) * */ namespace Random { - static std::random_device randomDevice; - static std::minstd_rand randomGenerator { randomDevice() }; +static std::random_device randomDevice; +static std::minstd_rand randomGenerator { randomDevice() }; } // namespace Random /** @@ -135,42 +136,42 @@ inline float midiNoteFrequency(const int noteNumber) * @param hi * @return T */ -template -constexpr T clamp( T v, T lo, T hi ) +template +constexpr T clamp(T v, T lo, T hi) { return max(min(v, hi), lo); } -template +template inline CXX14_CONSTEXPR void incrementAll(T& only) { only += Increment; } -template +template inline CXX14_CONSTEXPR void incrementAll(T& first, Args&... rest) { first += Increment; incrementAll(rest...); } -template +template constexpr ValueType linearInterpolation(ValueType left, ValueType right, ValueType leftCoeff, ValueType rightCoeff) { return left * leftCoeff + right * rightCoeff; } -template +template constexpr Type pi() { return static_cast(3.141592653589793238462643383279502884); }; -template +template constexpr Type twoPi() { return pi() * 2; }; -template +template constexpr Type piTwo() { return pi() / 2; }; -template +template constexpr Type piFour() { return pi() / 4; }; -template +template constexpr Type sqrtTwo() { return static_cast(1.414213562373095048801688724209698078569671875376948073176); }; -template +template constexpr Type sqrtTwoInv() { return static_cast(0.707106781186547524400844362104849039284835937688474036588); }; /** @@ -205,23 +206,23 @@ inline Fraction::operator float() const noexcept template struct FP_traits; -template <> struct FP_traits -{ +template <> +struct FP_traits { typedef double type; typedef uint64_t same_size_int; static_assert(sizeof(type) == sizeof(same_size_int), - "Unexpected size of floating point type"); + "Unexpected size of floating point type"); static constexpr int e_bits = 11; static constexpr int m_bits = 52; static constexpr int e_offset = -1023; }; -template <> struct FP_traits -{ +template <> +struct FP_traits { typedef float type; typedef uint32_t same_size_int; static_assert(sizeof(type) == sizeof(same_size_int), - "Unexpected size of floating point type"); + "Unexpected size of floating point type"); static constexpr int e_bits = 8; static constexpr int m_bits = 23; static constexpr int e_offset = -127; @@ -237,7 +238,10 @@ template inline bool fp_sign(F x) { typedef FP_traits T; - union { F real; typename T::same_size_int integer; } u; + union { + F real; + typename T::same_size_int integer; + } u; u.real = x; return ((u.integer >> (T::e_bits + T::m_bits)) & 1) != 0; } @@ -254,7 +258,10 @@ template inline int fp_exponent(F x) { typedef FP_traits T; - union { F real; typename T::same_size_int integer; } u; + union { + F real; + typename T::same_size_int integer; + } u; u.real = x; int ex = (u.integer >> T::m_bits) & ((1u << T::e_bits) - 1); return ex + T::e_offset; @@ -269,10 +276,13 @@ template inline Fraction fp_mantissa(F x) { typedef FP_traits T; - union { F real; typename T::same_size_int integer; } u; + union { + F real; + typename T::same_size_int integer; + } u; u.real = x; Fraction f; - f.den = uint64_t{1} << T::m_bits; + f.den = uint64_t { 1 } << T::m_bits; f.num = u.integer & (f.den - 1); return f; } @@ -287,10 +297,11 @@ inline F fp_from_parts(bool sgn, int ex, uint64_t mant) { typedef FP_traits T; typedef typename T::same_size_int I; - union { F real; I integer; } u; - u.integer = mant | - (static_cast(ex - T::e_offset) << T::m_bits) | - (static_cast(sgn) << (T::e_bits + T::m_bits)); + union { + F real; + I integer; + } u; + u.integer = mant | (static_cast(ex - T::e_offset) << T::m_bits) | (static_cast(sgn) << (T::e_bits + T::m_bits)); return u.real; } @@ -328,3 +339,20 @@ bool isValidAudio(absl::Span span) return true; } + +class ScopedRoundingMode { +public: + ScopedRoundingMode() = delete; + ScopedRoundingMode(int newRoundingMode) + : savedFloatMode(std::fegetround()) + { + std::fesetround(newRoundingMode); + } + ~ScopedRoundingMode() + { + std::fesetround(savedFloatMode); + } + +private: + const int savedFloatMode; +}; diff --git a/src/sfizz/ModifierHelpers.h b/src/sfizz/ModifierHelpers.h index a476f6ae..8e01610a 100644 --- a/src/sfizz/ModifierHelpers.h +++ b/src/sfizz/ModifierHelpers.h @@ -5,6 +5,7 @@ #include "SfzHelpers.h" #include "Resources.h" #include "absl/types/span.h" + namespace sfz { /** @@ -146,6 +147,8 @@ void multiplicativeEnvelope(const EventVector& events, absl::Span envelop ASSERT(events[0].delay == 0); ASSERT(step != 0.0f); + ScopedRoundingMode roundingMode { Round ? FE_TONEAREST : FE_TOWARDZERO }; + if (envelope.size() == 0) return; const auto maxDelay = static_cast(envelope.size() - 1); @@ -156,14 +159,7 @@ void multiplicativeEnvelope(const EventVector& events, absl::Span envelop // log q log q // and log(b)\log(q) is between 0 and 1. auto quantize = [logStep](float value) -> float { - IF_CONSTEXPR(Round) - { - return std::exp(logStep * std::round(std::log(value) / logStep)); - } - else - { - return std::exp(logStep * std::trunc(std::log(value) / logStep)); - } + return std::exp(logStep * std::rint(std::log(value) / logStep)); }; auto lastValue = quantize(lambda(events[0].value)); @@ -182,7 +178,7 @@ void multiplicativeEnvelope(const EventVector& events, absl::Span envelop const auto numSteps = std::round(std::log(difference) / logStep); const auto stepLength = static_cast(length / numSteps); - for (int i = 0; i < numSteps; ++i) { + for (int i = 0; i < static_cast(numSteps); ++i) { fill(envelope.subspan(lastDelay, stepLength), lastValue); lastValue = nextValue > lastValue ? lastValue * step : lastValue / step; lastDelay += stepLength; diff --git a/tests/EventEnvelopesT.cpp b/tests/EventEnvelopesT.cpp index 551cc807..8c7e2d55 100644 --- a/tests/EventEnvelopesT.cpp +++ b/tests/EventEnvelopesT.cpp @@ -42,13 +42,13 @@ TEST_CASE("[Envelopes] Empty") std::array expected { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }; std::array expectedMul { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; linearEnvelope(events, absl::MakeSpan(output), idModifier); - REQUIRE(output == expected); + REQUIRE(approxEqual(output, expected)); linearEnvelope(events, absl::MakeSpan(output), idModifier, 1.0f); - REQUIRE(output == expected); + REQUIRE(approxEqual(output, expected)); multiplicativeEnvelope(events, absl::MakeSpan(output), expModifier); - REQUIRE(output == expectedMul); + REQUIRE(approxEqual(output, expectedMul)); multiplicativeEnvelope(events, absl::MakeSpan(output), expModifier, 2.0f); - REQUIRE(output == expectedMul); + REQUIRE(approxEqual(output, expectedMul)); } TEST_CASE("[Envelopes] Linear basic") @@ -60,7 +60,7 @@ TEST_CASE("[Envelopes] Linear basic") std::array output; std::array expected { 0.0f, 0.25f, 0.5f, 0.75f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; linearEnvelope(events, absl::MakeSpan(output), idModifier); - REQUIRE(output == expected); + REQUIRE(approxEqual(output, expected)); } TEST_CASE("[LinearEnvelope] 2 events, close") @@ -73,7 +73,7 @@ TEST_CASE("[LinearEnvelope] 2 events, close") std::array output; std::array expected { 0.0f, 0.25f, 0.5f, 0.75f, 1.0f, 2.0f, 2.0f, 2.0f, 2.0f }; linearEnvelope(events, absl::MakeSpan(output), idModifier); - REQUIRE(output == expected); + REQUIRE(approxEqual(output, expected)); } TEST_CASE("[LinearEnvelope] 2 events, far") @@ -86,7 +86,7 @@ TEST_CASE("[LinearEnvelope] 2 events, far") std::array output; std::array expected { 0.0f, 0.5f, 1.0f, 1.25f, 1.5f, 1.75f, 2.0f, 2.0f, 2.0f }; linearEnvelope(events, absl::MakeSpan(output), idModifier); - REQUIRE(output == expected); + REQUIRE(approxEqual(output, expected)); } TEST_CASE("[LinearEnvelope] 3 events, out of block") @@ -100,7 +100,7 @@ TEST_CASE("[LinearEnvelope] 3 events, out of block") std::array output; std::array expected { 0.0f, 0.5f, 1.0f, 1.25f, 1.5f, 1.75f, 2.0f, 2.5f, 3.0f }; linearEnvelope(events, absl::MakeSpan(output), idModifier); - REQUIRE(output == expected); + REQUIRE(approxEqual(output, expected)); } TEST_CASE("[LinearEnvelope] 2 events, function") @@ -113,7 +113,7 @@ TEST_CASE("[LinearEnvelope] 2 events, function") std::array output; std::array expected { 0.0f, 1.0f, 2.0f, 2.5f, 3.0f, 3.5f, 4.0f, 4.0f, 4.0f }; linearEnvelope(events, absl::MakeSpan(output), twiceModifier); - REQUIRE(output == expected); + REQUIRE(approxEqual(output, expected)); } TEST_CASE("[LinearEnvelope] Get quantized") @@ -126,7 +126,7 @@ TEST_CASE("[LinearEnvelope] Get quantized") std::array output; std::array expected { 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 2.0f, 2.0f }; linearEnvelope(events, absl::MakeSpan(output), idModifier, 1.0f); - REQUIRE(output == expected); + REQUIRE(approxEqual(output, expected)); } TEST_CASE("[LinearEnvelope] Get quantized with unquantized targets") @@ -139,7 +139,7 @@ TEST_CASE("[LinearEnvelope] Get quantized with unquantized targets") std::array output; std::array expected { 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; linearEnvelope(events, absl::MakeSpan(output), idModifier, 1.0f); - REQUIRE(output == expected); + REQUIRE(approxEqual(output, expected)); } TEST_CASE("[LinearEnvelope] Get quantized with 2 steps") @@ -152,7 +152,7 @@ TEST_CASE("[LinearEnvelope] Get quantized with 2 steps") std::array output; std::array expected { 0.0f, 0.0f, 1.0f, 1.0f, 2.0f, 2.0f, 3.0f, 3.0f }; linearEnvelope(events, absl::MakeSpan(output), idModifier, 1.0f); - REQUIRE(output == expected); + REQUIRE(approxEqual(output, expected)); } TEST_CASE("[LinearEnvelope] Get quantized with 2 steps and an unquantized out of block step") @@ -166,7 +166,7 @@ TEST_CASE("[LinearEnvelope] Get quantized with 2 steps and an unquantized out of std::array output; std::array expected { 0.0f, 0.0f, 1.0f, 1.0f, 2.0f, 2.0f, 3.0f, 4.0f }; linearEnvelope(events, absl::MakeSpan(output), idModifier, 1.0f); - REQUIRE(output == expected); + REQUIRE(approxEqual(output, expected)); } @@ -180,7 +180,7 @@ TEST_CASE("[LinearEnvelope] Going down quantized with 2 steps") std::array output; std::array expected { 3.0f, 3.0f, 2.0f, 2.0f, 1.0f, 1.0f, 0.0f, 0.0f }; linearEnvelope(events, absl::MakeSpan(output), idModifier, 1.0f); - REQUIRE(output == expected); + REQUIRE(approxEqual(output, expected)); } TEST_CASE("[MultiplicativeEnvelope] Basic event") @@ -231,7 +231,7 @@ TEST_CASE("[MultiplicativeEnvelope] Get quantized with 2 steps") std::array output; std::array expected { 1.0f, 1.0f, 2.0f, 2.0f, 2.0f, 2.0f, 4.0f, 4.0f }; multiplicativeEnvelope(events, absl::MakeSpan(output), idModifier, 2.0f); - REQUIRE(output == expected); + REQUIRE(approxEqual(output, expected)); } TEST_CASE("[MultiplicativeEnvelope] Get quantized with an unquantized out of range step") @@ -245,7 +245,7 @@ TEST_CASE("[MultiplicativeEnvelope] Get quantized with an unquantized out of ran std::array output; std::array expected { 1.0f, 1.0f, 2.0f, 2.0f, 2.0f, 2.0f, 4.0f, 8.0f }; multiplicativeEnvelope(events, absl::MakeSpan(output), idModifier, 2.0f); - REQUIRE(output == expected); + REQUIRE(approxEqual(output, expected)); } TEST_CASE("[MultiplicativeEnvelope] Going down quantized with 2 steps") @@ -258,7 +258,7 @@ TEST_CASE("[MultiplicativeEnvelope] Going down quantized with 2 steps") std::array output; std::array expected { 4.0f, 4.0f, 2.0f, 2.0f, 1.0f, 1.0f, 0.5f, 0.5f }; multiplicativeEnvelope(events, absl::MakeSpan(output), idModifier, 2.0f); - REQUIRE(output == expected); + REQUIRE(approxEqual(output, expected)); } TEST_CASE("[MultiplicativeEnvelope] Get quantized with unclean events") @@ -271,7 +271,7 @@ TEST_CASE("[MultiplicativeEnvelope] Get quantized with unclean events") std::array output; std::array expected { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 2.0f, 2.0f }; multiplicativeEnvelope(events, absl::MakeSpan(output), idModifier, 2.0f); - REQUIRE(output == expected); + REQUIRE(approxEqual(output, expected)); } TEST_CASE("[linearModifiers] Compare with envelopes") @@ -284,7 +284,7 @@ TEST_CASE("[linearModifiers] Compare with envelopes") ccData.data.value = 100.0f; resources.midiState.ccEvent(5, 20, 0.1); - resources.midiState.ccEvent(10, 20, 0.2); + resources.midiState.ccEvent(10, 20, 0.8); std::array output; std::array envelope; @@ -312,7 +312,7 @@ TEST_CASE("[linearModifiers] Compare with envelopes") ccData.data.curve = 2; ccData.data.value = 20.0f; - ccData.data.step = 2.0f; + ccData.data.step = 2.5f; linearEnvelope(resources.midiState.getCCEvents(20), absl::MakeSpan(envelope), [&ccData](float x) { return ccData.data.value * (1 - x); }, ccData.data.step); @@ -330,7 +330,7 @@ TEST_CASE("[multiplicativeModifiers] Compare with envelopes") ccData.data.value = 100.0f; resources.midiState.ccEvent(5, 20, 0.1); - resources.midiState.ccEvent(10, 20, 0.8); + resources.midiState.ccEvent(15, 20, 0.8); std::array output; std::array envelope; @@ -364,7 +364,7 @@ TEST_CASE("[multiplicativeModifiers] Compare with envelopes") ccData.data.curve = 2; ccData.data.value = 20.0f; - ccData.data.step = 2.0f; + ccData.data.step = 2.5f; multiplicativeEnvelope(resources.midiState.getCCEvents(20), absl::MakeSpan(envelope), [&ccData](float x) { return db2mag(ccData.data.value * (1 - x)); }, db2mag(ccData.data.step) );