BogaudioModules

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

commit d1de8431996594c0f66086523c6b2dd0cb9c91d2
parent 00183a7babf8c06d9f432ddedcf71b8cd6b77098
Author: Matt Demanett <matt@demanett.net>
Date:   Tue, 18 Aug 2020 04:51:40 -0400

Initial work on a chirp / swept-sine generator.

Diffstat:
Mbenchmarks/oscillator_benchmark.cpp | 18++++++++++++++++++
Msrc/Test2.cpp | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/Test2.hpp | 21+++++++++++++++++++--
Msrc/dsp/oscillator.cpp | 45+++++++++++++++++++++++++++++++++++++++++++++
Msrc/dsp/oscillator.hpp | 36++++++++++++++++++++++++++++++++++++
Mtest/plot.cpp | 43++++++++++++++++++++++++++++++++++---------
6 files changed, 210 insertions(+), 11 deletions(-)

diff --git a/benchmarks/oscillator_benchmark.cpp b/benchmarks/oscillator_benchmark.cpp @@ -157,3 +157,21 @@ static void BM_Oscillator_SineBankOscillator15000(benchmark::State& state) { } } BENCHMARK(BM_Oscillator_SineBankOscillator15000); + +static void BM_Oscillator_ChirpOscillatorLinear(benchmark::State& state) { + ChirpOscillator o(44100.0, 100.0f, 20000.0f, 1.0f, true); + + for (auto _ : state) { + o.next(); + } +} +BENCHMARK(BM_Oscillator_ChirpOscillatorLinear); + +static void BM_Oscillator_ChirpOscillatorExp(benchmark::State& state) { + ChirpOscillator o(44100.0, 100.0f, 20000.0f, 1.0f, false); + + for (auto _ : state) { + o.next(); + } +} +BENCHMARK(BM_Oscillator_ChirpOscillatorExp); diff --git a/src/Test2.cpp b/src/Test2.cpp @@ -93,6 +93,63 @@ void Test2::processChannel(const ProcessArgs& args, int _c) { float scale = params[PARAM1B_PARAM].getValue() * 2.0f + 1.0f; _limiter.setParams(shape, knee, limit, scale); outputs[OUT_OUTPUT].setVoltage(_limiter.next(inputs[IN_INPUT].getVoltage())); + +#elif CHIRP + float sr = APP->engine->getSampleRate(); + _phasor.setSampleRate(sr); + + float f1 = params[PARAM1A_PARAM].getValue(); + f1 *= f1; + f1 *= sr * 0.5f * std::min(f1, 0.99f); + f1 = std::max(10.0f, f1); + float f2 = params[PARAM1B_PARAM].getValue(); + f2 *= f2; + f2 *= sr * 0.5f * std::min(f2, 0.99f); + f2 = std::max(10.0f, f2); + float T = std::max(0.001f, params[PARAM2A_PARAM].getValue()); // seconds + bool linear = params[PARAM2B_PARAM].getValue() < 0.5f; + + _time = _time * (float)(_time < T); + + // formulas from https://en.wikipedia.org/wiki/Chirp + float out = 0.0f; + if (linear) { + float c = (f2 - f1) / T; + float phase = 2.0f * M_PI * (0.5f * c * _time * _time + f1 * _time); + // out = sinf(phase); + out = _phasor.nextForPhase(Phasor::radiansToPhase(phase)); + } + else { + float k = powf(f2 / f1, 1.0f / T); + float phase = 2.0f * M_PI * f1 * ((powf(k, _time) - 1.0f) / logf(k)); + // out = sinf(phase); + out = _phasor.nextForPhase(Phasor::radiansToPhase(phase)); + } + outputs[OUT_OUTPUT].setVoltage(out * 5.0f); + + _time += 1.0f / sr; + +#elif CHIRP2 + float sr = APP->engine->getSampleRate(); + _chirp.setSampleRate(sr); + + float f1 = params[PARAM1A_PARAM].getValue(); + f1 *= f1; + f1 *= sr * 0.5f * std::min(f1, 0.99f); + f1 = std::max(10.0f, f1); + float f2 = params[PARAM1B_PARAM].getValue(); + f2 *= f2; + f2 *= sr * 0.5f * std::min(f2, 0.99f); + f2 = std::max(10.0f, f2); + float T = ChirpOscillator::minTimeSeconds + params[PARAM2A_PARAM].getValue() * (10.0f - ChirpOscillator::minTimeSeconds); + bool linear = params[PARAM2B_PARAM].getValue() < 0.5f; + _chirp.setParams(f1, f2, T, linear); + outputs[OUT_OUTPUT].setVoltage(_chirp.next() * 5.0f); + + if (_chirp.isCycleComplete()) { + _pulse.trigger(0.001f); + } + outputs[OUT2_OUTPUT].setVoltage(_pulse.process(1.0f / sr) * 5.0f); #endif } @@ -148,6 +205,7 @@ struct Test2Widget : BGModuleWidget { addInput(createInput<Port24>(inInputPosition, module, Test2::IN_INPUT)); addOutput(createOutput<Port24>(outOutputPosition, module, Test2::OUT_OUTPUT)); + addOutput(createOutput<Port24>(Vec(outOutputPosition.x, outOutputPosition.y + 10), module, Test2::OUT2_OUTPUT)); } }; diff --git a/src/Test2.hpp b/src/Test2.hpp @@ -7,7 +7,9 @@ extern Model* modelTest2; // #define COMPLEX_BIQUAD 1 // #define MULTIPOLE 1 // #define ADSR_ENVELOPE 1 -#define LIMITER 1 +// #define LIMITER 1 +// #define CHIRP 1 +#define CHIRP2 1 #ifdef COMPLEX_BIQUAD #include "dsp/filters/filter.hpp" @@ -17,6 +19,10 @@ extern Model* modelTest2; #include "dsp/envelope.hpp" #elif LIMITER #include "dsp/signal.hpp" +#elif CHIRP +#include "dsp/oscillator.hpp" +#elif CHIRP2 +#include "dsp/oscillator.hpp" #elif #error what #endif @@ -49,6 +55,7 @@ struct Test2 : BGModule { enum OutputsIds { OUT_OUTPUT, + OUT2_OUTPUT, NUM_OUTPUTS }; @@ -63,9 +70,19 @@ struct Test2 : BGModule { Trigger _trigger; #elif LIMITER Limiter _limiter; +#elif CHIRP + TablePhasor _phasor; + float _time = 0.0f; +#elif CHIRP2 + ChirpOscillator _chirp; + rack::dsp::PulseGenerator _pulse; #endif - Test2() { + Test2() +#ifdef CHIRP + : _phasor(StaticSineTable::table()) +#endif + { config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS); configParam(PARAM1A_PARAM, 0.0f, 1.0f, 0.0f, "param1a"); configParam(PARAM2A_PARAM, 0.0f, 1.0f, 0.0f, "param2a"); diff --git a/src/dsp/oscillator.cpp b/src/dsp/oscillator.cpp @@ -311,3 +311,48 @@ float SineBankOscillator::next(Phasor::phase_t phaseOffset) { } return next; } + + +void ChirpOscillator::setParams(float frequency1, float frequency2, float time, bool linear) { + frequency1 = std::max(minFrequency, std::min(frequency1, 0.99f * 0.5f * _sampleRate)); + frequency2 = std::max(minFrequency, std::min(frequency2, 0.99f * 0.5f * _sampleRate)); + assert(time >= minTimeSeconds); + + if (_f1 != frequency1 || _f2 != frequency2 || _Time != time || _linear != linear) { + _f1 = frequency1; + _f2 = frequency2; + _Time = time; + _linear = linear; + update(); + } +} + +void ChirpOscillator::_sampleRateChanged() { + _sampleTime = 1.0f / _sampleRate; + update(); +} + +void ChirpOscillator::update() { + _Time = std::max(2.0f * _sampleTime, _Time); + _c = (double)(_f2 - _f1) / (double)_Time; + _k = pow((double)(_f2 / _f1), (double)(1.0f / _Time)); + _invlogk = 1.0 / log(_k); +} + +float ChirpOscillator::_next() { + float phase = 0.0f; + if (_linear) { + phase = 2.0 * M_PI * (0.5 * _c * (double)(_time * _time) + (double)(_f1 * _time)); + } + else { + phase = 2.0 * M_PI * (double)_f1 * ((pow(_k, (double)_time) - 1.0) * _invlogk); + } + + _time += _sampleTime; + if (_time >= _Time) { + _time = 0.0f; + _complete = true; + } + + return _phasor.nextForPhase(Phasor::radiansToPhase(phase)); +} diff --git a/src/dsp/oscillator.hpp b/src/dsp/oscillator.hpp @@ -332,5 +332,41 @@ struct SineBankOscillator : Oscillator { float next(Phasor::phase_t phaseOffset = 0.0f); }; +struct ChirpOscillator : OscillatorGenerator { + static constexpr float minFrequency = 1.0f; + static constexpr float minTimeSeconds = 0.025f; + + TablePhasor _phasor; + float _f1 = -1.0f; + float _f2 = -1.0f; + float _Time = -1.0f; + bool _linear = false; + + float _sampleTime = 0.0f; + float _time = 0.0f; + bool _complete = false; + double _c = 0.0; + double _k = 0.0; + double _invlogk = 0.0; + + ChirpOscillator( + float sampleRate = 1000.0f, + float frequency1 = 100.0f, + float frequency2 = 300.0f, + float time = 1.0f, + bool linear = true + ) + : _phasor(StaticSineTable::table(), sampleRate, frequency1) + { + setParams(frequency1, frequency2, time, linear); + } + + inline bool isCycleComplete() { return _complete; } + void setParams(float frequency1, float frequency2, float, bool linear); + void _sampleRateChanged() override; + void update(); + float _next() override; +}; + } // namespace dsp } // namespace bogaudio diff --git a/test/plot.cpp b/test/plot.cpp @@ -31,19 +31,44 @@ using namespace bogaudio::dsp; // return std::pow(2.0f, x / 2.0f) / (std::pow(2.0f, x) - 1.0f); // } -const float m = 20000.0f; -float y(float x) { - x /= m; - x = powf(x, 1.25f); - x *= 0.5f * m; - return x; +// const float m = 20000.0f; +// float y(float x) { +// x /= m; +// x = powf(x, 1.25f); +// x *= 0.5f * m; +// return x; +// } +// +// int main() { +// const float xMin = 0.1f; +// // const float xMax = 1023.0f; +// const float xMax = 100.0f; +// const float samples = 1024.0f; +// +// const float delta = (xMax - xMin) / samples; +// float x = xMin; +// while (x <= xMax) { +// printf("%f, %f\n", x, y(x)); +// x += delta; +// } +// // printf("%f\n", y(1.0f)); +// return 0; +// } + +const float sr = 10000.0f; +float f1 = 100.0f; +float f2 = sr / 2.0f * 0.1f; +float T = 1.5f; +float y(float t) { + float k = powf(f2 / f1, 1.0f / T); + return sinf(2.0f * M_PI * f1 * ((powf(k, t) - 1.0f) / logf(k))); } int main() { - const float xMin = 0.1f; + const float xMin = 0.0f; // const float xMax = 1023.0f; - const float xMax = 100.0f; - const float samples = 1024.0f; + const float xMax = T; + const float samples = sr; const float delta = (xMax - xMin) / samples; float x = xMin;