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:
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;