From f20e74286aaba57bd6cc874a398efacb8a46fd91 Mon Sep 17 00:00:00 2001 From: jatinchowdhury18 Date: Fri, 4 Aug 2023 17:24:28 -0700 Subject: [PATCH] Updating for JUCE 7.0.6 (#449) * Updating for JUCE 7.0.6 * Fixing float-equality stuff * Apply clang-format * More fixing * Trying to fix more failing tests * Trying to fix failing tests * Fixing float-equal in GainReductionMeter --------- Co-authored-by: github-actions[bot] --- .github/workflows/bench.yml | 2 +- .github/workflows/build-arm-mac.yml | 2 +- .github/workflows/code-quality.yml | 2 +- .github/workflows/run-tests.yml | 2 +- examples/CMakeLists.txt | 4 +- .../JUCEHelpers/juce_MathsFunctions.h | 241 +++++++++--------- .../JUCEHelpers/juce_SmoothedValue.h | 2 +- modules/common/chowdsp_core/chowdsp_core.h | 16 ++ .../chowdsp_reflection/chowdsp_reflection.h | 2 +- .../chowdsp_CompressorGainComputer.h | 6 +- .../Other/chowdsp_UIToAudioPipeline.h | 4 + .../Processors/chowdsp_BypassProcessor.cpp | 6 +- .../Processors/chowdsp_BypassProcessor.h | 4 +- .../Processors/chowdsp_Gain.h | 7 +- .../chowdsp_EllipticFilter.h | 6 +- .../Utils/chowdsp_CoefficientCalculators.h | 2 + .../chowdsp_math/Math/chowdsp_BufferMath.cpp | 2 +- .../Math/chowdsp_JacobiElliptic.h | 6 +- .../chowdsp_math/Math/chowdsp_Polylogarithm.h | 6 +- .../dsp/chowdsp_math/Math/chowdsp_PowApprox.h | 3 + modules/dsp/chowdsp_math/chowdsp_math.h | 2 +- modules/dsp/chowdsp_simd/chowdsp_simd.h | 1 + .../chowdsp_AdditiveOscillator.cpp | 2 +- .../Oscillators/chowdsp_SawtoothWave.cpp | 2 + .../Oscillators/chowdsp_TriangleWave.cpp | 2 + .../GuiItems/chowdsp_PresetsItem.h | 2 +- .../PluginComponents/chowdsp_InfoComp.cpp | 2 +- .../PluginComponents/chowdsp_TitleComp.cpp | 4 +- .../Presets/chowdsp_PresetsComp.cpp | 18 +- .../chowdsp_GainReductionMeter.cpp | 4 +- modules/music/chowdsp_rhythm/chowdsp_rhythm.h | 2 +- .../chowdsp_ForwardingParameter.cpp | 2 +- .../Backend/chowdsp_ParameterListeners.cpp | 2 +- .../Backend/chowdsp_StateValue.h | 2 + .../Frontend/chowdsp_ParameterAttachment.cpp | 17 +- .../chowdsp_core_test/AtomicHelpersTest.cpp | 4 +- .../chowdsp_core_test/ScopedValueTest.cpp | 6 +- .../BucketArrayTest.cpp | 5 +- .../DoubleBufferTest.cpp | 8 +- .../LocalPointerTest.cpp | 8 +- .../SmallVectorTest.cpp | 36 +-- .../TupleHelpersTest.cpp | 4 +- .../chowdsp_units_test/TimeUnitsTest.cpp | 10 +- .../BufferIteratorsTest.cpp | 5 +- .../chowdsp_buffers_test/BufferSpanTest.cpp | 4 +- .../chowdsp_buffers_test/BufferTest.cpp | 5 +- .../chowdsp_buffers_test/BufferViewTest.cpp | 5 +- .../JUCEBufferViewTest.cpp | 4 +- .../SIMDBufferCopyTest.cpp | 19 +- .../SmoothedBufferValueTest.cpp | 6 +- .../chowdsp_dsp_juce_test/DiffuserTest.cpp | 2 +- .../data_structures_tests/BufferMathTest.cpp | 8 +- .../SmoothedBufferValueTest.cpp | 2 +- .../source_tests/RepitchedSourceTest.cpp | 2 +- .../chowdsp_dsp_utils_test/GainTest.cpp | 4 +- .../LevelDetectorTest.cpp | 6 +- .../chowdsp_dsp_utils_test/PannerTest.cpp | 8 +- .../chowdsp_dsp_utils_test/TunerTest.cpp | 2 +- .../ConformalMapsTest.cpp | 2 +- .../LinearTransformsTest.cpp | 16 +- .../FloatVectorOperationsTest.cpp | 2 +- .../chowdsp_math_test/MatrixOpsTest.cpp | 30 +-- .../chowdsp_math_test/PolynomialsTest.cpp | 36 +-- .../dsp_tests/chowdsp_math_test/RatioTest.cpp | 14 +- .../ModalFilterTest.cpp | 6 +- .../SIMDSmoothedValueTest.cpp | 6 +- .../chowdsp_simd_test/SIMDSpecialMathTest.cpp | 8 +- .../chowdsp_sources_test/PolygonalTest.cpp | 14 +- .../chowdsp_sources_test/SawtoothTest.cpp | 6 +- .../chowdsp_sources_test/SquareTest.cpp | 6 +- .../chowdsp_sources_test/TriangleTest.cpp | 6 +- .../EqualizerPlotTest.cpp | 4 +- .../GenericFilterPlotTest.cpp | 8 +- .../Images/eq_response_plot.png | Bin 16657 -> 15748 bytes .../Images/freq_grid_plot.png | Bin 3553 -> 3473 bytes .../Images/generic_filter_plot.png | Bin 6304 -> 6591 bytes .../Images/waveshaper_plot.png | Bin 4429 -> 5189 bytes .../SpectrumPlotBaseTest.cpp | 16 +- .../ForwardingParameterTest.cpp | 26 +- .../ParamModulationTest.cpp | 12 +- .../ParamStringsTest.cpp | 20 +- .../RhythmParameterTest.cpp | 2 +- .../PluginBaseTest.cpp | 2 +- .../ParameterAttachmentsTest.cpp | 19 +- .../StateListenersTest.cpp | 4 +- .../StateSerializationTest.cpp | 16 +- .../VersionStreamingTest.cpp | 8 +- .../AudioFileSaveLoadHelperTest.cpp | 4 +- .../PluginLoggerTest.cpp | 1 + .../TweaksFileTest.cpp | 12 +- .../ClipboardInterfaceTest.cpp | 10 +- .../PresetManagerTest.cpp | 35 ++- .../chowdsp_presets_v2_test/PresetTest.cpp | 2 +- 93 files changed, 496 insertions(+), 409 deletions(-) diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index 13051fc80..b1543dea5 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -47,7 +47,7 @@ jobs: - name: Set up environment working-directory: ${{github.workspace}} - run: git clone --depth 1 --branch 7.0.1 https://github.com/juce-framework/JUCE.git + run: git clone --depth 1 --branch 7.0.6 https://github.com/juce-framework/JUCE.git - name: Configure working-directory: ${{env.WORK_DIR}} diff --git a/.github/workflows/build-arm-mac.yml b/.github/workflows/build-arm-mac.yml index 962c46465..0965e8571 100644 --- a/.github/workflows/build-arm-mac.yml +++ b/.github/workflows/build-arm-mac.yml @@ -33,7 +33,7 @@ jobs: - name: Set up environment working-directory: ${{github.workspace}} run: | - git clone --depth 1 --branch 7.0.1 https://github.com/juce-framework/JUCE.git + git clone --depth 1 --branch 7.0.6 https://github.com/juce-framework/JUCE.git cd JUCE_modules git clone --single-branch --branch chowdsp https://github.com/Chowdhury-DSP/foleys_gui_magic.git git clone --single-branch --branch main --recursive https://github.com/free-audio/clap-juce-extensions.git diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml index 09f356fef..a4007a8cc 100644 --- a/.github/workflows/code-quality.yml +++ b/.github/workflows/code-quality.yml @@ -43,7 +43,7 @@ jobs: - name: Set up environment working-directory: ${{github.workspace}} run: | - git clone --depth 1 --branch 7.0.1 https://github.com/juce-framework/JUCE.git + git clone --depth 1 --branch 7.0.6 https://github.com/juce-framework/JUCE.git cd JUCE_modules git clone --single-branch --branch chowdsp https://github.com/Chowdhury-DSP/foleys_gui_magic.git git clone --single-branch --branch main --recursive https://github.com/free-audio/clap-juce-extensions.git diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 50f32ac0d..8fc45bd56 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -93,7 +93,7 @@ jobs: - name: Set up environment working-directory: ${{github.workspace}} run: | - git clone --depth 1 --branch 7.0.4 https://github.com/juce-framework/JUCE.git + git clone --depth 1 --branch 7.0.6 https://github.com/juce-framework/JUCE.git cd JUCE_modules git clone --single-branch --branch chowdsp https://github.com/Chowdhury-DSP/foleys_gui_magic.git git clone --single-branch --branch main --recursive https://github.com/free-audio/clap-juce-extensions.git diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 7095dfad3..71453009e 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -4,14 +4,14 @@ include(CPM) CPMAddPackage( NAME juce GITHUB_REPOSITORY juce-framework/juce - GIT_TAG 7.0.2 + GIT_TAG 7.0.6 ) # download CLAP extensions CPMAddPackage( NAME clap-juce-extensions GITHUB_REPOSITORY free-audio/clap-juce-extensions - GIT_TAG 61749181678ebc368795447477e831469360679a + GIT_TAG 10bc7d4ddb82eab4796b1ce7d1d2dadd46552f27 ) include(AddJUCEModules) diff --git a/modules/common/chowdsp_core/JUCEHelpers/juce_MathsFunctions.h b/modules/common/chowdsp_core/JUCEHelpers/juce_MathsFunctions.h index cfa65fd8c..3d7f538b6 100644 --- a/modules/common/chowdsp_core/JUCEHelpers/juce_MathsFunctions.h +++ b/modules/common/chowdsp_core/JUCEHelpers/juce_MathsFunctions.h @@ -1,30 +1,31 @@ /* - ============================================================================== +============================================================================== - This file is part of the JUCE library. - Copyright (c) 2020 - Raw Material Software Limited + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited - JUCE is an open source library subject to commercial or open-source - licensing. + JUCE is an open source library subject to commercial or open-source + licensing. - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. + The code included in this file is provided under the terms of the ISC license + http://www.isc.org/downloads/software-support-policy/isc-license. Permission + To use, copy, modify, and/or distribute this software for any purpose with or + without fee is hereby granted provided that the above copyright notice and + this permission notice appear in all copies. - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. + JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. - ============================================================================== +============================================================================== */ namespace juce { + //============================================================================== /* - This file sets up some handy mathematical typdefs and functions. + This file sets up some handy mathematical typdefs and functions. */ //============================================================================== @@ -57,10 +58,10 @@ using uint64 = unsigned long long; #ifndef DOXYGEN /** A macro for creating 64-bit literals. - Historically, this was needed to support portability with MSVC6, and is kept here - so that old code will still compile, but nowadays every compiler will support the - LL and ULL suffixes, so you should use those in preference to this macro. - */ + Historically, this was needed to support portability with MSVC6, and is kept here + so that old code will still compile, but nowadays every compiler will support the + LL and ULL suffixes, so you should use those in preference to this macro. +*/ #define literal64bit(longLiteral) (longLiteral##LL) #endif @@ -85,6 +86,29 @@ using pointer_sized_uint = unsigned int; using ssize_t = pointer_sized_int; #endif +/** Returns true if the two numbers are approximately equal. This is useful for floating-point + and double comparisons. +*/ +template +bool approximatelyEqual (Type a, Type b) noexcept +{ + return std::abs (a - b) <= (std::numeric_limits::epsilon() * std::max (a, b)) + || std::abs (a - b) < std::numeric_limits::min(); +} + +/** Equivalent to operator==, but suppresses float-equality warnings. + + This allows code to be explicit about float-equality checks that are known to have the correct + semantics. +*/ +template +constexpr bool exactlyEqual (Type a, Type b) +{ + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wfloat-equal") + return a == b; + JUCE_END_IGNORE_WARNINGS_GCC_LIKE +} + //============================================================================== // Some indispensable min/max functions @@ -131,7 +155,7 @@ constexpr Type jmin (Type a, Type b, Type c, Type d) } /** Remaps a normalised value (between 0 and 1) to a target range. - This effectively returns (targetRangeMin + value0To1 * (targetRangeMax - targetRangeMin)). + This effectively returns (targetRangeMin + value0To1 * (targetRangeMax - targetRangeMin)). */ template constexpr Type jmap (Type value0To1, Type targetRangeMin, Type targetRangeMax) @@ -143,19 +167,19 @@ constexpr Type jmap (Type value0To1, Type targetRangeMin, Type targetRangeMax) template Type jmap (Type sourceValue, Type sourceRangeMin, Type sourceRangeMax, Type targetRangeMin, Type targetRangeMax) { - jassert (sourceRangeMax != sourceRangeMin); // mapping from a range of zero will produce NaN! + jassert (! approximatelyEqual (sourceRangeMax, sourceRangeMin)); // mapping from a range of zero will produce NaN! return targetRangeMin + ((targetRangeMax - targetRangeMin) * (sourceValue - sourceRangeMin)) / (sourceRangeMax - sourceRangeMin); } /** Remaps a normalised value (between 0 and 1) to a logarithmic target range. - The entire target range must be greater than zero. + The entire target range must be greater than zero. - @see mapFromLog10 + @see mapFromLog10 - @code - mapToLog10 (0.5, 0.4, 40.0) == 4.0 - @endcode + @code + mapToLog10 (0.5, 0.4, 40.0) == 4.0 + @endcode */ template Type mapToLog10 (Type value0To1, Type logRangeMin, Type logRangeMax) @@ -171,13 +195,13 @@ Type mapToLog10 (Type value0To1, Type logRangeMin, Type logRangeMax) /** Remaps a logarithmic value in a target range to a normalised value (between 0 and 1). - The entire target range must be greater than zero. + The entire target range must be greater than zero. - @see mapToLog10 + @see mapToLog10 - @code - mapFromLog10 (4.0, 0.4, 40.0) == 0.5 - @endcode + @code + mapFromLog10 (4.0, 0.4, 40.0) == 0.5 + @endcode */ template Type mapFromLog10 (Type valueInLogRange, Type logRangeMin, Type logRangeMax) @@ -263,19 +287,19 @@ void findMinAndMax (const Type* values, int numValues, Type& lowest, Type& highe //============================================================================== /** Constrains a value to keep it within a given range. - This will check that the specified value lies between the lower and upper bounds - specified, and if not, will return the nearest value that would be in-range. Effectively, - it's like calling jmax (lowerLimit, jmin (upperLimit, value)). + This will check that the specified value lies between the lower and upper bounds + specified, and if not, will return the nearest value that would be in-range. Effectively, + it's like calling jmax (lowerLimit, jmin (upperLimit, value)). - Note that it expects that lowerLimit <= upperLimit. If this isn't true, - the results will be unpredictable. + Note that it expects that lowerLimit <= upperLimit. If this isn't true, + the results will be unpredictable. - @param lowerLimit the minimum value to return - @param upperLimit the maximum value to return - @param valueToConstrain the value to try to return - @returns the closest value to valueToConstrain which lies between lowerLimit - and upperLimit (inclusive) - @see jmin, jmax, jmap + @param lowerLimit the minimum value to return + @param upperLimit the maximum value to return + @param valueToConstrain the value to try to return + @returns the closest value to valueToConstrain which lies between lowerLimit + and upperLimit (inclusive) + @see jmin, jmax, jmap */ template Type jlimit (Type lowerLimit, @@ -290,9 +314,9 @@ Type jlimit (Type lowerLimit, } /** Returns true if a value is at least zero, and also below a specified upper limit. - This is basically a quicker way to write: - @code valueToTest >= 0 && valueToTest < upperLimit - @endcode + This is basically a quicker way to write: + @code valueToTest >= 0 && valueToTest < upperLimit + @endcode */ template bool isPositiveAndBelow (Type1 valueToTest, Type2 upperLimit) noexcept @@ -309,9 +333,9 @@ bool isPositiveAndBelow (int valueToTest, Type upperLimit) noexcept } /** Returns true if a value is at least zero, and also less than or equal to a specified upper limit. - This is basically a quicker way to write: - @code valueToTest >= 0 && valueToTest <= upperLimit - @endcode + This is basically a quicker way to write: + @code valueToTest >= 0 && valueToTest <= upperLimit + @endcode */ template bool isPositiveAndNotGreaterThan (Type1 valueToTest, Type2 upperLimit) noexcept @@ -328,7 +352,7 @@ bool isPositiveAndNotGreaterThan (int valueToTest, Type upperLimit) noexcept } /** Computes the absolute difference between two values and returns true if it is less than or equal - to a given tolerance, otherwise it returns false. + to a given tolerance, otherwise it returns false. */ template bool isWithin (Type a, Type b, Type tolerance) noexcept @@ -336,16 +360,6 @@ bool isWithin (Type a, Type b, Type tolerance) noexcept return std::abs (a - b) <= tolerance; } -/** Returns true if the two numbers are approximately equal. This is useful for floating-point - and double comparisons. -*/ -template -bool approximatelyEqual (Type a, Type b) noexcept -{ - return std::abs (a - b) <= (std::numeric_limits::epsilon() * std::max (a, b)) - || std::abs (a - b) < std::numeric_limits::min(); -} - //============================================================================== /** Handy function for avoiding unused variables warning. */ template @@ -354,12 +368,12 @@ void ignoreUnused (Types&&...) noexcept } /** Handy function for getting the number of elements in a simple const C array. - E.g. - @code - static int myArray[] = { 1, 2, 3 }; + E.g. + @code + static int myArray[] = { 1, 2, 3 }; - int numElements = numElementsInArray (myArray) // returns 3 - @endcode + int numElements = numElementsInArray (myArray) // returns 3 + @endcode */ template constexpr int numElementsInArray (Type (&)[N]) noexcept @@ -371,7 +385,7 @@ constexpr int numElementsInArray (Type (&)[N]) noexcept // Some useful maths functions that aren't always present with all compilers and build settings. /** Using juce_hypot is easier than dealing with the different types of hypot function - that are provided by the various platforms and compilers. */ + that are provided by the various platforms and compilers. */ template Type juce_hypot (Type a, Type b) noexcept { @@ -397,7 +411,7 @@ inline float juce_hypot (float a, float b) noexcept //============================================================================== /** Commonly used mathematical constants - @tags{Core} + @tags{Core} */ template struct MathConstants @@ -442,7 +456,7 @@ constexpr FloatType radiansToDegrees (FloatType radians) noexcept //============================================================================== /** The isfinite() method seems to vary between platforms, so this is a - platform-independent function for it. + platform-independent function for it. */ template bool juce_isfinite (NumericType) noexcept @@ -480,13 +494,13 @@ inline bool juce_isfinite (double value) noexcept /** Fast floating-point-to-integer conversion. - This is faster than using the normal c++ cast to convert a float to an int, and - it will round the value to the nearest integer, rather than rounding it down - like the normal cast does. + This is faster than using the normal c++ cast to convert a float to an int, and + it will round the value to the nearest integer, rather than rounding it down + like the normal cast does. - Note that this routine gets its speed at the expense of some accuracy, and when - rounding values whose floating point component is exactly 0.5, odd numbers and - even numbers will be rounded up or down differently. + Note that this routine gets its speed at the expense of some accuracy, and when + rounding values whose floating point component is exactly 0.5, odd numbers and + even numbers will be rounded up or down differently. */ template int roundToInt (const FloatType value) noexcept @@ -523,8 +537,8 @@ inline int roundToInt (int value) noexcept /** Fast floating-point-to-integer conversion. - This is a slightly slower and slightly more accurate version of roundToInt(). It works - fine for values above zero, but negative numbers are rounded the wrong way. + This is a slightly slower and slightly more accurate version of roundToInt(). It works + fine for values above zero, but negative numbers are rounded the wrong way. */ inline int roundToIntAccurate (double value) noexcept { @@ -538,9 +552,9 @@ inline int roundToIntAccurate (double value) noexcept //============================================================================== /** Truncates a positive floating-point number to an unsigned int. - This is generally faster than static_cast (std::floor (x)) - but it only works for positive numbers small enough to be represented as an - unsigned int. + This is generally faster than static_cast (std::floor (x)) + but it only works for positive numbers small enough to be represented as an + unsigned int. */ template unsigned int truncatePositiveToUnsignedInt (FloatType value) noexcept @@ -573,13 +587,13 @@ inline int nextPowerOfTwo (int n) noexcept } /** Returns the index of the highest set bit in a (non-zero) number. - So for n=3 this would return 1, for n=7 it returns 2, etc. - An input value of 0 is illegal! + So for n=3 this would return 1, for n=7 it returns 2, etc. + An input value of 0 is illegal! */ int findHighestSetBit (uint32 n) noexcept; /** Returns the number of bits in a 32-bit integer. */ -inline int countNumberOfBits (uint32 n) noexcept +constexpr int countNumberOfBits (uint32 n) noexcept { n -= ((n >> 1) & 0x55555555); n = (((n >> 2) & 0x33333333) + (n & 0x33333333)); @@ -590,13 +604,13 @@ inline int countNumberOfBits (uint32 n) noexcept } /** Returns the number of bits in a 64-bit integer. */ -inline int countNumberOfBits (uint64 n) noexcept +constexpr int countNumberOfBits (uint64 n) noexcept { return countNumberOfBits ((uint32) n) + countNumberOfBits ((uint32) (n >> 32)); } /** Performs a modulo operation, but can cope with the dividend being negative. - The divisor must be greater than zero. + The divisor must be greater than zero. */ template IntegerType negativeAwareModulo (IntegerType dividend, const IntegerType divisor) noexcept @@ -615,29 +629,29 @@ inline constexpr NumericType square (NumericType n) noexcept //============================================================================== /** Writes a number of bits into a memory buffer at a given bit index. - The buffer is treated as a sequence of 8-bit bytes, and the value is encoded in little-endian order, - so for example if startBit = 10, and numBits = 11 then the lower 6 bits of the value would be written - into bits 2-8 of targetBuffer[1], and the upper 5 bits of value into bits 0-5 of targetBuffer[2]. + The buffer is treated as a sequence of 8-bit bytes, and the value is encoded in little-endian order, + so for example if startBit = 10, and numBits = 11 then the lower 6 bits of the value would be written + into bits 2-8 of targetBuffer[1], and the upper 5 bits of value into bits 0-5 of targetBuffer[2]. - @see readLittleEndianBitsInBuffer + @see readLittleEndianBitsInBuffer */ void writeLittleEndianBitsInBuffer (void* targetBuffer, uint32 startBit, uint32 numBits, uint32 value) noexcept; /** Reads a number of bits from a buffer at a given bit index. - The buffer is treated as a sequence of 8-bit bytes, and the value is encoded in little-endian order, - so for example if startBit = 10, and numBits = 11 then the lower 6 bits of the result would be read - from bits 2-8 of sourceBuffer[1], and the upper 5 bits of the result from bits 0-5 of sourceBuffer[2]. + The buffer is treated as a sequence of 8-bit bytes, and the value is encoded in little-endian order, + so for example if startBit = 10, and numBits = 11 then the lower 6 bits of the result would be read + from bits 2-8 of sourceBuffer[1], and the upper 5 bits of the result from bits 0-5 of sourceBuffer[2]. - @see writeLittleEndianBitsInBuffer + @see writeLittleEndianBitsInBuffer */ uint32 readLittleEndianBitsInBuffer (const void* sourceBuffer, uint32 startBit, uint32 numBits) noexcept; //============================================================================== #if JUCE_INTEL || DOXYGEN /** This macro can be applied to a float variable to check whether it contains a denormalised - value, and to normalise it if necessary. - On CPUs that aren't vulnerable to denormalisation problems, this will have no effect. - */ + value, and to normalise it if necessary. + On CPUs that aren't vulnerable to denormalisation problems, this will have no effect. +*/ #define JUCE_UNDENORMALISE(x) \ { \ (x) += 0.1f; \ @@ -653,16 +667,16 @@ uint32 readLittleEndianBitsInBuffer (const void* sourceBuffer, uint32 startBit, namespace TypeHelpers { /** The ParameterType struct is used to find the best type to use when passing some kind - of object as a parameter. + of object as a parameter. - Of course, this is only likely to be useful in certain esoteric template situations. + Of course, this is only likely to be useful in certain esoteric template situations. - E.g. "myFunction (typename TypeHelpers::ParameterType::type, typename TypeHelpers::ParameterType::type)" - would evaluate to "myfunction (int, const MyObject&)", keeping any primitive types as - pass-by-value, but passing objects as a const reference, to avoid copying. + E.g. "myFunction (typename TypeHelpers::ParameterType::type, typename TypeHelpers::ParameterType::type)" + would evaluate to "myfunction (int, const MyObject&)", keeping any primitive types as + pass-by-value, but passing objects as a const reference, to avoid copying. - @tags{Core} - */ + @tags{Core} + */ template struct ParameterType { @@ -748,29 +762,18 @@ namespace TypeHelpers #endif /** These templates are designed to take a type, and if it's a double, they return a double - type; for anything else, they return a float type. + type; for anything else, they return a float type. - @tags{Core} - */ + @tags{Core} + */ template - struct SmallestFloatType - { - using type = float; - }; - -#ifndef DOXYGEN - template <> - struct SmallestFloatType - { - using type = double; - }; -#endif + using SmallestFloatType = std::conditional_t, double, float>; /** These templates are designed to take an integer type, and return an unsigned int - version with the same size. + version with the same size. - @tags{Core} - */ + @tags{Core} + */ template struct UnsignedTypeWithSize { diff --git a/modules/common/chowdsp_core/JUCEHelpers/juce_SmoothedValue.h b/modules/common/chowdsp_core/JUCEHelpers/juce_SmoothedValue.h index fccfd2d44..cb44cd575 100644 --- a/modules/common/chowdsp_core/JUCEHelpers/juce_SmoothedValue.h +++ b/modules/common/chowdsp_core/JUCEHelpers/juce_SmoothedValue.h @@ -274,7 +274,7 @@ class SmoothedValue : public SmoothedValueBasetarget) + if (approximatelyEqual (newValue, this->target)) return; if (stepsToTarget <= 0) diff --git a/modules/common/chowdsp_core/chowdsp_core.h b/modules/common/chowdsp_core/chowdsp_core.h index 05a18d80a..105ebf608 100644 --- a/modules/common/chowdsp_core/chowdsp_core.h +++ b/modules/common/chowdsp_core/chowdsp_core.h @@ -57,6 +57,22 @@ BEGIN_JUCE_MODULE_DECLARATION #include "JUCEHelpers/dsp/juce_LookupTable.h" #include "JUCEHelpers/dsp/juce_FastMathApproximations.h" #endif +#elif JUCE_VERSION < 0x070006 +namespace juce +{ +/** Equivalent to operator==, but suppresses float-equality warnings. + + This allows code to be explicit about float-equality checks that are known to have the correct + semantics. +*/ +template +constexpr bool exactlyEqual (Type a, Type b) +{ + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wfloat-equal") + return a == b; + JUCE_END_IGNORE_WARNINGS_GCC_LIKE +} +} // namespace juce #endif // CHOWDSP_USING_JUCE #endif // DOXYGEN diff --git a/modules/common/chowdsp_reflection/chowdsp_reflection.h b/modules/common/chowdsp_reflection/chowdsp_reflection.h index 00238fc42..86130c806 100644 --- a/modules/common/chowdsp_reflection/chowdsp_reflection.h +++ b/modules/common/chowdsp_reflection/chowdsp_reflection.h @@ -24,7 +24,7 @@ BEGIN_JUCE_MODULE_DECLARATION #include // third party includes -JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wzero-as-null-pointer-constant") +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wzero-as-null-pointer-constant", "-Wfloat-equal") #include "third_party/pfr/include/pfr.hpp" JUCE_END_IGNORE_WARNINGS_GCC_LIKE diff --git a/modules/dsp/chowdsp_compressor/Compressor/chowdsp_CompressorGainComputer.h b/modules/dsp/chowdsp_compressor/Compressor/chowdsp_CompressorGainComputer.h index fedadd8d9..37e280e46 100644 --- a/modules/dsp/chowdsp_compressor/Compressor/chowdsp_CompressorGainComputer.h +++ b/modules/dsp/chowdsp_compressor/Compressor/chowdsp_CompressorGainComputer.h @@ -48,7 +48,7 @@ class GainComputer /** Sets the gain computer threshold in Decibels */ void setThreshold (SampleType newThreshDB) { - if (threshDB != newThreshDB) + if (! juce::approximatelyEqual (threshDB, newThreshDB)) { threshDB = newThreshDB; recalcKnees(); @@ -58,7 +58,7 @@ class GainComputer /** Sets the gain computer ratio */ void setRatio (SampleType newRatio) { - if (ratio != newRatio) + if (! juce::approximatelyEqual (ratio, newRatio)) { ratio = newRatio; recalcConstants(); @@ -68,7 +68,7 @@ class GainComputer /** Sets the gain computer knee in Decibels */ void setKnee (SampleType newKneeDB) { - if (kneeDB != newKneeDB) + if (! juce::approximatelyEqual (kneeDB, newKneeDB)) { kneeDB = newKneeDB; recalcKnees(); diff --git a/modules/dsp/chowdsp_dsp_data_structures/Other/chowdsp_UIToAudioPipeline.h b/modules/dsp/chowdsp_dsp_data_structures/Other/chowdsp_UIToAudioPipeline.h index 6aa2bebf7..0b68adb31 100644 --- a/modules/dsp/chowdsp_dsp_data_structures/Other/chowdsp_UIToAudioPipeline.h +++ b/modules/dsp/chowdsp_dsp_data_structures/Other/chowdsp_UIToAudioPipeline.h @@ -1,5 +1,7 @@ #pragma once +JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4324) + namespace chowdsp { /** @@ -78,3 +80,5 @@ class UIToAudioPipeline JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UIToAudioPipeline) }; } // namespace chowdsp + +JUCE_END_IGNORE_WARNINGS_MSVC diff --git a/modules/dsp/chowdsp_dsp_utils/Processors/chowdsp_BypassProcessor.cpp b/modules/dsp/chowdsp_dsp_utils/Processors/chowdsp_BypassProcessor.cpp index 9f771a370..4b783b28a 100644 --- a/modules/dsp/chowdsp_dsp_utils/Processors/chowdsp_BypassProcessor.cpp +++ b/modules/dsp/chowdsp_dsp_utils/Processors/chowdsp_BypassProcessor.cpp @@ -103,12 +103,12 @@ void BypassProcessor void BypassProcessor>>::setLatencySamplesInternal (NumericType delaySamples) { - if (delaySamples == prevDelay) + if (juce::approximatelyEqual (delaySamples, prevDelay)) return; compDelay.setDelay ((NumericType) delaySamples); - if (delaySamples == 0) + if (juce::approximatelyEqual (delaySamples, (NumericType) 0)) compDelay.reset(); prevDelay = delaySamples; @@ -126,7 +126,7 @@ bool BypassProcessor* param) { - return static_cast (param->load()); + return ! juce::approximatelyEqual (param->load(), 0.0f); } /** Allocated required memory, and resets the property */ @@ -74,7 +74,7 @@ class BypassProcessor* param) { - return static_cast (param->load()); + return ! juce::approximatelyEqual (param->load(), 0.0f); } /** Allocated required memory, and resets the property */ diff --git a/modules/dsp/chowdsp_dsp_utils/Processors/chowdsp_Gain.h b/modules/dsp/chowdsp_dsp_utils/Processors/chowdsp_Gain.h index 0f5f18bd7..362f698f6 100644 --- a/modules/dsp/chowdsp_dsp_utils/Processors/chowdsp_Gain.h +++ b/modules/dsp/chowdsp_dsp_utils/Processors/chowdsp_Gain.h @@ -26,7 +26,7 @@ class Gain /** Sets the length of the ramp used for smoothing gain changes. */ void setRampDurationSeconds (double newDurationSeconds) noexcept { - if (rampDurationSeconds != newDurationSeconds) + if (! juce::approximatelyEqual (rampDurationSeconds, newDurationSeconds)) { rampDurationSeconds = newDurationSeconds; gain.setRampLength (rampDurationSeconds); @@ -37,7 +37,10 @@ class Gain [[nodiscard]] double getRampDurationSeconds() const noexcept { return rampDurationSeconds; } /** Returns true if the current value is currently being interpolated. */ - [[nodiscard]] bool isSmoothing() const noexcept { return gain.isSmoothing() || gainTargetLinear != gain.getCurrentValue(); } + [[nodiscard]] bool isSmoothing() const noexcept + { + return gain.isSmoothing() || ! juce::approximatelyEqual (gainTargetLinear, gain.getCurrentValue()); + } //============================================================================== /** Called before processing starts. */ diff --git a/modules/dsp/chowdsp_filters/HigherOrderFilters/chowdsp_EllipticFilter.h b/modules/dsp/chowdsp_filters/HigherOrderFilters/chowdsp_EllipticFilter.h index 6878b3f11..b8f44283a 100644 --- a/modules/dsp/chowdsp_filters/HigherOrderFilters/chowdsp_EllipticFilter.h +++ b/modules/dsp/chowdsp_filters/HigherOrderFilters/chowdsp_EllipticFilter.h @@ -157,12 +157,12 @@ class EllipticFilter : public SOSFilter const auto k = std::sqrt (m); jassert (k <= 1.0); // k is ill-formed - if (k == 1.0) + if (juce::exactlyEqual (k, 1.0)) return std::atanh (w); std::vector ks { k }; int nIter = 0; - while (ks.back() != 0.0) + while (! juce::exactlyEqual (ks.back(), 0.0)) { const auto k_ = ks.back(); const auto k_p = complement (k_); @@ -223,7 +223,7 @@ class EllipticFilter : public SOSFilter const auto eps = std::sqrt (eps_sq); const auto ck1_sq = eps_sq / pow10m1 (0.1 * (double) stopBandAttenuationDB); - jassert (ck1_sq != 0); // "Cannot design a filter with given rp and rs specifications." + jassert (! juce::exactlyEqual (ck1_sq, 0.0)); // "Cannot design a filter with given rp and rs specifications." const auto val0 = ellipticK (std::sqrt (ck1_sq)); const auto m = ellipdeg ((double) order, ck1_sq); diff --git a/modules/dsp/chowdsp_filters/Utils/chowdsp_CoefficientCalculators.h b/modules/dsp/chowdsp_filters/Utils/chowdsp_CoefficientCalculators.h index 521bdf251..7a4f770ff 100644 --- a/modules/dsp/chowdsp_filters/Utils/chowdsp_CoefficientCalculators.h +++ b/modules/dsp/chowdsp_filters/Utils/chowdsp_CoefficientCalculators.h @@ -50,6 +50,7 @@ namespace CoefficientCalculators void calcFirstOrderShelf (T (&b)[2], T (&a)[2], T lowGain, T highGain, T fc, NumericType fs) { // reduce to simple gain element + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wfloat-equal") if (SIMDUtils::all (lowGain == highGain)) { b[0] = lowGain; @@ -58,6 +59,7 @@ namespace CoefficientCalculators a[1] = (T) 0; return; } + JUCE_END_IGNORE_WARNINGS_GCC_LIKE CHOWDSP_USING_XSIMD_STD (sqrt); CHOWDSP_USING_XSIMD_STD (tan); diff --git a/modules/dsp/chowdsp_math/Math/chowdsp_BufferMath.cpp b/modules/dsp/chowdsp_math/Math/chowdsp_BufferMath.cpp index e9a55111a..2ed5e0d1c 100644 --- a/modules/dsp/chowdsp_math/Math/chowdsp_BufferMath.cpp +++ b/modules/dsp/chowdsp_math/Math/chowdsp_BufferMath.cpp @@ -378,7 +378,7 @@ void sumToMono (const BufferType1& bufferSrc, BufferType2& bufferDest, FloatType addBufferChannels (bufferSrc, bufferDest, ch, 0); // apply normalization gain - if (normGain != (FloatType) 1) + if (! juce::exactlyEqual (normGain, (FloatType) 1)) { if constexpr (std::is_floating_point_v) { diff --git a/modules/dsp/chowdsp_math/Math/chowdsp_JacobiElliptic.h b/modules/dsp/chowdsp_math/Math/chowdsp_JacobiElliptic.h index b2cb4c762..ca0fd67c5 100644 --- a/modules/dsp/chowdsp_math/Math/chowdsp_JacobiElliptic.h +++ b/modules/dsp/chowdsp_math/Math/chowdsp_JacobiElliptic.h @@ -51,15 +51,15 @@ namespace jacobi k = std::sqrt (k); // Special cases first: - if (x == (T) 0) + if (juce::exactlyEqual (x, (T) 0)) { return std::make_tuple ((T) 0, (T) 1, (T) 1); } - if (k == (T) 0) + if (juce::exactlyEqual (k, (T) 0)) { return std::make_tuple (std::sin (x), std::cos (x), (T) 1); } - if (k == (T) 1) + if (juce::exactlyEqual (k, (T) 1)) { const auto cn_dn = (T) 1 / std::cosh (x); return std::make_tuple (std::tanh (x), cn_dn, cn_dn); diff --git a/modules/dsp/chowdsp_math/Math/chowdsp_Polylogarithm.h b/modules/dsp/chowdsp_math/Math/chowdsp_Polylogarithm.h index 5aa02c8d9..79fec8780 100644 --- a/modules/dsp/chowdsp_math/Math/chowdsp_Polylogarithm.h +++ b/modules/dsp/chowdsp_math/Math/chowdsp_Polylogarithm.h @@ -42,7 +42,7 @@ namespace Polylogarithm r = -PI_ * PI_ / (T) 6 + l * ((T) 0.5 * l - std::log (-x)); s = (T) 1; } - else if (x == (T) -1) + else if (juce::exactlyEqual (x, (T) -1)) { return -PI_ * PI_ / (T) 12; } @@ -53,7 +53,7 @@ namespace Polylogarithm r = (T) -0.5 * l * l; s = (T) -1; } - else if (x == (T) 0) + else if (juce::exactlyEqual (x, (T) 0)) { return (T) 0; } @@ -69,7 +69,7 @@ namespace Polylogarithm r = PI_ * PI_ / (T) 6 - std::log (x) * std::log (y); s = (T) -1; } - else if (x == (T) 1) + else if (juce::exactlyEqual (x, (T) 1)) { return PI_ * PI_ / (T) 6; } diff --git a/modules/dsp/chowdsp_math/Math/chowdsp_PowApprox.h b/modules/dsp/chowdsp_math/Math/chowdsp_PowApprox.h index 338b454fe..2c6feb9de 100644 --- a/modules/dsp/chowdsp_math/Math/chowdsp_PowApprox.h +++ b/modules/dsp/chowdsp_math/Math/chowdsp_PowApprox.h @@ -1,3 +1,5 @@ +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wstrict-aliasing") + namespace chowdsp { /** @@ -157,3 +159,4 @@ namespace PowApprox } } // namespace PowApprox } // namespace chowdsp +JUCE_END_IGNORE_WARNINGS_GCC_LIKE diff --git a/modules/dsp/chowdsp_math/chowdsp_math.h b/modules/dsp/chowdsp_math/chowdsp_math.h index f0b636b31..924434cd3 100644 --- a/modules/dsp/chowdsp_math/chowdsp_math.h +++ b/modules/dsp/chowdsp_math/chowdsp_math.h @@ -25,7 +25,7 @@ BEGIN_JUCE_MODULE_DECLARATION #include #include -JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wimplicit-int-float-conversion") +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wimplicit-int-float-conversion", "-Wfloat-equal") #include "third_party/gcem/include/gcem.hpp" JUCE_END_IGNORE_WARNINGS_GCC_LIKE diff --git a/modules/dsp/chowdsp_simd/chowdsp_simd.h b/modules/dsp/chowdsp_simd/chowdsp_simd.h index b48aa104c..ea7bb1085 100644 --- a/modules/dsp/chowdsp_simd/chowdsp_simd.h +++ b/modules/dsp/chowdsp_simd/chowdsp_simd.h @@ -52,6 +52,7 @@ JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wcast-align", "-Wsign-compare", "-Wc++98-compat-extra-semi", "-Wshorten-64-to-32", + "-Wfloat-equal", "-Woverflow") JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4244) #include "third_party/xsimd/include/xsimd/xsimd.hpp" diff --git a/modules/dsp/chowdsp_sources/Oscillators/chowdsp_AdditiveOscillator.cpp b/modules/dsp/chowdsp_sources/Oscillators/chowdsp_AdditiveOscillator.cpp index 12a69fdcd..2d6db6a75 100644 --- a/modules/dsp/chowdsp_sources/Oscillators/chowdsp_AdditiveOscillator.cpp +++ b/modules/dsp/chowdsp_sources/Oscillators/chowdsp_AdditiveOscillator.cpp @@ -33,7 +33,7 @@ void AdditiveOscillator::setHarmonicAmp template void AdditiveOscillator::setFrequency (SampleType frequencyHz, bool force) { - if (oscFrequency == frequencyHz && ! force) + if (juce::approximatelyEqual (oscFrequency, frequencyHz) && ! force) return; oscFrequency = frequencyHz; diff --git a/modules/dsp/chowdsp_sources/Oscillators/chowdsp_SawtoothWave.cpp b/modules/dsp/chowdsp_sources/Oscillators/chowdsp_SawtoothWave.cpp index bfecabd47..e307d6502 100644 --- a/modules/dsp/chowdsp_sources/Oscillators/chowdsp_SawtoothWave.cpp +++ b/modules/dsp/chowdsp_sources/Oscillators/chowdsp_SawtoothWave.cpp @@ -7,8 +7,10 @@ void SawtoothWave::setFrequency (T newFrequency) noexcept deltaPhase = (T) 2 * freq / fs; // scale by zero if freq == 0, to avoid divide by zero issue + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wfloat-equal") waveformPreservingScale = fs / ((T) 4 * freq); waveformPreservingScale = SIMDUtils::select (freq == (T) 0, (T) 0, waveformPreservingScale); + JUCE_END_IGNORE_WARNINGS_GCC_LIKE } template diff --git a/modules/dsp/chowdsp_sources/Oscillators/chowdsp_TriangleWave.cpp b/modules/dsp/chowdsp_sources/Oscillators/chowdsp_TriangleWave.cpp index 39675ef1d..3c6d907d9 100644 --- a/modules/dsp/chowdsp_sources/Oscillators/chowdsp_TriangleWave.cpp +++ b/modules/dsp/chowdsp_sources/Oscillators/chowdsp_TriangleWave.cpp @@ -6,9 +6,11 @@ void TriangleWave::setFrequency (T newFrequency) noexcept freq = newFrequency; deltaPhase = (T) 2 * freq / fs; + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wfloat-equal") // scale by zero if freq == 0, to avoid divide by zero issue waveformPreservingScale = fs / ((T) 2 * freq); waveformPreservingScale = SIMDUtils::select (freq == (T) 0, (T) 0, waveformPreservingScale); + JUCE_END_IGNORE_WARNINGS_GCC_LIKE } template diff --git a/modules/gui/chowdsp_foleys/GuiItems/chowdsp_PresetsItem.h b/modules/gui/chowdsp_foleys/GuiItems/chowdsp_PresetsItem.h index 2d0507b7e..ca17cd1d3 100644 --- a/modules/gui/chowdsp_foleys/GuiItems/chowdsp_PresetsItem.h +++ b/modules/gui/chowdsp_foleys/GuiItems/chowdsp_PresetsItem.h @@ -55,7 +55,7 @@ class PresetsItem : public foleys::GuiItem [[nodiscard]] std::vector getSettableProperties() const override { - std::function createAssetFilesMenuLambda = [=] (juce::ComboBox&) + std::function createAssetFilesMenuLambda = [this] (juce::ComboBox&) { magicBuilder.getMagicState().createAssetFilesMenu(); }; diff --git a/modules/gui/chowdsp_gui/PluginComponents/chowdsp_InfoComp.cpp b/modules/gui/chowdsp_gui/PluginComponents/chowdsp_InfoComp.cpp index 6a560866a..c0605256e 100644 --- a/modules/gui/chowdsp_gui/PluginComponents/chowdsp_InfoComp.cpp +++ b/modules/gui/chowdsp_gui/PluginComponents/chowdsp_InfoComp.cpp @@ -33,7 +33,7 @@ void InfoComp::paint (juce::Graphics& g) auto font = g.getCurrentFont(); auto b = getLocalBounds(); - auto drawText = [=, &g, &b] (const juce::String& text) + auto drawText = [&font, &g, &b] (const juce::String& text) { auto w = font.getStringWidth (text); g.drawFittedText (text, b.removeFromLeft (w), juce::Justification::left, 1); diff --git a/modules/gui/chowdsp_gui/PluginComponents/chowdsp_TitleComp.cpp b/modules/gui/chowdsp_gui/PluginComponents/chowdsp_TitleComp.cpp index 98ed2649e..b6eb209bb 100644 --- a/modules/gui/chowdsp_gui/PluginComponents/chowdsp_TitleComp.cpp +++ b/modules/gui/chowdsp_gui/PluginComponents/chowdsp_TitleComp.cpp @@ -14,7 +14,7 @@ void TitleComp::paint (juce::Graphics& g) auto curFont = g.getCurrentFont(); auto b = getLocalBounds(); - auto drawText = [=, &g, &b] (const juce::String& text) + auto drawText = [&curFont, &g, &b] (const juce::String& text) { auto width = curFont.getStringWidth (text); g.drawFittedText (text, b.removeFromLeft (width), juce::Justification::left, 1); @@ -29,7 +29,7 @@ void TitleComp::paint (juce::Graphics& g) void TitleComp::setStrings (const juce::String& newTitle, const juce::String& newSubtitle, float newFont) { - font = newFont == 0.0f ? (float) getHeight() : newFont; + font = juce::exactlyEqual (newFont, 0.0f) ? (float) getHeight() : newFont; title = newTitle; subtitle = newSubtitle; diff --git a/modules/gui/chowdsp_gui/Presets/chowdsp_PresetsComp.cpp b/modules/gui/chowdsp_gui/Presets/chowdsp_PresetsComp.cpp index 3b195bfef..707e023e6 100644 --- a/modules/gui/chowdsp_gui/Presets/chowdsp_PresetsComp.cpp +++ b/modules/gui/chowdsp_gui/Presets/chowdsp_PresetsComp.cpp @@ -48,14 +48,14 @@ PresetsComp::PresetsComp (PresetManager& presetManager) : manager (presetManager presetNameEditor.setMultiLine (false, false); presetNameEditor.setJustification (juce::Justification::centred); - auto setupNextPrevButton = [=] (juce::DrawableButton& button, bool forward) + auto setupNextPrevButton = [this] (juce::DrawableButton& button, bool forward) { addAndMakeVisible (button); button.setWantsKeyboardFocus (false); button.setTitle ("Go to " + juce::String (forward ? "next" : "previous") + " preset"); button.setColour (juce::ComboBox::outlineColourId, juce::Colours::transparentBlack); button.setColour (juce::TextButton::buttonColourId, juce::Colours::transparentBlack); - button.onClick = [=] + button.onClick = [this, forward] { goToNextPreset (forward); }; }; @@ -157,6 +157,8 @@ void PresetsComp::presetListUpdated() #if ! JUCE_IOS optionID = addPresetFolderOptions (optionID); #endif + + juce::ignoreUnused (optionID); } int PresetsComp::createPresetsMenu (int optionID) @@ -175,7 +177,7 @@ int PresetsComp::createPresetsMenu (int optionID) juce::PopupMenu::Item presetItem { preset.getName() }; presetItem.itemID = presetID + 1; - presetItem.action = [=, &preset] + presetItem.action = [this, &preset] { updatePresetBoxText(); manager.loadPreset (preset); @@ -351,7 +353,7 @@ void PresetsComp::chooseUserPresetFolder (const std::function& onFinish) constexpr auto folderChooserFlags = juce::FileBrowserComponent::openMode | juce::FileBrowserComponent::canSelectDirectories; fileChooser = std::make_shared ("Choose User Preset Folder"); { - fileChooser->launchAsync (folderChooserFlags, [=] (const juce::FileChooser& chooser) + fileChooser->launchAsync (folderChooserFlags, [this, onFinish] (const juce::FileChooser& chooser) { manager.setUserPresetPath (chooser.getResult()); @@ -368,7 +370,7 @@ void PresetsComp::saveUserPreset() presetNameEditor.grabKeyboardFocus(); presetNameEditor.setHighlightedRegion ({ 0, 100 }); - presetNameEditor.onReturnKey = [=] + presetNameEditor.onReturnKey = [this] { presetNameEditor.setVisible (false); @@ -377,7 +379,7 @@ void PresetsComp::saveUserPreset() if (presetPath == juce::File() || ! presetPath.isDirectory()) { presetPath.deleteRecursively(); - chooseUserPresetFolder ([=] + chooseUserPresetFolder ([this, presetName] { savePresetFile (presetName + presetExt); }); } else @@ -386,13 +388,13 @@ void PresetsComp::saveUserPreset() } }; - presetNameEditor.onEscapeKey = [=] + presetNameEditor.onEscapeKey = [this] { presetNameEditor.setVisible (false); updatePresetBoxText(); }; - presetNameEditor.onFocusLost = [=] + presetNameEditor.onFocusLost = [this] { presetNameEditor.setVisible (false); updatePresetBoxText(); diff --git a/modules/gui/chowdsp_visualizers/CompressorPlots/chowdsp_GainReductionMeter.cpp b/modules/gui/chowdsp_visualizers/CompressorPlots/chowdsp_GainReductionMeter.cpp index 8ea81b931..5e7092ec9 100644 --- a/modules/gui/chowdsp_visualizers/CompressorPlots/chowdsp_GainReductionMeter.cpp +++ b/modules/gui/chowdsp_visualizers/CompressorPlots/chowdsp_GainReductionMeter.cpp @@ -46,7 +46,7 @@ void GainReductionMeter::BackgroundTask::runTask (const juce::AudioBuffer compressedLevelPeak = ballisticsFilter.processSample (1, std::abs (compressedData[n])); } - gainReductionDB = (inputLevelPeak == 0.0f) ? 0.0f : juce::Decibels::gainToDecibels (compressedLevelPeak / inputLevelPeak); + gainReductionDB = juce::approximatelyEqual (inputLevelPeak, 0.0f) ? 0.0f : juce::Decibels::gainToDecibels (compressedLevelPeak / inputLevelPeak); } void GainReductionMeter::BackgroundTask::pushBufferData (const chowdsp::BufferView& buffer, bool isInput) @@ -117,7 +117,7 @@ void GainReductionMeter::paint (juce::Graphics& g) .withLeft (meterWidth * 7 / 4); auto dbString = juce::String (dbLevel, 0); - if (dbLevel == 0.0f) + if (juce::approximatelyEqual (dbLevel, 0.0f)) dbString += " dB"; g.drawFittedText (dbString, dbRect, juce::Justification::centredLeft, 1); diff --git a/modules/music/chowdsp_rhythm/chowdsp_rhythm.h b/modules/music/chowdsp_rhythm/chowdsp_rhythm.h index bc9b9c68f..c9352d01e 100644 --- a/modules/music/chowdsp_rhythm/chowdsp_rhythm.h +++ b/modules/music/chowdsp_rhythm/chowdsp_rhythm.h @@ -51,7 +51,7 @@ namespace RhythmUtils constexpr bool operator== (const Rhythm& other) const { - return tempoFactor == other.tempoFactor; + return juce::exactlyEqual (tempoFactor, other.tempoFactor); } constexpr bool operator!= (const Rhythm& other) const diff --git a/modules/plugin/chowdsp_parameters/Forwarding/chowdsp_ForwardingParameter.cpp b/modules/plugin/chowdsp_parameters/Forwarding/chowdsp_ForwardingParameter.cpp index a7ac2a26d..681ef52fc 100644 --- a/modules/plugin/chowdsp_parameters/Forwarding/chowdsp_ForwardingParameter.cpp +++ b/modules/plugin/chowdsp_parameters/Forwarding/chowdsp_ForwardingParameter.cpp @@ -212,7 +212,7 @@ void ForwardingParameter::setValue (float newValue) return; #endif - if (internalParam != nullptr && internalParam->getValue() != newValue) + if (internalParam != nullptr && ! juce::approximatelyEqual (internalParam->getValue(), newValue)) attachment->setNewValue (newValue); } diff --git a/modules/plugin/chowdsp_plugin_state/Backend/chowdsp_ParameterListeners.cpp b/modules/plugin/chowdsp_plugin_state/Backend/chowdsp_ParameterListeners.cpp index 73f043d36..1b23a334d 100644 --- a/modules/plugin/chowdsp_plugin_state/Backend/chowdsp_ParameterListeners.cpp +++ b/modules/plugin/chowdsp_plugin_state/Backend/chowdsp_ParameterListeners.cpp @@ -25,7 +25,7 @@ void ParameterListeners::updateBroadcastersFromMessageThread() jassert (juce::MessageManager::existsAndIsCurrentThread()); for (const auto [index, paramInfo] : enumerate (paramInfoList)) { - if (paramInfo.paramCookie->getValue() == paramInfo.value) + if (juce::approximatelyEqual (paramInfo.paramCookie->getValue(), paramInfo.value)) continue; paramInfo.value = paramInfo.paramCookie->getValue(); diff --git a/modules/plugin/chowdsp_plugin_state/Backend/chowdsp_StateValue.h b/modules/plugin/chowdsp_plugin_state/Backend/chowdsp_StateValue.h index d1979e3a6..3ba036afc 100644 --- a/modules/plugin/chowdsp_plugin_state/Backend/chowdsp_StateValue.h +++ b/modules/plugin/chowdsp_plugin_state/Backend/chowdsp_StateValue.h @@ -39,8 +39,10 @@ struct StateValue : StateValueBase /** Sets a new value */ void set (T v) { + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wfloat-equal") if (v == currentValue) return; + JUCE_END_IGNORE_WARNINGS_GCC_LIKE currentValue = v; changeBroadcaster(); diff --git a/modules/plugin/chowdsp_plugin_state/Frontend/chowdsp_ParameterAttachment.cpp b/modules/plugin/chowdsp_plugin_state/Frontend/chowdsp_ParameterAttachment.cpp index ee9126169..bb0fc650d 100644 --- a/modules/plugin/chowdsp_plugin_state/Frontend/chowdsp_ParameterAttachment.cpp +++ b/modules/plugin/chowdsp_plugin_state/Frontend/chowdsp_ParameterAttachment.cpp @@ -76,7 +76,20 @@ template void ParameterAttachment::callIfParameterValueChanged (ParamElementType newValue, Func&& func) { - if (param != nullptr && ParameterTypeHelpers::getValue (*param) != newValue) - func (newValue); + if (param == nullptr) + return; + + if constexpr (std::is_floating_point_v) + { + if (juce::approximatelyEqual (ParameterTypeHelpers::getValue (*param), newValue)) + return; + } + else + { + if (ParameterTypeHelpers::getValue (*param) == newValue) + return; + } + + func (newValue); } } // namespace chowdsp diff --git a/tests/common_tests/chowdsp_core_test/AtomicHelpersTest.cpp b/tests/common_tests/chowdsp_core_test/AtomicHelpersTest.cpp index bd3cd9da6..2793a7ec5 100644 --- a/tests/common_tests/chowdsp_core_test/AtomicHelpersTest.cpp +++ b/tests/common_tests/chowdsp_core_test/AtomicHelpersTest.cpp @@ -11,10 +11,10 @@ TEST_CASE ("Atomic Helpers Test", "[common][data-structures]") { std::atomic f { 0.0f }; REQUIRE_MESSAGE (compareExchange (f, 0.0f, 1.0f), "Compare/Exchange should return true!"); - REQUIRE_MESSAGE (f.load() == 1.0f, "Compare/Exchange value update is incorrect!"); + REQUIRE_MESSAGE (juce::exactlyEqual (f.load(), 1.0f), "Compare/Exchange value update is incorrect!"); REQUIRE_MESSAGE (! compareExchange (f, 0.0f, 1.0f), "Compare/Exchange should return false!"); - REQUIRE_MESSAGE (f.load() == 1.0f, "Compare/Exchange value update is happening on false case!"); + REQUIRE_MESSAGE (juce::exactlyEqual (f.load(), 1.0f), "Compare/Exchange value update is happening on false case!"); } { diff --git a/tests/common_tests/chowdsp_core_test/ScopedValueTest.cpp b/tests/common_tests/chowdsp_core_test/ScopedValueTest.cpp index d78a3d839..93d9456cc 100644 --- a/tests/common_tests/chowdsp_core_test/ScopedValueTest.cpp +++ b/tests/common_tests/chowdsp_core_test/ScopedValueTest.cpp @@ -9,12 +9,12 @@ TEST_CASE ("Scoped Value Test", "[common][data-structures]") float x = 0.0f; { chowdsp::ScopedValue x_scoped { x }; - REQUIRE_MESSAGE (x_scoped.get() == x, "Initial value is incorrect!"); + REQUIRE_MESSAGE (juce::exactlyEqual (x_scoped.get(), x), "Initial value is incorrect!"); x_scoped.get() = testVal1; - REQUIRE_MESSAGE (x_scoped.get() == testVal1, "Set value is incorrect!"); + REQUIRE_MESSAGE (juce::exactlyEqual (x_scoped.get(), testVal1), "Set value is incorrect!"); } - REQUIRE_MESSAGE (x == testVal1, "Value after scope is incorrect!"); + REQUIRE_MESSAGE (juce::exactlyEqual (x, testVal1), "Value after scope is incorrect!"); } } diff --git a/tests/common_tests/chowdsp_data_structures_test/BucketArrayTest.cpp b/tests/common_tests/chowdsp_data_structures_test/BucketArrayTest.cpp index 85c094c2e..5fb1b8d44 100644 --- a/tests/common_tests/chowdsp_data_structures_test/BucketArrayTest.cpp +++ b/tests/common_tests/chowdsp_data_structures_test/BucketArrayTest.cpp @@ -1,6 +1,8 @@ #include #include +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wmissing-field-initializers") + struct TestType { std::string str; @@ -112,6 +114,7 @@ TEST_CASE ("Bucket Array Test", "[common][data-structures]") [[maybe_unused]] const auto [loc4, elem4] = array.emplace (4.0f); [[maybe_unused]] const auto [loc10, elem10] = array.emplace (10.0f); REQUIRE (array.size() == 2); - REQUIRE (elem4->x == 4.0f); + REQUIRE (juce::exactlyEqual (elem4->x, 4.0f)); } } +JUCE_END_IGNORE_WARNINGS_GCC_LIKE diff --git a/tests/common_tests/chowdsp_data_structures_test/DoubleBufferTest.cpp b/tests/common_tests/chowdsp_data_structures_test/DoubleBufferTest.cpp index 1d3afc347..0b01ffb10 100644 --- a/tests/common_tests/chowdsp_data_structures_test/DoubleBufferTest.cpp +++ b/tests/common_tests/chowdsp_data_structures_test/DoubleBufferTest.cpp @@ -14,7 +14,7 @@ void wrapTest() std::vector expected { -2, -3, -4, -5, 4, 5, 0, -1 }; const auto* actual = buffer.data(); for (int i = 0; i < buffer.size(); ++i) - REQUIRE_MESSAGE (actual[i] == expected[(size_t) i], "Incorrect value at index " << std::to_string (i)); + REQUIRE_MESSAGE (juce::exactlyEqual (actual[i], expected[(size_t) i]), "Incorrect value at index " << std::to_string (i)); } template @@ -49,15 +49,15 @@ void wrapAndClearTest() const auto* testData = buffer.data ((int) data.size()); for (int i = 0; i < buffer.size() / 2; ++i) { - REQUIRE_MESSAGE (testData[i] == lastData[(size_t) i], "Incorrect value at index " << std::to_string (i)); + REQUIRE_MESSAGE (juce::exactlyEqual (testData[i], lastData[(size_t) i]), "Incorrect value at index " << std::to_string (i)); auto secondIdx = i + buffer.size() / 2; - REQUIRE_MESSAGE (testData[secondIdx] == secondToLastData[(size_t) i], "Incorrect value at index " << std::to_string (secondIdx)); + REQUIRE_MESSAGE (juce::exactlyEqual (testData[secondIdx], secondToLastData[(size_t) i]), "Incorrect value at index " << std::to_string (secondIdx)); } buffer.clear(); for (int i = 0; i < buffer.size(); ++i) - REQUIRE_MESSAGE (testData[i] == (T) 0, "Erroneous non-zero value at index " << std::to_string (i)); + REQUIRE_MESSAGE (juce::exactlyEqual (testData[i], (T) 0), "Erroneous non-zero value at index " << std::to_string (i)); } TEST_CASE ("Double Buffer Test", "[common][data-structures]") diff --git a/tests/common_tests/chowdsp_data_structures_test/LocalPointerTest.cpp b/tests/common_tests/chowdsp_data_structures_test/LocalPointerTest.cpp index 2e8969d28..2e7f7d6a3 100644 --- a/tests/common_tests/chowdsp_data_structures_test/LocalPointerTest.cpp +++ b/tests/common_tests/chowdsp_data_structures_test/LocalPointerTest.cpp @@ -15,14 +15,14 @@ TEST_CASE ("Local Pointer Test", "[common][data-structures]") std::array data {}; }; ptr.emplace (5.0f, 6.0f); - REQUIRE (ptr->data[0] == 5.0f); - REQUIRE (ptr->data[1] == 6.0f); + REQUIRE (juce::exactlyEqual (ptr->data[0], 5.0f)); + REQUIRE (juce::exactlyEqual (ptr->data[1], 6.0f)); ptr->data[0] = 7.0f; - REQUIRE (std::as_const (ptr)->data[0] == 7.0f); + REQUIRE (juce::exactlyEqual (std::as_const (ptr)->data[0], 7.0f)); (*ptr).data[1] = 8.0f; - REQUIRE ((*std::as_const (ptr)).data[1] == 8.0f); + REQUIRE (juce::exactlyEqual ((*std::as_const (ptr)).data[1], 8.0f)); ptr.reset(); REQUIRE (ptr == nullptr); diff --git a/tests/common_tests/chowdsp_data_structures_test/SmallVectorTest.cpp b/tests/common_tests/chowdsp_data_structures_test/SmallVectorTest.cpp index b03c8d7f2..5a7eaca6c 100644 --- a/tests/common_tests/chowdsp_data_structures_test/SmallVectorTest.cpp +++ b/tests/common_tests/chowdsp_data_structures_test/SmallVectorTest.cpp @@ -282,15 +282,15 @@ TEST_CASE ("Small Vector Test", "[common][data-structures]") vec.insert (vec.begin() + 1, 1.5); REQUIRE (vec.size() == 3); - REQUIRE (vec[1] == 1.5); + REQUIRE (juce::exactlyEqual (vec[1], 1.5)); vec.insert (vec.begin() + 1, 1.25); REQUIRE (vec.size() == 4); - REQUIRE (vec[1] == 1.25); + REQUIRE (juce::exactlyEqual (vec[1], 1.25)); vec.insert (vec.begin() + 3, 1.75); REQUIRE (vec.size() == 5); - REQUIRE (vec[3] == 1.75); + REQUIRE (juce::exactlyEqual (vec[3], 1.75)); } SECTION ("Insert Copy Single Value Test") @@ -320,25 +320,25 @@ TEST_CASE ("Small Vector Test", "[common][data-structures]") chowdsp::SmallVector vec { 1.0, 2.0 }; vec.insert (vec.begin() + 1, decimals.begin(), decimals.end()); REQUIRE (vec.size() == 6); - REQUIRE (vec[1] == 1.2); - REQUIRE (vec[3] == 1.6); - REQUIRE (vec[5] == 2.0); + REQUIRE (juce::exactlyEqual (vec[1], 1.2)); + REQUIRE (juce::exactlyEqual (vec[3], 1.6)); + REQUIRE (juce::exactlyEqual (vec[5], 2.0)); } { chowdsp::SmallVector vec { 1.0, 2.0 }; vec.insert (vec.begin() + 1, decimals.begin(), decimals.end()); REQUIRE (vec.size() == 6); - REQUIRE (vec[1] == 1.2); - REQUIRE (vec[3] == 1.6); - REQUIRE (vec[5] == 2.0); + REQUIRE (juce::exactlyEqual (vec[1], 1.2)); + REQUIRE (juce::exactlyEqual (vec[3], 1.6)); + REQUIRE (juce::exactlyEqual (vec[5], 2.0)); } { chowdsp::SmallVector vec { 1.0, 2.0 }; vec.insert (vec.begin() + 1, { 1.2, 1.4, 1.6, 1.8 }); REQUIRE (vec.size() == 6); - REQUIRE (vec[1] == 1.2); - REQUIRE (vec[3] == 1.6); - REQUIRE (vec[5] == 2.0); + REQUIRE (juce::exactlyEqual (vec[1], 1.2)); + REQUIRE (juce::exactlyEqual (vec[3], 1.6)); + REQUIRE (juce::exactlyEqual (vec[5], 2.0)); } } @@ -348,17 +348,17 @@ TEST_CASE ("Small Vector Test", "[common][data-structures]") chowdsp::SmallVector vec { 1.0, 2.0 }; vec.insert (vec.begin() + 1, 3, 1.5); REQUIRE (vec.size() == 5); - REQUIRE (vec[1] == 1.5); - REQUIRE (vec[3] == 1.5); - REQUIRE (vec[4] == 2.0); + REQUIRE (juce::exactlyEqual (vec[1], 1.5)); + REQUIRE (juce::exactlyEqual (vec[3], 1.5)); + REQUIRE (juce::exactlyEqual (vec[4], 2.0)); } { chowdsp::SmallVector vec { 1.0, 2.0 }; vec.insert (vec.begin() + 1, 3, 1.5); REQUIRE (vec.size() == 5); - REQUIRE (vec[1] == 1.5); - REQUIRE (vec[3] == 1.5); - REQUIRE (vec[4] == 2.0); + REQUIRE (juce::exactlyEqual (vec[1], 1.5)); + REQUIRE (juce::exactlyEqual (vec[3], 1.5)); + REQUIRE (juce::exactlyEqual (vec[4], 2.0)); } } diff --git a/tests/common_tests/chowdsp_data_structures_test/TupleHelpersTest.cpp b/tests/common_tests/chowdsp_data_structures_test/TupleHelpersTest.cpp index 42e4a1b1a..65e789d68 100644 --- a/tests/common_tests/chowdsp_data_structures_test/TupleHelpersTest.cpp +++ b/tests/common_tests/chowdsp_data_structures_test/TupleHelpersTest.cpp @@ -12,7 +12,7 @@ TEST_CASE ("Tuple Helpers Test", "[common][data-structures]") ints); chowdsp::TupleHelpers::forEachInTuple ([] (auto& x, size_t i) - { REQUIRE (x == (std::remove_reference_t) i); }, + { REQUIRE (juce::exactlyEqual (x, (std::remove_reference_t) i)); }, ints); } @@ -38,7 +38,7 @@ TEST_CASE ("Tuple Helpers Test", "[common][data-structures]") { x.set (5); }); REQUIRE (std::get<0> (tuple).x == 4); - REQUIRE (std::get<1> (tuple).x == 5.0f); + REQUIRE (juce::exactlyEqual (std::get<1> (tuple).x, 5.0f)); } SECTION ("const visit_at Test") diff --git a/tests/common_tests/chowdsp_units_test/TimeUnitsTest.cpp b/tests/common_tests/chowdsp_units_test/TimeUnitsTest.cpp index be339b1be..118840693 100644 --- a/tests/common_tests/chowdsp_units_test/TimeUnitsTest.cpp +++ b/tests/common_tests/chowdsp_units_test/TimeUnitsTest.cpp @@ -1,6 +1,9 @@ #include -#include +#include + +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wfloat-equal") +#include using namespace chowdsp::Units; TEST_CASE ("Time Units Test", "[common][units]") @@ -10,7 +13,7 @@ TEST_CASE ("Time Units Test", "[common][units]") constexpr auto tt_seconds = Time { 0.1f }; constexpr Time tt_milliseconds = tt_seconds; static_assert (tt_milliseconds.value() == 100.0f); - REQUIRE_MESSAGE (tt_milliseconds.value() == 100.0f, tt_milliseconds << " should be equal to " << 100.0f); + REQUIRE_MESSAGE (juce::approximatelyEqual (tt_milliseconds.value(), 100.0f), tt_milliseconds << " should be equal to " << 100.0f); } SECTION ("Samples To Seconds (and back)") @@ -18,7 +21,7 @@ TEST_CASE ("Time Units Test", "[common][units]") constexpr auto tt_samples = Time { 1000.0f, 1000.0f }; constexpr auto ttt_sec = Time { tt_samples }; constexpr auto ttt_samples = Time { ttt_sec, 500.0f }; - REQUIRE_MESSAGE (ttt_samples.value() == 500.0f, ttt_samples << " should be equal to " << 500.0f); + REQUIRE_MESSAGE (juce::approximatelyEqual (ttt_samples.value(), 500.0f), ttt_samples << " should be equal to " << 500.0f); } SECTION ("Integer Samples") @@ -66,3 +69,4 @@ TEST_CASE ("Time Units Test", "[common][units]") REQUIRE (t_microseconds < tt_seconds); } } +JUCE_END_IGNORE_WARNINGS_GCC_LIKE diff --git a/tests/dsp_tests/chowdsp_buffers_test/BufferIteratorsTest.cpp b/tests/dsp_tests/chowdsp_buffers_test/BufferIteratorsTest.cpp index 1564044d3..17d54bee7 100644 --- a/tests/dsp_tests/chowdsp_buffers_test/BufferIteratorsTest.cpp +++ b/tests/dsp_tests/chowdsp_buffers_test/BufferIteratorsTest.cpp @@ -1,6 +1,8 @@ -#include #include +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wfloat-equal") +#include + static_assert (! chowdsp::IsConstBuffer>); static_assert (! chowdsp::IsConstBuffer&>); static_assert (chowdsp::IsConstBuffer>); @@ -178,3 +180,4 @@ TEMPLATE_TEST_CASE ("Buffer Iterators Test", } } } +JUCE_END_IGNORE_WARNINGS_GCC_LIKE diff --git a/tests/dsp_tests/chowdsp_buffers_test/BufferSpanTest.cpp b/tests/dsp_tests/chowdsp_buffers_test/BufferSpanTest.cpp index 8bef3230e..194057321 100644 --- a/tests/dsp_tests/chowdsp_buffers_test/BufferSpanTest.cpp +++ b/tests/dsp_tests/chowdsp_buffers_test/BufferSpanTest.cpp @@ -23,12 +23,12 @@ TEMPLATE_TEST_CASE ("Buffer Span Test", { auto readSpan0 = testBuffer.getReadSpan (0); - REQUIRE (std::accumulate (readSpan0.begin(), readSpan0.end(), 0.0f) == 4.0f); + REQUIRE (juce::exactlyEqual (std::accumulate (readSpan0.begin(), readSpan0.end(), 0.0f), 4.0f)); } { auto readSpan1 = testBuffer.getReadSpan (1); - REQUIRE (std::accumulate (readSpan1.begin(), readSpan1.end(), 0.0f) == 8.0f); + REQUIRE (juce::exactlyEqual (std::accumulate (readSpan1.begin(), readSpan1.end(), 0.0f), 8.0f)); } }; diff --git a/tests/dsp_tests/chowdsp_buffers_test/BufferTest.cpp b/tests/dsp_tests/chowdsp_buffers_test/BufferTest.cpp index 027da7b38..e98abafb7 100644 --- a/tests/dsp_tests/chowdsp_buffers_test/BufferTest.cpp +++ b/tests/dsp_tests/chowdsp_buffers_test/BufferTest.cpp @@ -1,7 +1,9 @@ -#include #include #include +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wfloat-equal") +#include + template class chowdsp::Buffer; template @@ -122,3 +124,4 @@ TEMPLATE_PRODUCT_TEST_CASE ("Buffer Test", "[dsp][buffers][simd]", (chowdsp::Buf } } } +JUCE_END_IGNORE_WARNINGS_GCC_LIKE diff --git a/tests/dsp_tests/chowdsp_buffers_test/BufferViewTest.cpp b/tests/dsp_tests/chowdsp_buffers_test/BufferViewTest.cpp index 14570a151..265f4c186 100644 --- a/tests/dsp_tests/chowdsp_buffers_test/BufferViewTest.cpp +++ b/tests/dsp_tests/chowdsp_buffers_test/BufferViewTest.cpp @@ -1,6 +1,8 @@ -#include #include +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wfloat-equal") +#include + template void testBufferView (const BufferViewType view, const chowdsp::Buffer& buffer) { @@ -138,3 +140,4 @@ TEMPLATE_TEST_CASE ("Buffer View Test", "[dsp][buffers][simd]", float, double, x } } } +JUCE_END_IGNORE_WARNINGS_GCC_LIKE diff --git a/tests/dsp_tests/chowdsp_buffers_test/JUCEBufferViewTest.cpp b/tests/dsp_tests/chowdsp_buffers_test/JUCEBufferViewTest.cpp index 8d6192a69..5a5fb9af6 100644 --- a/tests/dsp_tests/chowdsp_buffers_test/JUCEBufferViewTest.cpp +++ b/tests/dsp_tests/chowdsp_buffers_test/JUCEBufferViewTest.cpp @@ -13,7 +13,7 @@ void testBufferView (const BufferViewType& bufferView, const juce::AudioBuffer::size; ++i) { const auto scalarChIndex = ch * (int) xsimd::batch::size + i; - REQUIRE_MESSAGE (simdBuffer - .getReadPointer (ch)[n] - .get ((size_t) i) - == (scalarChIndex < scalarBuffer.getNumChannels() ? scalarBuffer.getReadPointer (scalarChIndex)[n] - : 0.0f), + REQUIRE_MESSAGE (juce::exactlyEqual (simdBuffer.getReadPointer (ch)[n].get ((size_t) i), + scalarChIndex < scalarBuffer.getNumChannels() + ? scalarBuffer.getReadPointer (scalarChIndex)[n] + : 0.0f), std::string ("Failure at channel ") + std::to_string (ch) + std::string (", sample ") + std::to_string (n) + std::string (", index ") + std::to_string (i)); @@ -78,9 +77,9 @@ TEMPLATE_TEST_CASE ("SIMD Buffer Copy Test", "[dsp][buffers][simd]", float, doub { for (int n = 0; n < scalarBuffer.getNumSamples(); ++n) { - REQUIRE_MESSAGE (scalarBuffer.getReadPointer (ch)[n] - == simdBuffer.getReadPointer (ch / (int) xsimd::batch::size)[n] - .get ((size_t) ch % xsimd::batch::size), + REQUIRE_MESSAGE (juce::exactlyEqual (scalarBuffer.getReadPointer (ch)[n], + simdBuffer.getReadPointer (ch / (int) xsimd::batch::size)[n] + .get ((size_t) ch % xsimd::batch::size)), std::string ("Failure at channel ") + std::to_string (ch) + std::string (", sample ") + std::to_string (n)); } @@ -114,8 +113,8 @@ TEMPLATE_TEST_CASE ("SIMD Buffer Copy Test", "[dsp][buffers][simd]", float, doub { for (int n = 0; n < scalarBufferTest.getNumSamples(); ++n) { - REQUIRE_MESSAGE (scalarBufferTest.getReadPointer (ch)[n] - == scalarBufferRef.getReadPointer (ch)[n] * 2.0f, + REQUIRE_MESSAGE (juce::exactlyEqual (scalarBufferTest.getReadPointer (ch)[n], + scalarBufferRef.getReadPointer (ch)[n] * 2.0f), std::string ("Failure at channel ") + std::to_string (ch) + std::string (", sample ") + std::to_string (n)); } diff --git a/tests/dsp_tests/chowdsp_dsp_data_structures_test/SmoothedBufferValueTest.cpp b/tests/dsp_tests/chowdsp_dsp_data_structures_test/SmoothedBufferValueTest.cpp index 4e87eaeb3..9fec1ca44 100644 --- a/tests/dsp_tests/chowdsp_dsp_data_structures_test/SmoothedBufferValueTest.cpp +++ b/tests/dsp_tests/chowdsp_dsp_data_structures_test/SmoothedBufferValueTest.cpp @@ -30,11 +30,11 @@ TEMPLATE_PRODUCT_TEST_CASE ("Smoothed Buffer Value Test", "[dsp][data-structures const auto* smoothData = comp.getSmoothedBuffer(); for (int n = 0; n < maxBlockSize; ++n) - REQUIRE_MESSAGE (smoothData[n] == ref.getNextValue(), "SmoothedValue was inaccurate!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (smoothData[n], ref.getNextValue()), "SmoothedValue was inaccurate!"); - REQUIRE_MESSAGE (comp.getCurrentValue() == ref.getCurrentValue(), "Current value is innacurate!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (comp.getCurrentValue(), ref.getCurrentValue()), "Current value is innacurate!"); - const auto isActuallySmoothing = smoothData[0] != smoothData[maxBlockSize - 1]; + const auto isActuallySmoothing = ! juce::approximatelyEqual (smoothData[0], smoothData[maxBlockSize - 1]); REQUIRE_MESSAGE (comp.isSmoothing() == isActuallySmoothing, "SmoothedBufferValue is not smoothing correctly!"); } }; diff --git a/tests/dsp_tests/chowdsp_dsp_juce_test/DiffuserTest.cpp b/tests/dsp_tests/chowdsp_dsp_juce_test/DiffuserTest.cpp index 3a9990be4..898770c96 100644 --- a/tests/dsp_tests/chowdsp_dsp_juce_test/DiffuserTest.cpp +++ b/tests/dsp_tests/chowdsp_dsp_juce_test/DiffuserTest.cpp @@ -61,6 +61,6 @@ TEST_CASE ("Diffuser Test", "[dsp][reverb]") sumAfterReset += chowdsp::FloatVectorOperations::accumulate (outVec, nChannels); } - REQUIRE_MESSAGE (sumAfterReset == 0.0f, "State was not cleared after reset!"); + REQUIRE_MESSAGE (juce::exactlyEqual (sumAfterReset, 0.0f), "State was not cleared after reset!"); } } diff --git a/tests/dsp_tests/chowdsp_dsp_juce_test/data_structures_tests/BufferMathTest.cpp b/tests/dsp_tests/chowdsp_dsp_juce_test/data_structures_tests/BufferMathTest.cpp index 6054ab81e..086fcb85b 100644 --- a/tests/dsp_tests/chowdsp_dsp_juce_test/data_structures_tests/BufferMathTest.cpp +++ b/tests/dsp_tests/chowdsp_dsp_juce_test/data_structures_tests/BufferMathTest.cpp @@ -1,8 +1,10 @@ -#include -#include #include using namespace chowdsp::BufferMath; +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wfloat-equal") + +#include +#include template static void fillBufferWithOnes (BufferType& buffer) @@ -408,3 +410,5 @@ TEMPLATE_TEST_CASE ("Buffer Math Test", } } } + +JUCE_END_IGNORE_WARNINGS_GCC_LIKE diff --git a/tests/dsp_tests/chowdsp_dsp_juce_test/data_structures_tests/SmoothedBufferValueTest.cpp b/tests/dsp_tests/chowdsp_dsp_juce_test/data_structures_tests/SmoothedBufferValueTest.cpp index d3df388c6..c092f7654 100644 --- a/tests/dsp_tests/chowdsp_dsp_juce_test/data_structures_tests/SmoothedBufferValueTest.cpp +++ b/tests/dsp_tests/chowdsp_dsp_juce_test/data_structures_tests/SmoothedBufferValueTest.cpp @@ -33,7 +33,7 @@ TEMPLATE_TEST_CASE ("Smoothed Buffer Value Test", const auto* smoothData = comp.getSmoothedBuffer(); for (int n = 0; n < maxBlockSize; ++n) - REQUIRE_MESSAGE (smoothData[n] == mapFunc (ref.getNextValue()), "SmoothedValue was inaccurate!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (smoothData[n], mapFunc (ref.getNextValue())), "SmoothedValue was inaccurate!"); } REQUIRE_MESSAGE (comp.isSmoothing() == ref.isSmoothing(), "SmoothedBufferValue is not smoothing correctly!"); diff --git a/tests/dsp_tests/chowdsp_dsp_juce_test/source_tests/RepitchedSourceTest.cpp b/tests/dsp_tests/chowdsp_dsp_juce_test/source_tests/RepitchedSourceTest.cpp index 4df556658..fce82c46e 100644 --- a/tests/dsp_tests/chowdsp_dsp_juce_test/source_tests/RepitchedSourceTest.cpp +++ b/tests/dsp_tests/chowdsp_dsp_juce_test/source_tests/RepitchedSourceTest.cpp @@ -38,7 +38,7 @@ TEST_CASE ("Repitched Source Test", "[dsp][sources][resampling]") sineProc.prepare ({ fs, (juce::uint32) blockSize, 1 }); sineProc.sine.setFrequency (sineFreq); sineProc.setRepitchFactor (repitchFactor); - REQUIRE_MESSAGE (sineProc.getRepitchFactor() == repitchFactor, "Set repitch factor is incorrect!"); + REQUIRE_MESSAGE (juce::exactlyEqual (sineProc.getRepitchFactor(), repitchFactor), "Set repitch factor is incorrect!"); chowdsp::TunerProcessor tuner; tuner.prepare (fs); diff --git a/tests/dsp_tests/chowdsp_dsp_utils_test/GainTest.cpp b/tests/dsp_tests/chowdsp_dsp_utils_test/GainTest.cpp index 610bcddf1..ab3d48d6d 100644 --- a/tests/dsp_tests/chowdsp_dsp_utils_test/GainTest.cpp +++ b/tests/dsp_tests/chowdsp_dsp_utils_test/GainTest.cpp @@ -19,7 +19,7 @@ TEMPLATE_TEST_CASE ("Gain Test", "[dsp][misc]", float, double, xsimd::batch gain; gain.setGainLinear ((NumericType) 2); - REQUIRE_MESSAGE (gain.getGainLinear() == (NumericType) 2, "Set linear gain is incorrect!"); + REQUIRE_MESSAGE (juce::exactlyEqual (gain.getGainLinear(), (NumericType) 2), "Set linear gain is incorrect!"); REQUIRE_MESSAGE (gain.getGainDecibels() == Catch::Approx ((NumericType) 6).margin (0.03), "Get Decibels gain is incorrect!"); gain.setGainDecibels ((NumericType) -6); @@ -27,7 +27,7 @@ TEMPLATE_TEST_CASE ("Gain Test", "[dsp][misc]", float, double, xsimd::batch #include +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wfloat-equal") +#include + TEMPLATE_TEST_CASE ("Level Detector Test", "[dsp][misc]", float, double) { SECTION ("Single-Channel Test") @@ -99,3 +101,5 @@ TEMPLATE_TEST_CASE ("Level Detector Test", "[dsp][misc]", float, double) } } } + +JUCE_END_IGNORE_WARNINGS_GCC_LIKE diff --git a/tests/dsp_tests/chowdsp_dsp_utils_test/PannerTest.cpp b/tests/dsp_tests/chowdsp_dsp_utils_test/PannerTest.cpp index 5330f782e..d3d73e28d 100644 --- a/tests/dsp_tests/chowdsp_dsp_utils_test/PannerTest.cpp +++ b/tests/dsp_tests/chowdsp_dsp_utils_test/PannerTest.cpp @@ -27,8 +27,8 @@ TEST_CASE ("Panner Test", "[dsp][misc]") auto leftMag = chowdsp::BufferMath::getMagnitude (buffer, 0, nSamples, 0); auto rightMag = chowdsp::BufferMath::getMagnitude (buffer, 0, nSamples, 1); - REQUIRE (leftMag == 2.0f); // expect both channels summed into left channel - REQUIRE (rightMag == 0.0f); // expect silence on right channel + REQUIRE (juce::approximatelyEqual (leftMag, 2.0f)); // expect both channels summed into left channel + REQUIRE (juce::approximatelyEqual (rightMag, 0.0f)); // expect silence on right channel } SECTION ("Single-sample Test") @@ -52,8 +52,8 @@ TEST_CASE ("Panner Test", "[dsp][misc]") auto leftMag = chowdsp::BufferMath::getMagnitude (buffer, 0, nSamples, 0); auto rightMag = chowdsp::BufferMath::getMagnitude (buffer, 0, nSamples, 1); - REQUIRE (leftMag == 0.0f); // expect silenSecondsce on left channel - REQUIRE (rightMag == 2.0f); // expect both channels summed to right channel + REQUIRE (juce::approximatelyEqual (leftMag, 0.0f)); // expect silenSecondsce on left channel + REQUIRE (juce::approximatelyEqual (rightMag, 2.0f)); // expect both channels summed to right channel } SECTION ("Center Test") diff --git a/tests/dsp_tests/chowdsp_dsp_utils_test/TunerTest.cpp b/tests/dsp_tests/chowdsp_dsp_utils_test/TunerTest.cpp index 398d753c2..5e3f14118 100644 --- a/tests/dsp_tests/chowdsp_dsp_utils_test/TunerTest.cpp +++ b/tests/dsp_tests/chowdsp_dsp_utils_test/TunerTest.cpp @@ -41,7 +41,7 @@ TEST_CASE ("Tuner Test", "[dsp][misc]") chowdsp::BufferMath::applyGain (buffer, 0.001f); tuner.process (buffer.getReadPointer (0)); - REQUIRE_MESSAGE (tuner.getCurrentFrequencyHz() == 1.0f, "Tuner frequency should read 1.0 Hz for silence!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (tuner.getCurrentFrequencyHz(), 1.0f), "Tuner frequency should read 1.0 Hz for silence!"); } SECTION ("100 Hz Test") diff --git a/tests/dsp_tests/chowdsp_filters_test/ConformalMapsTest.cpp b/tests/dsp_tests/chowdsp_filters_test/ConformalMapsTest.cpp index 02d43940e..8f0732659 100644 --- a/tests/dsp_tests/chowdsp_filters_test/ConformalMapsTest.cpp +++ b/tests/dsp_tests/chowdsp_filters_test/ConformalMapsTest.cpp @@ -52,7 +52,7 @@ TEST_CASE ("Conformal Maps Test", "[dsp][filters]") float a[2] {}; chowdsp::ConformalMaps::Transform::alpha (b, a, bs, as, 2.0f * fs, 0.0f, fs); - REQUIRE_MESSAGE (b[0] == -b[1], "BE should map zero at DC to zero at DC"); + REQUIRE_MESSAGE (juce::exactlyEqual (b[0], -b[1]), "BE should map zero at DC to zero at DC"); REQUIRE_MESSAGE (std::abs (a[1]) < 0.01f, "BE should map high-frequency pole to near zero"); } diff --git a/tests/dsp_tests/chowdsp_filters_test/LinearTransformsTest.cpp b/tests/dsp_tests/chowdsp_filters_test/LinearTransformsTest.cpp index 294c4c1d9..85a647250 100644 --- a/tests/dsp_tests/chowdsp_filters_test/LinearTransformsTest.cpp +++ b/tests/dsp_tests/chowdsp_filters_test/LinearTransformsTest.cpp @@ -10,10 +10,10 @@ TEST_CASE ("Linear Transforms Test", "[dsp][filters]") float a[2] = { 0.5f, 1.0f }; chowdsp::LinearTransforms::transformFeedback<1, float, false> (b, a, 0.5f); - REQUIRE (b[0] == 0.5f); - REQUIRE (b[1] == -1.0f); - REQUIRE (a[0] == 0.25f); - REQUIRE (a[1] == 1.5f); + REQUIRE (juce::approximatelyEqual (b[0], 0.5f)); + REQUIRE (juce::approximatelyEqual (b[1], -1.0f)); + REQUIRE (juce::approximatelyEqual (a[0], 0.25f)); + REQUIRE (juce::approximatelyEqual (a[1], 1.5f)); } { // with normalization @@ -21,10 +21,10 @@ TEST_CASE ("Linear Transforms Test", "[dsp][filters]") double a[2] = { 0.5, 1.0 }; chowdsp::LinearTransforms::transformFeedback<1> (b, a, 0.5); - REQUIRE (b[0] == 2.0); - REQUIRE (b[1] == -4.0); - REQUIRE (a[0] == 1.0); - REQUIRE (a[1] == 6.0); + REQUIRE (juce::approximatelyEqual (b[0], 2.0)); + REQUIRE (juce::approximatelyEqual (b[1], -4.0)); + REQUIRE (juce::approximatelyEqual (a[0], 1.0)); + REQUIRE (juce::approximatelyEqual (a[1], 6.0)); } } } diff --git a/tests/dsp_tests/chowdsp_math_test/FloatVectorOperationsTest.cpp b/tests/dsp_tests/chowdsp_math_test/FloatVectorOperationsTest.cpp index b55b7eae5..313fa2400 100644 --- a/tests/dsp_tests/chowdsp_math_test/FloatVectorOperationsTest.cpp +++ b/tests/dsp_tests/chowdsp_math_test/FloatVectorOperationsTest.cpp @@ -381,7 +381,7 @@ TEMPLATE_TEST_CASE ("FloatVectorOperations Test", "[dsp][math]", float, double) chowdsp::FloatVectorOperations::rotate (data.data(), rotate, (int) numValues, scratchData.data()); for (auto [exp, actual] : chowdsp::zip (refData, data)) - REQUIRE (actual == exp); + REQUIRE (juce::approximatelyEqual (actual, exp)); } } #endif diff --git a/tests/dsp_tests/chowdsp_math_test/MatrixOpsTest.cpp b/tests/dsp_tests/chowdsp_math_test/MatrixOpsTest.cpp index 3cb905f67..dce96be0a 100644 --- a/tests/dsp_tests/chowdsp_math_test/MatrixOpsTest.cpp +++ b/tests/dsp_tests/chowdsp_math_test/MatrixOpsTest.cpp @@ -16,10 +16,10 @@ TEST_CASE ("Matrix Ops Test", "[dsp][math][simd]") chowdsp::MatrixOps::HouseHolder::inPlace (data.data()); for (auto& x : data) - REQUIRE_MESSAGE (x == -1.0f, "Householder ouput is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (x, -1.0f), "Householder ouput is incorrect!"); for (auto& x : data2) - REQUIRE_MESSAGE (x == -1.0f, "Householder out-of-place ouput is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (x, -1.0f), "Householder out-of-place ouput is incorrect!"); } SECTION ("Householder Scalar Odd Test") @@ -34,10 +34,10 @@ TEST_CASE ("Matrix Ops Test", "[dsp][math][simd]") chowdsp::MatrixOps::HouseHolder::inPlace (data.data()); for (auto& x : data) - REQUIRE_MESSAGE (x == -1.0f, "Householder ouput is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (x, -1.0f), "Householder ouput is incorrect!"); for (auto& x : data2) - REQUIRE_MESSAGE (x == -1.0f, "Householder out-of-place ouput is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (x, -1.0f), "Householder out-of-place ouput is incorrect!"); } SECTION ("Householder Vector Test") @@ -60,9 +60,9 @@ TEST_CASE ("Matrix Ops Test", "[dsp][math][simd]") chowdsp::MatrixOps::Hadamard::inPlace (data.data()); - REQUIRE_MESSAGE (data[0] == (float) size / (float) sqrt (size), "Hadamard value 0 is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (data[0], (float) size / (float) sqrt (size)), "Hadamard value 0 is incorrect!"); for (size_t i = 1; i < size; ++i) - REQUIRE_MESSAGE (data[i] == 0.0f, "Hadamard output is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (data[i], 0.0f), "Hadamard output is incorrect!"); } SECTION ("Hadamard Scalar Test Small") @@ -74,9 +74,9 @@ TEST_CASE ("Matrix Ops Test", "[dsp][math][simd]") chowdsp::MatrixOps::Hadamard::inPlace (data.data()); - REQUIRE_MESSAGE (data[0] == (float) size / (float) sqrt (size), "Hadamard value 0 is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (data[0], (float) size / (float) sqrt (size)), "Hadamard value 0 is incorrect!"); for (size_t i = 1; i < size; ++i) - REQUIRE_MESSAGE (data[i] == 0.0f, "Hadamard output is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (data[i], 0.0f), "Hadamard output is incorrect!"); } SECTION ("Hadamard Scalar Test Large") @@ -88,9 +88,9 @@ TEST_CASE ("Matrix Ops Test", "[dsp][math][simd]") chowdsp::MatrixOps::Hadamard::inPlace (data.data()); - REQUIRE_MESSAGE (data[0] == (float) size / (float) sqrt (size), "Hadamard value 0 is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (data[0], (float) size / (float) sqrt (size)), "Hadamard value 0 is incorrect!"); for (size_t i = 1; i < size; ++i) - REQUIRE_MESSAGE (data[i] == 0.0f, "Hadamard output is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (data[i], 0.0f), "Hadamard output is incorrect!"); } SECTION ("Hadamard Vector Test") @@ -101,12 +101,12 @@ TEST_CASE ("Matrix Ops Test", "[dsp][math][simd]") std::fill (data.begin(), data.end(), 1.0f); chowdsp::MatrixOps::Hadamard::inPlace (data.data()); - REQUIRE_MESSAGE (data[0].get (0) == float (size * VecType ::size) / (float) sqrt (size * VecType::size), "Hadamard value 0 is incorrect!"); - REQUIRE_MESSAGE (data[0].get (1) == 0.0f, "Hadamard output is incorrect!"); - REQUIRE_MESSAGE (data[0].get (2) == 0.0f, "Hadamard output is incorrect!"); - REQUIRE_MESSAGE (data[0].get (3) == 0.0f, "Hadamard output is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (data[0].get (0), float (size * VecType ::size) / (float) sqrt (size * VecType::size)), "Hadamard value 0 is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (data[0].get (1), 0.0f), "Hadamard output is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (data[0].get (2), 0.0f), "Hadamard output is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (data[0].get (3), 0.0f), "Hadamard output is incorrect!"); for (size_t i = 1; i < size; ++i) for (size_t j = 0; j < VecType::size; ++j) - REQUIRE_MESSAGE (data[i].get (j) == 0.0f, "Hadamard output is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (data[i].get (j), 0.0f), "Hadamard output is incorrect!"); } } diff --git a/tests/dsp_tests/chowdsp_math_test/PolynomialsTest.cpp b/tests/dsp_tests/chowdsp_math_test/PolynomialsTest.cpp index 5e50f020b..e7c989690 100644 --- a/tests/dsp_tests/chowdsp_math_test/PolynomialsTest.cpp +++ b/tests/dsp_tests/chowdsp_math_test/PolynomialsTest.cpp @@ -135,22 +135,22 @@ TEST_CASE ("Polynomials Test", "[dsp][math][simd]") { float testArr[4]; chowdsp::Polynomials::antiderivative<2> ({ 1.0f, 2.0f, 1.0f }, testArr, -1.0f); - REQUIRE_MESSAGE (testArr[0] == 1.0f / 3.0f, "Degree 3 is incorrect!"); - REQUIRE_MESSAGE (testArr[1] == 1.0f, "Degree 2 is incorrect!"); - REQUIRE_MESSAGE (testArr[2] == 1.0f, "Degree 1 is incorrect!"); - REQUIRE_MESSAGE (testArr[3] == -1.0f, "Degree 0 is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (testArr[0], 1.0f / 3.0f), "Degree 3 is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (testArr[1], 1.0f), "Degree 2 is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (testArr[2], 1.0f), "Degree 1 is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (testArr[3], -1.0f), "Degree 0 is incorrect!"); } { double testArr[7]; chowdsp::Polynomials::antiderivative<5> ({ 10.0, 2.0, 1.0, 0.0, -3.0, -2.5 }, testArr); - REQUIRE_MESSAGE (testArr[0] == 10.0 / 6.0, "Degree 6 is incorrect!"); - REQUIRE_MESSAGE (testArr[1] == 2.0 / 5.0, "Degree 5 is incorrect!"); - REQUIRE_MESSAGE (testArr[2] == 1.0 / 4.0, "Degree 4 is incorrect!"); - REQUIRE_MESSAGE (testArr[3] == 0.0, "Degree 3 is incorrect!"); - REQUIRE_MESSAGE (testArr[4] == -3.0 / 2.0, "Degree 2 is incorrect!"); - REQUIRE_MESSAGE (testArr[5] == -2.5, "Degree 1 is incorrect!"); - REQUIRE_MESSAGE (testArr[6] == 0.0, "Degree 0 is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (testArr[0], 10.0 / 6.0), "Degree 6 is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (testArr[1], 2.0 / 5.0), "Degree 5 is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (testArr[2], 1.0 / 4.0), "Degree 4 is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (testArr[3], 0.0), "Degree 3 is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (testArr[4], -3.0 / 2.0), "Degree 2 is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (testArr[5], -2.5), "Degree 1 is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (testArr[6], 0.0), "Degree 0 is incorrect!"); } } @@ -159,18 +159,18 @@ TEST_CASE ("Polynomials Test", "[dsp][math][simd]") { float testArr[2]; chowdsp::Polynomials::derivative<2> ({ 1.0f, 2.0f, 1.0f }, testArr); - REQUIRE_MESSAGE (testArr[0] == 2.0f, "Degree 1 is incorrect!"); - REQUIRE_MESSAGE (testArr[1] == 2.0f, "Degree 0 is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (testArr[0], 2.0f), "Degree 1 is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (testArr[1], 2.0f), "Degree 0 is incorrect!"); } { double testArr[5]; chowdsp::Polynomials::derivative<5> ({ 10.0, 2.0, 1.0, 0.0, -3.0, -2.5 }, testArr); - REQUIRE_MESSAGE (testArr[0] == 50.0, "Degree 4 is incorrect!"); - REQUIRE_MESSAGE (testArr[1] == 8.0, "Degree 3 is incorrect!"); - REQUIRE_MESSAGE (testArr[2] == 3.0, "Degree 2 is incorrect!"); - REQUIRE_MESSAGE (testArr[3] == 0.0, "Degree 1 is incorrect!"); - REQUIRE_MESSAGE (testArr[4] == -3.0, "Degree 0 is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (testArr[0], 50.0), "Degree 4 is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (testArr[1], 8.0), "Degree 3 is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (testArr[2], 3.0), "Degree 2 is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (testArr[3], 0.0), "Degree 1 is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (testArr[4], -3.0), "Degree 0 is incorrect!"); } } } diff --git a/tests/dsp_tests/chowdsp_math_test/RatioTest.cpp b/tests/dsp_tests/chowdsp_math_test/RatioTest.cpp index 4269c5016..73f9d20b6 100644 --- a/tests/dsp_tests/chowdsp_math_test/RatioTest.cpp +++ b/tests/dsp_tests/chowdsp_math_test/RatioTest.cpp @@ -5,16 +5,16 @@ TEST_CASE ("Constexpr Ratio Test", "[dsp][math]") { SECTION ("Ratio Test") { - static_assert (chowdsp::Ratio<33, 100>::value == 0.33f); - static_assert (chowdsp::Ratio<100, 1>::value == 100.0); - static_assert (chowdsp::Ratio<25, 50>::value == 0.5f); - static_assert (chowdsp::Ratio<12, 10>::value == 1.2f); + static_assert (juce::exactlyEqual (chowdsp::Ratio<33, 100>::value, 0.33f)); + static_assert (juce::exactlyEqual (chowdsp::Ratio<100, 1>::value, 100.0)); + static_assert (juce::exactlyEqual (chowdsp::Ratio<25, 50>::value, 0.5f)); + static_assert (juce::exactlyEqual (chowdsp::Ratio<12, 10>::value, 1.2f)); } SECTION ("Scientific Ratio Test") { - static_assert (chowdsp::ScientificRatio<33, 0>::value == 33.0f); - static_assert (chowdsp::ScientificRatio<12, -5>::value == 12.0e-5f); - static_assert (chowdsp::ScientificRatio<42, 10>::value == 42.0e10); + static_assert (juce::exactlyEqual (chowdsp::ScientificRatio<33, 0>::value, 33.0f)); + static_assert (juce::exactlyEqual (chowdsp::ScientificRatio<12, -5>::value, 12.0e-5f)); + static_assert (juce::exactlyEqual (chowdsp::ScientificRatio<42, 10>::value, 42.0e10)); } } diff --git a/tests/dsp_tests/chowdsp_modal_dsp_test/ModalFilterTest.cpp b/tests/dsp_tests/chowdsp_modal_dsp_test/ModalFilterTest.cpp index 3f0fc9e28..3545ff480 100644 --- a/tests/dsp_tests/chowdsp_modal_dsp_test/ModalFilterTest.cpp +++ b/tests/dsp_tests/chowdsp_modal_dsp_test/ModalFilterTest.cpp @@ -87,9 +87,9 @@ TEMPLATE_TEST_CASE ("Modal Filter Test", "[dsp][modal][simd]", float, double, xs filter.processBlock (buffer.data(), (int) buffer.size()); if constexpr (std::is_floating_point_v) - REQUIRE_MESSAGE (filter.getFreq() == (NumericType) testFreq2, "Modal filter frequency is incorrect"); + REQUIRE_MESSAGE (juce::exactlyEqual (filter.getFreq(), (NumericType) testFreq2), "Modal filter frequency is incorrect"); else - REQUIRE_MESSAGE (filter.getFreq().get (0) == (NumericType) testFreq2, "Modal filter frequency is incorrect"); + REQUIRE_MESSAGE (juce::exactlyEqual (filter.getFreq().get (0), (NumericType) testFreq2), "Modal filter frequency is incorrect"); auto mag = juce::Decibels::gainToDecibels (getMagnitude (buffer)); REQUIRE_MESSAGE ((mag - refMag) < (NumericType) -24.0f, "Modal filter is resonating at an incorrect frequency."); @@ -123,6 +123,8 @@ TEMPLATE_TEST_CASE ("Modal Filter Test", "[dsp][modal][simd]", float, double, xs const auto actual = filter.processSample ((T) 1); const auto expected = std::sin (initialPhase); + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wfloat-equal") REQUIRE_MESSAGE (chowdsp::SIMDUtils::all (actual == expected), "Incorrect initial phase!"); + JUCE_END_IGNORE_WARNINGS_GCC_LIKE } } diff --git a/tests/dsp_tests/chowdsp_simd_test/SIMDSmoothedValueTest.cpp b/tests/dsp_tests/chowdsp_simd_test/SIMDSmoothedValueTest.cpp index b1aa8e226..94e451e23 100644 --- a/tests/dsp_tests/chowdsp_simd_test/SIMDSmoothedValueTest.cpp +++ b/tests/dsp_tests/chowdsp_simd_test/SIMDSmoothedValueTest.cpp @@ -1,8 +1,9 @@ -#include #include - using namespace chowdsp::SIMDUtils; +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wfloat-equal") +#include + //static CommonSmoothedValueTests floatSIMDSmoothTest; //static CommonSmoothedValueTests doubleSIMDSmoothTest; TEMPLATE_PRODUCT_TEST_CASE ("Common Smoothed Value Tests", "[dsp][simd]", SIMDSmoothedValue, ((float, juce::ValueSmoothingTypes::Linear), (double, juce::ValueSmoothingTypes::Multiplicative))) @@ -156,3 +157,4 @@ TEMPLATE_PRODUCT_TEST_CASE ("Common Smoothed Value Tests", "[dsp][simd]", SIMDSm } } } +JUCE_END_IGNORE_WARNINGS_GCC_LIKE diff --git a/tests/dsp_tests/chowdsp_simd_test/SIMDSpecialMathTest.cpp b/tests/dsp_tests/chowdsp_simd_test/SIMDSpecialMathTest.cpp index 45c2b5f73..99b1f7611 100644 --- a/tests/dsp_tests/chowdsp_simd_test/SIMDSpecialMathTest.cpp +++ b/tests/dsp_tests/chowdsp_simd_test/SIMDSpecialMathTest.cpp @@ -1,9 +1,9 @@ -#include #include -#include "CatchUtils.h" - using namespace chowdsp::SIMDUtils; +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wfloat-equal") +#include + #define FLOATFUNC(func) [] (FloatType x) { return func (x); } #define SIMDFUNC(func) [] (xsimd::batch x) { return func (x); } @@ -73,3 +73,5 @@ TEMPLATE_TEST_CASE ("SIMD Special Math Test", "[dsp][simd]", float, double) #undef FLOATFUNC #undef SIMDFUNC + +JUCE_END_IGNORE_WARNINGS_GCC_LIKE diff --git a/tests/dsp_tests/chowdsp_sources_test/PolygonalTest.cpp b/tests/dsp_tests/chowdsp_sources_test/PolygonalTest.cpp index aea356658..6fdc26f6d 100644 --- a/tests/dsp_tests/chowdsp_sources_test/PolygonalTest.cpp +++ b/tests/dsp_tests/chowdsp_sources_test/PolygonalTest.cpp @@ -40,9 +40,9 @@ TEST_CASE ("Polygonal Test", "[dsp][sources]") testOsc.setFrequency (testFreq); testOsc.setOrder (polygonOrder); testOsc.setTeeth (polygonTeeth); - REQUIRE_MESSAGE (testOsc.getFrequency() == testFreq, "Set frequency is incorrect!"); - REQUIRE_MESSAGE (testOsc.getOrder() == polygonOrder, "Set Order is incorrect!"); - REQUIRE_MESSAGE (testOsc.getTeeth() == polygonTeeth, "Set Teeth is incorrect!"); + REQUIRE_MESSAGE (juce::exactlyEqual (testOsc.getFrequency(), testFreq), "Set frequency is incorrect!"); + REQUIRE_MESSAGE (juce::exactlyEqual (testOsc.getOrder(), polygonOrder), "Set Order is incorrect!"); + REQUIRE_MESSAGE (juce::exactlyEqual (testOsc.getTeeth(), polygonTeeth), "Set Teeth is incorrect!"); for (int i = 0; i < 20; ++i) { @@ -71,9 +71,9 @@ TEST_CASE ("Polygonal Test", "[dsp][sources]") testOsc.setFrequency (testFreq); testOsc.setOrder (polygonOrder); testOsc.setTeeth (polygonTeeth); - REQUIRE_MESSAGE (testOsc.getFrequency().get (0) == testFreq, "Set frequency is incorrect!"); - REQUIRE_MESSAGE (testOsc.getOrder().get (0) == polygonOrder, "Set Order is incorrect!"); - REQUIRE_MESSAGE (testOsc.getTeeth().get (0) == polygonTeeth, "Set Teeth is incorrect!"); + REQUIRE_MESSAGE (juce::exactlyEqual (testOsc.getFrequency().get (0), testFreq), "Set frequency is incorrect!"); + REQUIRE_MESSAGE (juce::exactlyEqual (testOsc.getOrder().get (0), polygonOrder), "Set Order is incorrect!"); + REQUIRE_MESSAGE (juce::exactlyEqual (testOsc.getTeeth().get (0), polygonTeeth), "Set Teeth is incorrect!"); for (int i = 0; i < 20; ++i) { @@ -124,6 +124,6 @@ TEST_CASE ("Polygonal Test", "[dsp][sources]") testOsc.setFrequency (0.0f); for (int i = 0; i < 10; ++i) - REQUIRE_MESSAGE (testOsc.processSample() == 0.0f, "Zero Hz output is non-zero!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (testOsc.processSample(), 0.0f), "Zero Hz output is non-zero!"); } } diff --git a/tests/dsp_tests/chowdsp_sources_test/SawtoothTest.cpp b/tests/dsp_tests/chowdsp_sources_test/SawtoothTest.cpp index 5f75fe5f1..0255d17d3 100644 --- a/tests/dsp_tests/chowdsp_sources_test/SawtoothTest.cpp +++ b/tests/dsp_tests/chowdsp_sources_test/SawtoothTest.cpp @@ -29,7 +29,7 @@ TEST_CASE ("Sawtooth Test", "[dsp][sources]") chowdsp::SawtoothWave testOsc; testOsc.prepare ({ _sampleRate, (juce::uint32) _blockSize, 1 }); testOsc.setFrequency (testFreq); - REQUIRE_MESSAGE (testOsc.getFrequency() == testFreq, "Set frequency is incorrect!"); + REQUIRE_MESSAGE (juce::exactlyEqual (testOsc.getFrequency(), testFreq), "Set frequency is incorrect!"); testOsc.processSample(); // for half-sample delay? for (int i = 0; i < 20; ++i) @@ -58,7 +58,7 @@ TEST_CASE ("Sawtooth Test", "[dsp][sources]") chowdsp::SawtoothWave> testOsc; testOsc.prepare ({ _sampleRate, (juce::uint32) _blockSize, 1 }); testOsc.setFrequency (testFreq); - REQUIRE_MESSAGE (testOsc.getFrequency().get (0) == testFreq, "Set frequency is incorrect!"); + REQUIRE_MESSAGE (juce::exactlyEqual (testOsc.getFrequency().get (0), testFreq), "Set frequency is incorrect!"); testOsc.processSample(); // for half-sample delay for (int i = 0; i < 20; ++i) @@ -111,6 +111,6 @@ TEST_CASE ("Sawtooth Test", "[dsp][sources]") testOsc.setFrequency (0.0f); for (int i = 0; i < 10; ++i) - REQUIRE_MESSAGE (testOsc.processSample() == 0.0f, "Zero Hz output is non-zero!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (testOsc.processSample(), 0.0f), "Zero Hz output is non-zero!"); } } diff --git a/tests/dsp_tests/chowdsp_sources_test/SquareTest.cpp b/tests/dsp_tests/chowdsp_sources_test/SquareTest.cpp index af4f672a9..9c6c10e92 100644 --- a/tests/dsp_tests/chowdsp_sources_test/SquareTest.cpp +++ b/tests/dsp_tests/chowdsp_sources_test/SquareTest.cpp @@ -29,7 +29,7 @@ TEST_CASE ("Square Test", "[dsp][sources]") chowdsp::SquareWave testOsc; testOsc.prepare ({ _sampleRate, (juce::uint32) _blockSize, 1 }); testOsc.setFrequency (testFreq); - REQUIRE_MESSAGE (testOsc.getFrequency() == testFreq, "Set frequency is incorrect!"); + REQUIRE_MESSAGE (juce::exactlyEqual (testOsc.getFrequency(), testFreq), "Set frequency is incorrect!"); testOsc.processSample(); // for half-sample delay for (int i = 0; i < 20; ++i) @@ -58,7 +58,7 @@ TEST_CASE ("Square Test", "[dsp][sources]") chowdsp::SquareWave> testOsc; testOsc.prepare ({ _sampleRate, (juce::uint32) _blockSize, 1 }); testOsc.setFrequency (testFreq); - REQUIRE_MESSAGE (testOsc.getFrequency().get (0) == testFreq, "Set frequency is incorrect!"); + REQUIRE_MESSAGE (juce::exactlyEqual (testOsc.getFrequency().get (0), testFreq), "Set frequency is incorrect!"); testOsc.processSample(); // for half-sample delay for (int i = 0; i < 20; ++i) @@ -111,6 +111,6 @@ TEST_CASE ("Square Test", "[dsp][sources]") testOsc.setFrequency (0.0f); for (int i = 0; i < 10; ++i) - REQUIRE_MESSAGE (testOsc.processSample() == 0.0f, "Zero Hz output is non-zero!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (testOsc.processSample(), 0.0f), "Zero Hz output is non-zero!"); } } diff --git a/tests/dsp_tests/chowdsp_sources_test/TriangleTest.cpp b/tests/dsp_tests/chowdsp_sources_test/TriangleTest.cpp index 47fc97a62..d1df828c2 100644 --- a/tests/dsp_tests/chowdsp_sources_test/TriangleTest.cpp +++ b/tests/dsp_tests/chowdsp_sources_test/TriangleTest.cpp @@ -29,7 +29,7 @@ TEST_CASE ("Triangle Test", "[dsp][sources]") chowdsp::TriangleWave testOsc; testOsc.prepare ({ _sampleRate, (juce::uint32) _blockSize, 1 }); testOsc.setFrequency (testFreq); - REQUIRE_MESSAGE (testOsc.getFrequency() == testFreq, "Set frequency is incorrect!"); + REQUIRE_MESSAGE (juce::exactlyEqual (testOsc.getFrequency(), testFreq), "Set frequency is incorrect!"); testOsc.processSample(); // for half-sample delay? for (int i = 0; i < 20; ++i) @@ -58,7 +58,7 @@ TEST_CASE ("Triangle Test", "[dsp][sources]") chowdsp::TriangleWave> testOsc; testOsc.prepare ({ _sampleRate, (juce::uint32) _blockSize, 1 }); testOsc.setFrequency (testFreq); - REQUIRE_MESSAGE (testOsc.getFrequency().get (0) == testFreq, "Set frequency is incorrect!"); + REQUIRE_MESSAGE (juce::exactlyEqual (testOsc.getFrequency().get (0), testFreq), "Set frequency is incorrect!"); testOsc.processSample(); // for half-sample delay for (int i = 0; i < 20; ++i) @@ -111,6 +111,6 @@ TEST_CASE ("Triangle Test", "[dsp][sources]") testOsc.setFrequency (0.0f); for (int i = 0; i < 10; ++i) - REQUIRE_MESSAGE (testOsc.processSample() == 0.0f, "Zero Hz output is non-zero!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (testOsc.processSample(), 0.0f), "Zero Hz output is non-zero!"); } } diff --git a/tests/gui_tests/chowdsp_visualizers_test/EqualizerPlotTest.cpp b/tests/gui_tests/chowdsp_visualizers_test/EqualizerPlotTest.cpp index e3d4dd4d6..8820b71e2 100644 --- a/tests/gui_tests/chowdsp_visualizers_test/EqualizerPlotTest.cpp +++ b/tests/gui_tests/chowdsp_visualizers_test/EqualizerPlotTest.cpp @@ -114,10 +114,12 @@ TEST_CASE ("Equalizer Plot Test", "[visualizers][EQ]") chowdsp::ParameterTypeHelpers::setValue (false, *state.params.testEQParams.eqParams[3].onOffParam); juce::MessageManager::getInstance()->runDispatchLoopUntil (100); +#if JUCE_MAC const auto testScreenshot = plotComp.createComponentSnapshot ({ 500, 300 }); - // VizTestUtils::saveImage (testScreenshot, "eq_response_plot.png"); + // VizTestUtils::saveImage (testScreenshot, "eq_response_plot.png"); const auto refScreenshot = VizTestUtils::loadImage ("eq_response_plot.png"); VizTestUtils::compareImages (testScreenshot, refScreenshot); +#endif } } diff --git a/tests/gui_tests/chowdsp_visualizers_test/GenericFilterPlotTest.cpp b/tests/gui_tests/chowdsp_visualizers_test/GenericFilterPlotTest.cpp index c3d8da16b..7b9464b32 100644 --- a/tests/gui_tests/chowdsp_visualizers_test/GenericFilterPlotTest.cpp +++ b/tests/gui_tests/chowdsp_visualizers_test/GenericFilterPlotTest.cpp @@ -19,10 +19,10 @@ TEST_CASE ("Generic Filter Plot Test", "[visualizers]") }; const auto [freqAxis, magAxis] = plotter.plotFilterMagnitudeResponse(); - REQUIRE (freqAxis.size() == (1 << plotter.params.fftOrder) / 2 + 1); + REQUIRE (freqAxis.size() == size_t (1 << plotter.params.fftOrder) / 2 + 1); REQUIRE (magAxis.size() == freqAxis.size()); for (auto& mag : magAxis) - REQUIRE (mag == 0.0f); + REQUIRE (juce::approximatelyEqual (mag, 0.0f)); } SECTION ("Filter Plot Test") @@ -56,11 +56,13 @@ TEST_CASE ("Generic Filter Plot Test", "[visualizers]") chowdsp::SVFNotch filter; }; +#if JUCE_MAC TestComponent comp {}; const auto testScreenshot = comp.createComponentSnapshot ({ 500, 300 }); - // VizTestUtils::saveImage (testScreenshot, "generic_filter_plot.png"); + // VizTestUtils::saveImage (testScreenshot, "generic_filter_plot.png"); const auto refScreenshot = VizTestUtils::loadImage ("generic_filter_plot.png"); VizTestUtils::compareImages (testScreenshot, refScreenshot); +#endif } } diff --git a/tests/gui_tests/chowdsp_visualizers_test/Images/eq_response_plot.png b/tests/gui_tests/chowdsp_visualizers_test/Images/eq_response_plot.png index 760afca037cf3b75ec9d9810fdf6535e88d592e4..952dd80d498178f88f221b64043400e6ef75b058 100644 GIT binary patch literal 15748 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 DibDe0 diff --git a/tests/gui_tests/chowdsp_visualizers_test/Images/freq_grid_plot.png b/tests/gui_tests/chowdsp_visualizers_test/Images/freq_grid_plot.png index 5ae0fdd2ce9fe60c1f3e9f8a9c23995bb4a37996..88ce7dd901ea915263c372e5cd1e3c861122101d 100644 GIT binary patch literal 3473 zcmeHKZA?>V7`@2+7!weO(P>eKm@##r>QrJjU|OMQxv?^W5Sc+asKv7CS)@r;U!nn6-< zXr(Xs=btM(J3H?q=`xfx)QUCe{h=NO`>Nw62l+_033klI4Egpkxn;ixid<4Y`ie2Wj=#;9R0upa+=rd7L`r)+)27c zWkEZRdkZ74k>?lpMXo&USm2oe-YNjlgB8;dl|`N3ToUOXv1%s?a_wQ`}1g>nDmy;7O)urJZeJ=CL_Yh6LZin}&3v_yT z7pbJdJ;Kwsx#V#)!NLDS3xz@%)fcYD{8lQHC5LU3dd9b8+hikRFi1Oxp776|LAG^1 z;m@`XWRUOfUzAhhkTh4`?s94i8qgy?e6eeIYDzz$g-grI%Ji!fOt~RajYeY`AJ_61 zRPU4OYRz1mF;r|M*J0xm#wORPdKgY)GMQ$h@q2vLLYii``)p_dpx2AyS>i2>qZ8u? ztat%d9!9gS|FP>0ru|X?J4q!}mKV^>R!||lzTM#p1fb&)(-2Yg^a`TrH8SM=;Die_ z^K~Bz<89}ByX&N;|AmQBp~Og%Gk%P!+B$7&UVar0#52j~W@Tx%SH}M0Q+A1l|RajTNUwCq&=0$MAfzm@X1Ry|PD;pab0oo~RBMYD3 zS%ctfHR8(3N{$TS8jKwm26+T6uiJU6Xpzgepd0Hy4~^|e4HVZIP(fg^#DMB1Ck4Q7 z;XU`BD*a>NtO6!5mFfL4XOLee#?4#KD2gg8kQ5$|Hv~UuZX0Ct1%kASiVAp7uM=06 zs7sfIPDwTh4*)5JPT6`yKrI%FMI90nl2db}gp(K!y!GgGnDpNumOYw91!5@%MzhNI z4W|8407QZT`+k*R|IxiUl%=|1+}@s__@XV(qNS+TffQ25r-+pUxF!efYt=R}va+(k zN`;$_JkkP+oI5C?q0(0t48o*?qN-A24Rz{mB{WT~%|&zm5KYY@$>N|h2@}?Q*SH$$0 za2q#2-vIm;-R(oTtpNlCiF zK?b_J{uq+3sneN7_Ee$qY1<$svWHlW7E2ur!?hxjNdH5^!9*gfI^FTFb!Q~&?~ literal 3553 zcmeH~e^8Ql9LFDLm#(Ho^YX{YR^**rW}2*#QD{+H=DB7We+u_o=HT=0H(wueyK~i{kZ1ee!2ovAhk?_j3U6GfKf)EQ(as;k^_(R+^7<2b#5Vi>u%SP(ko z%Q&8J%yHVTJzSQgQ~Q@MwSLY;?|m0TBXmfZZSl| zuW@vRtLQevlItp}mx?ba(ql>z2KU<{i*}x{Ty9Bao?75DpAdnM%2Fdg;EKt#Sq-_l zuDr8UCUVo--Wwq;w+jrpMpy!ukGR?*PV=$}E)jUgEOlduofUHMr4Q*CAg-yR^8s7P9E}r`w5@iJ|8Yc*Um`ak$*;BdOu%n-<3JsPmW->&4sh zloP|Y89t$BN8pKJ?RK7R24`GJZfE7WQQN>~shCX^jXFOlxXh54Gy^}%loRV=>eB+T1+f5 z3j()=&V!(&n2da$F?|l=^gamEje9Q0%!b^Dsrphz%yllg#IWWU7OL;gn)>`K(tugjK83jP0rNSFmun^KPM3D%n)kmLC^f0hXaF z8XoP=PE^t!tD8H8@j{`HNfEAjJb0s1>G6(5XO6~!qf)>mJTx)D)a@kyWRY zx2mgHC%s#>^N?7#IVvA>1A};C{SOy@>^gg~>S!q{APBLmcwc7Z?HmzM%^)4d}+47|ta)1hj z$(lPVvv0_~MGC_ieDt_YPDc}!XY@Pk&+4QC+8OZ5QQeUAV`o0*6;U3CuIObKXdaBD z_6wSTPLftqSQQ4+f4T8Y4$$T>Xww+VjO`J-LZPU2x`nOUq`tL`!LhM9K<3-`ExC?z&rzAAX6m0eQBIC#13)^? zVP9rS5t`|7u(>j^<16y!JFKoyB9>>(LwmdC@&mCvqur2+w+;C=GYzQ}w5f;2;jfQB zRze3JPvFKgIRxsUUqG8bMC9^8+G~P~8QGH)3N`uO-*Iu{Zn`}V*HliWW>nOgzKqHz zfo1Nn=z!_pDi<|CmW`ztM@AgAhGh-zlENCBx|xSs$h==s4oud5nckmyiWIh{-lvUC zEeP1EtnB>=@2Qjai}JuLTORDK>F3K&f>*+caWRSu5NVfEC_+=uu2P6JudsG^NR|oQ zcqT_E^Au>a1inP25W!!s11@G{PfDmV)0ngVqH>W`TvjvP>XFnvKduy9vD5o_ah_Va z;5eVpA08deHjG5Gj|fuJ(&Rwq3ut=Gb!LuCzYT)!DQB*Rdo}kJJg%?$9mEo>dpF-G3gHVX8n1A9ctXl`4406ZQUoi@ShC{ M_7A6uSA6#M-ykzIX#fBK diff --git a/tests/gui_tests/chowdsp_visualizers_test/Images/generic_filter_plot.png b/tests/gui_tests/chowdsp_visualizers_test/Images/generic_filter_plot.png index 799b5b80562dcc930119fcde67d94179fda13280..7ed2a8f7ac13e5a869b1d922ac6a2c7b9a92bdb8 100644 GIT binary patch literal 6591 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 literal 6304 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 diff --git a/tests/gui_tests/chowdsp_visualizers_test/Images/waveshaper_plot.png b/tests/gui_tests/chowdsp_visualizers_test/Images/waveshaper_plot.png index 40633fbd56d99a58aed07f2f228debec6f3ef917..c432a64faf376a83094ddd020d989b92ad619224 100644 GIT binary patch literal 5189 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 literal 4429 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 (0.5f); REQUIRE_MESSAGE (testParam->getName (1024) == juce::String ("NONE"), "Parameter name is incorrect!"); - REQUIRE_MESSAGE (testParam->getValue() == 0.0f, "Parameter value is incorrect!"); - REQUIRE_MESSAGE (testParam->getDefaultValue() == 0.0f, "Default parameter value is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (testParam->getValue(), 0.0f), "Parameter value is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (testParam->getDefaultValue(), 0.0f), "Default parameter value is incorrect!"); REQUIRE_MESSAGE (testParam->getText (0.2f, 1024) == juce::String(), "Parameter text is incorrect!"); - REQUIRE_MESSAGE (testParam->getValueForText ("0.9") == 0.0f, "Parameter value for text is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (testParam->getValueForText ("0.9"), 0.0f), "Parameter value for text is incorrect!"); auto& norm = dynamic_cast (testParam.get())->getNormalisableRange(); - REQUIRE_MESSAGE (norm.interval == 0.01f, "Parameter normalization is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (norm.interval, 0.01f), "Parameter normalization is incorrect!"); auto* modulatableTestParam = dynamic_cast (testParam.get()); REQUIRE_MESSAGE (! modulatableTestParam->supportsMonophonicModulation(), "Null parameter should not support modulation!"); @@ -62,16 +62,16 @@ TEST_CASE ("Forwarding Parameter Test", "[plugin][parameters]") auto* testParam = (juce::AudioProcessorParameter*) forwardingParam; REQUIRE_MESSAGE (testParam->getName (1024) == dummyParam->getName (1024), "Parameter name is incorrect!"); - REQUIRE_MESSAGE (testParam->getDefaultValue() == dummyParam->getDefaultValue(), "Default parameter value is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (testParam->getDefaultValue(), dummyParam->getDefaultValue()), "Default parameter value is incorrect!"); REQUIRE_MESSAGE (testParam->getText (0.2f, 1024) == dummyParam->getText (0.2f, 1024), "Parameter text is incorrect!"); - REQUIRE_MESSAGE (testParam->getValueForText ("0.9") == dummyParam->getValueForText ("0.9"), "Parameter value for text is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (testParam->getValueForText ("0.9"), dummyParam->getValueForText ("0.9")), "Parameter value for text is incorrect!"); auto& expNorm = dummy.getState().params.dummy->getNormalisableRange(); auto& actualNorm = dynamic_cast (testParam)->getNormalisableRange(); - REQUIRE_MESSAGE (actualNorm.start == expNorm.start, "Range start is incorrect!"); - REQUIRE_MESSAGE (actualNorm.end == expNorm.end, "Range end is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (actualNorm.start, expNorm.start), "Range start is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (actualNorm.end, expNorm.end), "Range end is incorrect!"); - REQUIRE_MESSAGE (testParam->getValue() == dummyParam->getValue(), "Initial parameter value is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (testParam->getValue(), dummyParam->getValue()), "Initial parameter value is incorrect!"); constexpr float error = 1.0e-6f; constexpr float value1 = 0.8f; @@ -160,9 +160,9 @@ TEST_CASE ("Forwarding Parameter Test", "[plugin][parameters]") REQUIRE_MESSAGE (! testParamAsModParam->supportsMonophonicModulation(), "juce::AudioParameterFloat should not support modulation!"); REQUIRE_MESSAGE (! testParamAsModParam->supportsPolyphonicModulation(), "juce::AudioParameterFloat should not support modulation!"); - REQUIRE_MESSAGE (forwardParam->get() == defaultValue, "Parameter has incorrect value before parameter modulation!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (forwardParam->get(), defaultValue), "Parameter has incorrect value before parameter modulation!"); testParamAsModParam->applyMonophonicModulation ((double) modulationAmount); // should have no effect, since the parameter doesn't support modulation! - REQUIRE_MESSAGE (forwardParam->get() == defaultValue, "Parameter has incorrect value after parameter modulation!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (forwardParam->get(), defaultValue), "Parameter has incorrect value after parameter modulation!"); testParam->setParam (nullptr); } @@ -191,9 +191,9 @@ TEST_CASE ("Forwarding Parameter Test", "[plugin][parameters]") REQUIRE_MESSAGE (testParamAsModParam->supportsMonophonicModulation(), "chowdsp::FloatParameter should support monophonic modulation!"); REQUIRE_MESSAGE (! testParamAsModParam->supportsPolyphonicModulation(), "chowdsp::FloatParameter should not support polyphonic modulation!"); - REQUIRE_MESSAGE (forwardParam->getCurrentValue() == defaultValue, "Parameter has incorrect value before parameter modulation!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (forwardParam->getCurrentValue(), defaultValue), "Parameter has incorrect value before parameter modulation!"); testParamAsModParam->applyMonophonicModulation ((double) modulationAmount); - REQUIRE_MESSAGE (forwardParam->getCurrentValue() == defaultValue + modulationAmount, "Parameter has incorrect value after parameter modulation!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (forwardParam->getCurrentValue(), defaultValue + modulationAmount), "Parameter has incorrect value after parameter modulation!"); testParam->setParam (nullptr); } diff --git a/tests/plugin_tests/chowdsp_parameters_test/ParamModulationTest.cpp b/tests/plugin_tests/chowdsp_parameters_test/ParamModulationTest.cpp index 405e1d98d..d0aeafbdb 100644 --- a/tests/plugin_tests/chowdsp_parameters_test/ParamModulationTest.cpp +++ b/tests/plugin_tests/chowdsp_parameters_test/ParamModulationTest.cpp @@ -25,14 +25,14 @@ TEST_CASE ("Parameter Modulation Test", "[plugin][parameters]") chowdsp::FloatParameter floatParam { "test", "Test", juce::NormalisableRange { 0.0f, 1.0f }, 0.5f, &floatValToString, &stringToFloatVal }; REQUIRE_MESSAGE (floatParam.supportsMonophonicModulation(), "Float Parameters should support monophonic modulation"); REQUIRE_MESSAGE (! floatParam.supportsPolyphonicModulation(), "Float Parameters should not support polyphonic modulation"); - REQUIRE_MESSAGE (floatParam.getDefaultValue() == 0.5f, "Float parameter default value is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (floatParam.getDefaultValue(), 0.5f), "Float parameter default value is incorrect!"); floatParam.applyMonophonicModulation (0.25); - REQUIRE_MESSAGE ((float) floatParam == 0.75f, "Float parameter modulation is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual ((float) floatParam, 0.75f), "Float parameter modulation is incorrect!"); floatParam.setValueNotifyingHost (1.0f); floatParam.applyMonophonicModulation (-0.75); - REQUIRE_MESSAGE (floatParam.getCurrentValue() == 0.25f, "Float parameter modulation is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (floatParam.getCurrentValue(), 0.25f), "Float parameter modulation is incorrect!"); } SECTION ("Check Smooth Buffered Float Param Modulation") @@ -47,15 +47,15 @@ TEST_CASE ("Parameter Modulation Test", "[plugin][parameters]") smoothedParam.setRampLength ((double) blockSize / fs); smoothedParam.prepare (fs, blockSize); - REQUIRE_MESSAGE (smoothedParam.getCurrentValue() == 0.5f, "Initial smoothed value is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (smoothedParam.getCurrentValue(), 0.5f), "Initial smoothed value is incorrect!"); floatParam.applyMonophonicModulation (0.5); smoothedParam.process (blockSize); - REQUIRE_MESSAGE (smoothedParam.getCurrentValue() == 1.0f, "Smoothed value after modulation is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (smoothedParam.getCurrentValue(), 1.0f), "Smoothed value after modulation is incorrect!"); floatParam.setValueNotifyingHost (1.0f); floatParam.applyMonophonicModulation (-0.75); smoothedParam.process (blockSize); - REQUIRE_MESSAGE (floatParam.getCurrentValue() == 0.25f, "Smoothed value after parameter change and modulation is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (floatParam.getCurrentValue(), 0.25f), "Smoothed value after parameter change and modulation is incorrect!"); } } diff --git a/tests/plugin_tests/chowdsp_parameters_test/ParamStringsTest.cpp b/tests/plugin_tests/chowdsp_parameters_test/ParamStringsTest.cpp index dbbfe39e7..54db285bd 100644 --- a/tests/plugin_tests/chowdsp_parameters_test/ParamStringsTest.cpp +++ b/tests/plugin_tests/chowdsp_parameters_test/ParamStringsTest.cpp @@ -9,42 +9,42 @@ TEST_CASE ("Param Strings Test", "[plugin][parameters]") { REQUIRE_MESSAGE (freqValToString (100.0f) == juce::String ("100.00 Hz"), "Incorrect small freq string!"); REQUIRE_MESSAGE (freqValToString (10000.0f) == juce::String ("10.00 kHz"), "Incorrect large freq string!"); - REQUIRE_MESSAGE (stringToFreqVal ("100") == 100.0f, "Incorrect small freq value!"); - REQUIRE_MESSAGE (stringToFreqVal ("10k") == 10000.0f, "Incorrect large freq value!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (stringToFreqVal ("100"), 100.0f), "Incorrect small freq value!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (stringToFreqVal ("10k"), 10000.0f), "Incorrect large freq value!"); } SECTION ("Percent Param Test") { REQUIRE_MESSAGE (percentValToString (0.5f) == juce::String ("50%"), "Incorrect percent string!"); - REQUIRE_MESSAGE (stringToPercentVal ("50%") == 0.5f, "Incorrect percent value!"); - REQUIRE_MESSAGE (stringToPercentVal ("50") == 0.5f, "Incorrect value!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (stringToPercentVal ("50%"), 0.5f), "Incorrect percent value!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (stringToPercentVal ("50"), 0.5f), "Incorrect value!"); } SECTION ("Gain Param Test") { REQUIRE_MESSAGE (gainValToString (10.0f) == juce::String ("10.00 dB"), "Incorrect dB string!"); - REQUIRE_MESSAGE (stringToGainVal ("10 dB") == 10.0f, "Incorrect dB value!"); - REQUIRE_MESSAGE (stringToGainVal ("10") == 10.0f, "Incorrect value!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (stringToGainVal ("10 dB"), 10.0f), "Incorrect dB value!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (stringToGainVal ("10"), 10.0f), "Incorrect value!"); } SECTION ("Ratio Param Test") { REQUIRE_MESSAGE (ratioValToString (10.0f) == juce::String ("10.00 : 1"), "Incorrect ratio string!"); - REQUIRE_MESSAGE (stringToRatioVal ("10 : 1") == 10.0f, "Incorrect ratio value!"); - REQUIRE_MESSAGE (stringToRatioVal ("10") == 10.0f, "Incorrect value!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (stringToRatioVal ("10 : 1"), 10.0f), "Incorrect ratio value!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (stringToRatioVal ("10"), 10.0f), "Incorrect value!"); } SECTION ("Time Param Test") { REQUIRE_MESSAGE (timeMsValToString (10.0f) == juce::String ("10.00 ms"), "Incorrect milliseconds string!"); REQUIRE_MESSAGE (timeMsValToString (2000.0f) == juce::String ("2.00 s"), "Incorrect seconds string!"); - REQUIRE_MESSAGE (stringToTimeMsVal ("200 ms") == 200.0f, "Incorrect milliseconds value!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (stringToTimeMsVal ("200 ms"), 200.0f), "Incorrect milliseconds value!"); } SECTION ("Float Param Test") { REQUIRE_MESSAGE (floatValToString (10.0f) == juce::String ("10.00"), "Incorrect float string!"); REQUIRE_MESSAGE (floatValToStringDecimal<1> (10.0f) == juce::String ("10.0"), "Incorrect float decimal string!"); - REQUIRE_MESSAGE (stringToFloatVal ("200") == 200.0f, "Incorrect float value!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (stringToFloatVal ("200"), 200.0f), "Incorrect float value!"); } } diff --git a/tests/plugin_tests/chowdsp_parameters_test/RhythmParameterTest.cpp b/tests/plugin_tests/chowdsp_parameters_test/RhythmParameterTest.cpp index 2e527349e..66778e51b 100644 --- a/tests/plugin_tests/chowdsp_parameters_test/RhythmParameterTest.cpp +++ b/tests/plugin_tests/chowdsp_parameters_test/RhythmParameterTest.cpp @@ -7,6 +7,6 @@ TEST_CASE ("Rhythm Parameter Test", "[plugin][parameters]") { auto&& param = chowdsp::RhythmParameter ("rhythm", "Rhythm"); REQUIRE_MESSAGE (param.getCurrentChoiceName() == juce::String ("1/4"), "Parameter choice label is incorrect!"); - REQUIRE_MESSAGE (param.getRhythmTimeSeconds (60.0) == 1.0, "Quarter Note rhythm time is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (param.getRhythmTimeSeconds (60.0), 1.0), "Quarter Note rhythm time is incorrect!"); } } diff --git a/tests/plugin_tests/chowdsp_plugin_base_test/PluginBaseTest.cpp b/tests/plugin_tests/chowdsp_plugin_base_test/PluginBaseTest.cpp index f71905753..4a25b3ffb 100644 --- a/tests/plugin_tests/chowdsp_plugin_base_test/PluginBaseTest.cpp +++ b/tests/plugin_tests/chowdsp_plugin_base_test/PluginBaseTest.cpp @@ -67,7 +67,7 @@ TEST_CASE ("Plugin Base Test", "[plugin]") bool midiBehaviour = dummy1.acceptsMidi() || dummy1.producesMidi() || dummy1.isMidiEffect(); REQUIRE_MESSAGE (! midiBehaviour, "MIDI behaviour incorrect!"); - REQUIRE_MESSAGE (dummy1.getTailLengthSeconds() == 0.0, "Tail length incorrect!"); + REQUIRE_MESSAGE (juce::exactlyEqual (dummy1.getTailLengthSeconds(), 0.0), "Tail length incorrect!"); REQUIRE_MESSAGE (dummy1.getNumPrograms() > 0, "Num programs is to low!"); dummy1.setCurrentProgram (0); diff --git a/tests/plugin_tests/chowdsp_plugin_state_test/ParameterAttachmentsTest.cpp b/tests/plugin_tests/chowdsp_plugin_state_test/ParameterAttachmentsTest.cpp index f4ed69061..efec5e1a6 100644 --- a/tests/plugin_tests/chowdsp_plugin_state_test/ParameterAttachmentsTest.cpp +++ b/tests/plugin_tests/chowdsp_plugin_state_test/ParameterAttachmentsTest.cpp @@ -22,12 +22,12 @@ TEST_CASE ("Slider Attachment Test", "[plugin][state][attachments]") juce::Slider slider; chowdsp::SliderAttachment attach { param, state, slider }; - REQUIRE_MESSAGE (slider.getValue() == (double) param->get(), "Incorrect slider value after setup!"); - REQUIRE_MESSAGE (slider.getRange().getStart() == 0.0, "Slider range start is incorrect!"); - REQUIRE_MESSAGE (slider.getRange().getEnd() == 1.0, "Slider range end is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (slider.getValue(), (double) param->get()), "Incorrect slider value after setup!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (slider.getRange().getStart(), 0.0), "Slider range start is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (slider.getRange().getEnd(), 1.0), "Slider range end is incorrect!"); REQUIRE_MESSAGE (slider.textFromValueFunction (param->get()) == param->getCurrentValueAsText(), "Slider text from value function is incorrect!"); - REQUIRE_MESSAGE (slider.valueFromTextFunction (param->getCurrentValueAsText()) == (double) param->get(), "Slider value from text function is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (slider.valueFromTextFunction (param->getCurrentValueAsText()), (double) param->get()), "Slider value from text function is incorrect!"); } SECTION ("Slider Change Test") @@ -45,7 +45,7 @@ TEST_CASE ("Slider Attachment Test", "[plugin][state][attachments]") slider.setValue ((double) newValue, juce::sendNotificationSync); } - REQUIRE_MESSAGE (param->get() == newValue, "Parameter value after slider drag is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (param->get(), newValue), "Parameter value after slider drag is incorrect!"); } SECTION ("Host Change Test") @@ -60,8 +60,7 @@ TEST_CASE ("Slider Attachment Test", "[plugin][state][attachments]") chowdsp::ParameterTypeHelpers::setValue (newValue, *param); state.getParameterListeners().updateBroadcastersFromMessageThread(); - - REQUIRE_MESSAGE (slider.getValue() == (double) newValue, "Slider value after parameter change is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (slider.getValue(), (double) newValue), "Slider value after parameter change is incorrect!"); } SECTION ("With Undo/Redo Test") @@ -82,15 +81,15 @@ TEST_CASE ("Slider Attachment Test", "[plugin][state][attachments]") slider.setValue ((double) newValue, juce::sendNotificationSync); } - REQUIRE_MESSAGE (param->get() == newValue, "Parameter value after slider drag is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (param->get(), newValue), "Parameter value after slider drag is incorrect!"); REQUIRE_MESSAGE (um.canUndo(), "Slider drag is not creating undoable action!"); um.undo(); - REQUIRE_MESSAGE (param->get() == originalValue, "Parameter value after undo action is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (param->get(), originalValue), "Parameter value after undo action is incorrect!"); REQUIRE_MESSAGE (um.canRedo(), "Slider drag undo is not creating redoable action!"); um.redo(); - REQUIRE_MESSAGE (param->get() == newValue, "Parameter value after redo action is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (param->get(), newValue), "Parameter value after redo action is incorrect!"); } } diff --git a/tests/plugin_tests/chowdsp_plugin_state_test/StateListenersTest.cpp b/tests/plugin_tests/chowdsp_plugin_state_test/StateListenersTest.cpp index 5c11c1ac7..f0e72302c 100644 --- a/tests/plugin_tests/chowdsp_plugin_state_test/StateListenersTest.cpp +++ b/tests/plugin_tests/chowdsp_plugin_state_test/StateListenersTest.cpp @@ -19,7 +19,7 @@ TEST_CASE ("State Listeners Test", "[plugin][state][listeners]") { REQUIRE_MESSAGE (juce::MessageManager::getInstance()->isThisTheMessageThread(), "Listener called on a thread other than the message thread!"); - REQUIRE_MESSAGE (pct->getCurrentValue() == mostRecentParamValue, + REQUIRE_MESSAGE (juce::exactlyEqual (pct->getCurrentValue(), mostRecentParamValue), "Parameter has the incorrect value when the listener is called!"); listenerCount++; }); @@ -49,7 +49,7 @@ TEST_CASE ("State Listeners Test", "[plugin][state][listeners]") chowdsp::ParameterListenerThread::AudioThread, [&listenerCount, &pct, &mostRecentParamValue] { - REQUIRE_MESSAGE (pct->getCurrentValue() == mostRecentParamValue, + REQUIRE_MESSAGE (juce::exactlyEqual (pct->getCurrentValue(), mostRecentParamValue), "Parameter has the incorrect value when the listener is called!"); listenerCount++; }); diff --git a/tests/plugin_tests/chowdsp_plugin_state_test/StateSerializationTest.cpp b/tests/plugin_tests/chowdsp_plugin_state_test/StateSerializationTest.cpp index e036b8dea..a0a0051e9 100644 --- a/tests/plugin_tests/chowdsp_plugin_state_test/StateSerializationTest.cpp +++ b/tests/plugin_tests/chowdsp_plugin_state_test/StateSerializationTest.cpp @@ -158,8 +158,8 @@ TEST_CASE ("State Serialization Test", "[plugin][state]") um.beginNewTransaction(); um.perform (new DummyAction {}); state.deserialize (block); - REQUIRE_MESSAGE (state.params.levelParams.percent->get() == percentVal, "Percent value is incorrect"); - REQUIRE_MESSAGE (state.params.levelParams.gain->get() == gainVal, "Gain value is incorrect"); + REQUIRE_MESSAGE (juce::approximatelyEqual (state.params.levelParams.percent->get(), percentVal), "Percent value is incorrect"); + REQUIRE_MESSAGE (juce::approximatelyEqual (state.params.levelParams.gain->get(), gainVal), "Gain value is incorrect"); REQUIRE_MESSAGE (state.params.mode->getIndex() == choiceVal, "Choice value is incorrect"); REQUIRE_MESSAGE (state.params.onOff->get() == boolVal, "Bool value is incorrect"); REQUIRE_MESSAGE (! um.canUndo(), "Undo manager was not cleared after loading new state!"); @@ -239,12 +239,12 @@ TEST_CASE ("State Serialization Test", "[plugin][state]") StateWithTripleOfSameType state {}; state.deserialize (block); - REQUIRE_MESSAGE (state.params.levelParams1.percent->get() == percentVal1, "Percent value 1 is incorrect"); - REQUIRE_MESSAGE (state.params.levelParams1.gain->get() == gainVal1, "Gain value 1 is incorrect"); - REQUIRE_MESSAGE (state.params.levelParams2.percent->get() == percentVal2, "Percent value 2 is incorrect"); - REQUIRE_MESSAGE (state.params.levelParams2.gain->get() == gainVal2, "Gain value 2 is incorrect"); - REQUIRE_MESSAGE (state.params.levelParams3.percent->get() == 0.5f, "Percent value 3 is incorrect"); - REQUIRE_MESSAGE (state.params.levelParams3.gain->get() == 0.0f, "Gain value 3 is incorrect"); + REQUIRE_MESSAGE (juce::approximatelyEqual (state.params.levelParams1.percent->get(), percentVal1), "Percent value 1 is incorrect"); + REQUIRE_MESSAGE (juce::approximatelyEqual (state.params.levelParams1.gain->get(), gainVal1), "Gain value 1 is incorrect"); + REQUIRE_MESSAGE (juce::approximatelyEqual (state.params.levelParams2.percent->get(), percentVal2), "Percent value 2 is incorrect"); + REQUIRE_MESSAGE (juce::approximatelyEqual (state.params.levelParams2.gain->get(), gainVal2), "Gain value 2 is incorrect"); + REQUIRE_MESSAGE (juce::approximatelyEqual (state.params.levelParams3.percent->get(), 0.5f), "Percent value 3 is incorrect"); + REQUIRE_MESSAGE (juce::approximatelyEqual (state.params.levelParams3.gain->get(), 0.0f), "Gain value 3 is incorrect"); REQUIRE_MESSAGE (state.params.mode->getIndex() == choiceVal, "Choice value is incorrect"); REQUIRE_MESSAGE (state.params.onOff->get() == boolVal, "Bool value is incorrect"); } diff --git a/tests/plugin_tests/chowdsp_plugin_state_test/VersionStreamingTest.cpp b/tests/plugin_tests/chowdsp_plugin_state_test/VersionStreamingTest.cpp index 4faa647df..4bb84eeb3 100644 --- a/tests/plugin_tests/chowdsp_plugin_state_test/VersionStreamingTest.cpp +++ b/tests/plugin_tests/chowdsp_plugin_state_test/VersionStreamingTest.cpp @@ -65,9 +65,9 @@ TEST_CASE ("Version Streaming Test", "[plugin][state][version]") SECTION ("Apply Version Streaming to Non-Parameters") { NonParams nonParams; - REQUIRE (nonParams.editorSize == 1.0f); + REQUIRE (juce::approximatelyEqual (nonParams.editorSize.get(), 1.0f)); nonParams.versionStreamingCallback ("1.0.0"_v); - REQUIRE (nonParams.editorSize == 1.5f); + REQUIRE (juce::approximatelyEqual (nonParams.editorSize.get(), 1.5f)); } SECTION ("Version Streaming with State Serialization") @@ -87,9 +87,9 @@ TEST_CASE ("Version Streaming Test", "[plugin][state][version]") // check new state State state {}; REQUIRE (state.params.innerParams.param->get() == false); - REQUIRE (state.nonParams.editorSize == 1.0f); + REQUIRE (juce::approximatelyEqual (state.nonParams.editorSize.get(), 1.0f)); state.deserialize (stateBlock); REQUIRE (state.params.innerParams.param->get() == true); - REQUIRE (state.nonParams.editorSize == 1.5f); + REQUIRE (juce::approximatelyEqual (state.nonParams.editorSize.get(), 1.5f)); } } diff --git a/tests/plugin_tests/chowdsp_plugin_utils_test/AudioFileSaveLoadHelperTest.cpp b/tests/plugin_tests/chowdsp_plugin_utils_test/AudioFileSaveLoadHelperTest.cpp index be307181a..0722faaeb 100644 --- a/tests/plugin_tests/chowdsp_plugin_utils_test/AudioFileSaveLoadHelperTest.cpp +++ b/tests/plugin_tests/chowdsp_plugin_utils_test/AudioFileSaveLoadHelperTest.cpp @@ -36,7 +36,7 @@ TEST_CASE ("Audio File Save/Load Helper Test", "[plugin][utilities]") saveLoadHelper.saveBufferToFile (testFile.file, testBuffer.toAudioBuffer(), fileSampleRate); auto [buffer, sampleRate] = saveLoadHelper.loadFile (testFile.file); - REQUIRE_MESSAGE (sampleRate == fileSampleRate, "Loaded file has incorrect sample rate!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (sampleRate, fileSampleRate), "Loaded file has incorrect sample rate!"); checkBuffersEqual (buffer, testBuffer.toAudioBuffer()); } @@ -58,6 +58,6 @@ TEST_CASE ("Audio File Save/Load Helper Test", "[plugin][utilities]") auto [buffer, sampleRate] = saveLoadHelper.loadFile (juce::File {}); REQUIRE_MESSAGE ((buffer.getNumChannels() == 0 && buffer.getNumSamples() == 0), "Buffer should be empty!"); - REQUIRE_MESSAGE (sampleRate == 0.0, "Sample rate should be zero!"); + REQUIRE_MESSAGE (juce::exactlyEqual (sampleRate, 0.0), "Sample rate should be zero!"); } } diff --git a/tests/plugin_tests/chowdsp_plugin_utils_test/PluginLoggerTest.cpp b/tests/plugin_tests/chowdsp_plugin_utils_test/PluginLoggerTest.cpp index bc7ac6f77..d57a17c48 100644 --- a/tests/plugin_tests/chowdsp_plugin_utils_test/PluginLoggerTest.cpp +++ b/tests/plugin_tests/chowdsp_plugin_utils_test/PluginLoggerTest.cpp @@ -77,6 +77,7 @@ TEST_CASE ("Plugin Logger Test", "[plugin][utilities]") { auto prevNumTopLevelWindows = juce::TopLevelWindow::getNumTopLevelWindows(); chowdsp::PluginLogger logger { logFileSubDir, logFileNameRoot }; + juce::MessageManager::getInstance()->runDispatchLoopUntil (100); auto newNumTopLevelWindows = juce::TopLevelWindow::getNumTopLevelWindows(); REQUIRE_MESSAGE (newNumTopLevelWindows == prevNumTopLevelWindows + 1, "AlertWindow not created!"); diff --git a/tests/plugin_tests/chowdsp_plugin_utils_test/TweaksFileTest.cpp b/tests/plugin_tests/chowdsp_plugin_utils_test/TweaksFileTest.cpp index bcd0fd3c5..e08e04ddf 100644 --- a/tests/plugin_tests/chowdsp_plugin_utils_test/TweaksFileTest.cpp +++ b/tests/plugin_tests/chowdsp_plugin_utils_test/TweaksFileTest.cpp @@ -51,7 +51,7 @@ TEST_CASE ("Tweaks File Test", "[plugin][utilities]") REQUIRE_MESSAGE (tweaksFile.getProperty ("test_int") == 0, "Initial integer property is incorrect"); REQUIRE_MESSAGE (tweaksFile.getProperty ("test_string") == juce::String {}, "Initial string property is incorrect"); - REQUIRE_MESSAGE (tweaksFile.getProperty ("test_float") == -1.0f, "Initial float property is incorrect"); + REQUIRE_MESSAGE (juce::approximatelyEqual (tweaksFile.getProperty ("test_float"), -1.0f), "Initial float property is incorrect"); test_utils::RandomIntGenerator randInt { -100, 100 }; test_utils::RandomFloatGenerator randFloat { 0.0f, 1.0f }; @@ -72,7 +72,7 @@ TEST_CASE ("Tweaks File Test", "[plugin][utilities]") REQUIRE_MESSAGE (tweaksFile.getProperty ("test_int") == newInt, "Integer property is incorrect"); REQUIRE_MESSAGE (tweaksFile.getProperty ("test_string") == newStr, "String property is incorrect"); - REQUIRE_MESSAGE (tweaksFile.getProperty ("test_float") == newFloat, "Float property is incorrect"); + REQUIRE_MESSAGE (juce::approximatelyEqual (tweaksFile.getProperty ("test_float"), newFloat), "Float property is incorrect"); } testTweaksFile.deleteFile(); @@ -88,7 +88,7 @@ TEST_CASE ("Tweaks File Test", "[plugin][utilities]") REQUIRE_MESSAGE (tweaksFile.getProperty ("test_int") == 0, "Initial integer property is incorrect"); REQUIRE_MESSAGE (tweaksFile.getProperty ("test_string") == juce::String {}, "Initial string property is incorrect"); - REQUIRE_MESSAGE (tweaksFile.getProperty ("test_float") == -1.0f, "Initial float property is incorrect"); + REQUIRE_MESSAGE (juce::approximatelyEqual (tweaksFile.getProperty ("test_float"), -1.0f), "Initial float property is incorrect"); static constexpr int newInt = 440; static constexpr float newFloat = -110.0f; @@ -101,7 +101,7 @@ TEST_CASE ("Tweaks File Test", "[plugin][utilities]") if (name == "test_int") REQUIRE_MESSAGE (tweaksFile.getProperty ("test_int") == newInt, "Integer property is incorrect"); else if (name == "test_float") - REQUIRE_MESSAGE (tweaksFile.getProperty ("test_float") == newFloat, "Float property is incorrect"); + REQUIRE_MESSAGE (juce::approximatelyEqual (tweaksFile.getProperty ("test_float"), newFloat), "Float property is incorrect"); }); const auto jsonConfig = chowdsp::json { @@ -113,7 +113,7 @@ TEST_CASE ("Tweaks File Test", "[plugin][utilities]") juce::MessageManager::getInstance()->runDispatchLoopUntil (2000); REQUIRE_MESSAGE (tweaksFile.getProperty ("test_int") == newInt, "Integer property is incorrect"); - REQUIRE_MESSAGE (tweaksFile.getProperty ("test_float") == newFloat, "Float property is incorrect"); + REQUIRE_MESSAGE (juce::approximatelyEqual (tweaksFile.getProperty ("test_float"), newFloat), "Float property is incorrect"); REQUIRE_MESSAGE (listenerHit, "Tweaks file listener was never hit!"); testTweaksFile.deleteFile(); @@ -126,6 +126,6 @@ TEST_CASE ("Tweaks File Test", "[plugin][utilities]") REQUIRE_MESSAGE (tweaksFile.getProperty ("test_int") == 44, "Integer property is incorrect"); REQUIRE_MESSAGE (tweaksFile.getProperty ("test_string") == juce::String { "blah blah" }, "String property is incorrect"); - REQUIRE_MESSAGE (tweaksFile.getProperty ("test_float") == 42.0f, "Float property is incorrect"); + REQUIRE_MESSAGE (juce::approximatelyEqual (tweaksFile.getProperty ("test_float"), 42.0f), "Float property is incorrect"); } } diff --git a/tests/plugin_tests/chowdsp_presets_v2_test/ClipboardInterfaceTest.cpp b/tests/plugin_tests/chowdsp_presets_v2_test/ClipboardInterfaceTest.cpp index 967828f0a..ce99ed25a 100644 --- a/tests/plugin_tests/chowdsp_presets_v2_test/ClipboardInterfaceTest.cpp +++ b/tests/plugin_tests/chowdsp_presets_v2_test/ClipboardInterfaceTest.cpp @@ -30,16 +30,16 @@ TEST_CASE ("Clipboard Interface Test", "[plugin][presets]") SECTION ("Copy/Paste") { loadPreset (0); - REQUIRE (state.params.floatParam->get() == val1); + REQUIRE (juce::approximatelyEqual (state.params.floatParam->get(), val1)); chowdsp::presets::frontend::ClipboardInterface clipInterface { presetMgr }; clipInterface.copyCurrentPreset(); loadPreset (1); - REQUIRE (state.params.floatParam->get() == val2); + REQUIRE (juce::approximatelyEqual (state.params.floatParam->get(), val2)); REQUIRE (clipInterface.tryToPastePreset()); - REQUIRE (state.params.floatParam->get() == val1); + REQUIRE (juce::approximatelyEqual (state.params.floatParam->get(), val1)); } SECTION ("Empty Paste") @@ -51,7 +51,7 @@ TEST_CASE ("Clipboard Interface Test", "[plugin][presets]") REQUIRE (! clipInterface.tryToPastePreset()); REQUIRE (presetMgr.getCurrentPreset()->getName() == "Preset0"); - REQUIRE (state.params.floatParam->get() == val1); + REQUIRE (juce::approximatelyEqual (state.params.floatParam->get(), val1)); } SECTION ("Invalid Preset Paste") @@ -64,7 +64,7 @@ TEST_CASE ("Clipboard Interface Test", "[plugin][presets]") REQUIRE (! clipInterface.tryToPastePreset()); REQUIRE (presetMgr.getCurrentPreset()->getName() == "Preset0"); - REQUIRE (state.params.floatParam->get() == val1); + REQUIRE (juce::approximatelyEqual (state.params.floatParam->get(), val1)); } } diff --git a/tests/plugin_tests/chowdsp_presets_v2_test/PresetManagerTest.cpp b/tests/plugin_tests/chowdsp_presets_v2_test/PresetManagerTest.cpp index 170b2b26f..14e90b8b6 100644 --- a/tests/plugin_tests/chowdsp_presets_v2_test/PresetManagerTest.cpp +++ b/tests/plugin_tests/chowdsp_presets_v2_test/PresetManagerTest.cpp @@ -95,10 +95,10 @@ TEST_CASE ("Preset Manager Test", "[plugin][presets][state]") presetMgr->saveUserPreset (presetFile); presetMgr.setFloatParam (dummyValue); - REQUIRE_MESSAGE (presetMgr.getFloatParam() == dummyValue, "Changed value is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (presetMgr.getFloatParam(), dummyValue), "Changed value is incorrect!"); presetMgr.loadPreset (0); - REQUIRE_MESSAGE (presetMgr.getFloatParam() == testValue, "Preset value is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (presetMgr.getFloatParam(), testValue), "Preset value is incorrect!"); } SECTION ("Factory Presets") @@ -108,9 +108,8 @@ TEST_CASE ("Preset Manager Test", "[plugin][presets][state]") ScopedPresetManager presetMgr {}; presetMgr->addPresets ({ preset }); - presetMgr.loadPreset (0); - REQUIRE_MESSAGE (presetMgr.getFloatParam() == testValue, "Preset value is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (presetMgr.getFloatParam(), testValue), "Preset value is incorrect!"); } SECTION ("Preset Agnostic Params") @@ -138,16 +137,16 @@ TEST_CASE ("Preset Manager Test", "[plugin][presets][state]") auto preset = saveUserPreset ("test.preset", testValue); ScopedPresetManager presetMgr {}; - REQUIRE_MESSAGE (presetMgr.state.params.extraParam->get() == defaultValue, "Initial value is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (presetMgr.state.params.extraParam->get(), defaultValue), "Initial value is incorrect!"); chowdsp::ParameterTypeHelpers::setValue (extraValue, *presetMgr.state.params.extraParam); presetMgr.state.getParameterListeners().updateBroadcastersFromMessageThread(); - REQUIRE_MESSAGE (presetMgr.state.params.extraParam->get() == extraValue, "Set value is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (presetMgr.state.params.extraParam->get(), extraValue), "Set value is incorrect!"); REQUIRE_MESSAGE (presetMgr->getIsPresetDirty(), "Preset dirty after set value is incorrect!"); presetMgr->addPresets ({ preset }); presetMgr.loadPreset (0); - REQUIRE_MESSAGE (presetMgr.state.params.extraParam->get() == defaultValue, "Reset value is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (presetMgr.state.params.extraParam->get(), defaultValue), "Reset value is incorrect!"); } SECTION ("Dirty Presets") @@ -196,7 +195,7 @@ TEST_CASE ("Preset Manager Test", "[plugin][presets][state]") presetMgr.setFloatParam (otherValue); presetMgr->loadDefaultPreset(); - REQUIRE_MESSAGE (presetMgr.getFloatParam() == testValue, "Preset value is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (presetMgr.getFloatParam(), testValue), "Preset value is incorrect!"); } SECTION ("User Presets") @@ -235,14 +234,14 @@ TEST_CASE ("Preset Manager Test", "[plugin][presets][state]") ScopedPresetManager presetMgr {}; presetMgr.setFloatParam (otherValue); - REQUIRE (presetMgr.getFloatParam() == otherValue); + REQUIRE (juce::approximatelyEqual (presetMgr.getFloatParam(), otherValue)); presetMgr.state.serialize (state); } ScopedPresetManager presetMgr {}; presetMgr.state.deserialize (state); - REQUIRE_MESSAGE (presetMgr.getFloatParam() == otherValue, "Preset state is overriding parameter state!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (presetMgr.getFloatParam(), otherValue), "Preset state is overriding parameter state!"); REQUIRE (presetMgr->getCurrentPreset() == nullptr); REQUIRE (presetMgr->getIsPresetDirty()); } @@ -258,17 +257,17 @@ TEST_CASE ("Preset Manager Test", "[plugin][presets][state]") ScopedPresetManager presetMgr {}; presetMgr->addPresets ({ chowdsp::presets::Preset { preset } }); presetMgr.loadPreset (0); - REQUIRE (presetMgr.getFloatParam() == testValue); + REQUIRE (juce::approximatelyEqual (presetMgr.getFloatParam(), testValue)); presetMgr.setFloatParam (otherValue); - REQUIRE (presetMgr.getFloatParam() == otherValue); + REQUIRE (juce::approximatelyEqual (presetMgr.getFloatParam(), otherValue)); presetMgr.state.serialize (state); } ScopedPresetManager presetMgr {}; presetMgr.state.deserialize (state); - REQUIRE_MESSAGE (presetMgr.getFloatParam() == otherValue, "Preset state is overriding parameter state!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (presetMgr.getFloatParam(), otherValue), "Preset state is overriding parameter state!"); REQUIRE (*presetMgr->getCurrentPreset() == preset); REQUIRE (presetMgr->getIsPresetDirty()); } @@ -287,24 +286,24 @@ TEST_CASE ("Preset Manager Test", "[plugin][presets][state]") presetMgr->addPresets ({ chowdsp::presets::Preset { preset }, chowdsp::presets::Preset { preset2 } }); presetMgr.loadPreset (0); - REQUIRE (presetMgr.getFloatParam() == testValue); + REQUIRE (juce::approximatelyEqual (presetMgr.getFloatParam(), testValue)); presetMgr.setFloatParam (dirtyVal); - REQUIRE (presetMgr.getFloatParam() == dirtyVal); + REQUIRE (juce::approximatelyEqual (presetMgr.getFloatParam(), dirtyVal)); REQUIRE (presetMgr->getIsPresetDirty()); presetMgr.loadPreset (1); - REQUIRE (presetMgr.getFloatParam() == testValue2); + REQUIRE (juce::approximatelyEqual (presetMgr.getFloatParam(), testValue2)); REQUIRE (! presetMgr->getIsPresetDirty()); REQUIRE (um.canUndo()); REQUIRE (um.undo()); REQUIRE (presetMgr->getIsPresetDirty()); - REQUIRE (presetMgr.getFloatParam() == dirtyVal); + REQUIRE (juce::approximatelyEqual (presetMgr.getFloatParam(), dirtyVal)); REQUIRE (um.canRedo()); REQUIRE (um.redo()); REQUIRE (! presetMgr->getIsPresetDirty()); - REQUIRE (presetMgr.getFloatParam() == testValue2); + REQUIRE (juce::approximatelyEqual (presetMgr.getFloatParam(), testValue2)); } } diff --git a/tests/plugin_tests/chowdsp_presets_v2_test/PresetTest.cpp b/tests/plugin_tests/chowdsp_presets_v2_test/PresetTest.cpp index 08ff6d18e..e3d14a4f2 100644 --- a/tests/plugin_tests/chowdsp_presets_v2_test/PresetTest.cpp +++ b/tests/plugin_tests/chowdsp_presets_v2_test/PresetTest.cpp @@ -23,7 +23,7 @@ TEST_CASE ("Preset Test", "[plugin][presets]") REQUIRE_MESSAGE (filePreset.getPresetFile().getFullPathName() == testFile.file.getFullPathName(), "Preset file is incorrect!"); auto compareVal = filePreset.getState()[testTag].get(); - REQUIRE_MESSAGE (compareVal == testValue, "Saved value is incorrect!"); + REQUIRE_MESSAGE (juce::approximatelyEqual (compareVal, (float) testValue), "Saved value is incorrect!"); } SECTION ("Invalid Preset")