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