BogaudioModules

BogaudioModules for VCV Rack
Log | Files | Refs | README | LICENSE

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:
Mbenchmarks/oscillator_benchmark.cpp | 42+++++++++++++++++++++++++++++++++++++++---
Msrc/Test.cpp | 30+++++++++++++++---------------
Msrc/Test.hpp | 44++++++++++++++++++++++----------------------
Msrc/dsp/oscillator.cpp | 72+++++++++++++++++++++++++++++++++++++++++++++++-------------------------
Msrc/dsp/oscillator.hpp | 73++++++++++++++++++++++++++++++++-----------------------------------------
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