Add wavetable selection by frequency

This commit is contained in:
Jean Pierre Cimalando 2020-02-13 21:42:03 +01:00 committed by Paul Fd
parent e6b0b31f9b
commit 9d4d857e97
4 changed files with 103 additions and 0 deletions

View file

@ -5,6 +5,7 @@
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
#include "Wavetables.h"
#include "MathHelpers.h"
#include <kiss_fftr.h>
#include <memory>
@ -112,4 +113,33 @@ const HarmonicProfile& HarmonicProfile::getSquare()
return squareProfile;
}
//------------------------------------------------------------------------------
constexpr unsigned WavetableRange::countOctaves;
constexpr float WavetableRange::frequencyScaleFactor;
unsigned WavetableRange::getOctaveForFrequency(float f)
{
int oct = fp_exponent(frequencyScaleFactor * f);
return clamp<int>(oct, 0, countOctaves - 1);
}
WavetableRange WavetableRange::getRangeForOctave(int o)
{
WavetableRange range;
Fraction<uint64_t> mant = fp_mantissa(0.0f);
float k = 1.0f / frequencyScaleFactor;
range.minFrequency = k * fp_from_parts<float>(0, o, 0);
range.maxFrequency = k * fp_from_parts<float>(0, o, mant.den - 1);
return range;
}
WavetableRange WavetableRange::getRangeForFrequency(float f)
{
int oct = getOctaveForFrequency(f);
return getRangeForOctave(oct);
}
} // namespace sfz

View file

@ -38,4 +38,36 @@ public:
void generate(absl::Span<float> table, double amplitude, double cutoff) const;
};
/**
A helper to select ranges of a multi-sampled oscillator, according to the
frequency of an oscillator.
The ranges are identified by octave numbers; not octaves in a musical sense,
but as logarithmic divisions of the frequency range.
*/
class WavetableRange {
public:
float minFrequency = 0;
float maxFrequency = 0;
static constexpr unsigned countOctaves = 10;
static constexpr float frequencyScaleFactor = 0.05;
static unsigned getOctaveForFrequency(float f);
static WavetableRange getRangeForOctave(int o);
static WavetableRange getRangeForFrequency(float f);
// Note: using the frequency factor 0.05, octaves are as follows:
// octave 0: 20 Hz - 40 Hz
// octave 1: 40 Hz - 80 Hz
// octave 2: 80 Hz - 160 Hz
// octave 3: 160 Hz - 320 Hz
// octave 4: 320 Hz - 640 Hz
// octave 5: 640 Hz - 1280 Hz
// octave 6: 1280 Hz - 2560 Hz
// octave 7: 2560 Hz - 5120 Hz
// octave 8: 5120 Hz - 10240 Hz
// octave 9: 10240 Hz - 20480 Hz
};
} // namespace sfz

View file

@ -26,6 +26,7 @@ set(SFIZZ_TEST_SOURCES
CurveT.cpp
RegionTriggersT.cpp
FloatHelpersT.cpp
WavetablesT.cpp
)
add_executable(sfizz_tests ${SFIZZ_TEST_SOURCES})

40
tests/WavetablesT.cpp Normal file
View file

@ -0,0 +1,40 @@
// SPDX-License-Identifier: BSD-2-Clause
// This code is part of the sfizz library and is licensed under a BSD 2-clause
// license. You should have receive a LICENSE.md file along with the code.
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
#include "sfizz/Wavetables.h"
#include "sfizz/MathHelpers.h"
#include "catch2/catch.hpp"
#include <algorithm>
TEST_CASE("[Wavetables] Frequency ranges")
{
int cur_oct = std::numeric_limits<int>::min();
int min_oct = std::numeric_limits<int>::max();
int max_oct = std::numeric_limits<int>::min();
for (int note = 0; note < 128; ++note) {
double f = midiNoteFrequency(note);
int oct = sfz::WavetableRange::getOctaveForFrequency(f);
REQUIRE(oct >= 0);
REQUIRE(oct < sfz::WavetableRange::countOctaves);
REQUIRE(oct >= cur_oct);
cur_oct = oct;
min_oct = std::min(min_oct, oct);
max_oct = std::max(max_oct, oct);
sfz::WavetableRange range = sfz::WavetableRange::getRangeForOctave(oct);
REQUIRE((f >= range.minFrequency || oct == 0));
REQUIRE((f <= range.maxFrequency || oct == sfz::WavetableRange::countOctaves - 1));
}
// check ranges to be decently adjusted to the MIDI frequency range
REQUIRE(min_oct == 0);
REQUIRE(max_oct == sfz::WavetableRange::countOctaves - 1);
}