diff --git a/devtools/CaptureEG.cpp b/devtools/CaptureEG.cpp index 558b0d14..b5313254 100644 --- a/devtools/CaptureEG.cpp +++ b/devtools/CaptureEG.cpp @@ -284,6 +284,7 @@ void Application::performSfzUpdate() QString code; code += "\n"; + code += "key=69\n"; code += "sample="; code += QFileInfo(samplePath).fileName(); code += "\n"; code += _ui->envelopeEdit->toPlainText(); diff --git a/src/sfizz/ADSREnvelope.cpp b/src/sfizz/ADSREnvelope.cpp index 9581ab7a..bb86115b 100644 --- a/src/sfizz/ADSREnvelope.cpp +++ b/src/sfizz/ADSREnvelope.cpp @@ -40,11 +40,9 @@ void ADSREnvelope::reset(const EGDescription& desc, const Region& region, releaseDelay = 0; shouldRelease = false; - freeRunning = ( - (region.trigger == SfzTrigger::release) + freeRunning = ((region.trigger == SfzTrigger::release) || (region.trigger == SfzTrigger::release_key) - || region.loopMode == SfzLoopMode::one_shot - ); + || (region.loopMode == SfzLoopMode::one_shot && (region.isGenerator() || region.oscillator))); currentValue = this->start; currentState = State::Delay; } @@ -117,8 +115,7 @@ void ADSREnvelope::getBlock(absl::Span output) noexcept // release takes effect this frame currentState = State::Release; releaseDelay = -1; - } - else if (shouldRelease && releaseDelay > 0) { + } else if (shouldRelease && releaseDelay > 0) { // prevent computing the segment further than release point size = std::min(size, releaseDelay); } @@ -188,6 +185,8 @@ void ADSREnvelope::getBlock(absl::Span output) noexcept this->currentValue = currentValue; this->shouldRelease = shouldRelease; this->releaseDelay = releaseDelay; + + ASSERT(!hasNanInf(output)); } template diff --git a/src/sfizz/MathHelpers.h b/src/sfizz/MathHelpers.h index 960ccb73..9fa734f1 100644 --- a/src/sfizz/MathHelpers.h +++ b/src/sfizz/MathHelpers.h @@ -11,6 +11,7 @@ #pragma once #include "Config.h" #include "Macros.h" +#include "absl/types/span.h" #include #include #include @@ -292,3 +293,38 @@ inline F fp_from_parts(bool sgn, int ex, uint64_t mant) (static_cast(sgn) << (T::e_bits + T::m_bits)); return u.real; } + +template +inline bool fp_naninf(F x) +{ + typedef FP_traits T; + typedef typename T::same_size_int I; + union { + F real; + I integer; + } u; + u.real = x; + const auto all_ones = ((1u << T::e_bits) - 1); + const auto ex = (u.integer >> T::m_bits) & all_ones; + return ex == all_ones; +} + +template +bool hasNanInf(absl::Span span) +{ + for (const auto& x : span) + if (fp_naninf(x)) + return true; + + return false; +} + +template +bool isValidAudio(absl::Span span) +{ + for (const auto& x : span) + if (x < -1.0f || x > 1.0f) + return false; + + return true; +} diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 1141624c..0870905f 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -624,6 +624,11 @@ void sfz::Synth::renderBlock(AudioSpan buffer) noexcept // Reset the dispatch counter dispatchDuration = Duration(0); + + ASSERT(!hasNanInf(buffer.getConstSpan(0))); + ASSERT(!hasNanInf(buffer.getConstSpan(1))); + ASSERT(isValidAudio(buffer.getConstSpan(0))); + ASSERT(isValidAudio(buffer.getConstSpan(1))); } void sfz::Synth::noteOn(int delay, int noteNumber, uint8_t velocity) noexcept diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index 251a1167..fcb1d9fd 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -241,6 +241,12 @@ void sfz::Voice::renderBlock(AudioSpan buffer) noexcept powerHistory.push(buffer.meanSquared()); this->triggerDelay = absl::nullopt; +#if 0 + ASSERT(!hasNanInf(buffer.getConstSpan(0))); + ASSERT(!hasNanInf(buffer.getConstSpan(1))); + ASSERT(isValidAudio(buffer.getConstSpan(0))); + ASSERT(isValidAudio(buffer.getConstSpan(1))); +#endif } void sfz::Voice::amplitudeEnvelope(absl::Span modulationSpan) noexcept @@ -515,6 +521,13 @@ void sfz::Voice::fillWithData(AudioSpan buffer) noexcept sourcePosition = indices->back(); floatPositionOffset = rightCoeffs->back(); + +#if 0 + ASSERT(!hasNanInf(buffer.getConstSpan(0))); + ASSERT(!hasNanInf(buffer.getConstSpan(1))); + ASSERT(isValidAudio(buffer.getConstSpan(0))); + ASSERT(isValidAudio(buffer.getConstSpan(1))); +#endif } void sfz::Voice::fillWithGenerator(AudioSpan buffer) noexcept @@ -573,6 +586,13 @@ void sfz::Voice::fillWithGenerator(AudioSpan buffer) noexcept } } } + +#if 0 + ASSERT(!hasNanInf(buffer.getConstSpan(0))); + ASSERT(!hasNanInf(buffer.getConstSpan(1))); + ASSERT(isValidAudio(buffer.getConstSpan(0))); + ASSERT(isValidAudio(buffer.getConstSpan(1))); +#endif } bool sfz::Voice::checkOffGroup(int delay, uint32_t group) noexcept diff --git a/src/sfizz/Wavetables.cpp b/src/sfizz/Wavetables.cpp index b04c0a8c..617d3901 100644 --- a/src/sfizz/Wavetables.cpp +++ b/src/sfizz/Wavetables.cpp @@ -253,8 +253,18 @@ WavetableMulti WavetableMulti::createForHarmonicProfile( const WavetableMulti* WavetableMulti::getSilenceWavetable() { static WavetableMulti wm; - wm.allocateStorage(1); - wm.fillExtra(); + static bool initialized { false }; + + if (!initialized) { + constexpr unsigned numTables = WavetableMulti::numTables(); + wm.allocateStorage(1); + for (unsigned m = 0; m < numTables; ++m) { + float* ptr = const_cast(wm.getTablePointer(m)); + *ptr = 0; + } + wm.fillExtra(); + initialized = true; + } return &wm; } diff --git a/tests/FloatHelpersT.cpp b/tests/FloatHelpersT.cpp index 58b92acc..ecfc0cf0 100644 --- a/tests/FloatHelpersT.cpp +++ b/tests/FloatHelpersT.cpp @@ -7,6 +7,7 @@ #include "catch2/catch.hpp" #include "sfizz/MathHelpers.h" #include +#include TEST_CASE("[FloatMath] Fast ilog2 (float)") { @@ -51,3 +52,19 @@ TEST_CASE("[FloatMath] Break apart and reconstruct (double)") REQUIRE(fp_from_parts(sgn, ex, mant.num) == f); } } + +TEST_CASE("[FloatMath] Nan/Inf checker") +{ + REQUIRE(fp_naninf(std::numeric_limits::quiet_NaN())); + REQUIRE(fp_naninf(std::numeric_limits::quiet_NaN())); + REQUIRE(fp_naninf(std::numeric_limits::infinity())); + REQUIRE(fp_naninf(std::numeric_limits::infinity())); + REQUIRE(fp_naninf(-std::numeric_limits::infinity())); + REQUIRE(fp_naninf(-std::numeric_limits::infinity())); + REQUIRE(!fp_naninf(0.0f)); + REQUIRE(!fp_naninf(0.0)); + REQUIRE(!fp_naninf(1.0f)); + REQUIRE(!fp_naninf(1.0)); + REQUIRE(!fp_naninf(-1.0f)); + REQUIRE(!fp_naninf(-1.0)); +}