diff --git a/src/sfizz/Wavetables.cpp b/src/sfizz/Wavetables.cpp index e8995b69..d13a8e7c 100644 --- a/src/sfizz/Wavetables.cpp +++ b/src/sfizz/Wavetables.cpp @@ -200,6 +200,39 @@ unsigned WavetableRange::getOctaveForFrequency(float f) return clamp(oct, 0, countOctaves - 1); } +static const auto octaveForFrequencyTable = []() +{ + static constexpr unsigned N = 1024; + std::array table; + + constexpr double fmin = 1 / WavetableRange::frequencyScaleFactor; + constexpr double fmax = (1 << (WavetableRange::countOctaves - 1)) / WavetableRange::frequencyScaleFactor; + + for (unsigned i = 0; i < N; ++i) { + double f = fmin + (i * (1.0 / (N - 1))) * (fmax - fmin); + table[i] = std::log2(f * WavetableRange::frequencyScaleFactor); + } + + return table; +}(); + +float WavetableRange::getFractionalOctaveForFrequency(float f) +{ + static constexpr unsigned N = octaveForFrequencyTable.size(); + + constexpr double fmin = 1 / WavetableRange::frequencyScaleFactor; + constexpr double fmax = (1 << (WavetableRange::countOctaves - 1)) / WavetableRange::frequencyScaleFactor; + + float pos = (f - fmin) * ((N - 1) / static_cast(fmax - fmin)); + int index1 = static_cast(pos); + float frac = pos - index1; + index1 = clamp(index1, 0, N - 1); + int index2 = std::min(index1 + 1, N - 1); + + return (1.0f - frac) * octaveForFrequencyTable[index1] + + frac * octaveForFrequencyTable[index2]; +} + WavetableRange WavetableRange::getRangeForOctave(int o) { WavetableRange range; diff --git a/src/sfizz/Wavetables.h b/src/sfizz/Wavetables.h index 3d0c9039..e5a5df36 100644 --- a/src/sfizz/Wavetables.h +++ b/src/sfizz/Wavetables.h @@ -113,6 +113,7 @@ public: static constexpr float frequencyScaleFactor = 0.05; static unsigned getOctaveForFrequency(float f); + static float getFractionalOctaveForFrequency(float f); static WavetableRange getRangeForOctave(int o); static WavetableRange getRangeForFrequency(float f); diff --git a/tests/WavetablesT.cpp b/tests/WavetablesT.cpp index 51231614..c4d5b683 100644 --- a/tests/WavetablesT.cpp +++ b/tests/WavetablesT.cpp @@ -8,6 +8,7 @@ #include "sfizz/MathHelpers.h" #include "catch2/catch.hpp" #include +#include TEST_CASE("[Wavetables] Frequency ranges") { @@ -38,3 +39,18 @@ TEST_CASE("[Wavetables] Frequency ranges") 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)); + } +}