Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
wt.hpp File Reference

OSC-WT (modern tier): a wavetable oscillator that plays a set of single-cycle tables with band-limited band-switching and a smooth scan across the set. More...

#include <pulp/signal/wavetable.hpp>
#include <algorithm>
#include <cmath>
#include <cstddef>
#include <utility>
#include <vector>

Go to the source code of this file.

Classes

class  pulp::signal::osc::WtOscillator
 Modern wavetable oscillator over a set of band-limited WavetableTs. More...

Namespaces

namespace  pulp
namespace  pulp::signal
namespace  pulp::signal::osc

Detailed Description

OSC-WT (modern tier): a wavetable oscillator that plays a set of single-cycle tables with band-limited band-switching and a smooth scan across the set.

It is a thin osc-module front-end over the shipped WavetableBankT / WavetableT engine (signal/wavetable.hpp) — the composition engine, not a reimplementation.

── The decided architecture ──────────────────────────────────────────────

The wavetable-architecture question was settled by measurement, not assertion: at a real band boundary the existing band-switch-crossfade is at least as alias-clean as mip-map+interpolate and click-free at the sample level, so the modern tier EXTENDS WavetableT rather than forking a new mip-map engine. This class is what that decision looks like in code: it holds a WavetableBankT<double>, drives it per-sample, and adds only the two things the bank does not already own — the osc-module per-sample frequency contract and a scan smoother.

  • Band-switching is WavetableT's: each WavetableT selects the band whose Nyquist budget covers the current frequency and crossfades across kCrossfadeSamples (128) samples when the selection changes, so crossing a band ceiling is click-free.
  • Scan / morph is WavetableBankT's: a 0..1 position linearly interpolates between adjacent tables in the set. This front-end SLEWS the position with a one-pole so a stepped (block-rate) scan control does not zipper — the modern tier is the clean one. (The intentional stepped-scan grit of the lo-fi tier is a separate engine, not this.)

── Per-sample frequency, the osc-module way ──────────────────────────────

next(increment) takes the per-sample phase step (frequency ÷ sample rate), matching VaOscillator / VcoOscillator / the DCO front-end, so per-sample pitch modulation composes without an API change. The increment is converted to a frequency (increment · sample_rate) and handed to the bank, which owns the phase clock and the band selection. Playback is for positive frequencies — standard wavetable readout; through-zero FM is the VA/VCO's domain, not the wavetable engine's (WavetableT wraps its phase forward only).

RT contract: set_wavetable_set allocates and rebuilds the bank, so it runs off the audio thread; prepare re-arms the bank (no allocation) and is conventionally a setup-time call too. set_position, set_scan_time_ms, reset, and next allocate nothing, lock nothing, and perform no I/O. double throughout, matching the osc modules; a float caller narrows once on store.