diff --git a/src/sfizz/Wavetables.cpp b/src/sfizz/Wavetables.cpp index 352f4e2d..55a39be6 100644 --- a/src/sfizz/Wavetables.cpp +++ b/src/sfizz/Wavetables.cpp @@ -295,10 +295,9 @@ const std::array MipmapRange::FrequencyToIndex = []() std::array table; for (unsigned i = 0; i < table.size() - 1; ++i) { - double r = i * (1.0 / (table.size() - 1)); - double f = F1 + r * (FN - F1); - double t = std::log(K * f) / LogB; - table[i] = clamp(t, 0, N - 1); + float r = i * (1.0f / (table.size() - 1)); + float f = F1 + r * (FN - F1); + table[i] = getExactIndexForFrequency(f); } // ensure the last element to be exact table[table.size() - 1] = N - 1; @@ -321,6 +320,12 @@ float MipmapRange::getIndexForFrequency(float f) frac * FrequencyToIndex[index2]; } +float MipmapRange::getExactIndexForFrequency(float f) +{ + float t = (f < F1) ? 0.0f : (std::log(K * f) / LogB); + return clamp(t, 0, N - 1); +} + const std::array MipmapRange::IndexToStartFrequency = []() { std::array table; diff --git a/src/sfizz/Wavetables.h b/src/sfizz/Wavetables.h index 66b56e34..b57feeb0 100644 --- a/src/sfizz/Wavetables.h +++ b/src/sfizz/Wavetables.h @@ -142,6 +142,7 @@ public: static constexpr float FN = 12000.0; static float getIndexForFrequency(float f); + static float getExactIndexForFrequency(float f); static MipmapRange getRangeForIndex(int o); static MipmapRange getRangeForFrequency(float f); diff --git a/tests/WavetablesT.cpp b/tests/WavetablesT.cpp index c4d5b683..f1fd72da 100644 --- a/tests/WavetablesT.cpp +++ b/tests/WavetablesT.cpp @@ -12,45 +12,38 @@ TEST_CASE("[Wavetables] Frequency ranges") { - int cur_oct = std::numeric_limits::min(); - int min_oct = std::numeric_limits::max(); - int max_oct = std::numeric_limits::min(); + int cur_index = std::numeric_limits::min(); + int min_index = std::numeric_limits::max(); + int max_index = std::numeric_limits::min(); for (int note = 0; note < 128; ++note) { double f = midiNoteFrequency(note); - int oct = sfz::WavetableRange::getOctaveForFrequency(f); + float fractionalIndex = sfz::MipmapRange::getExactIndexForFrequency(f); + int index = static_cast(fractionalIndex); - REQUIRE(oct >= 0); - REQUIRE(oct < sfz::WavetableRange::countOctaves); + REQUIRE(index >= 0); + REQUIRE(static_cast(index) < sfz::MipmapRange::N); - REQUIRE(oct >= cur_oct); - cur_oct = oct; + float lerpFractionalIndex = sfz::MipmapRange::getIndexForFrequency(f); + int lerpIndex = static_cast(lerpFractionalIndex); - min_oct = std::min(min_oct, oct); - max_oct = std::max(max_oct, oct); + // approximation should be equal or off by 1 table in worst cases + bool lerpIndexValid = (lerpIndex - index) == 0 || (lerpIndex - index) == -1; + REQUIRE(lerpIndexValid); - sfz::WavetableRange range = sfz::WavetableRange::getRangeForOctave(oct); - REQUIRE((f >= range.minFrequency || oct == 0)); - REQUIRE((f <= range.maxFrequency || oct == sfz::WavetableRange::countOctaves - 1)); + REQUIRE(index >= cur_index); + cur_index = index; + + min_index = std::min(min_index, index); + max_index = std::max(max_index, index); + + sfz::MipmapRange range = sfz::MipmapRange::getRangeForIndex(index); + REQUIRE((f >= range.minFrequency || index == 0)); + REQUIRE((f <= range.maxFrequency || index == sfz::MipmapRange::N - 1)); } // check ranges to be decently adjusted to the MIDI frequency range - REQUIRE(min_oct == 0); - REQUIRE(max_oct == sfz::WavetableRange::countOctaves - 1); -} - -TEST_CASE("[Wavetables] Octave number lookup") -{ - for (int note = 0; note < 128; ++note) { - double f = midiNoteFrequency(note); - - float ref = std::log2(f * sfz::WavetableRange::frequencyScaleFactor); - float oct = sfz::WavetableRange::getFractionalOctaveForFrequency(f); - - ref = clamp(ref, 0, sfz::WavetableRange::countOctaves - 1); - oct = clamp(oct, 0, sfz::WavetableRange::countOctaves - 1); - - REQUIRE(oct == Approx(ref).margin(0.03f)); - } + REQUIRE(min_index == 0); + REQUIRE(max_index == sfz::MipmapRange::N - 1); }