BogaudioModules

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

commit 92d485cae583ece05e887127b89e6a67b75948d5
parent 3a2e641c7ae8b4de94ff18a85a1a3a8e67ee220e
Author: Matt Demanett <matt@demanett.net>
Date:   Sun, 18 Mar 2018 01:03:32 -0400

Band-limited saw and square oscillators.

Diffstat:
Msrc/Test.cpp | 20++++++++++++++++++++
Msrc/Test.hpp | 20++++++++++++++++++--
Msrc/dsp/oscillator.cpp | 74+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/dsp/oscillator.hpp | 61++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Msrc/dsp/table.cpp | 35+++++++++++++++++++++++++++++++++++
Msrc/dsp/table.hpp | 7++++++-
6 files changed, 208 insertions(+), 9 deletions(-)

diff --git a/src/Test.cpp b/src/Test.cpp @@ -49,11 +49,22 @@ void Test::step() { _square.setPulseWidth(pw); outputs[OUT_OUTPUT].value = _square.next(); + _square2.setSampleRate(engineGetSampleRate()); + _square2.setFrequency(oscillatorPitch()); + _square2.setPulseWidth(pw); + _square2.setQuality(params[PARAM3_PARAM].value * 200); + outputs[OUT2_OUTPUT].value = _square2.next(); + #elif SAW _saw.setSampleRate(engineGetSampleRate()); _saw.setFrequency(oscillatorPitch()); outputs[OUT_OUTPUT].value = _saw.next(); + _saw2.setSampleRate(engineGetSampleRate()); + _saw2.setFrequency(oscillatorPitch()); + _saw2.setQuality(params[PARAM2_PARAM].value * 200); + outputs[OUT2_OUTPUT].value = _saw2.next(); + #elif TRIANGLE _triangle.setSampleRate(engineGetSampleRate()); _triangle.setFrequency(oscillatorPitch()); @@ -150,6 +161,15 @@ void Test::step() { _envelope.setRelease(params[PARAM2_PARAM].value); _envelope.setGate(inputs[CV1_INPUT].value > 0.1f); outputs[OUT_OUTPUT].value = _envelope.next() * 10.0f; + +#elif TABLES + _sine.setSampleRate(engineGetSampleRate()); + _sine.setFrequency(oscillatorPitch()); + outputs[OUT_OUTPUT].value = _sine.next(); + + _table.setSampleRate(engineGetSampleRate()); + _table.setFrequency(oscillatorPitch()); + outputs[OUT2_OUTPUT].value = _table.next(); #endif } diff --git a/src/Test.hpp b/src/Test.hpp @@ -6,8 +6,8 @@ extern Model* modelTest; // #define LPF 1 // #define LPFNOISE 1 -#define SINE 1 -// #define SQUARE 1 +// #define SINE 1 +#define SQUARE 1 // #define SAW 1 // #define TRIANGLE 1 // #define SINEBANK 1 @@ -16,6 +16,7 @@ extern Model* modelTest; // #define PM 1 // #define FEEDBACK_PM 1 // #define EG 1 +// #define TABLES 1 #include "pitch.hpp" #ifdef LPF @@ -46,6 +47,8 @@ extern Model* modelTest; #include "dsp/oscillator.hpp" #elif EG #include "dsp/envelope.hpp" +#elif TABLES +#include "dsp/oscillator.hpp" #else #error what #endif @@ -90,8 +93,10 @@ struct Test : Module { SineTableOscillator _sine2; #elif SQUARE SquareOscillator _square; + BandLimitedSquareOscillator _square2; #elif SAW SawOscillator _saw; + BandLimitedSawOscillator _saw2; #elif TRIANGLE TriangleOscillator _triangle; #elif SINEBANK @@ -115,6 +120,9 @@ struct Test : Module { float _feedbackSample = 0.0f; #elif EG ADSR _envelope; +#elif TABLES + SineTableOscillator _sine; + TablePhasor _table; #endif Test() @@ -128,8 +136,10 @@ struct Test : Module { , _sine2(44100.0, 1000.0, 5.0) #elif SQUARE , _square(44100.0, 1000.0, 5.0) + , _square2(44100.0, 1000.0, 5.0) #elif SAW , _saw(44100.0, 1000.0, 5.0) + , _saw2(44100.0, 1000.0, 5.0, 8) #elif TRIANGLE , _triangle(44100.0, 1000.0, 5.0) #elif SINEBANK @@ -151,6 +161,9 @@ struct Test : Module { , _carrierOutput(44100.0, 1000.0) #elif EG , _envelope(44100.0) +#elif TABLES + , _sine(44100.0, 1000.0, 5.0) + , _table(StaticBlepTable::table(), 44100.0, 1000.0, 5.0) #endif { onReset(); @@ -158,6 +171,9 @@ struct Test : Module { #ifdef SINE _sine2.setPhase(M_PI); +#elif SAW + _saw2.setPhase(M_PI); + #elif SINEBANK const float baseAmplitude = 5.0; switch (5) { diff --git a/src/dsp/oscillator.cpp b/src/dsp/oscillator.cpp @@ -19,7 +19,7 @@ float Phasor::nextFromPhasor(const Phasor& phasor, float offset) { return _nextForPhase(p); } -void Phasor::_updateDelta() { +void Phasor::_update() { _delta = (_frequency / _sampleRate) * maxPhase; } @@ -79,7 +79,43 @@ float SawOscillator::_nextForPhase(float phase) { } +void BandLimitedSawOscillator::setQuality(int quality) { + if (_quality != quality) { + assert(quality >= 0); + _quality = quality; + _update(); + } +} + +void BandLimitedSawOscillator::_update() { + Phasor::_update(); + int q = std::min(_quality, (int)(0.5f * (_sampleRate / _frequency))); + _qd = q * _delta; +} + +float BandLimitedSawOscillator::_nextForPhase(float phase) { + float sample = SawOscillator::_nextForPhase(phase); + if (phase > maxPhase - _qd) { + float i = (maxPhase - phase) / _qd; + i = (1.0f - i) * _halfTableLen; + sample -= _amplitude * _table.value((int)i); + } + else if (phase < _qd) { + float i = phase / _qd; + i *= _halfTableLen - 1; + i += _halfTableLen; + sample -= _amplitude * _table.value((int)i); + } + return sample; +} + + void SquareOscillator::setPulseWidth(float pw) { + if (_pulseWidthInput == pw) { + return; + } + _pulseWidthInput = pw; + if (pw >= 0.97f) { _pulseWidth = 0.97f; } @@ -108,6 +144,42 @@ float SquareOscillator::_nextForPhase(float phase) { } +void BandLimitedSquareOscillator::setPulseWidth(float pw) { + if (_pulseWidthInput == pw) { + return; + } + _pulseWidthInput = pw; + + if (pw >= 0.97f) { + _pulseWidth = 0.97f; + } + else if (pw <= 0.03f) { + _pulseWidth = 0.03f; + } + else { + _pulseWidth = pw; + } + _pulseWidth *= maxPhase; + + if (_pulseWidth >= 1.0f) { + _offset = (_pulseWidth - 1.0f) * _amplitude; + } + else { + _offset = -(1.0f - _pulseWidth) * _amplitude; + } +} + +float BandLimitedSquareOscillator::_nextForPhase(float phase) { + float sample = -BandLimitedSawOscillator::_nextForPhase(phase); + phase -= _pulseWidth; + if (phase < 0.0f) { + phase += maxPhase; + } + sample += BandLimitedSawOscillator::_nextForPhase(phase); + return sample + _offset; +} + + float TriangleOscillator::_nextForPhase(float phase) { float p = maxPhase * phase; if (phase < quarterMaxPhase) { diff --git a/src/dsp/oscillator.hpp b/src/dsp/oscillator.hpp @@ -1,6 +1,7 @@ #pragma once #include <stdlib.h> +#include <math.h> #include <vector> #include "base.hpp" @@ -55,20 +56,20 @@ struct Phasor : OscillatorGenerator { : OscillatorGenerator(sampleRate, frequency) { setPhase(initialPhase); - _updateDelta(); + _update(); } virtual void _sampleRateChanged() override { - _updateDelta(); + _update(); } virtual void _frequencyChanged() override { - _updateDelta(); + _update(); } void setPhase(float radians); float nextFromPhasor(const Phasor& phasor, float offset = 0.0f); // offset is not radians, but local phase. - void _updateDelta(); + virtual void _update(); virtual float _next() override final; virtual float _nextForPhase(float phase); @@ -145,7 +146,7 @@ struct SawOscillator : Phasor { SawOscillator( float sampleRate, float frequency, - float amplitude = 1.0 + float amplitude = 1.0f ) : Phasor(sampleRate, frequency) , _amplitude(amplitude) @@ -155,9 +156,37 @@ struct SawOscillator : Phasor { virtual float _nextForPhase(float phase) override; }; +struct BandLimitedSawOscillator : SawOscillator { + int _quality; + const Table& _table; + float _qd = 0.0f; + float _halfTableLen; + + BandLimitedSawOscillator( + float sampleRate, + float frequency, + float amplitude = 1.0f, + int quality = 5, + const Table& table = StaticBlepTable::table() + ) + : SawOscillator(sampleRate, frequency, amplitude) + , _quality(quality) + , _table(table) + , _halfTableLen(_table.length() / 2) + { + setQuality(quality); + } + + void setQuality(int quality); + + virtual void _update() override; + virtual float _nextForPhase(float phase) override; +}; + struct SquareOscillator : Phasor { float _amplitude; float _negativeAmplitude; + float _pulseWidthInput; float _pulseWidth = 0.5; bool positive = true; @@ -177,6 +206,28 @@ struct SquareOscillator : Phasor { virtual float _nextForPhase(float phase) override; }; +struct BandLimitedSquareOscillator : BandLimitedSawOscillator { + float _pulseWidthInput; + float _pulseWidth; + float _offset; + + BandLimitedSquareOscillator( + float sampleRate, + float frequency, + float amplitude = 1.0f, + int quality = 5, + const Table& table = StaticBlepTable::table() + ) + : BandLimitedSawOscillator(sampleRate, frequency, amplitude, quality, table) + { + setPulseWidth(0.05f); + } + + void setPulseWidth(float pw); + + virtual float _nextForPhase(float phase) override; +}; + struct TriangleOscillator : Phasor { const float quarterMaxPhase = 0.25f * maxPhase; const float threeQuartersMaxPhase = 0.75f * maxPhase; diff --git a/src/dsp/table.cpp b/src/dsp/table.cpp @@ -1,6 +1,8 @@ +#include <stdio.h> #include <math.h> #include "table.hpp" +#include "analyzer.hpp" using namespace bogaudio::dsp; @@ -24,3 +26,36 @@ void SineTable::_generate() { _table[i + j] = -_table[i]; } } + + +void BlepTable::_generate() { + // some amount of a sinc function. + const float scaledPi = M_PI * 10.0f; + _table[_length / 2] = 0.0f; + for (int i = 1, j = _length / 2; i < j; ++i) { + float radians = scaledPi * (i / (float)j); + _table[j + i] = sinf(radians) / radians; + } + + // "integrate": FIXME: avoid magic normalization value. + const float norm = _length / 40.0f; + float sum = 0.0f; + for (int i = _length / 2; i < _length; ++i) { + sum += _table[i]; + _table[i] = sum / norm; + } + + // offset. + for (int i = _length / 2; i < _length; ++i) { + _table[i] -= 1.0f; // assumes successful normalization to 1-ish. + } + + // copy to first half of table. + for (int i = 0, j = _length / 2; i < j; ++i) { + _table[i] = -_table[_length - 1 - i]; + } + + // smooth it out even more. + HammingWindow hw(_length); + hw.apply(_table, _table); +} diff --git a/src/dsp/table.hpp b/src/dsp/table.hpp @@ -64,12 +64,17 @@ public: } }; - struct SineTable : Table { SineTable(int n = 10) : Table(n) {} virtual void _generate() override; }; struct StaticSineTable : StaticTable<SineTable, 12> {}; +struct BlepTable : Table { + BlepTable(int n = 10) : Table(n) {} + virtual void _generate() override; +}; +struct StaticBlepTable : StaticTable<BlepTable, 12> {}; + } // namespace dsp } // namespace bogaudio