mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-11-13 20:58:33 +00:00
* feat(core): add a lookahead true-peak limiter FX (truepeak) The existing limiter is an envelope follower with no lookahead and no inter-sample detection, so a -14 dB ceiling can leave the true peak several dB over. truepeak estimates the true peak on a 4x polyphase interpolator, takes the minimum required gain over a lookahead window and smooths it with a boxcar of the same length, so the gain is already down when the peak leaves the delay line. Release is exponential and can only lower the gain further; channels share one gain. The offline render lengthens the OfflineAudioContext by the chain's latency and drops the lead-in, so a limited clip stays sample-aligned with its source. Preview plays the lookahead (about 3.3 ms at the default). — Jerrai * docs(audio): truepeak ceiling is a 4x estimate; measure it with an ideal interpolator The unit test's meter was a 96-tap Kaiser sinc, which rolls off near Nyquist and reads full-band noise about 0.5 dB low, so the PR reported a 0.3 dB broadband overshoot. Against an ideal 16x interpolation (zero-padded FFT, cross-checked with a 16x soxr resample) hot full-band white noise ends 1.2 to 1.7 dB over the ceiling (median 1.4 over 28 runs of 10 to 30 s). ffmpeg's ebur128 true peak is also a 4x meter and reads the same files 0.1 dB over. - The test meter is now the FFT interpolation. Tolerances are what it measures on each fixed signal: tones 0.01 dB (they land on the ceiling), the dense program 0.5 dB (reads 0.445, the old 0.4 passed only because the meter read low), and a new full-band noise case at -1, -6 and -14 dBTP with 1.35 dB (reads 1.09 to 1.31). - audio-effects.mdx and the hyperframes-audio skill say the ceiling is a 4x estimate, give the measured overshoot and recommend a 2 dB margin under a hard delivery limit. - skills-manifest.json regenerated. — Jerrai * fix(core): truepeak UI copy no longer promises a hard ceiling The registry description and the rack summary said the true peak is never above the ceiling. The limiter holds a 4x estimate, and full-band noise can land up to about 1.7 dB above it, as the docs now say. The summary reads "held near", the description names the estimate and the overshoot, and the rack blurb says "holds ... to a ceiling". No skill file changed, so the skills manifest is unchanged. — Jerrai * fix(core): Studio truepeak copy states the 4x-estimate margin The ceiling control was labelled "Never exceed" and the module said the mix "must not clip", so a Studio user never saw that dense full-band content can end up to about 1.7 dB above the ceiling. The label is now "Target ceiling" with a hint that names the 4x estimate and the headroom to leave, the blurb says "toward a target ceiling", the rack summary says peaks are pulled down toward the value, and the registry hint no longer says "highest true peak allowed". The audio skill's "hard dBTP ceiling" wording is gone and the skills manifest is regenerated. A copy test keeps the promise out. — Jerrai * fix(core): truepeak clears on a seek, and the review nits (fallow, comments) - Unexport TRUE_PEAK_OVERSAMPLE, TRUE_PEAK_DETECTOR_DELAY and truePeakLookaheadSamples: nothing imports them (Fallow audit). - A persistent group bus keeps its chain across a seek, so the limiter's delay line and held gain carried over. The worklet takes __hfReset, the graph exposes FxChainHandle.reset(), and reanchor() calls it. - The processor-loading harness is shared by the truepeak and worklet tests (it was a 12-line clone); the test FFT no longer repeats audioCarve's bit-reversal; two test bodies are under the CRAP threshold. - Comment blocks cut to four lines or fewer (Comments check). - Docs state the preview delay in ms. — Jerrai
99 lines
7.8 KiB
Markdown
99 lines
7.8 KiB
Markdown
# Effect registry
|
||
|
||
Every effect, its parameters and the usable range of each. Values outside a range
|
||
are clamped on read, so anything that parses is safe to realise. **AUTO** marks a
|
||
parameter an automation lane can drive; anything unmarked cannot move over time
|
||
(see the note at the bottom).
|
||
|
||
Generated from `HF_AUDIO_FX` in `@hyperframes/core/audio-fx`, which is the source
|
||
of truth — if this table and the code disagree, the code is right.
|
||
|
||
## Filter — which frequencies a track may occupy
|
||
|
||
| Effect | Parameter |
|
||
| ----------- | ----------------------------------------------------------------------------------------------------------- |
|
||
| `highpass` | `frequency` 20–20000 Hz (300, log) **AUTO** · `q` 0.1–20 (0.707, log) **AUTO** · `poles` `1`\|`2` (2) |
|
||
| `lowpass` | `frequency` 100–20000 Hz (8000, log) **AUTO** · `q` 0.1–20 (0.707, log) **AUTO** · `poles` `1`\|`2` (2) |
|
||
| `peaking` | `frequency` 20–20000 Hz (1000, log) **AUTO** · `gain` −40–40 dB (0) **AUTO** · `q` 0.1–20 (1, log) **AUTO** |
|
||
| `lowshelf` | `frequency` 20–2000 Hz (200, log) **AUTO** · `gain` −40–40 dB (0) **AUTO** |
|
||
| `highshelf` | `frequency` 500–20000 Hz (4000, log) **AUTO** · `gain` −40–40 dB (0) **AUTO** |
|
||
|
||
`q` is bandwidth — higher is narrower. `poles` is the slope: `2` is the usual
|
||
biquad (12 dB/oct), `1` is gentler (6 dB/oct). Shelving filters have no `q`: the
|
||
Web Audio spec leaves it unused for them, so a control would have moved nothing.
|
||
|
||
## Dynamics — how level behaves over time
|
||
|
||
| Effect | Parameter |
|
||
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||
| `gain` | `gain` −60–12 dB (0) **AUTO** |
|
||
| `compressor` | `threshold` −60–0 dB (−24) · `ratio` 1–20 (4) · `attack` 0.01–2000 ms (20, log) · `release` 0.01–9000 ms (250, log) · `knee` 1–8 (2.83) · `makeup` 0–36 dB (0) · `mix` 0–1 (1) |
|
||
| `limiter` | `limit` −24–0 dB (−1) · `attack` 0.1–80 ms (5) · `release` 1–8000 ms (50, log) · `level_out` −24–24 dB (0) |
|
||
| `truepeak` | `ceiling` −24–0 dBTP (−1) · `lookahead` 0.5–10 ms (3) · `release` 10–2000 ms (80, log) |
|
||
| `gate` | `threshold` −80–0 dB (−35) · `range` −80–0 dB (−24) · `ratio` 1–20 (10) · `attack` 0.01–9000 ms (1, log) · `release` 0.01–9000 ms (100, log) · `knee` 1–8 (2.83) |
|
||
|
||
Cuts on `gain` go to −60 dB, boosts stop at +12: it is a level stage for making
|
||
room, and a chain that could add 40 dB would clip long before that was useful.
|
||
`knee` of 1 is a hard corner, higher eases into it. `mix` below 1 blends the dry
|
||
signal back in (parallel compression). `range` is how far down the gate pulls
|
||
when closed — a gate that pulls all the way to silence sounds like a switch.
|
||
|
||
`limiter` follows the signal's level and has no lookahead, so it does not
|
||
guarantee a peak ceiling. For a delivery ceiling use `truepeak`: it estimates the
|
||
inter-sample (true) peak at 4x, looks `lookahead` ms ahead, and holds that estimate
|
||
at `ceiling` dBTP. It delays its output by `lookahead` plus about 0.3 ms; the
|
||
render trims the delay, live preview plays it.
|
||
|
||
The ceiling is a 4x estimate, so the real true peak can end above it. Against an
|
||
ideal 16x interpolation, tones, pink noise and a dense mix ended at most 0.6 dB
|
||
over; full-band white noise driven 8 dB or more into the limiter ended 1.2 to 1.7
|
||
dB over (median 1.4). A 4x meter such as ffmpeg `ebur128=peak=true` reads that
|
||
noise about 0.1 dB over and will not show it. Under a hard delivery limit set
|
||
`ceiling` at least 2 dB below it (−3 dBTP for a −1 dBTP limit).
|
||
|
||
## Nonlinear — changes the waveform's shape
|
||
|
||
| Effect | Parameter |
|
||
| ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||
| `saturate` | `type` `tanh`\|`atan`\|`cubic`\|`exp`\|`alg`\|`quintic`\|`sin`\|`erf`\|`hard` (tanh) · `threshold` −40–0 dB (−6) · `output` −24–24 dB (0) **AUTO** · `oversample` 1–8× (4) |
|
||
| `bitcrush` | `bits` 1–32 (8) · `samples` 1–250× (1) · `mix` 0–1 (1) |
|
||
|
||
`tanh` is the gentlest curve and `hard` is outright clipping. Higher `oversample`
|
||
costs more CPU and keeps aliasing down. `samples` repeats each sample N times — a
|
||
crude downsample, which is where the lo-fi character comes from.
|
||
|
||
## Time — space and width
|
||
|
||
| Effect | Parameter |
|
||
| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||
| `delay` | `time` 1–5000 ms (250, log) **AUTO** · `feedback` 0.01–0.95 (0.35) **AUTO** · `mix` 0–1 (0.4) **AUTO** |
|
||
| `reverb` | `size` 0.05–1 (0.7) · `damping` 0–1 (0.5) · `wet` 0–1 (0.35) **AUTO** · `dry` 0–1 (0.7) **AUTO** |
|
||
| `chorus` | `delay` 1–100 ms (7) **AUTO** · `depth` 0–10 ms (2) **AUTO** · `speed` 0.01–10 Hz (1) **AUTO** · `mix` 0–1 (0.5) **AUTO** |
|
||
| `phaser` | `in_gain` 0–1 (0.4) **AUTO** · `out_gain` 0–2 (0.74) **AUTO** · `delay` 0.1–5 ms (3) · `decay` 0–0.99 (0.4) · `speed` 0.1–2 Hz (0.5) **AUTO** · `type` `0`\|`1` (0) |
|
||
|
||
Reverb convolves a _generated_ impulse, and both preview and render generate the
|
||
same one — so a room is reproducible without shipping an impulse file. Higher
|
||
`damping` rolls the top off the tail faster, which is what makes a large room
|
||
sound like a soft one. `feedback` near the top of its range is a very long tail;
|
||
it is bounded below 1 because at 1 it never decays.
|
||
|
||
## Why some parameters cannot be automated
|
||
|
||
Automation is handed to the audio thread once, as native `AudioParam` ramps and
|
||
curves, which is what keeps it sample-accurate and identical between preview and
|
||
render. A parameter can therefore only be automated if an `AudioParam` backs it.
|
||
Three kinds do not:
|
||
|
||
- **worklet processor options** — `compressor`, `limiter`, `truepeak`, `gate` and `bitcrush`
|
||
are AudioWorklets configured wholesale, so **none of their parameters are
|
||
automatable at all**.
|
||
- **a WaveShaper curve** — `saturate`'s `type`, `threshold` and `oversample`
|
||
rebuild the curve; only its `output` stage is a real param.
|
||
- **a convolution impulse** — `reverb`'s `size` and `damping` regenerate the
|
||
impulse; `wet`/`dry` are gain stages and automate fine.
|
||
|
||
To make one of those behave differently over time, automate a `gain` stage
|
||
around it instead: a lane on a `gain` before a compressor changes how hard the
|
||
compressor is driven, which is most of what automating its threshold would have
|
||
done.
|