Skip to content

Tremolo

Tier: Color | ComponentType: 17 | Params: 2

Amplitude modulation via an internal Oscillator LFO, with a rate range spanning sub-Hz tremolo through audio-rate AM synthesis.

Overview

Tremolo modulates a signal's amplitude using a low-frequency oscillator (LFO). The LFO is a full Oscillator instance running at the specified rate, which means it inherits all of the Oscillator's waveform capability. At low rates (0.5--8 Hz), this produces the classic tremolo effect heard in guitar amplifiers and electric pianos. As the rate increases past 20 Hz, the modulation enters the audio range and the effect transitions from rhythmic pulsing into amplitude modulation (AM) synthesis, generating sum and difference sidebands around the input frequency.

The depth parameter controls how much the LFO attenuates the signal. At depth = 0, the signal passes through unmodified. At depth = 1.0, the signal is fully modulated, dropping to silence at the LFO trough. The gain formula ensures the signal is always attenuated (never boosted), keeping the output bounded.

Unlike most components in FolioDSP, Tremolo processes audio as a buffer rather than a single sample, via process(float* buffer, int numSamples). The LFO ticks once per sample inside the buffer loop, ensuring phase-accurate modulation even at audio rates.

File Locations

Path
Header Sources/FolioDSP/include/FolioDSP/Color/Tremolo.h
Implementation Sources/FolioDSP/src/Color/Tremolo.cpp
Tests Tests/FolioDSPTests/TremoloTests.swift
Bridge Sources/FolioDSPBridge/src/FolioDSPBridge.mm (TremoloBridge)

Parameters

Index Name Description Min Max Default Min Default Max Default Unit
0 Rate LFO frequency -- sub-Hz for slow tremolo, audio-rate for AM synthesis 0.01 20000 0.5 15 4.0 Hz
1 Depth Modulation depth -- 0 = no effect, 1 = full silence at trough 0.0 1.0 0.0 1.0 0.5

Processing Algorithm

The process() function iterates over the buffer, applying per-sample LFO modulation:

1. LFO Tick

The internal Oscillator advances by one sample, producing the current LFO value in the range \([-1, 1]\):

\[\text{lfoValue} = \text{Oscillator::tick}()\]

The Oscillator's frequency is set to the current rate before the buffer loop begins.

2. Gain Calculation

The LFO value is rescaled from \([-1, 1]\) to \([0, 1]\), then used to compute the amplitude gain:

\[g = 1 - d \cdot \left(\frac{1}{2} + \frac{1}{2} \cdot \text{lfoValue}\right)\]

Where \(d\) is the depth parameter. When \(\text{lfoValue} = -1\), the parenthesized term is 0 and \(g = 1\) (no attenuation). When \(\text{lfoValue} = 1\), the term is 1 and \(g = 1 - d\) (maximum attenuation).

3. Apply Gain

Each sample in the buffer is multiplied by the gain:

\[\text{buffer}[i] = \text{buffer}[i] \cdot g\]

Core Equations

\[g = 1 - d \cdot \left(\frac{1 + \sin(2\pi r t)}{2}\right)\]
\[y = x \cdot g\]

Where \(r\) = rate (Hz), \(d\) = depth, \(t\) = time.

Snapshot Fields

Field Type Range Unit Description
Rate Float 0.01--20000 Hz Current LFO frequency
Depth Float 0--1 Current modulation depth
Gain Float 0--1 Instantaneous amplitude gain
LFO Phase Float 0--1 Normalized LFO phase position

Implementation Notes

  • Buffer-based processing: Unlike most components that process single samples, Tremolo takes a float* buffer and sample count. The bridge wraps this as a 1-sample buffer call for chain mode compatibility.
  • Full Oscillator as LFO: The LFO is an Oscillator instance, inheriting its waveform generation (sine by default). This is deliberate -- it enables future waveform selection for the LFO shape.
  • Phase normalization: The snapshot reports lfoPhase as the Oscillator's raw phase divided by \(2\pi\), yielding a 0--1 range for UI display.
  • No parameter smoothing on depth: Both rate and depth use std::atomic<float> for lock-free thread safety. Both are marked as smoothed in ParamInfo for bridge-level smoothing.
  • Snapshot emission is decimated to ~60 fps (every 735 samples at 44.1 kHz).

Equation Summary

y = x · (1 - depth·(1/2+1/2·sin(2πrt)))