Skip to content

Vinyl Noise

Tier: Color | ComponentType: 30 | Params: 6

Additive surface noise, crackle, and pop generation simulating vinyl record playback artifacts.

Overview

VinylNoise synthesizes three distinct layers of vinyl playback artifacts and adds them to the input signal. Surface noise is the continuous, broadband hiss produced by the stylus tracking the groove's microscopic surface irregularities. Crackle consists of short, bright impulses caused by dust particles and minor groove damage. Pops are louder, lower-frequency transients from larger defects like scratches or static discharge.

Each layer is independently controlled: surface level sets the continuous hiss floor, crackle and pop rates control the average frequency of impulse events (via Poisson process triggering), and their level parameters set the amplitude. The master Amount control scales the total noise mix relative to the input signal. Because the noise is purely additive, the input audio passes through unchanged at Amount = 0.

The noise generation uses a shared xorshift32 PRNG for all random processes, ensuring deterministic behavior from a given seed state. Each noise layer has its own spectral shaping: surface noise is highpass-filtered with HF emphasis (matching real vinyl's RIAA-curve residual), crackles are bandpass-filtered to the 800--8000 Hz range for their characteristic "snap," and pops are lowpass-filtered at 200 Hz for their deep "thump" quality.

File Locations

Path
Header Sources/FolioDSP/include/FolioDSP/Color/VinylNoise.h
Implementation Sources/FolioDSP/src/Color/VinylNoise.cpp
Tests Tests/FolioDSPTests/VinylNoiseTests.swift
Bridge Sources/FolioDSPBridge/src/FolioDSPBridge.mm (VinylNoiseBridge)

Parameters

Index Name Description Min Max Default Min Default Max Default Unit
0 Surface Level Amplitude of continuous surface hiss 0.0 1.0 0.0 1.0 0.3
1 Crackle Rate Average crackle events per second 0 200 0 100 15 Hz
2 Crackle Level Amplitude of crackle impulses 0.0 1.0 0.0 1.0 0.4
3 Pop Rate Average pop events per second 0 20 0 10 1.5 Hz
4 Pop Level Amplitude of pop impulses 0.0 1.0 0.0 1.0 0.5
5 Amount Master noise mix amount 0.0 1.0 0.0 1.0 0.5

Processing Algorithm

The process() function executes these steps for each input sample:

1. Xorshift32 PRNG

All random values are generated by a shared xorshift32 state:

\[s \mathrel{\oplus}= s \ll 13, \quad s \mathrel{\oplus}= s \gg 17, \quad s \mathrel{\oplus}= s \ll 5\]
\[\text{rand} = \frac{\text{int32}(s)}{2^{31}}\]

This maps the unsigned state to a signed float in \([-1, 1]\).

2. Surface Noise

White noise is highpass-filtered at 300 Hz to remove low-frequency rumble, then given HF emphasis:

\[\alpha_{\text{hp}} = e^{-2\pi \cdot 300 / f_s}\]
\[y_{\text{hp}}[n] = \alpha_{\text{hp}} \cdot (y_{\text{hp}}[n-1] + w[n] - w[n-1])\]
\[\text{surface} = y_{\text{hp}} \cdot (1 + 0.5 \cdot w[n]) \cdot L_s \cdot 0.05\]

where \(w[n]\) is the raw white noise sample and \(L_s\) is the surface level parameter. The \((1 + 0.5w)\) factor adds subtle amplitude modulation that increases perceived high-frequency content.

3. Crackle Generation

Crackle events are triggered by a Poisson process. Each sample, a random value is compared against the event probability:

\[P_{\text{crackle}} = \frac{r_c}{f_s}\]

When triggered, the crackle envelope resets to 1.0 and decays exponentially with a time constant of approximately 2 ms:

\[\tau_c = 0.002 \cdot f_s\]
\[e_c[n] = e_c[n-1] \cdot e^{-1/\tau_c}\]

The envelope modulates white noise, which is then bandpass-filtered via two cascaded one-pole filters (LP at 8000 Hz minus LP at 800 Hz):

\[s_0 = s_0 + \alpha_{8k} \cdot (e_c \cdot w - s_0)\]
\[s_1 = s_1 + \alpha_{800} \cdot (s_0 - s_1)\]
\[\text{crackle} = (s_0 - s_1) \cdot L_c\]

4. Pop Generation

Pops follow the same Poisson triggering mechanism but with a lower event rate and a slower decay envelope (time constant approximately 10 ms):

\[P_{\text{pop}} = \frac{r_p}{f_s}\]
\[\tau_p = 0.01 \cdot f_s\]
\[e_p[n] = e_p[n-1] \cdot e^{-1/\tau_p}\]

The pop envelope is lowpass-filtered at 200 Hz to produce a deep "thump" character:

\[\alpha_{200} = 1 - e^{-2\pi \cdot 200 / f_s}\]
\[\text{lpState} = \text{lpState} + \alpha_{200} \cdot (e_p - \text{lpState})\]
\[\text{pop} = \text{lpState} \cdot L_p\]

5. Summation

All noise layers are summed and added to the input:

\[y = x + (\text{surface} + \text{crackle} + \text{pop}) \cdot A\]

where \(A\) is the Amount parameter.

Core Equations

\[\text{surface} = \text{HP}_{300}(w) \cdot (1 + 0.5w) \cdot L_s \cdot 0.05\]
\[\text{crackle} = \text{BP}_{800-8k}(w \cdot e_c) \cdot L_c\]
\[\text{pop} = \text{LP}_{200}(e_p) \cdot L_p\]
\[y = x + (\text{surface} + \text{crackle} + \text{pop}) \cdot A\]

Snapshot Fields

Field Type Range Unit Description
Input Level Float 0--1 Smoothed input amplitude
Output Level Float 0--1 Smoothed output amplitude
Surface Level Float 0--1 Tracked surface noise amplitude
Crackle Active Uint8 0--1 Whether a crackle event is currently sounding
Pop Active Uint8 0--1 Whether a pop event is currently sounding
Noise Level Float 0--1 Combined noise output level

Implementation Notes

  • Poisson triggering uses xorshift() * 0.5 + 0.5 to map the PRNG output from \([-1, 1]\) to \([0, 1]\) before comparing against the event probability. This avoids the biased bit-extraction approach.
  • Crackle bandpass is approximated by subtracting two one-pole lowpass filter states (\(s_0 - s_1\)), where \(s_0\) has a higher cutoff (8000 Hz) and \(s_1\) has a lower cutoff (800 Hz).
  • Pop LP filter at 200 Hz gives pops their characteristic low-frequency "thump" quality.
  • Surface noise scaling factor of 0.05 keeps the continuous hiss at a realistic level relative to the input signal.
  • The noise is purely additive -- the input signal passes through unchanged, and noise layers are summed on top.
  • All parameters use std::atomic<float> for lock-free thread safety.
  • Level tracking uses exponential smoothing with coefficient 0.01 for display-rate visualization.
  • Snapshot emission is decimated to ~60 fps (every 735 samples at 44.1 kHz).

Equation Summary

y = x + surface + crackle + pop