commit 4e21afe05eaa842e58afe347a4b72f32614d8b8c
parent 432b812c93a5b521d336e4a35799598b5924daf5
Author: Matt Demanett <matt@demanett.net>
Date: Sun, 25 Mar 2018 15:28:25 -0400
Convert SineBankOscillator to use SineTableOscillator; this is slower but allows for phase offsets; doing this turned up bugs in Phasor etc with frequencies above the sample rate. Fixes, optimizations, more benchmarks.
Diffstat:
5 files changed, 155 insertions(+), 106 deletions(-)
diff --git a/benchmarks/oscillator_benchmark.cpp b/benchmarks/oscillator_benchmark.cpp
@@ -101,8 +101,8 @@ static void BM_TriangleOscillator(benchmark::State& state) {
}
BENCHMARK(BM_TriangleOscillator);
-static void BM_SineBankOscillator(benchmark::State& state) {
- SineBankOscillator o(44100.0, 440.0, 100);
+static void BM_SineBankOscillator100(benchmark::State& state) {
+ SineBankOscillator o(44100.0, 100.0, 100);
for (int i = 1, n = o.partialCount(); i <= n; ++i) {
o.setPartial(i, i, 1.0 / (float)i);
}
@@ -111,4 +111,40 @@ static void BM_SineBankOscillator(benchmark::State& state) {
o.next();
}
}
-BENCHMARK(BM_SineBankOscillator);
+BENCHMARK(BM_SineBankOscillator100);
+
+static void BM_SineBankOscillator500(benchmark::State& state) {
+ SineBankOscillator o(44100.0, 500.0, 100);
+ for (int i = 1, n = o.partialCount(); i <= n; ++i) {
+ o.setPartial(i, i, 1.0 / (float)i);
+ }
+
+ for (auto _ : state) {
+ o.next();
+ }
+}
+BENCHMARK(BM_SineBankOscillator500);
+
+static void BM_SineBankOscillator5000(benchmark::State& state) {
+ SineBankOscillator o(44100.0, 5000.0, 100);
+ for (int i = 1, n = o.partialCount(); i <= n; ++i) {
+ o.setPartial(i, i, 1.0 / (float)i);
+ }
+
+ for (auto _ : state) {
+ o.next();
+ }
+}
+BENCHMARK(BM_SineBankOscillator5000);
+
+static void BM_SineBankOscillator15000(benchmark::State& state) {
+ SineBankOscillator o(44100.0, 15000.0, 100);
+ for (int i = 1, n = o.partialCount(); i <= n; ++i) {
+ o.setPartial(i, i, 1.0 / (float)i);
+ }
+
+ for (auto _ : state) {
+ o.next();
+ }
+}
+BENCHMARK(BM_SineBankOscillator15000);
diff --git a/src/Test.cpp b/src/Test.cpp
@@ -33,11 +33,11 @@ void Test::step() {
#elif SINE
_sine.setSampleRate(engineGetSampleRate());
_sine.setFrequency(oscillatorPitch());
- outputs[OUT_OUTPUT].value = _sine.next();
+ outputs[OUT_OUTPUT].value = _sine.next() * 5.0f;
_sine2.setSampleRate(engineGetSampleRate());
_sine2.setFrequency(oscillatorPitch());
- outputs[OUT2_OUTPUT].value = _sine2.next();
+ outputs[OUT2_OUTPUT].value = _sine2.next() * 5.0f;
#elif SQUARE
_square.setSampleRate(engineGetSampleRate());
@@ -47,23 +47,23 @@ void Test::step() {
pw += clamp(inputs[CV2_INPUT].value, -5.0f, 5.0f) / 10.0f;
}
_square.setPulseWidth(pw);
- outputs[OUT_OUTPUT].value = _square.next();
+ outputs[OUT_OUTPUT].value = _square.next() * 5.0f;
_square2.setSampleRate(engineGetSampleRate());
_square2.setFrequency(oscillatorPitch());
_square2.setPulseWidth(pw);
_square2.setQuality(params[PARAM3_PARAM].value * 200);
- outputs[OUT2_OUTPUT].value = _square2.next();
+ outputs[OUT2_OUTPUT].value = _square2.next() * 5.0f;
#elif SAW
_saw.setSampleRate(engineGetSampleRate());
_saw.setFrequency(oscillatorPitch());
- outputs[OUT_OUTPUT].value = _saw.next();
+ outputs[OUT_OUTPUT].value = _saw.next() * 5.0f;
_saw2.setSampleRate(engineGetSampleRate());
_saw2.setFrequency(oscillatorPitch());
_saw2.setQuality(params[PARAM2_PARAM].value * 200);
- outputs[OUT2_OUTPUT].value = _saw2.next();
+ outputs[OUT2_OUTPUT].value = _saw2.next() * 5.0f;
#elif SATSAW
float saturation = params[PARAM2_PARAM].value * 10.0f;
@@ -73,18 +73,18 @@ void Test::step() {
_saw.setSampleRate(engineGetSampleRate());
_saw.setFrequency(oscillatorPitch());
_saw.setSaturation(saturation);
- outputs[OUT_OUTPUT].value = _saw.next();
+ outputs[OUT_OUTPUT].value = _saw.next() * 5.0f;
_saw2.setSampleRate(engineGetSampleRate());
_saw2.setFrequency(oscillatorPitch());
_saw2.setSaturation(saturation);
_saw2.setQuality(params[PARAM3_PARAM].value * 200);
- outputs[OUT2_OUTPUT].value = _saw2.next();
+ outputs[OUT2_OUTPUT].value = _saw2.next() * 5.0f;
#elif TRIANGLE
_triangle.setSampleRate(engineGetSampleRate());
_triangle.setFrequency(oscillatorPitch());
- outputs[OUT_OUTPUT].value = _triangle.next();
+ outputs[OUT_OUTPUT].value = _triangle.next() * 5.0f;
#elif SINEBANK
_sineBank.setSampleRate(engineGetSampleRate());
@@ -98,7 +98,7 @@ void Test::step() {
for (int i = 0; i < OVERSAMPLEN; ++i) {
buf[i] = _saw1.next();
}
- outputs[OUT_OUTPUT].value = _rackDecimator.process(buf);
+ outputs[OUT_OUTPUT].value = _rackDecimator.process(buf) * 5.0f;
_saw2.setSampleRate(engineGetSampleRate());
_saw2.setFrequency(oscillatorPitch() / (float)OVERSAMPLEN);
@@ -128,13 +128,13 @@ void Test::step() {
_saw1.setSampleRate(engineGetSampleRate());
_saw1.setFrequency(oscillatorPitch());
_saw1.setQuality(quality);
- outputs[OUT_OUTPUT].value = _saw1.next();
+ outputs[OUT_OUTPUT].value = _saw1.next() * 5.0f;
_saw2.setSampleRate(engineGetSampleRate());
_saw2.setQuality(quality);
if (oversample < 1) {
_saw2.setFrequency(oscillatorPitch());
- outputs[OUT2_OUTPUT].value = _saw2.next();
+ outputs[OUT2_OUTPUT].value = _saw2.next() * 5.0f;
}
else {
_saw2.setFrequency(oscillatorPitch() / (float)oversample);
@@ -147,7 +147,7 @@ void Test::step() {
for (int i = 0; i < oversample; ++i) {
s = _lpf.next(_saw2.next());
}
- outputs[OUT2_OUTPUT].value = s;
+ outputs[OUT2_OUTPUT].value = s * 5.0f;
}
#elif FM
@@ -229,11 +229,11 @@ void Test::step() {
#elif TABLES
_sine.setSampleRate(engineGetSampleRate());
_sine.setFrequency(oscillatorPitch());
- outputs[OUT_OUTPUT].value = _sine.next();
+ outputs[OUT_OUTPUT].value = _sine.next() * 5.0f;
_table.setSampleRate(engineGetSampleRate());
_table.setFrequency(oscillatorPitch());
- outputs[OUT2_OUTPUT].value = _table.next();
+ outputs[OUT2_OUTPUT].value = _table.next() * 5.0f;
#endif
}
diff --git a/src/Test.hpp b/src/Test.hpp
@@ -11,10 +11,10 @@ extern Model* modelTest;
// #define SAW 1
// #define SATSAW 1
// #define TRIANGLE 1
-// #define SINEBANK 1
+#define SINEBANK 1
// #define OVERSAMPLING 1
// #define OVERSAMPLED_BL 1
-#define FM 1
+// #define FM 1
// #define PM 1
// #define FEEDBACK_PM 1
// #define EG 1
@@ -150,37 +150,37 @@ struct Test : Module {
#elif LPFNOISE
, _lpf(44100.0, 1000.0, 1.0)
#elif SINE
- , _sine(44100.0, 1000.0, 5.0)
- , _sine2(44100.0, 1000.0, 5.0)
+ , _sine(44100.0, 1000.0)
+ , _sine2(44100.0, 1000.0)
#elif SQUARE
- , _square(44100.0, 1000.0, 5.0)
- , _square2(44100.0, 1000.0, 5.0)
+ , _square(44100.0, 1000.0)
+ , _square2(44100.0, 1000.0)
#elif SAW
- , _saw(44100.0, 1000.0, 5.0)
- , _saw2(44100.0, 1000.0, 5.0, 8)
+ , _saw(44100.0, 1000.0)
+ , _saw2(44100.0, 1000.0, 8)
#elif SATSAW
- , _saw(44100.0, 1000.0, 5.0)
- , _saw2(44100.0, 1000.0, 5.0, 8)
+ , _saw(44100.0, 1000.0)
+ , _saw2(44100.0, 1000.0, 8)
#elif TRIANGLE
- , _triangle(44100.0, 1000.0, 5.0)
+ , _triangle(44100.0, 1000.0)
#elif SINEBANK
, _sineBank(44101.0, 1000.0, 50)
#elif OVERSAMPLING
- , _saw1(44100.0, 1000.0, 5.0)
- , _saw2(44100.0, 1000.0, 1.0)
+ , _saw1(44100.0, 1000.0)
+ , _saw2(44100.0, 1000.0)
, _lpf(44100.0, 1000.0, 1.0)
, _lpf2(44100.0, 1000.0, 1.0)
#elif OVERSAMPLED_BL
- , _saw1(44100.0, 1000.0, 5.0)
- , _saw2(44100.0, 1000.0, 5.0)
+ , _saw1(44100.0, 1000.0)
+ , _saw2(44100.0, 1000.0)
, _lpf(44100.0, 1000.0, 1.0)
#elif FM
- , _modulator(44100.0, 1000.0, 1.0)
- , _carrier(44100.0, 1000.0, 1.0)
- , _modulator2(44100.0, 1000.0, 1.0)
- , _carrier2(44100.0, 1000.0, 1.0)
+ , _modulator(44100.0, 1000.0)
+ , _carrier(44100.0, 1000.0)
+ , _modulator2(44100.0, 1000.0)
+ , _carrier2(44100.0, 1000.0)
#elif PM
- , _modulator(44100.0, 1000.0, 1.0)
+ , _modulator(44100.0, 1000.0)
, _carrier(44100.0, 1000.0)
#elif FEEDBACK_PM
, _carrier(44100.0, 1000.0)
@@ -188,8 +188,8 @@ struct Test : Module {
#elif EG
, _envelope(44100.0)
#elif TABLES
- , _sine(44100.0, 1000.0, 5.0)
- , _table(StaticBlepTable::table(), 44100.0, 1000.0, 5.0)
+ , _sine(44100.0, 1000.0)
+ , _table(StaticBlepTable::table(), 44100.0, 1000.0)
#endif
{
onReset();
diff --git a/src/dsp/oscillator.cpp b/src/dsp/oscillator.cpp
@@ -21,6 +21,18 @@ float Phasor::nextFromPhasor(const Phasor& phasor, float offset) {
void Phasor::_update() {
_delta = (_frequency / _sampleRate) * maxPhase;
+ if (_delta >= maxPhase) {
+ _delta -= maxPhase;
+ if (_delta >= maxPhase) {
+ _delta = fmodf(_delta, maxPhase);
+ }
+ }
+ else if (_delta < 0.0f) {
+ _delta += maxPhase;
+ if (_delta < 0.0f) {
+ _delta = maxPhase - fmodf(-_delta, maxPhase);
+ }
+ }
}
void Phasor::advancePhase() {
@@ -28,9 +40,20 @@ void Phasor::advancePhase() {
if (_phase >= maxPhase) {
_phase -= maxPhase;
}
- else if (_phase <= 0.0f && _delta != 0.0f) {
+ if (_phase < 0.0f) {
_phase += maxPhase;
}
+ assert(_phase >= 0.0f);
+ assert(_phase < maxPhase);
+}
+
+void Phasor::advancePhasePositive() {
+ assert(_delta >= 0.0f);
+ _phase += _delta;
+ if (_phase >= maxPhase) {
+ _phase -= maxPhase;
+ }
+ assert(_phase < maxPhase);
}
float Phasor::_next() {
@@ -50,14 +73,15 @@ float TablePhasor::_nextForPhase(float phase) {
while (phase < 0.0f) {
phase += maxPhase;
}
- float fi = phase * (_table.length() / 2);
+
+ float fi = phase * (_tableLength / 2);
int i = (int)fi;
float v1 = _table.value(i);
- if (_table.length() >= 1024) {
- return v1 * _amplitude;
+ if (_tableLength >= 1024) {
+ return v1;
}
- float v2 = _table.value(i + 1 == _table.length() ? 0 : i + 1);
- return _amplitude * (v1 + (fi - i)*(v2 - v1));
+ float v2 = _table.value(i + 1 == _tableLength ? 0 : i + 1);
+ return v1 + (fi - i)*(v2 - v1);
}
@@ -77,12 +101,12 @@ float SineOscillator::_next() {
double t = _x - _k1*_y;
_y = _y + _k2*t;
_x = t - _k1*_y;
- return _amplitude * _y;
+ return _y;
}
float SawOscillator::_nextForPhase(float phase) {
- return _amplitude * (phase - halfMaxPhase);
+ return phase - halfMaxPhase;
}
@@ -104,7 +128,7 @@ float SaturatingSawOscillator::_nextForPhase(float phase) {
if (_saturation >= 0.1f) {
sample = tanhf(-sample * _saturation * M_PI) * _saturationNormalization;
}
- return _amplitude2 * sample;
+ return sample;
}
@@ -127,13 +151,13 @@ float BandLimitedSawOscillator::_nextForPhase(float phase) {
if (phase > maxPhase - _qd) {
float i = (maxPhase - phase) / _qd;
i = (1.0f - i) * _halfTableLen;
- sample -= _amplitude * _table.value((int)i);
+ sample -= _table.value((int)i);
}
else if (phase < _qd) {
float i = phase / _qd;
i *= _halfTableLen - 1;
i += _halfTableLen;
- sample -= _amplitude * _table.value((int)i);
+ sample -= _table.value((int)i);
}
return sample;
}
@@ -161,15 +185,15 @@ float SquareOscillator::_nextForPhase(float phase) {
if (positive) {
if (phase >= _pulseWidth) {
positive = false;
- return _negativeAmplitude;
+ return -1.0f;
}
- return _amplitude;
+ return 1.0f;
}
if (phase < _pulseWidth) {
positive = true;
- return _amplitude;
+ return 1.0f;
}
- return _negativeAmplitude;
+ return -1.0f;
}
@@ -191,10 +215,10 @@ void BandLimitedSquareOscillator::setPulseWidth(float pw) {
_pulseWidth *= maxPhase;
if (_pulseWidth >= 1.0f) {
- _offset = (_pulseWidth - 1.0f) * _amplitude;
+ _offset = _pulseWidth - 1.0f;
}
else {
- _offset = -(1.0f - _pulseWidth) * _amplitude;
+ _offset = -(1.0f - _pulseWidth);
}
}
@@ -212,12 +236,12 @@ float BandLimitedSquareOscillator::_nextForPhase(float phase) {
float TriangleOscillator::_nextForPhase(float phase) {
float p = maxPhase * phase;
if (phase < quarterMaxPhase) {
- return _amplitude * p;
+ return p;
}
if (phase < threeQuartersMaxPhase) {
- return _amplitude * (maxPhase - p);
+ return maxPhase - p;
}
- return _amplitude * (p - twiceMaxPhase);
+ return p - twiceMaxPhase;
}
@@ -274,9 +298,10 @@ void SineBankOscillator::_frequencyChanged() {
}
}
-float SineBankOscillator::_next() {
+float SineBankOscillator::next(float phaseOffset) {
float next = 0.0;
for (Partial& p : _partials) {
+ p.sine.advancePhasePositive();
if (p.frequency < _maxPartialFrequency && (p.amplitude > 0.001 || p.amplitude < -0.001 || p.amplitudeSteps > 0)) {
if (p.amplitudeSteps > 0) {
if (p.amplitudeSteps == 1) {
@@ -287,10 +312,7 @@ float SineBankOscillator::_next() {
}
--p.amplitudeSteps;
}
- next += p.sine.next() * p.amplitude;
- }
- else {
- p.sine.next(); // keep spinning, maintain phase.
+ next += p.sine.nextFromPhasor(p.sine, phaseOffset) * p.amplitude;
}
}
return next;
diff --git a/src/dsp/oscillator.hpp b/src/dsp/oscillator.hpp
@@ -10,11 +10,11 @@
namespace bogaudio {
namespace dsp {
-struct OscillatorGenerator : Generator {
+struct Oscillator {
float _sampleRate;
float _frequency;
- OscillatorGenerator(
+ Oscillator(
float sampleRate,
float frequency
)
@@ -22,6 +22,7 @@ struct OscillatorGenerator : Generator {
, _frequency(frequency)
{
}
+ virtual ~Oscillator() {}
void setSampleRate(float sampleRate) {
if (_sampleRate != sampleRate && sampleRate >= 1.0) {
@@ -42,6 +43,16 @@ struct OscillatorGenerator : Generator {
virtual void _frequencyChanged() {}
};
+struct OscillatorGenerator : Oscillator, Generator {
+ OscillatorGenerator(
+ float sampleRate,
+ float frequency
+ )
+ : Oscillator(sampleRate, frequency)
+ {
+ }
+};
+
struct Phasor : OscillatorGenerator {
static constexpr float maxPhase = 2.0f;
@@ -71,26 +82,26 @@ struct Phasor : OscillatorGenerator {
float nextFromPhasor(const Phasor& phasor, float offset = 0.0f); // offset is not radians, but local phase.
virtual void _update();
void advancePhase();
+ void advancePhasePositive();
virtual float _next() override final;
virtual float _nextForPhase(float phase);
- static float radiansToPhase(float radians) { return radians / M_PI; }
- static float phaseToRadians(float phase) { return phase * M_PI; }
+ inline static float radiansToPhase(float radians) { return radians / M_PI; }
+ inline static float phaseToRadians(float phase) { return phase * M_PI; }
};
struct TablePhasor : Phasor {
const Table& _table;
- float _amplitude;
+ int _tableLength;
TablePhasor(
const Table& table,
double sampleRate,
- double frequency,
- float amplitude = 1.0f
+ double frequency
)
: Phasor(sampleRate, frequency)
, _table(table)
- , _amplitude(amplitude)
+ , _tableLength(table.length())
{
}
@@ -101,16 +112,13 @@ struct SineOscillator : OscillatorGenerator {
double _k1, _k2;
double _x;
double _y;
- double _amplitude;
SineOscillator(
double sampleRate,
double frequency,
- double amplitude = 1.0,
double initialPhase = 0.0
)
: OscillatorGenerator(sampleRate, frequency)
- , _amplitude(amplitude)
{
setPhase(initialPhase);
update();
@@ -132,25 +140,21 @@ struct SineOscillator : OscillatorGenerator {
struct SineTableOscillator : TablePhasor {
SineTableOscillator(
float sampleRate,
- float frequency,
- float amplitude = 1.0
+ float frequency
)
- : TablePhasor(StaticSineTable::table(), sampleRate, frequency, amplitude)
+ : TablePhasor(StaticSineTable::table(), sampleRate, frequency)
{
}
};
struct SawOscillator : Phasor {
const float halfMaxPhase = 0.5f * maxPhase;
- float _amplitude;
SawOscillator(
float sampleRate,
- float frequency,
- float amplitude = 1.0f
+ float frequency
)
: Phasor(sampleRate, frequency)
- , _amplitude(amplitude)
{
}
@@ -160,17 +164,14 @@ struct SawOscillator : Phasor {
struct SaturatingSawOscillator : SawOscillator {
float _saturation;
float _saturationNormalization;
- float _amplitude2;
SaturatingSawOscillator(
float sampleRate,
- float frequency,
- float amplitude = 1.0f
+ float frequency
)
: SawOscillator(sampleRate, frequency)
, _saturation(0.0f)
, _saturationNormalization(1.0f)
- , _amplitude2(amplitude)
{
}
@@ -188,11 +189,10 @@ struct BandLimitedSawOscillator : SaturatingSawOscillator {
BandLimitedSawOscillator(
float sampleRate,
float frequency,
- float amplitude = 1.0f,
int quality = 5,
const Table& table = StaticBlepTable::table()
)
- : SaturatingSawOscillator(sampleRate, frequency, amplitude)
+ : SaturatingSawOscillator(sampleRate, frequency)
, _quality(quality)
, _table(table)
, _halfTableLen(_table.length() / 2)
@@ -209,20 +209,15 @@ struct BandLimitedSawOscillator : SaturatingSawOscillator {
struct SquareOscillator : Phasor {
const float minPulseWidth = 0.03f;
const float maxPulseWidth = 1.0f - minPulseWidth;
- float _amplitude;
- float _negativeAmplitude;
float _pulseWidthInput;
float _pulseWidth = 0.5;
bool positive = true;
SquareOscillator(
float sampleRate,
- float frequency,
- float amplitude = 1.0
+ float frequency
)
: Phasor(sampleRate, frequency)
- , _amplitude(amplitude)
- , _negativeAmplitude(-amplitude)
{
}
@@ -241,11 +236,10 @@ struct BandLimitedSquareOscillator : BandLimitedSawOscillator {
BandLimitedSquareOscillator(
float sampleRate,
float frequency,
- float amplitude = 1.0f,
int quality = 5,
const Table& table = StaticBlepTable::table()
)
- : BandLimitedSawOscillator(sampleRate, frequency, amplitude, quality, table)
+ : BandLimitedSawOscillator(sampleRate, frequency, quality, table)
{
setPulseWidth(0.05f);
}
@@ -259,22 +253,19 @@ struct TriangleOscillator : Phasor {
const float quarterMaxPhase = 0.25f * maxPhase;
const float threeQuartersMaxPhase = 0.75f * maxPhase;
const float twiceMaxPhase = 2.0f * maxPhase;
- float _amplitude;
TriangleOscillator(
float sampleRate,
- float frequency,
- float amplitude = 1.0
+ float frequency
)
: Phasor(sampleRate, frequency)
- , _amplitude(amplitude)
{
}
virtual float _nextForPhase(float phase) override;
};
-struct SineBankOscillator : OscillatorGenerator {
+struct SineBankOscillator : Oscillator {
struct Partial {
float frequency;
float frequencyRatio;
@@ -282,7 +273,7 @@ struct SineBankOscillator : OscillatorGenerator {
float amplitudeTarget;
float amplitudeStepDelta;
int amplitudeSteps;
- SineOscillator sine;
+ SineTableOscillator sine;
Partial()
: frequency(0.0)
@@ -291,7 +282,7 @@ struct SineBankOscillator : OscillatorGenerator {
, amplitudeTarget(0.0)
, amplitudeStepDelta(0.0)
, amplitudeSteps(0)
- , sine(0.0, 0.0, 1.0)
+ , sine(0.0, 0.0)
{}
};
@@ -306,7 +297,7 @@ struct SineBankOscillator : OscillatorGenerator {
float frequency,
int partialCount
)
- : OscillatorGenerator(sampleRate, frequency)
+ : Oscillator(sampleRate, frequency)
, _partials(partialCount)
{
_sampleRateChanged();
@@ -326,7 +317,7 @@ struct SineBankOscillator : OscillatorGenerator {
virtual void _sampleRateChanged() override;
virtual void _frequencyChanged() override;
- virtual float _next() override;
+ float next(float phaseOffset = 0.0f); // Phasor "phase", not radians.
};
} // namespace dsp