commit 92d485cae583ece05e887127b89e6a67b75948d5
parent 3a2e641c7ae8b4de94ff18a85a1a3a8e67ee220e
Author: Matt Demanett <matt@demanett.net>
Date: Sun, 18 Mar 2018 01:03:32 -0400
Band-limited saw and square oscillators.
Diffstat:
6 files changed, 208 insertions(+), 9 deletions(-)
diff --git a/src/Test.cpp b/src/Test.cpp
@@ -49,11 +49,22 @@ void Test::step() {
_square.setPulseWidth(pw);
outputs[OUT_OUTPUT].value = _square.next();
+ _square2.setSampleRate(engineGetSampleRate());
+ _square2.setFrequency(oscillatorPitch());
+ _square2.setPulseWidth(pw);
+ _square2.setQuality(params[PARAM3_PARAM].value * 200);
+ outputs[OUT2_OUTPUT].value = _square2.next();
+
#elif SAW
_saw.setSampleRate(engineGetSampleRate());
_saw.setFrequency(oscillatorPitch());
outputs[OUT_OUTPUT].value = _saw.next();
+ _saw2.setSampleRate(engineGetSampleRate());
+ _saw2.setFrequency(oscillatorPitch());
+ _saw2.setQuality(params[PARAM2_PARAM].value * 200);
+ outputs[OUT2_OUTPUT].value = _saw2.next();
+
#elif TRIANGLE
_triangle.setSampleRate(engineGetSampleRate());
_triangle.setFrequency(oscillatorPitch());
@@ -150,6 +161,15 @@ void Test::step() {
_envelope.setRelease(params[PARAM2_PARAM].value);
_envelope.setGate(inputs[CV1_INPUT].value > 0.1f);
outputs[OUT_OUTPUT].value = _envelope.next() * 10.0f;
+
+#elif TABLES
+ _sine.setSampleRate(engineGetSampleRate());
+ _sine.setFrequency(oscillatorPitch());
+ outputs[OUT_OUTPUT].value = _sine.next();
+
+ _table.setSampleRate(engineGetSampleRate());
+ _table.setFrequency(oscillatorPitch());
+ outputs[OUT2_OUTPUT].value = _table.next();
#endif
}
diff --git a/src/Test.hpp b/src/Test.hpp
@@ -6,8 +6,8 @@ extern Model* modelTest;
// #define LPF 1
// #define LPFNOISE 1
-#define SINE 1
-// #define SQUARE 1
+// #define SINE 1
+#define SQUARE 1
// #define SAW 1
// #define TRIANGLE 1
// #define SINEBANK 1
@@ -16,6 +16,7 @@ extern Model* modelTest;
// #define PM 1
// #define FEEDBACK_PM 1
// #define EG 1
+// #define TABLES 1
#include "pitch.hpp"
#ifdef LPF
@@ -46,6 +47,8 @@ extern Model* modelTest;
#include "dsp/oscillator.hpp"
#elif EG
#include "dsp/envelope.hpp"
+#elif TABLES
+#include "dsp/oscillator.hpp"
#else
#error what
#endif
@@ -90,8 +93,10 @@ struct Test : Module {
SineTableOscillator _sine2;
#elif SQUARE
SquareOscillator _square;
+ BandLimitedSquareOscillator _square2;
#elif SAW
SawOscillator _saw;
+ BandLimitedSawOscillator _saw2;
#elif TRIANGLE
TriangleOscillator _triangle;
#elif SINEBANK
@@ -115,6 +120,9 @@ struct Test : Module {
float _feedbackSample = 0.0f;
#elif EG
ADSR _envelope;
+#elif TABLES
+ SineTableOscillator _sine;
+ TablePhasor _table;
#endif
Test()
@@ -128,8 +136,10 @@ struct Test : Module {
, _sine2(44100.0, 1000.0, 5.0)
#elif SQUARE
, _square(44100.0, 1000.0, 5.0)
+ , _square2(44100.0, 1000.0, 5.0)
#elif SAW
, _saw(44100.0, 1000.0, 5.0)
+ , _saw2(44100.0, 1000.0, 5.0, 8)
#elif TRIANGLE
, _triangle(44100.0, 1000.0, 5.0)
#elif SINEBANK
@@ -151,6 +161,9 @@ struct Test : Module {
, _carrierOutput(44100.0, 1000.0)
#elif EG
, _envelope(44100.0)
+#elif TABLES
+ , _sine(44100.0, 1000.0, 5.0)
+ , _table(StaticBlepTable::table(), 44100.0, 1000.0, 5.0)
#endif
{
onReset();
@@ -158,6 +171,9 @@ struct Test : Module {
#ifdef SINE
_sine2.setPhase(M_PI);
+#elif SAW
+ _saw2.setPhase(M_PI);
+
#elif SINEBANK
const float baseAmplitude = 5.0;
switch (5) {
diff --git a/src/dsp/oscillator.cpp b/src/dsp/oscillator.cpp
@@ -19,7 +19,7 @@ float Phasor::nextFromPhasor(const Phasor& phasor, float offset) {
return _nextForPhase(p);
}
-void Phasor::_updateDelta() {
+void Phasor::_update() {
_delta = (_frequency / _sampleRate) * maxPhase;
}
@@ -79,7 +79,43 @@ float SawOscillator::_nextForPhase(float phase) {
}
+void BandLimitedSawOscillator::setQuality(int quality) {
+ if (_quality != quality) {
+ assert(quality >= 0);
+ _quality = quality;
+ _update();
+ }
+}
+
+void BandLimitedSawOscillator::_update() {
+ Phasor::_update();
+ int q = std::min(_quality, (int)(0.5f * (_sampleRate / _frequency)));
+ _qd = q * _delta;
+}
+
+float BandLimitedSawOscillator::_nextForPhase(float phase) {
+ float sample = SawOscillator::_nextForPhase(phase);
+ if (phase > maxPhase - _qd) {
+ float i = (maxPhase - phase) / _qd;
+ i = (1.0f - i) * _halfTableLen;
+ sample -= _amplitude * _table.value((int)i);
+ }
+ else if (phase < _qd) {
+ float i = phase / _qd;
+ i *= _halfTableLen - 1;
+ i += _halfTableLen;
+ sample -= _amplitude * _table.value((int)i);
+ }
+ return sample;
+}
+
+
void SquareOscillator::setPulseWidth(float pw) {
+ if (_pulseWidthInput == pw) {
+ return;
+ }
+ _pulseWidthInput = pw;
+
if (pw >= 0.97f) {
_pulseWidth = 0.97f;
}
@@ -108,6 +144,42 @@ float SquareOscillator::_nextForPhase(float phase) {
}
+void BandLimitedSquareOscillator::setPulseWidth(float pw) {
+ if (_pulseWidthInput == pw) {
+ return;
+ }
+ _pulseWidthInput = pw;
+
+ if (pw >= 0.97f) {
+ _pulseWidth = 0.97f;
+ }
+ else if (pw <= 0.03f) {
+ _pulseWidth = 0.03f;
+ }
+ else {
+ _pulseWidth = pw;
+ }
+ _pulseWidth *= maxPhase;
+
+ if (_pulseWidth >= 1.0f) {
+ _offset = (_pulseWidth - 1.0f) * _amplitude;
+ }
+ else {
+ _offset = -(1.0f - _pulseWidth) * _amplitude;
+ }
+}
+
+float BandLimitedSquareOscillator::_nextForPhase(float phase) {
+ float sample = -BandLimitedSawOscillator::_nextForPhase(phase);
+ phase -= _pulseWidth;
+ if (phase < 0.0f) {
+ phase += maxPhase;
+ }
+ sample += BandLimitedSawOscillator::_nextForPhase(phase);
+ return sample + _offset;
+}
+
+
float TriangleOscillator::_nextForPhase(float phase) {
float p = maxPhase * phase;
if (phase < quarterMaxPhase) {
diff --git a/src/dsp/oscillator.hpp b/src/dsp/oscillator.hpp
@@ -1,6 +1,7 @@
#pragma once
#include <stdlib.h>
+#include <math.h>
#include <vector>
#include "base.hpp"
@@ -55,20 +56,20 @@ struct Phasor : OscillatorGenerator {
: OscillatorGenerator(sampleRate, frequency)
{
setPhase(initialPhase);
- _updateDelta();
+ _update();
}
virtual void _sampleRateChanged() override {
- _updateDelta();
+ _update();
}
virtual void _frequencyChanged() override {
- _updateDelta();
+ _update();
}
void setPhase(float radians);
float nextFromPhasor(const Phasor& phasor, float offset = 0.0f); // offset is not radians, but local phase.
- void _updateDelta();
+ virtual void _update();
virtual float _next() override final;
virtual float _nextForPhase(float phase);
@@ -145,7 +146,7 @@ struct SawOscillator : Phasor {
SawOscillator(
float sampleRate,
float frequency,
- float amplitude = 1.0
+ float amplitude = 1.0f
)
: Phasor(sampleRate, frequency)
, _amplitude(amplitude)
@@ -155,9 +156,37 @@ struct SawOscillator : Phasor {
virtual float _nextForPhase(float phase) override;
};
+struct BandLimitedSawOscillator : SawOscillator {
+ int _quality;
+ const Table& _table;
+ float _qd = 0.0f;
+ float _halfTableLen;
+
+ BandLimitedSawOscillator(
+ float sampleRate,
+ float frequency,
+ float amplitude = 1.0f,
+ int quality = 5,
+ const Table& table = StaticBlepTable::table()
+ )
+ : SawOscillator(sampleRate, frequency, amplitude)
+ , _quality(quality)
+ , _table(table)
+ , _halfTableLen(_table.length() / 2)
+ {
+ setQuality(quality);
+ }
+
+ void setQuality(int quality);
+
+ virtual void _update() override;
+ virtual float _nextForPhase(float phase) override;
+};
+
struct SquareOscillator : Phasor {
float _amplitude;
float _negativeAmplitude;
+ float _pulseWidthInput;
float _pulseWidth = 0.5;
bool positive = true;
@@ -177,6 +206,28 @@ struct SquareOscillator : Phasor {
virtual float _nextForPhase(float phase) override;
};
+struct BandLimitedSquareOscillator : BandLimitedSawOscillator {
+ float _pulseWidthInput;
+ float _pulseWidth;
+ float _offset;
+
+ BandLimitedSquareOscillator(
+ float sampleRate,
+ float frequency,
+ float amplitude = 1.0f,
+ int quality = 5,
+ const Table& table = StaticBlepTable::table()
+ )
+ : BandLimitedSawOscillator(sampleRate, frequency, amplitude, quality, table)
+ {
+ setPulseWidth(0.05f);
+ }
+
+ void setPulseWidth(float pw);
+
+ virtual float _nextForPhase(float phase) override;
+};
+
struct TriangleOscillator : Phasor {
const float quarterMaxPhase = 0.25f * maxPhase;
const float threeQuartersMaxPhase = 0.75f * maxPhase;
diff --git a/src/dsp/table.cpp b/src/dsp/table.cpp
@@ -1,6 +1,8 @@
+#include <stdio.h>
#include <math.h>
#include "table.hpp"
+#include "analyzer.hpp"
using namespace bogaudio::dsp;
@@ -24,3 +26,36 @@ void SineTable::_generate() {
_table[i + j] = -_table[i];
}
}
+
+
+void BlepTable::_generate() {
+ // some amount of a sinc function.
+ const float scaledPi = M_PI * 10.0f;
+ _table[_length / 2] = 0.0f;
+ for (int i = 1, j = _length / 2; i < j; ++i) {
+ float radians = scaledPi * (i / (float)j);
+ _table[j + i] = sinf(radians) / radians;
+ }
+
+ // "integrate": FIXME: avoid magic normalization value.
+ const float norm = _length / 40.0f;
+ float sum = 0.0f;
+ for (int i = _length / 2; i < _length; ++i) {
+ sum += _table[i];
+ _table[i] = sum / norm;
+ }
+
+ // offset.
+ for (int i = _length / 2; i < _length; ++i) {
+ _table[i] -= 1.0f; // assumes successful normalization to 1-ish.
+ }
+
+ // copy to first half of table.
+ for (int i = 0, j = _length / 2; i < j; ++i) {
+ _table[i] = -_table[_length - 1 - i];
+ }
+
+ // smooth it out even more.
+ HammingWindow hw(_length);
+ hw.apply(_table, _table);
+}
diff --git a/src/dsp/table.hpp b/src/dsp/table.hpp
@@ -64,12 +64,17 @@ public:
}
};
-
struct SineTable : Table {
SineTable(int n = 10) : Table(n) {}
virtual void _generate() override;
};
struct StaticSineTable : StaticTable<SineTable, 12> {};
+struct BlepTable : Table {
+ BlepTable(int n = 10) : Table(n) {}
+ virtual void _generate() override;
+};
+struct StaticBlepTable : StaticTable<BlepTable, 12> {};
+
} // namespace dsp
} // namespace bogaudio