Files

82 lines
2.7 KiB
C++

#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