From be8cb0f890e1883557382b377e3246598e34f971 Mon Sep 17 00:00:00 2001 From: Blitblank Date: Sat, 17 Jan 2026 15:56:39 -0600 Subject: [PATCH] add wavetables --- CMakeLists.txt | 2 ++ src/synth/Oscillator.cpp | 29 +++++------------------------ src/synth/Oscillator.h | 13 +++++-------- src/synth/Synth.cpp | 2 +- src/synth/Synth.h | 4 ++++ src/synth/Voice.cpp | 15 ++++++++++++--- src/synth/Voice.h | 5 ++++- 7 files changed, 33 insertions(+), 37 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c1802f6..50d0f52 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,6 +86,8 @@ qt_add_executable(metabolus src/synth/Oscillator.h src/synth/Voice.cpp src/synth/Voice.h + src/synth/WavetableController.cpp + src/synth/WavetableController.h resources/resources.qrc src/ui/widgets/SmartSlider/SmartSlider.cpp src/ui/widgets/SmartSlider/SmartSlider.h diff --git a/src/synth/Oscillator.cpp b/src/synth/Oscillator.cpp index fc236a4..2a746de 100644 --- a/src/synth/Oscillator.cpp +++ b/src/synth/Oscillator.cpp @@ -1,6 +1,10 @@ #include "Oscillator.h" +Oscillator::Oscillator(WavetableController* wavetable) : wavetable_(wavetable) { + +} + void Oscillator::setWavetable(uint8_t waveTableId) { activeWavetable_ = waveTableId; } @@ -20,33 +24,10 @@ float Oscillator::process(uint8_t note, bool& scopeTrigger) { float Oscillator::process(float frequency, bool& scopeTrigger) { - float sampleOut = 0.0f; float pitchOffset = 0.5f; float phaseInc = pitchOffset * 2.0f * M_PI * frequency / sampleRate_; - switch (activeWavetable_) { - case 0: // sine - sampleOut = std::sin(phase_) / 0.707f; - break; - case 1: // square - sampleOut = (phase_ >= M_PI) ? 1.0f : -1.0f; - break; - case 2: // saw - sampleOut = ((phase_ / M_PI) - 1.0f) / 0.577f; - break; - case 3: // triangle - if(phase_ <= M_PI/2.0f) { - sampleOut = phase_ * 2.0f/M_PI; - } else if(phase_ <= 3.0f*M_PI/2.0f) { - sampleOut = phase_ * -2.0f/M_PI + 2.0f; - } else { - sampleOut = phase_ * 2.0f/M_PI - 4.0f; - } - sampleOut /= 0.577f; - break; - default: // unreachable - break; - } + float sampleOut = wavetable_->sample(activeWavetable_, phase_); phase_ += phaseInc; if (phase_ > 2.0f * M_PI) { diff --git a/src/synth/Oscillator.h b/src/synth/Oscillator.h index 3ecf14a..0fbdc71 100644 --- a/src/synth/Oscillator.h +++ b/src/synth/Oscillator.h @@ -1,6 +1,8 @@ #pragma once +#include "WavetableController.h" + #include #include #include @@ -13,12 +15,12 @@ #define SYNTH_PITCH_STANDARD 440.0f // frequency of home pitch #define SYNTH_MIDI_HOME 69 // midi note index of home pitch #define SYNTH_NOTES_PER_OCTAVE 12 -#define SYNTH_WAVETABLE_SIZE 2048 class Oscillator { public: Oscillator() = default; + Oscillator(WavetableController* wavetable); ~Oscillator() = default; void setWavetable(uint8_t waveTableId); @@ -39,12 +41,7 @@ private: uint8_t activeWavetable_; float frequency_ = 220.0f; - // TODO: implement - // TODO: wavetable class that can load from files - // TODO: wavetables should be shared among the entire synth - std::array wavetable1_; - std::array wavetable2_; - std::array wavetable3_; - std::array wavetable4_; + // wavetables + WavetableController* wavetable_; }; diff --git a/src/synth/Synth.cpp b/src/synth/Synth.cpp index c4dbbbd..0e10cb6 100644 --- a/src/synth/Synth.cpp +++ b/src/synth/Synth.cpp @@ -9,7 +9,7 @@ #endif Synth::Synth(const ParameterStore& params) : paramStore_(params) { - voices_.fill(Voice(params_.data())); + voices_.fill(Voice(params_.data(), &wavetable_)); } void Synth::updateParams() { diff --git a/src/synth/Synth.h b/src/synth/Synth.h index f373cca..b57f513 100644 --- a/src/synth/Synth.h +++ b/src/synth/Synth.h @@ -7,6 +7,7 @@ #include "ScopeBuffer.h" #include "Filter.h" #include "Voice.h" +#include "WavetableController.h" #include #include @@ -54,4 +55,7 @@ private: std::chrono::time_point lastTime; + // waveforms + WavetableController wavetable_; + }; diff --git a/src/synth/Voice.cpp b/src/synth/Voice.cpp index 77c80a0..04f6342 100644 --- a/src/synth/Voice.cpp +++ b/src/synth/Voice.cpp @@ -3,8 +3,9 @@ #include #include -Voice::Voice(SmoothedParam* params) : params_(params) { +Voice::Voice(SmoothedParam* params, WavetableController* wavetable) : params_(params), wavetable_(wavetable) { + oscillators_.fill(Oscillator(wavetable_)); } // cascade sample rate to all descendant objects @@ -82,9 +83,17 @@ float Voice::process(float* params, bool& scopeTrigger) { // sample generation uint8_t osc1Wave = (static_cast(std::round(getParam(ParamId::Osc1WaveSelector1)))); - oscillators_[0].setWavetable(osc1Wave); + for(Oscillator& o : oscillators_) { + o.setWavetable(osc1Wave); + } - float sampleOut = oscillators_[0].process(note_, scopeTrigger) * gain; + // TODO: oscillator pitch offset + bool temp = false; + float osc1 = oscillators_[0].process(note_, scopeTrigger); + float osc2 = oscillators_[1].process(static_cast(note_+12), temp); + float osc3 = oscillators_[2].process(static_cast(note_+19), temp); + + float sampleOut = (osc1 + osc2*0.5f + osc3*0.25f) * gain; // filter sample float baseFreq = oscillators_[0].frequency(); diff --git a/src/synth/Voice.h b/src/synth/Voice.h index cb75731..fa65795 100644 --- a/src/synth/Voice.h +++ b/src/synth/Voice.h @@ -25,7 +25,7 @@ class Voice { public: Voice() = default; - Voice(SmoothedParam* params); + Voice(SmoothedParam* params, WavetableController* wavetable); ~Voice() = default; void setSampleRate(float sampleRate); @@ -67,6 +67,9 @@ private: // paramstore pointer SmoothedParam* params_; + // wavetables + WavetableController* wavetable_; + // TODO: add a parameter in the paramstore for this float velocitySensitivity = 0.7f; float velocityCenter = 2.0f;