Oscillator
Tier: Primitives | ComponentType: 0 | Params: 3
Multi-waveform oscillator with wavetable morphing support.
Overview
The Oscillator generates periodic waveforms via phase accumulation. It supports six waveform types: sine, triangle, square, sawtooth, white noise, and custom wavetables. In wavetable mode, multi-frame tables can be smoothly morphed between frames using a position parameter.
At sub-Hz frequencies it acts as an LFO; at audio rate it's a tone generator; at high frequencies it becomes a whistle or modulator. The same component covers the entire range — no artificial ceiling separates "LFO" from "oscillator."
The wavetable engine supports 10 single-frame algorithmic presets (WarmAsymmetric, ExponentialRise, Plateau, etc.) and 5 multi-frame morphing presets (SineToSquare, OrganDrawbars, etc.) with 8 frames each and smooth cross-frame interpolation.
File Locations
| Path | |
|---|---|
| Header | Sources/FolioDSP/include/FolioDSP/Primitives/Oscillator.h |
| Implementation | Sources/FolioDSP/src/Primitives/Oscillator.cpp |
| Tests | Tests/FolioDSPTests/OscillatorTests.swift |
| Bridge | Sources/FolioDSPBridge/src/FolioDSPBridge.mm (OscillatorBridge) |
Parameters
| Index | Name | Description | Min | Max | Default Min | Default Max | Default | Unit |
|---|---|---|---|---|---|---|---|---|
| 0 | Frequency | Oscillator frequency — sub-Hz for LFO, audio rate for tones | 0.01 | 20000.0 | 0.1 | 2000.0 | 1.0 | Hz |
| 1 | Waveform | Waveform selection: 0=Sine, 1=Tri, 2=Square, 3=Saw, 4=Noise, 5=Wavetable | 0.0 | 5.0 | 0.0 | 5.0 | 0.0 | |
| 2 | Morph Position | Crossfade position between wavetable frames (wavetable mode only) | 0.0 | 1.0 | 0.0 | 1.0 | 0.0 |
Processing Algorithm
1. Waveform Generation
The oscillator generates one sample per call based on the current phase:
- Sine: \(y = \sin(\phi)\)
- Triangle: \(y = 4 \cdot |t - 0.5| - 1\) where \(t = \phi / 2\pi\)
- Square: \(y = \begin{cases} 1 & \phi < \pi \\ -1 & \phi \geq \pi \end{cases}\)
- Sawtooth: \(y = 2t - 1\) where \(t = \phi / 2\pi\)
- Noise: xorshift32 PRNG, independent of phase
- Wavetable: Linear interpolation within frame, cross-frame morph blending
2. Phase Accumulation
Phase wraps at \(2\pi\).
3. Wavetable Morphing (multi-frame)
For multi-frame wavetables, two adjacent frames are blended:
Within each frame, samples are linearly interpolated.
Snapshot Fields
| Field | Type | Range | Unit | Description |
|---|---|---|---|---|
| Phase | Float | 0–1 | Normalized phase position | |
| Output | Float | -1–1 | Current output sample | |
| Frequency | Float | 0.01–20000 | Hz | Current frequency |
| Waveform | Float | 0–5 | Active waveform type | |
| Wavetable | Uint8 | 0–1 | Whether wavetable is loaded | |
| Morph | Float | 0–1 | Current morph position | |
| Frames | Float | 0–256 | Number of wavetable frames | |
| Preview | Float[32] | -1–1 | 32-point waveform preview |
Implementation Notes
- xorshift32 PRNG for noise:
state ^= state << 13; state ^= state >> 17; state ^= state << 5 - Wavetable loading via
loadWaveform()(single-frame) orloadWavetable()(multi-frame) - 10 single-frame presets synthesized at 256 samples; 5 multi-frame presets with 8 frames each
- 32-point waveform preview computed in snapshot emission, not per-sample
- All parameters use
std::atomic<float>with relaxed memory order
Equation Summary
phase += 2π·f/sr; y = waveform(phase)