BogaudioModules

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

commit 5592ba3f51b551dd6f5a143351bbf74c1db5ae86
parent 46bc16deb7e5315c7255d2419c564bdf2e55dafe
Author: Matt Demanett <matt@demanett.net>
Date:   Tue,  6 Feb 2018 01:09:06 -0500

Crude additive oscillator dsp.

Diffstat:
Mbenchmarks/oscillator.cpp | 12++++++++++++
Msrc/Test.cpp | 52+++++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/dsp/oscillator.cpp | 82++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Msrc/dsp/oscillator.hpp | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
4 files changed, 200 insertions(+), 12 deletions(-)

diff --git a/benchmarks/oscillator.cpp b/benchmarks/oscillator.cpp @@ -44,3 +44,15 @@ static void BM_TriangleOscillator(benchmark::State& state) { } } BENCHMARK(BM_TriangleOscillator); + +static void BM_SineBankOscillator(benchmark::State& state) { + SineBankOscillator o(44100.0, 440.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_SineBankOscillator); diff --git a/src/Test.cpp b/src/Test.cpp @@ -2,8 +2,9 @@ // #define LPF 1 // #define SINE 1 // #define SQUARE 1 -#define SAW 1 +// #define SAW 1 // #define TRIANGLE 1 +#define SINEBANK 1 #include "bogaudio.hpp" #include "pitch.hpp" @@ -17,6 +18,8 @@ #include "dsp/oscillator.hpp" #elif TRIANGLE #include "dsp/oscillator.hpp" +#elif SINEBANK +#include "dsp/oscillator.hpp" #else #error what #endif @@ -56,6 +59,8 @@ struct Test : Module { SawOscillator _saw; #elif TRIANGLE TriangleOscillator _triangle; +#elif SINEBANK + SineBankOscillator _sineBank; #endif Test() @@ -70,9 +75,49 @@ struct Test : Module { , _saw(44100.0, 1000.0, 5.0) #elif TRIANGLE , _triangle(44100.0, 1000.0, 5.0) +#elif SINEBANK + , _sineBank(44101.0, 1000.0, 100) #endif { reset(); + +#ifdef SINEBANK + const float baseAmplitude = 5.0; + switch (4) { + case 1: { + // saw + for (int i = 1, n = _sineBank.partialCount(); i <= n; ++i) { + _sineBank.setPartial(i, i, baseAmplitude / (float)i); + } + break; + } + + case 2: { + // square + for (int i = 1, n = _sineBank.partialCount(); i <= n; ++i) { + _sineBank.setPartial(i, i, i % 2 == 1 ? baseAmplitude / (float)i : 0.0); + } + break; + } + + case 3: { + // triangle + float phase = M_PI / 2.0; + for (int i = 1, n = _sineBank.partialCount(); i <= n; ++i) { + _sineBank.setPartial(i, i, i % 2 == 1 ? baseAmplitude / (float)(i * i) : 0.0, &phase); + } + break; + } + + case 4: { + // saw-square + for (int i = 1, n = _sineBank.partialCount(); i <= n; ++i) { + _sineBank.setPartial(i, i, i % 2 == 1 ? baseAmplitude / (float)i : baseAmplitude / (float)(2 * i)); + } + break; + } + } +#endif } virtual void reset() override; @@ -123,6 +168,11 @@ void Test::step() { _triangle.setSampleRate(engineGetSampleRate()); _triangle.setFrequency(oscillatorPitch()); outputs[OUT_OUTPUT].value = _triangle.next(); + +#elif SINEBANK + _sineBank.setSampleRate(engineGetSampleRate()); + _sineBank.setFrequency(oscillatorPitch()); + outputs[OUT_OUTPUT].value = _sineBank.next(); #endif } diff --git a/src/dsp/oscillator.cpp b/src/dsp/oscillator.cpp @@ -4,6 +4,10 @@ using namespace bogaudio::dsp; +void Phasor::setPhase(float phase) { + _phase = phase / (2.0 * M_PI); +} + void Phasor::updateDelta() { float sampleTime = 1.0 / _sampleRate; float cycleTime = 1.0 / _frequency; @@ -19,13 +23,26 @@ float Phasor::_next() { } +void SineOscillator::setPhase(float phase) { + float x = cosf(phase) * _amplitude; + float y = sinf(phase) * _amplitude; + for (int i = 0; i < _n; ++i) { + _x[i] = x; + _y[i] = y; + } + _step = 0; +} + void SineOscillator::updateDeltaTheta() { - float sampleTime = 1.0 / _sampleRate; - float cycleTime = 1.0 / _frequency; - float _deltaTheta = (sampleTime / cycleTime) * 2.0f * M_PI; + float deltaTheta = 0.0f; + if (_sampleRate > 0.0f && _frequency > 0.0f) { + float sampleTime = 1.0f / _sampleRate; + float cycleTime = 1.0f / _frequency; + deltaTheta = (sampleTime / cycleTime) * 2.0f * M_PI; + } for (int i = 0; i < _n; ++i) { - _sinDeltaTheta[i] = sinf((i + 1) * _deltaTheta); - _cosDeltaTheta[i] = cosf((i + 1) * _deltaTheta); + _sinDeltaTheta[i] = sinf((i + 1) * deltaTheta); + _cosDeltaTheta[i] = cosf((i + 1) * deltaTheta); } } @@ -99,3 +116,58 @@ float TriangleOscillator::_next() { } return _amplitude * (p - 4.0); } + + +void SineBankOscillator::setPartial(int i, float frequencyRatio, float amplitude, float* phase) { + if (i > (int)_partials.size()) { + return; + } + + if (amplitude > 0.01) { + _partials[i - 1].enabled = true; + _partials[i - 1].frequencyRatio = frequencyRatio; + _partials[i - 1].frequency = _frequency * frequencyRatio; + _partials[i - 1].sine.setFrequency(_frequency * frequencyRatio); + _partials[i - 1].amplitude = amplitude; + if (phase) { + _partials[i - 1].sine.setPhase(*phase); + } + } + else { + _partials[i - 1].enabled = false; + } +} + +void SineBankOscillator::disablePartial(int i) { + if (i > (int)_partials.size()) { + return; + } + _partials[i - 1].enabled = false; +} + +void SineBankOscillator::_sampleRateChanged() { + _maxPartialFrequency = _maxPartialFrequencySRRatio * _sampleRate; + for (Partial& p : _partials) { + p.sine.setSampleRate(_sampleRate); + } +} + +void SineBankOscillator::_frequencyChanged() { + for (Partial& p : _partials) { + p.frequency = _frequency * p.frequencyRatio; + p.sine.setFrequency(_frequency * p.frequencyRatio); + } +} + +float SineBankOscillator::_next() { + float next = 0.0; + for (Partial& p : _partials) { + if (p.enabled && p.frequency < _maxPartialFrequency) { + next += p.sine.next() * p.amplitude; + } + else { + p.sine.next(); // keep spinning, maintain phase. + } + } + return next; +} diff --git a/src/dsp/oscillator.hpp b/src/dsp/oscillator.hpp @@ -1,5 +1,7 @@ #pragma once +#include <vector> + #include "base.hpp" namespace bogaudio { @@ -43,10 +45,12 @@ struct Phasor : OscillatorGenerator { Phasor( float sampleRate, - float frequency + float frequency, + float initialPhase = 0.0 ) : OscillatorGenerator(sampleRate, frequency) { + setPhase(initialPhase); updateDelta(); } @@ -58,6 +62,7 @@ struct Phasor : OscillatorGenerator { updateDelta(); } + void setPhase(float phase); void updateDelta(); virtual float _next() override; }; @@ -70,17 +75,18 @@ struct SineOscillator : OscillatorGenerator { float _sinDeltaTheta[_n]; float _cosDeltaTheta[_n]; + float _amplitude; + SineOscillator( float sampleRate, float frequency, - float amplitude = 1.0 + float amplitude = 1.0, + float initialPhase = 0.0 ) : OscillatorGenerator(sampleRate, frequency) + , _amplitude(amplitude) { - for (int i = 0; i < _n; ++i) { - _x[i] = amplitude; - _y[i] = 0.0; - } + setPhase(initialPhase); updateDeltaTheta(); } @@ -92,6 +98,7 @@ struct SineOscillator : OscillatorGenerator { updateDeltaTheta(); } + void setPhase(float phase); void updateDeltaTheta(); virtual float _next() override; }; @@ -150,5 +157,52 @@ struct TriangleOscillator : Phasor { virtual float _next() override; }; +struct SineBankOscillator : OscillatorGenerator { + struct Partial { + bool enabled; + float frequency; + float frequencyRatio; + float amplitude; + SineOscillator sine; + + Partial() + : enabled(false) + , frequency(0.0) + , frequencyRatio(0.0) + , amplitude(0.0) + , sine(0.0, 0.0, 1.0) + {} + }; + + const float _maxPartialFrequencySRRatio = 0.48; + float _maxPartialFrequency; + std::vector<Partial> _partials; + + SineBankOscillator( + float sampleRate, + float frequency, + int partialCount + ) + : OscillatorGenerator(sampleRate, frequency) + , _maxPartialFrequency(0.0) + , _partials(partialCount) + { + _sampleRateChanged(); + _frequencyChanged(); + } + + int partialCount() { + return _partials.size(); + } + + // one-based indexes. + void setPartial(int i, float frequencyRatio, float amplitude, float* phase = NULL); + void disablePartial(int i); + + virtual void _sampleRateChanged() override; + virtual void _frequencyChanged() override; + virtual float _next() override; +}; + } // namespace dsp } // namespace bogaudio