BogaudioModules

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

commit a2e4dc31065936f6fb948b5d92c2781d2446e0b1
parent 464e7ac36f5ba93ec4637fb0e3f93a3d59d0a112
Author: Matt Demanett <matt@demanett.net>
Date:   Wed,  4 Apr 2018 16:45:58 -0400

Add sampling feature to Phasor (phase proceeds by steps); use for XCO triangle mod.

Diffstat:
Mbenchmarks/oscillator_benchmark.cpp | 9+++++++++
Msrc/Test.cpp | 24++++++++++++++++++++++++
Msrc/Test.hpp | 17++++++++++++++++-
Msrc/XCO.cpp | 41+++++++----------------------------------
Msrc/XCO.hpp | 3---
Msrc/dsp/oscillator.cpp | 25+++++++++++++++++++++++++
Msrc/dsp/oscillator.hpp | 6+++++-
7 files changed, 86 insertions(+), 39 deletions(-)

diff --git a/benchmarks/oscillator_benchmark.cpp b/benchmarks/oscillator_benchmark.cpp @@ -101,6 +101,15 @@ static void BM_TriangleOscillator(benchmark::State& state) { } BENCHMARK(BM_TriangleOscillator); +static void BM_SampledTriangleOscillator(benchmark::State& state) { + TriangleOscillator o(44100.0, 440.0); + o.setSampleWidth(Phasor::maxSampleWidth / 2.0); + for (auto _ : state) { + o.next(); + } +} +BENCHMARK(BM_SampledTriangleOscillator); + static void BM_SineBankOscillator100(benchmark::State& state) { SineBankOscillator o(44100.0, 100.0, 100); for (int i = 1, n = o.partialCount(); i <= n; ++i) { diff --git a/src/Test.cpp b/src/Test.cpp @@ -86,6 +86,30 @@ void Test::step() { _triangle.setFrequency(oscillatorPitch()); outputs[OUT_OUTPUT].value = _triangle.next() * 5.0f; +#elif SAMPLED_TRIANGLE + float sample = params[PARAM2_PARAM].value * Phasor::maxSampleWidth; + if (inputs[CV2_INPUT].active) { + sample *= clamp(inputs[CV2_INPUT].value / 10.0f, 0.0f, 1.0f); + } + _triangle.setSampleRate(engineGetSampleRate()); + _triangle.setFrequency(oscillatorPitch()); + _triangle.setSampleWidth(sample); + outputs[OUT_OUTPUT].value = _triangle.next() * 5.0f; + + _triangle2.setSampleRate(engineGetSampleRate()); + _triangle2.setFrequency(oscillatorPitch()); + float maxSampleSteps = (_triangle2._sampleRate / _triangle2._frequency) / 4.0f; + _sampleSteps = clamp((int)((4.0f * sample) * maxSampleSteps), 1, (int)maxSampleSteps); + ++_sampleStep; + if (_sampleStep >= _sampleSteps) { + _sampleStep = 0; + _sample = _triangle2.next() * 5.0f; + } + else { + _triangle2.advancePhase(); + } + outputs[OUT2_OUTPUT].value = _sample; + #elif SINEBANK _sineBank.setSampleRate(engineGetSampleRate()); _sineBank.setFrequency(oscillatorPitch()); diff --git a/src/Test.hpp b/src/Test.hpp @@ -11,7 +11,8 @@ extern Model* modelTest; // #define SAW 1 // #define SATSAW 1 // #define TRIANGLE 1 -#define SINEBANK 1 +#define SAMPLED_TRIANGLE 1 +// #define SINEBANK 1 // #define OVERSAMPLING 1 // #define OVERSAMPLED_BL 1 // #define FM 1 @@ -36,6 +37,8 @@ extern Model* modelTest; #include "dsp/oscillator.hpp" #elif TRIANGLE #include "dsp/oscillator.hpp" +#elif SAMPLED_TRIANGLE +#include "dsp/oscillator.hpp" #elif SINEBANK #include "dsp/oscillator.hpp" #elif OVERSAMPLING @@ -109,6 +112,12 @@ struct Test : Module { BandLimitedSawOscillator _saw2; #elif TRIANGLE TriangleOscillator _triangle; +#elif SAMPLED_TRIANGLE + TriangleOscillator _triangle; + TriangleOscillator _triangle2; + int _sampleSteps = 1 << 20; + int _sampleStep = 0; + float _sample = 0.0f; #elif SINEBANK SineBankOscillator _sineBank; #elif OVERSAMPLING @@ -163,6 +172,9 @@ struct Test : Module { , _saw2(44100.0, 1000.0, 8) #elif TRIANGLE , _triangle(44100.0, 1000.0) +#elif SAMPLED_TRIANGLE + , _triangle(44100.0, 1000.0) + , _triangle2(44100.0, 1000.0) #elif SINEBANK , _sineBank(44101.0, 1000.0, 50) #elif OVERSAMPLING @@ -203,6 +215,9 @@ struct Test : Module { #elif SATSAW _saw2.setPhase(M_PI); +#elif SAMPLED_TRIANGLE + _triangle2.setPhase(M_PI); + #elif SINEBANK const float baseAmplitude = 5.0; switch (5) { diff --git a/src/XCO.cpp b/src/XCO.cpp @@ -5,13 +5,11 @@ void XCO::onReset() { _syncTrigger.reset(); _modulationStep = modulationSteps; - _triangleSampleStep = _phasor._sampleRate; } void XCO::onSampleRateChange() { _phasor.setSampleRate(engineGetSampleRate()); _modulationStep = modulationSteps; - _triangleSampleStep = _phasor._sampleRate; } void XCO::step() { @@ -62,18 +60,17 @@ void XCO::step() { } _saw.setSaturation(saturation * 10.f); + float sample = params[TRIANGLE_SAMPLE_PARAM].value * Phasor::maxSampleWidth; + if (inputs[TRIANGLE_SAMPLE_INPUT].active) { + sample *= clamp(inputs[TRIANGLE_SAMPLE_INPUT].value / 10.0f, 0.0f, 1.0f); + } + _triangle.setSampleWidth(sample); + _sineFeedback = params[SINE_FEEDBACK_PARAM].value; if (inputs[SINE_FEEDBACK_INPUT].active) { _sineFeedback *= clamp(inputs[SINE_FEEDBACK_INPUT].value / 10.0f, 0.0f, 1.0f); } - float sample = params[TRIANGLE_SAMPLE_PARAM].value; - if (inputs[TRIANGLE_SAMPLE_INPUT].active) { - sample *= clamp(inputs[TRIANGLE_SAMPLE_INPUT].value / 10.0f, 0.0f, 1.0f); - } - float maxSampleSteps = (_phasor._sampleRate / _phasor._frequency) / 4.0f; - _triangleSampleSteps = clamp((int)(sample * maxSampleSteps), 1, (int)maxSampleSteps); - _fmDepth = params[FM_PARAM].value; _squarePhaseOffset = phaseOffset(params[SQUARE_PHASE_PARAM], inputs[SQUARE_PHASE_INPUT]); @@ -118,31 +115,7 @@ void XCO::step() { mix += outputs[SAW_OUTPUT].value = amplitude * _sawMix * _saw.nextFromPhasor(_phasor, _sawPhaseOffset + phaseOffset); } if (outputs[MIX_OUTPUT].active || outputs[TRIANGLE_OUTPUT].active) { - bool useSample = false; - if (_triangleSampleSteps > 1) { - ++_triangleSampleStep; - if (_triangleSampleStep < _triangleSampleSteps) { - useSample = true; - } - else { - _triangleSampleStep = 0; - } - } - - if (fmOn && _fmLinearMode) { - if (!useSample) { - _triangleSample = _phasor._phase; - } - mix += outputs[TRIANGLE_OUTPUT].value = amplitude * _triangleMix * _triangle.nextForPhase(_triangleSample + _trianglePhaseOffset + phaseOffset); - } - else { - if (useSample) { - mix += outputs[TRIANGLE_OUTPUT].value = _triangleSample; - } - else { - mix += outputs[TRIANGLE_OUTPUT].value = _triangleSample = amplitude * _triangleMix * _triangle.nextFromPhasor(_phasor, _trianglePhaseOffset + phaseOffset); - } - } + mix += outputs[TRIANGLE_OUTPUT].value = amplitude * _triangleMix * _triangle.nextFromPhasor(_phasor, _trianglePhaseOffset + phaseOffset); } if (outputs[MIX_OUTPUT].active || outputs[SINE_OUTPUT].active) { mix += outputs[SINE_OUTPUT].value = amplitude * _sineMix * _sine.nextFromPhasor(_phasor, sineFeedbackOffset + _sinePhaseOffset + phaseOffset); diff --git a/src/XCO.hpp b/src/XCO.hpp @@ -74,9 +74,6 @@ struct XCO : Module { bool _slowMode = false; float _fmDepth = 0.0f; bool _fmLinearMode = false; - int _triangleSampleStep = 0; - int _triangleSampleSteps = 0; - float _triangleSample = 0.0f; float _sineFeedback = 0.0f; float _squarePhaseOffset = 0.0f; float _sawPhaseOffset = 0.0f; diff --git a/src/dsp/oscillator.cpp b/src/dsp/oscillator.cpp @@ -4,6 +4,24 @@ using namespace bogaudio::dsp; +void Phasor::setSampleWidth(float sw) { + if (sw < 0.0f) { + sw = 0.0f; + } + else if (sw > maxSampleWidth) { + sw = maxSampleWidth; + } + if (_sampleWidth != sw) { + _sampleWidth = sw; + if (_sampleWidth > 0.001f) { + _samplePhase = _sampleWidth * maxPhase; + } + else { + _samplePhase = 0.0f; + } + } +} + void Phasor::setPhase(float radians) { _phase = radiansToPhase(radians); } @@ -16,6 +34,10 @@ float Phasor::nextFromPhasor(const Phasor& phasor, float offset) { while (p < 0.0f) { p += maxPhase; } + + if (_samplePhase > 0.0f) { + p -= fmodf(p, _samplePhase); + } return _nextForPhase(p); } @@ -68,6 +90,9 @@ void Phasor::advancePhasePositive() { float Phasor::_next() { advancePhase(); + if (_samplePhase > 0.0f) { + return _nextForPhase(_phase - fmodf(_phase, _samplePhase)); + } return _nextForPhase(_phase); } diff --git a/src/dsp/oscillator.hpp b/src/dsp/oscillator.hpp @@ -55,14 +55,17 @@ struct OscillatorGenerator : Oscillator, Generator { struct Phasor : OscillatorGenerator { static constexpr float maxPhase = 2.0f; + static constexpr float maxSampleWidth = 0.25f; float _delta; double _phase = 0.0; + float _sampleWidth = 0.0f; + float _samplePhase = 0.0f; Phasor( float sampleRate, float frequency, - float initialPhase = 0.0 + float initialPhase = 0.0f ) : OscillatorGenerator(sampleRate, frequency) { @@ -78,6 +81,7 @@ struct Phasor : OscillatorGenerator { _update(); } + void setSampleWidth(float sw); // fraction of maxPhase. void setPhase(float radians); float nextFromPhasor(const Phasor& phasor, float offset = 0.0f); // offset is not radians, but local phase. float nextForPhase(float phase); // local phase, not radians.