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:
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:
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:
When triggered, the crackle envelope resets to 1.0 and decays exponentially with a time constant of approximately 2 ms:
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):
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):
The pop envelope is lowpass-filtered at 200 Hz to produce a deep "thump" character:
5. Summation
All noise layers are summed and added to the input:
where \(A\) is the Amount parameter.
Core Equations
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.5to 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