feat(dsp): add core synthesizer oscillators, wavetable and voice engine
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
#pragma once
|
||||
|
||||
#include <JuceHeader.h>
|
||||
#include "Params.h"
|
||||
|
||||
namespace serum
|
||||
{
|
||||
|
||||
// ===========================================================================
|
||||
// A single wavetable: 256 frames of 2048 samples each. Frames morph smoothly
|
||||
// from a simple base shape to a complex one. Lookups interpolate bilinearly
|
||||
// (across frames and within a frame). Tables are RAM-resident and generated
|
||||
// lazily by the WavetableLibrary.
|
||||
// ===========================================================================
|
||||
class Wavetable
|
||||
{
|
||||
public:
|
||||
static constexpr int kFrames = 256;
|
||||
static constexpr int kTableSize = 2048;
|
||||
|
||||
Wavetable() = default;
|
||||
|
||||
juce::String name;
|
||||
std::vector<std::vector<float>> frames; // [frame][sample]
|
||||
|
||||
bool isValid() const noexcept { return (int) frames.size() == kFrames; }
|
||||
|
||||
void clear();
|
||||
|
||||
// Build a table from a per-frame harmonic amplitude function.
|
||||
// ampFn(frameIndex, harmonicNumber) -> amplitude (harmonic 1 = fundamental)
|
||||
using HarmonicAmpFn = std::function<float (int, int)>;
|
||||
void buildHarmonic (const juce::String& name, HarmonicAmpFn ampFn, int numHarmonics = 64);
|
||||
|
||||
// Read the table at a fractional frame position and a phase in [0,1).
|
||||
float read (float framePos, float phase) const noexcept;
|
||||
|
||||
// Read with phase wrapped and clamped frame position.
|
||||
float readSafe (float framePos, float phase) const noexcept
|
||||
{
|
||||
phase -= std::floor (phase);
|
||||
return read (juce::jlimit (0.0f, (float) kFrames - 1.001f, framePos), phase);
|
||||
}
|
||||
|
||||
// Copy raw samples of a frame into a destination buffer (for the GUI).
|
||||
void copyFrame (int frameIndex, float* dest, int numSamples) const;
|
||||
|
||||
private:
|
||||
static float lerp (float a, float b, float t) noexcept { return a + (b - a) * t; }
|
||||
};
|
||||
|
||||
// ===========================================================================
|
||||
// Lazily-built library of factory wavetables. Indices map to kWavetableNames.
|
||||
// ===========================================================================
|
||||
class WavetableLibrary
|
||||
{
|
||||
public:
|
||||
const Wavetable& getTable (int index) const;
|
||||
int size() const noexcept { return kNumWavetables; }
|
||||
|
||||
// Force generation of all tables (used at prepare time).
|
||||
void prebuild();
|
||||
|
||||
private:
|
||||
mutable std::array<Wavetable, kNumWavetables> tables;
|
||||
mutable std::array<bool, kNumWavetables> built { { false } };
|
||||
|
||||
void buildTable (int index) const;
|
||||
static Wavetable makeBasic();
|
||||
static Wavetable makeSawPwm();
|
||||
static Wavetable makeSquareSync();
|
||||
static Wavetable makeTriangleFold();
|
||||
static Wavetable makeVowel();
|
||||
static Wavetable makeOrgan();
|
||||
static Wavetable makeWarmSaw();
|
||||
static Wavetable makeDigital();
|
||||
static Wavetable makeGlass();
|
||||
static Wavetable makeBass();
|
||||
};
|
||||
|
||||
} // namespace serum
|
||||
Reference in New Issue
Block a user