From 9b3ddeaf56dc3778e61f1d76a16d2e950a042d18 Mon Sep 17 00:00:00 2001 From: Nils Pukropp Date: Fri, 6 Nov 2026 08:31:41 +0100 Subject: [PATCH] feat(weld): phase alignment for layered bass, release 0.1.0 Weld hears the other bass layer through the sidechain and fits polarity plus up to four RBJ allpass sections to the measured cross-spectrum, so the layers sum instead of cancelling. No latency; the layer on its own keeps its sound. - Worker thread: Welch cross-spectrum and coherence, Nelder-Mead fit with 1/f weighting, a per-band loss penalty and model selection by share of the achievable gain; GCC-PHAT lag detection flags a late layer. - Audio thread: allocation-free chain with a 40 ms warm-up and 40 ms crossfade per new fit; mono blocks go to the worker through a lock-free queue. - Follow / Learn / Hold; the fit is persisted and restored without a crossfade. - Editor: per-band loss and recovery against a perfect alignment, phase before and after, recovered dB, time offset and status. - Tests for flat magnitude, crossfade, fit targets, Follow hysteresis, Learn, silence, realtime allocation and the persisted fit in the host. --- Cargo.lock | 2 + README.md | 13 +- docs/development.md | 2 +- docs/ideas.md | 9 +- docs/releases.md | 2 +- docs/weld/CHANGELOG.md | 16 +- docs/weld/README.md | 122 +++++-- docs/weld/assets/editor.png | Bin 0 -> 264013 bytes plugins/weld/core/Cargo.toml | 8 +- plugins/weld/core/src/analyzer.rs | 429 ++++++++++++++++++++++ plugins/weld/core/src/chain.rs | 163 +++++++++ plugins/weld/core/src/fit.rs | 372 +++++++++++++++++++ plugins/weld/core/src/lib.rs | 190 +++++++++- plugins/weld/core/src/report.rs | 108 ++++++ plugins/weld/core/src/settings.rs | 43 ++- plugins/weld/core/src/worker.rs | 77 ++++ plugins/weld/core/tests/dsp.rs | 241 +++++++++++++ plugins/weld/core/tests/fit.rs | 294 +++++++++++++++ plugins/weld/core/tests/realtime.rs | 70 ++++ plugins/weld/plugin/Cargo.lock | 68 ++++ plugins/weld/plugin/src/lib.rs | 45 ++- plugins/weld/plugin/src/params.rs | 155 +++++++- plugins/weld/plugin/tests/host.rs | 37 +- plugins/weld/ui/src/lib.rs | 537 +++++++++++++++++++++++++--- tools/editor-check/Cargo.lock | 2 + tools/editor-check/src/main.rs | 37 +- 26 files changed, 2898 insertions(+), 144 deletions(-) create mode 100644 docs/weld/assets/editor.png create mode 100644 plugins/weld/core/src/analyzer.rs create mode 100644 plugins/weld/core/src/chain.rs create mode 100644 plugins/weld/core/src/fit.rs create mode 100644 plugins/weld/core/src/report.rs create mode 100644 plugins/weld/core/src/worker.rs create mode 100644 plugins/weld/core/tests/fit.rs create mode 100644 plugins/weld/core/tests/realtime.rs diff --git a/Cargo.lock b/Cargo.lock index c8e6e9c..b014de6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -530,6 +530,8 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" name = "weld-core" version = "0.1.0" dependencies = [ + "crossbeam-queue", + "rustfft", "serde", "serde_json", ] diff --git a/README.md b/README.md index 5469577..07e5ba9 100644 --- a/README.md +++ b/README.md @@ -24,12 +24,11 @@ note. Each plugin is a separate product with its own version, guide and release. | **[Crucible](docs/crucible/README.md)** | Records the last bars all the time, then slices, cleans and exports key-labelled samples with one button | End of the chain to resample | none | [Guide](docs/crucible/README.md) · [Changes](docs/crucible/CHANGELOG.md) | | **[Lathe](docs/lathe/README.md)** | EQ with one band on each harmonic of the played note: a cut on harmonic 3 stays on harmonic 3 | Bass, after the synth | ≈ 67 ms | [Guide](docs/lathe/README.md) · [Changes](docs/lathe/CHANGELOG.md) | | **[Temper](docs/temper/README.md)** | Distortion that drives every note the same: level and tone filters follow the note | Bass | ≈ 67 ms + oversampling | [Guide](docs/temper/README.md) · [Changes](docs/temper/CHANGELOG.md) | -| **[Weld](docs/weld/README.md)** *(in development)* | Phase-aligns one bass layer to another layer through the sidechain, so the layers sum in the mids instead of cancelling | One layer's track | none | [Guide](docs/weld/README.md) · [Changes](docs/weld/CHANGELOG.md) | +| **[Weld](docs/weld/README.md)** | Phase-aligns one bass layer to another layer through the sidechain, so the layers sum in the mids instead of cancelling | One layer's track, other layer as sidechain | none | [Guide](docs/weld/README.md) · [Changes](docs/weld/CHANGELOG.md) | Plugins with latency report it to the host, so FL Studio compensates the delay. Anvil, Lathe and Temper detect pitch from the audio. They can also take the -pitch from MIDI. Weld is a sidechain test build at the moment; the phase -alignment comes next. +pitch from MIDI. The names come from metalwork: a **rivet** joins pieces; **patina** changes a surface and keeps its character; an **anvil** is the solid base that everything @@ -54,6 +53,10 @@ finished. Hosts list the plugins under the vendor name *Bass Foundry*. Lathe editor
Lathe: harmonic EQ Temper editor
Temper: tracked distortion + + Weld editor
Weld: layer phase alignment + + The previews are rendered headless from the real editor code by @@ -123,8 +126,8 @@ Every push and pull request runs these checks in CI - **Engine tests.** Every engine has tests for its signal behaviour and latency against measured targets. -- **Realtime safety.** Patina, Anvil, Crucible, Lathe and Temper have a test - that fails if the audio thread allocates memory. +- **Realtime safety.** Every audio effect has a test that fails if the audio + thread allocates memory. - **Host tests.** Each wrapper is loaded through the exported CLAP ABI, without a DAW. The tests check the ports, the reported latency, state save and load, and processing at 44.1, 48 and 96 kHz. diff --git a/docs/development.md b/docs/development.md index 50cc304..4466d70 100644 --- a/docs/development.md +++ b/docs/development.md @@ -32,7 +32,7 @@ NIH-plug Git dependency stays out of the root workspace. Host identities and saved state are separate for every product. The engines run allocation-free on the audio thread. Analysis that is too heavy -for the audio thread (Rivet's generation jobs, and Weld's planned phase fit) runs on a worker +for the audio thread (Rivet's generation jobs, Weld's cross-spectrum and fit) runs on a worker thread. Data goes to the editor through lock-free queues. ## Checks diff --git a/docs/ideas.md b/docs/ideas.md index 3b60fb8..a794618 100644 --- a/docs/ideas.md +++ b/docs/ideas.md @@ -67,16 +67,15 @@ Input level is normalised before the shaper, so drive does not depend on how loud the patch is, and the output is gain-matched. Oversampled to keep aliasing (which is never harmonic) out. See [Temper](temper/README.md). -## Ideas - -### Weld — layer phase alignment (problem 10) — in development +### Weld — layer phase alignment (problem 10) Takes a second bass layer through the sidechain. It fits polarity and a chain of up to four allpass sections to the measured cross-spectrum (no added delay), so the layers sum instead of cancel. A display shows how much level the cancellation cost and how much Weld recovered. Anvil solves the sub band; Weld -solves the mids. A sidechain test build exists; the fit comes after the FL -Studio routing test. See [Weld](weld/README.md). +solves the mids. See [Weld](weld/README.md). + +## Ideas ### Ballast — switch-up loudness leveller (problem 2) diff --git a/docs/releases.md b/docs/releases.md index a0a7511..8cd64cb 100644 --- a/docs/releases.md +++ b/docs/releases.md @@ -13,7 +13,7 @@ workflows are in `.gitea/workflows/`. A plugin's version is the `version` in | Crucible | `crucible-v0.1.0` | `crucible-windows-x86_64-0.1.0.zip` | | Lathe | `lathe-v0.1.0` | `lathe-windows-x86_64-0.1.0.zip` | | Temper | `temper-v0.1.0` | `temper-windows-x86_64-0.1.0.zip` | -| Weld | not released yet | | +| Weld | `weld-v0.1.0` | `weld-windows-x86_64-0.1.0.zip` | ## Make a release diff --git a/docs/weld/CHANGELOG.md b/docs/weld/CHANGELOG.md index 06e011d..9cd6a30 100644 --- a/docs/weld/CHANGELOG.md +++ b/docs/weld/CHANGELOG.md @@ -1,7 +1,15 @@ # Weld changelog -## Unreleased +## 0.1.0 -- Sidechain test build: stereo or mono main input with a matching sidechain - input, a sidechain level readout, Listen (Layer, Sum, Sum dry), Output and - Bypass. No phase alignment yet. +- Introduce Weld, a phase aligner for layered bass. +- Hears the other layer through the sidechain input and fits polarity plus up to + four allpass sections, so the layers sum instead of cancelling. No latency; + the layer on its own keeps its sound. +- Modes: Follow (refits only when clearly better), Learn (measure one take, + apply on exit), Hold. The alignment is saved with the project. +- Display: level per band against a perfect alignment (lost and won back), + phase before and after, recovered dB, cancellation cost, time offset, and + warnings for a late layer, unrelated layers and a missing sidechain. +- Controls: Mode, Sections, Response, Low, High, Gate, Listen (Layer, Sum, + Sum dry), Output, Bypass. diff --git a/docs/weld/README.md b/docs/weld/README.md index c8f0e2e..1c85af5 100644 --- a/docs/weld/README.md +++ b/docs/weld/README.md @@ -1,32 +1,110 @@ # Weld -Weld aligns the phase of one bass layer to another layer so the two sum -instead of cancelling in the mids. +Weld fuses two bass layers so they add up instead of cancelling. Current version +**0.1.0**. -> **Status: sidechain test build.** This build only checks that FL Studio -> delivers a sidechain signal to Weld. It passes the main layer through -> unchanged and shows the sidechain level. Phase alignment comes in the next -> build. +Bass patches are layered: a sub, one or more mid growls, a top. Where the layers +overlap in the mids, they partly cancel. The causes are a polarity difference, a +small time offset between the layers, and the phase shift of the filters and EQs +in one chain. The result is a thin, hollow sum that changes with every +switch-up. Anvil fixes this for the sub band; Weld fixes it for the mids. + +Weld sits on one layer and hears the other one through the sidechain input. It +measures how the two layers line up, then turns the phase of its own layer with +a polarity flip and up to four allpass filters. An allpass changes only phase, +so the layer on its own sounds the same; only the sum changes. Weld adds no +latency. + +![Weld](assets/editor.png) + +## Install on Windows + +Copy `Weld.clap` into `C:\Program Files\Common Files\CLAP\`. In FL Studio 2024.1 +or newer, open Options → Manage plugins, enable Verify plugins, and Find installed +plugins. Keep the font license with redistributed packages. ## Set up in FL Studio -1. Load Weld on the mixer track of one layer (the main layer). -2. On the mixer track of the other layer, right-click the route arrow to the - main layer's track and choose **Sidechain to this track**. -3. In Weld's plugin wrapper, open the settings (gear icon) and, under the - processing or input routing options, set Weld's sidechain input to that - track. -4. Play both layers. The large readout must show `sidechain: −NN dBFS`, and the - SIDECHAIN meter must move. +1. Put each layer on its own mixer track. Load Weld on the track of one layer, + the main layer. +2. Select the other layer's track. Right-click the route arrow under the main + layer's track and choose **Sidechain to this track**. The other layer still + goes to the master on its own; the sidechain only lets Weld hear it. +3. In Weld's plugin wrapper, open the settings (gear icon) and set Weld's + sidechain input to that track. +4. Play both layers. The SIDECHAIN meter must move. If the display says "No + sidechain signal", check step 3. + +## Work with it + +1. Leave **Mode** on **FOLLOW** and play the part where both layers sound. After + a second or two, Weld shows its alignment: polarity, and the allpass sections + with their frequency and Q. +2. Read the display: + - **Recovered** is how much level the sum gains, as an average per band + between Low and High. **Cancellation cost** is how much a perfect alignment + would gain. When the two numbers are close, Weld got nearly everything. + - The upper plot shows each band against a perfectly aligned sum (0 dB). Red + is what the unprocessed sum loses to cancellation; pink is the part Weld + wins back. The white line is the level with Weld. + - The lower plot shows the phase of this layer against the other one, before + (grey) and after (pink), and Weld's own phase curve (blue). After alignment + the pink line sits near 0°. +3. Compare by ear: mute the other layer's own route to the master, set + **Listen** to **SUM**, then to **SUM DRY**. Set Listen back to **LAYER** and + unmute the route for the mix. +4. When the alignment is right, switch to **HOLD**. It is saved with the project + and comes back when the project opens. + +For one drop or switch-up, use **LEARN**: press it, play the part, then press +**HOLD** (or LEARN again). Weld measures the whole take with equal weight and +applies the result when Learn ends. ## Controls -- **Listen**: - - **Layer** outputs the main layer; use it in the mix. - - **Sum** outputs the main layer plus the sidechain layer. Mute the other - layer's own send to the master while you listen to Sum. - - **Sum dry** outputs the unprocessed sum, for comparison. -- **Output**: output trim. -- **Bypass**. +- **Mode**: + - **Follow** keeps measuring and refits only when a new alignment is clearly + better (at least 0.3 dB on average), so the filter does not wander. + - **Learn** starts a fresh measurement and applies its result when Learn ends. + - **Hold** keeps the current alignment. The display keeps measuring. +- **Sections** (1–4) is the largest number of allpass sections the fit may use. + Weld uses fewer when fewer are enough. +- **Response** (0.3–5 s) is the averaging time in Follow. Longer is steadier. +- **Low** and **High** set the range the fit listens to. Low defaults to 60 Hz, + leaving the sub to Anvil. The allpass still acts below Low, and the display + shows that honestly. +- **Gate** (−70 to −30 dB): moments where either layer is quieter are not + measured, so tails and silence do not pull the fit. +- **Listen**: **Layer** outputs this layer, aligned, for the mix. **Sum** outputs + this layer aligned plus the sidechain layer. **Sum dry** outputs both + unprocessed. +- **Output** trims the level; **Bypass** passes this layer unchanged. -Weld adds no latency. +## Limits + +- **A late layer cannot be moved earlier.** An allpass only delays. If the layer + Weld sits on is the later one, the display shows "This layer is late" and the + offset in ms. Put Weld on the other layer instead. +- **Free-running layers cannot be aligned.** Without oscillator retrigger, or + with detuned unison, the phase between the layers drifts all the time. Weld + marks those bands as *loose* (hatched) and does not fit them. Enable retrigger + in the synth if you want Weld to align the layers. +- **Delays leave a comb.** A time offset between the layers cancels at regular + frequencies. A few allpass sections cannot follow a delay all the way up, so + Weld aligns the lower range and moves the higher notches rather than removing + them. For a large offset, move the layer in time first. +- **One alignment for the whole patch.** Weld does not change the alignment per + note. If switch-ups need different alignments, use Follow, or automate Mode. +- **The sidechain must arrive.** With no sidechain signal Weld changes nothing. + +## How it works + +Every 256 samples the audio thread hands mono copies of both layers to a worker +thread through a lock-free queue. The worker estimates their cross-spectrum +(Welch, Hann window, about 11.7 Hz per bin, 50 % overlap) and their coherence. +It fits polarity plus up to four second-order allpass sections so that +Σ w(f)·Re(S(f)·H(f)) is largest, with w = 1/f so every octave counts, ignoring +bins where the layers are not coherent, and with a penalty for any band that +would lose more than 1 dB. A new fit fades in over 40 ms after its filters have +settled for 40 ms, so there are no clicks. One fit takes about 15 ms on one +core, twice per second in Follow. diff --git a/docs/weld/assets/editor.png b/docs/weld/assets/editor.png new file mode 100644 index 0000000000000000000000000000000000000000..f571c6116883fa9acf3b10aac56d4859e851389c GIT binary patch literal 264013 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 AF8}}l literal 0 HcmV?d00001 diff --git a/plugins/weld/core/Cargo.toml b/plugins/weld/core/Cargo.toml index bd047af..9025a54 100644 --- a/plugins/weld/core/Cargo.toml +++ b/plugins/weld/core/Cargo.toml @@ -1,16 +1,14 @@ [package] - name = "weld-core" version = "0.1.0" edition.workspace = true license.workspace = true - [dependencies] - +crossbeam-queue = "0.3" +rustfft = "6.4" serde = { workspace = true } - [dev-dependencies] - +rustfft = "6.4" serde_json = { workspace = true } diff --git a/plugins/weld/core/src/analyzer.rs b/plugins/weld/core/src/analyzer.rs new file mode 100644 index 0000000..6922157 --- /dev/null +++ b/plugins/weld/core/src/analyzer.rs @@ -0,0 +1,429 @@ +//! The measuring half of Weld. Runs on the worker thread; deterministic, so tests drive +//! it directly. +//! +//! A Welch estimate of the cross-spectrum S = F_A·conj(F_B) and both auto-spectra runs +//! over Hann-windowed frames with 50 % overlap. Both layers share one complex FFT +//! (A in the real part, B in the imaginary part). +use crate::chain::Fit; +use crate::fit::{search, Point, Problem}; +use crate::report::{band_of, curve_frequency, Report, Status, BANDS}; +use crate::settings::{Mode, Settings}; +use rustfft::{num_complex::Complex32, Fft, FftPlanner}; +use std::sync::Arc; + +/// Bins below this coherence do not steer the fit. +const MIN_COHERENCE: f32 = 0.3; +/// Below this mean coherence the layers count as unrelated. +const LOOSE_COHERENCE: f32 = 0.3; +/// Frames needed before the first fit. +const MIN_FRAMES: u32 = 8; +/// A new fit replaces the current one only when it gains this much (Follow mode). +const FOLLOW_GAIN_DB: f32 = 0.3; +const LEARN_GAIN_DB: f32 = 0.1; +/// Lag search range and the smallest lag reported as "late". +const LAG_RANGE_S: f32 = 0.005; +const LATE_S: f32 = 0.0001; +const LATE_STRENGTH: f32 = 0.3; +/// Report and fit intervals in seconds of analysed audio. +const REPORT_S: f32 = 0.25; +const FIT_S: f32 = 0.5; +/// −90 dBFS: anything quieter on the sidechain counts as no signal. +const SILENCE: f32 = 3.2e-5; + +/// What a poll produced. +#[derive(Debug, Clone, Copy)] +pub struct Update { + /// Set when a new alignment was accepted. + pub fit: Option, + pub report: Report, +} + +pub struct Analyzer { + rate: f32, + n: usize, + hop: usize, + window: Vec, + ring_a: Vec, + ring_b: Vec, + write: usize, + filled: usize, + since_frame: usize, + fft: Arc>, + ifft: Arc>, + buf: Vec, + scratch: Vec, + sab: Vec, + saa: Vec, + sbb: Vec, + frames: u32, + settings: Settings, + current: Fit, + since_report: usize, + since_fit: usize, + finish_learn: bool, + side_level: f32, + gated: bool, +} + +impl Analyzer { + pub fn new(rate: f32, fit: Fit) -> Self { + // About 11.7 Hz per bin at any rate. + let n = ((4096. * rate / 48000.) as usize) + .next_power_of_two() + .clamp(1024, 65536); + let mut planner = FftPlanner::new(); + let fft = planner.plan_fft_forward(n); + let ifft = planner.plan_fft_inverse(n); + let scratch_len = fft + .get_inplace_scratch_len() + .max(ifft.get_inplace_scratch_len()); + let window = (0..n) + .map(|i| 0.5 - 0.5 * (std::f32::consts::TAU * i as f32 / n as f32).cos()) + .collect(); + let bins = n / 2 + 1; + Self { + rate, + n, + hop: n / 2, + window, + ring_a: vec![0.; n], + ring_b: vec![0.; n], + write: 0, + filled: 0, + since_frame: 0, + fft, + ifft, + buf: vec![Complex32::default(); n], + scratch: vec![Complex32::default(); scratch_len], + sab: vec![Complex32::default(); bins], + saa: vec![0.; bins], + sbb: vec![0.; bins], + frames: 0, + settings: Settings::default(), + current: fit.sanitized(rate), + since_report: 0, + since_fit: 0, + finish_learn: false, + side_level: 0., + gated: false, + } + } + pub fn fft_size(&self) -> usize { + self.n + } + pub fn current(&self) -> &Fit { + &self.current + } + pub fn frames(&self) -> u32 { + self.frames + } + pub fn set_settings(&mut self, settings: Settings) { + let old = self.settings.mode; + if settings.mode == Mode::Learn && old != Mode::Learn { + self.clear(); + } + if old == Mode::Learn && settings.mode != Mode::Learn { + self.finish_learn = true; + } + self.settings = settings; + } + /// Forgets the measurement. + pub fn clear(&mut self) { + self.sab.fill(Complex32::default()); + self.saa.fill(0.); + self.sbb.fill(0.); + self.frames = 0; + } + /// Feeds mono samples of the main layer `a` and the sidechain layer `b`. + pub fn push(&mut self, a: &[f32], b: &[f32]) { + for (&x, &y) in a.iter().zip(b) { + self.ring_a[self.write] = x; + self.ring_b[self.write] = y; + self.write = (self.write + 1) % self.n; + self.filled = (self.filled + 1).min(self.n); + self.since_frame += 1; + if self.filled == self.n && self.since_frame >= self.hop { + self.since_frame = 0; + self.frame(); + } + } + self.since_report += a.len().min(b.len()); + self.since_fit += a.len().min(b.len()); + } + fn frame(&mut self) { + let n = self.n; + let (mut ea, mut eb) = (0f32, 0f32); + for i in 0..n { + let j = (self.write + i) % n; + let (a, b) = (self.ring_a[j], self.ring_b[j]); + ea += a * a; + eb += b * b; + self.buf[i] = Complex32::new(a * self.window[i], b * self.window[i]); + } + let (ra, rb) = ((ea / n as f32).sqrt(), (eb / n as f32).sqrt()); + self.side_level = rb; + let gate = 10f32.powf(self.settings.gate / 20.); + self.gated = ra < gate || rb < gate; + if self.gated { + return; + } + self.fft + .process_with_scratch(&mut self.buf, &mut self.scratch); + self.frames = self.frames.saturating_add(1); + let count = 1. / self.frames as f32; + let alpha = match self.settings.mode { + Mode::Learn => count, + _ => { + let follow = 1. - (-(self.hop as f32) / (self.settings.response * self.rate)).exp(); + count.max(follow) + } + }; + for k in 0..=n / 2 { + let x = self.buf[k]; + let xc = self.buf[(n - k) % n].conj(); + let fa = (x + xc) * 0.5; + let fb = (x - xc) * Complex32::new(0., -0.5); + let old = self.sab[k]; + self.sab[k] = old + (fa * fb.conj() - old) * alpha; + self.saa[k] += (fa.norm_sqr() - self.saa[k]) * alpha; + self.sbb[k] += (fb.norm_sqr() - self.sbb[k]) * alpha; + } + } + fn frequency(&self, k: usize) -> f32 { + k as f32 * self.rate / self.n as f32 + } + fn coherence(&self, k: usize) -> f32 { + let d = self.saa[k] * self.sbb[k]; + if d > 0. { + (self.sab[k].norm_sqr() / d).min(1.) + } else { + 0. + } + } + fn range(&self) -> (f32, f32) { + let high = self.settings.high.min(0.45 * self.rate); + (self.settings.low.min(high * 0.5), high) + } + /// Fit points between Low and High: one per bin at the bottom, merged to 1/32 octave + /// higher up. + fn points(&self) -> Vec { + let (low, high) = self.range(); + let mut points: Vec = Vec::new(); + let mut last = usize::MAX; + for k in 1..=self.n / 2 { + let f = self.frequency(k); + if f < low || f > high { + continue; + } + let slot = (32. * (f / low).log2()) as usize; + if slot != last { + points.push(Point::new(f, self.rate)); + last = slot; + } + let p = points.last_mut().expect("pushed above"); + if self.coherence(k) >= MIN_COHERENCE { + p.fit += self.sab[k]; + } + p.all += self.sab[k]; + p.power += self.saa[k] + self.sbb[k]; + } + points + } + /// Produces an update once enough audio has been analysed since the last one. + pub fn poll(&mut self) -> Option { + let report_due = self.since_report as f32 >= REPORT_S * self.rate; + if !report_due && !self.finish_learn { + return None; + } + self.since_report = 0; + let fit_due = self.since_fit as f32 >= FIT_S * self.rate; + let mode = self.settings.mode; + let threshold = if self.finish_learn { + Some(LEARN_GAIN_DB) + } else if fit_due && mode == Mode::Follow { + Some(FOLLOW_GAIN_DB) + } else if fit_due && mode == Mode::Learn { + Some(LEARN_GAIN_DB) + } else { + None + }; + if fit_due { + self.since_fit = 0; + } + self.finish_learn = false; + Some(self.analyse(threshold)) + } + /// Measures, fits when `threshold` is given (minimum predicted gain in dB over the + /// current fit), and reports. + pub fn analyse(&mut self, threshold: Option) -> Update { + let points = self.points(); + let problem = Problem::new(points, self.rate); + let (weight, coherent) = problem.points.iter().fold((0., 0.), |(w, c), p| { + let x = p.all.norm(); + (w + p.w * x, c + p.w * x * coherence_of(p)) + }); + let mean_coherence = if weight > 0. { coherent / weight } else { 0. }; + let (lag_ms, lag_strength) = self.lag(); + let late = lag_ms > LATE_S * 1000. && lag_strength > LATE_STRENGTH; + let status = if self.side_level < SILENCE && self.frames == 0 { + Status::NoSidechain + } else if self.frames < MIN_FRAMES { + if self.gated { + Status::Quiet + } else { + Status::Waiting + } + } else if mean_coherence < LOOSE_COHERENCE || problem.is_empty() { + Status::Loose + } else if late { + Status::Late + } else { + Status::Aligned + }; + let mut changed = None; + if let Some(threshold) = threshold { + if matches!(status, Status::Aligned | Status::Late) { + let candidate = search(&problem, self.settings.sections, &self.current); + let gain = problem.mean_gain( + &problem.bands(&self.current), + &problem.bands(&candidate.fit), + ); + if gain.is_finite() && gain >= threshold && candidate.fit != self.current { + self.current = candidate.fit; + changed = Some(candidate.fit); + } + } + } + let mut report = self.report(status, &problem); + report.lag_ms = lag_ms; + report.lag_strength = lag_strength; + report.changed = changed.is_some(); + Update { + fit: changed, + report, + } + } + fn report(&self, status: Status, problem: &Problem) -> Report { + let fit = self.current; + let mut report = Report { + status, + fit, + frames: self.frames, + low: self.range().0, + high: self.range().1, + ..Report::default() + }; + // Display bands over all bins. + // Sums per band: before, after, ideal, coherence·power, power. + let mut sums = [[0f32; 5]; BANDS]; + for k in 1..=self.n / 2 { + let f = self.frequency(k); + if !(20. ..=0.45 * self.rate).contains(&f) { + continue; + } + let (hr, hi) = fit.response(f, self.rate); + let s = self.sab[k]; + let p = self.saa[k] + self.sbb[k]; + let b = &mut sums[band_of(f)]; + b[0] += p + 2. * s.re; + b[1] += p + 2. * (s * Complex32::new(hr, hi)).re; + b[2] += p + 2. * s.norm(); + b[3] += self.coherence(k) * p; + b[4] += p; + } + for (band, [before, after, ideal, coherent, power]) in report.bands.iter_mut().zip(sums) { + if ideal > 1e-12 && power > 0. { + band.before = db_ratio(before, ideal); + band.after = db_ratio(after, ideal); + band.coherence = (coherent / power).clamp(0., 1.); + band.valid = true; + } + } + // Phase curve: bins within ±1/24 octave of each point. + for (i, point) in report.curve.iter_mut().enumerate() { + let f = curve_frequency(i); + if f > 0.45 * self.rate { + continue; + } + let lo = ((f * 2f32.powf(-1. / 24.)) * self.n as f32 / self.rate).floor() as usize; + let hi = ((f * 2f32.powf(1. / 24.)) * self.n as f32 / self.rate).ceil() as usize; + let centre = (f * self.n as f32 / self.rate).round() as usize; + let (lo, hi) = ( + lo.max(1).min(centre.max(1)), + hi.min(self.n / 2).max(centre.max(1)), + ); + let (mut s, mut pa, mut pb) = (Complex32::default(), 0f32, 0f32); + for k in lo..=hi { + s += self.sab[k]; + pa += self.saa[k]; + pb += self.sbb[k]; + } + if pa * pb <= 0. { + continue; + } + let (hr, hi) = fit.response(f, self.rate); + let h = Complex32::new(hr, hi); + point.before = s.arg().to_degrees(); + point.after = (s * h).arg().to_degrees(); + point.chain = h.arg().to_degrees(); + point.coherence = (s.norm_sqr() / (pa * pb)).min(1.); + point.valid = true; + } + // Headline numbers over the fit range, every band weighted the same. + if !problem.is_empty() { + let before = problem.before_bands(); + report.cost_db = problem.mean_gain(&before, &problem.ideal_bands()); + report.recovered_db = problem.mean_gain(&before, &problem.bands(&fit)); + } + report + } + /// Time offset from the phase transform of the cross-spectrum (GCC-PHAT) between + /// Low and High. Returns (ms, strength); positive means this layer is late. + fn lag(&mut self) -> (f32, f32) { + let n = self.n; + let (low, high) = self.range(); + self.buf.fill(Complex32::default()); + let mut used = 0usize; + for k in 1..n / 2 { + let f = self.frequency(k); + if f < low || f > high || self.coherence(k) < MIN_COHERENCE { + continue; + } + let s = self.sab[k]; + let m = s.norm(); + if m <= 0. { + continue; + } + let u = s / m; + self.buf[k] = u; + self.buf[n - k] = u.conj(); + used += 1; + } + if used < 4 { + return (0., 0.); + } + self.ifft + .process_with_scratch(&mut self.buf, &mut self.scratch); + let range = (LAG_RANGE_S * self.rate) as isize; + let (mut best, mut at) = (0f32, 0isize); + for tau in -range..=range { + let v = self.buf[tau.rem_euclid(n as isize) as usize].re.abs(); + if v > best { + best = v; + at = tau; + } + } + (at as f32 / self.rate * 1000., best / (2 * used) as f32) + } +} + +fn coherence_of(p: &Point) -> f32 { + if p.all.norm() > 0. { + p.fit.norm() / p.all.norm() + } else { + 0. + } +} +fn db_ratio(a: f32, b: f32) -> f32 { + (10. * (a.max(1e-20) / b.max(1e-20)).log10()).clamp(-40., 40.) +} diff --git a/plugins/weld/core/src/chain.rs b/plugins/weld/core/src/chain.rs new file mode 100644 index 0000000..472b22c --- /dev/null +++ b/plugins/weld/core/src/chain.rs @@ -0,0 +1,163 @@ +//! Polarity plus a cascade of second-order allpass sections. Magnitude is flat by +//! construction; only the phase of the main layer changes. +use serde::{Deserialize, Serialize}; + +pub const MAX_SECTIONS: usize = 4; +pub const MIN_FC: f32 = 30.; +pub const MIN_Q: f32 = 0.2; +pub const MAX_Q: f32 = 4.; + +#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] +pub struct Section { + /// Centre frequency in Hz, where the section's phase passes −180°. + pub fc: f32, + pub q: f32, +} +impl Default for Section { + fn default() -> Self { + Self { fc: 1000., q: 0.7 } + } +} + +/// The result of a fit: what Weld applies to the main layer. +#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Default)] +pub struct Fit { + /// Invert the main layer. + pub flip: bool, + /// Number of active sections, 0..=MAX_SECTIONS. + pub count: usize, + pub sections: [Section; MAX_SECTIONS], +} +impl Fit { + /// The identity: no polarity change, no sections. + pub const NONE: Fit = Fit { + flip: false, + count: 0, + sections: [Section { fc: 1000., q: 0.7 }; MAX_SECTIONS], + }; + pub fn active(&self) -> &[Section] { + &self.sections[..self.count.min(MAX_SECTIONS)] + } + /// Clamps every value into the range the filter supports; repairs restored state. + pub fn sanitized(mut self, rate: f32) -> Self { + self.count = self.count.min(MAX_SECTIONS); + let top = max_fc(rate); + for s in &mut self.sections { + s.fc = if s.fc.is_finite() { + s.fc.clamp(MIN_FC, top) + } else { + 1000f32.min(top) + }; + s.q = if s.q.is_finite() { + s.q.clamp(MIN_Q, MAX_Q) + } else { + 0.7 + }; + } + self + } + /// Complex response at `f` Hz as (re, im). + pub fn response(&self, f: f32, rate: f32) -> (f32, f32) { + let w = std::f32::consts::TAU * f / rate; + let (mut re, mut im) = (if self.flip { -1. } else { 1. }, 0.); + for s in self.active() { + let c = Coefs::allpass(s.fc, s.q, rate); + let (hr, hi) = c.response(w.cos(), w.sin(), (2. * w).cos(), (2. * w).sin()); + (re, im) = (re * hr - im * hi, re * hi + im * hr); + } + (re, im) + } +} + +pub fn max_fc(rate: f32) -> f32 { + 0.45 * rate +} + +/// Normalised allpass coefficients: H(z) = (a2 + a1 z⁻¹ + z⁻²) / (1 + a1 z⁻¹ + a2 z⁻²). +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Coefs { + pub a1: f32, + pub a2: f32, +} +impl Coefs { + /// RBJ cookbook allpass. + pub fn allpass(fc: f32, q: f32, rate: f32) -> Self { + let fc = fc.clamp(MIN_FC, max_fc(rate)); + let q = q.clamp(MIN_Q, MAX_Q); + let w = std::f32::consts::TAU * fc / rate; + let alpha = w.sin() / (2. * q); + let a0 = 1. + alpha; + Self { + a1: -2. * w.cos() / a0, + a2: (1. - alpha) / a0, + } + } + /// Response at z⁻¹ = e^{−jω}, given cos/sin of ω and 2ω. + /// For an allpass the numerator is z⁻²·conj(den), so H = z⁻²·conj(den)/den. + #[inline] + pub fn response(&self, c1: f32, s1: f32, c2: f32, s2: f32) -> (f32, f32) { + let x = 1. + self.a1 * c1 + self.a2 * c2; + let y = -(self.a1 * s1 + self.a2 * s2); + let m = x * x + y * y; + // conj(den)/den = conj(den)² / |den|² + let (qr, qi) = ((x * x - y * y) / m, -2. * x * y / m); + // times z⁻² = c2 − j s2 + (qr * c2 + qi * s2, qi * c2 - qr * s2) + } +} + +/// Transposed direct form II state, in f64 so sections near 30 Hz stay quiet. +#[derive(Debug, Clone, Copy, Default)] +struct State { + s1: f64, + s2: f64, +} + +/// A running chain for up to two channels. Allocation-free. +#[derive(Debug, Clone)] +pub struct Chain { + fit: Fit, + sign: f32, + coefs: [(f64, f64); MAX_SECTIONS], + state: [[State; MAX_SECTIONS]; 2], +} +impl Chain { + pub fn new(fit: Fit, rate: f32) -> Self { + let mut chain = Self { + fit: Fit::NONE, + sign: 1., + coefs: [(0., 0.); MAX_SECTIONS], + state: [[State::default(); MAX_SECTIONS]; 2], + }; + chain.set(fit, rate); + chain + } + pub fn fit(&self) -> &Fit { + &self.fit + } + /// Replaces the coefficients and clears the state. + pub fn set(&mut self, fit: Fit, rate: f32) { + self.fit = fit.sanitized(rate); + self.sign = if self.fit.flip { -1. } else { 1. }; + for (c, s) in self.coefs.iter_mut().zip(self.fit.active()) { + let k = Coefs::allpass(s.fc, s.q, rate); + *c = (k.a1 as f64, k.a2 as f64); + } + self.reset(); + } + pub fn reset(&mut self) { + self.state = [[State::default(); MAX_SECTIONS]; 2]; + } + #[inline] + pub fn process(&mut self, channel: usize, x: f32) -> f32 { + let mut y = (self.sign * x) as f64; + for (k, &(a1, a2)) in self.coefs[..self.fit.count].iter().enumerate() { + let st = &mut self.state[channel][k]; + let out = a2 * y + st.s1; + st.s1 = a1 * y - a1 * out + st.s2; + st.s2 = y - a2 * out; + y = out; + } + y as f32 + } +} diff --git a/plugins/weld/core/src/fit.rs b/plugins/weld/core/src/fit.rs new file mode 100644 index 0000000..2ba8d30 --- /dev/null +++ b/plugins/weld/core/src/fit.rs @@ -0,0 +1,372 @@ +//! Fits polarity plus allpass sections to a measured cross-spectrum. +//! +//! With S = F_A·conj(F_B), the power of the sum after rotating A by H is +//! |A|² + |B|² + 2·Re(S·H). The fit maximises Σ w(f)·Re(S·H) with w = 1/f, so every +//! octave counts the same. Flipping polarity negates the sum, so the best polarity is +//! the sign of the result and only the sections need searching. +use crate::chain::{max_fc, Coefs, Fit, Section, MAX_Q, MAX_SECTIONS, MIN_FC, MIN_Q}; +use crate::report::{band_of, BANDS, DISPLAY_HIGH, DISPLAY_LOW}; + +/// Bands for the loss penalty. Narrower bands make the penalty brittle next to the +/// notches of a comb, which a delay between the layers always has. +const FINE: usize = 10; +fn fine_of(f: f32) -> usize { + let t = (f / DISPLAY_LOW).ln() / (DISPLAY_HIGH / DISPLAY_LOW).ln(); + ((t * FINE as f32).max(0.) as usize).min(FINE - 1) +} +use rustfft::num_complex::Complex32; + +/// One fit point: bins merged over at most 1/32 octave. +#[derive(Debug, Clone, Copy)] +pub(crate) struct Point { + pub c1: f32, + pub s1: f32, + pub c2: f32, + pub s2: f32, + /// 1/f weight. + pub w: f32, + /// Cross-spectrum of the coherent bins only; drives the objective. + pub fit: Complex32, + /// Cross-spectrum and summed auto-spectra of all bins; drive the predicted levels. + pub all: Complex32, + pub power: f32, + pub band: usize, + pub fine: usize, +} +impl Point { + pub fn new(f: f32, rate: f32) -> Self { + let w = std::f32::consts::TAU * f / rate; + Self { + c1: w.cos(), + s1: w.sin(), + c2: (2. * w).cos(), + s2: (2. * w).sin(), + w: 1. / f, + fit: Complex32::default(), + all: Complex32::default(), + power: 0., + band: band_of(f), + fine: fine_of(f), + } + } +} + +pub(crate) struct Problem { + pub points: Vec, + pub rate: f32, + /// Σ w·|S| over the coherent bins: the objective of a perfect per-bin alignment. + norm: f32, + /// Per-band level of the unprocessed sum. + before: [f32; BANDS], + /// The same in third-octave bands, for the loss penalty. + before_fine: [f32; FINE], +} + +/// Score of a perfectly aligned sum is 1. Losing more than 1 dB in any band against the +/// unprocessed sum costs extra, so a fit cannot win the mids by giving away the top. +const LOSS_FREE_DB: f32 = 1.0; +const LOSS_WEIGHT: f32 = 0.05; +/// The smallest model that reaches this share of the best model's improvement wins. +const MODEL_SHARE: f32 = 0.95; +/// Improvements below this score never justify another section. +const MODEL_FLOOR: f32 = 0.001; + +#[derive(Debug, Clone, Copy)] +pub struct Candidate { + pub fit: Fit, + pub score: f32, +} + +impl Problem { + pub fn new(points: Vec, rate: f32) -> Self { + let norm = points.iter().map(|p| p.w * p.fit.norm()).sum::(); + let mut before = [0f32; BANDS]; + let mut before_fine = [0f32; FINE]; + for p in &points { + before[p.band] += p.power + 2. * p.all.re; + before_fine[p.fine] += p.power + 2. * p.all.re; + } + Self { + points, + rate, + norm, + before, + before_fine, + } + } + pub fn is_empty(&self) -> bool { + self.norm <= 0. + } + fn coefs(&self, x: &[f32], out: &mut [Coefs; MAX_SECTIONS]) -> (usize, f32) { + let k = x.len() / 2; + let mut outside = 0.; + let (lo_f, hi_f) = (MIN_FC.ln(), max_fc(self.rate).ln()); + let (lo_q, hi_q) = (MIN_Q.ln(), MAX_Q.ln()); + for i in 0..k { + let (f, q) = (x[2 * i], x[2 * i + 1]); + outside += + (lo_f - f).max(0.) + (f - hi_f).max(0.) + (lo_q - q).max(0.) + (q - hi_q).max(0.); + out[i] = Coefs::allpass( + f.clamp(lo_f, hi_f).exp(), + q.clamp(lo_q, hi_q).exp(), + self.rate, + ); + } + (k, outside) + } + #[inline] + fn response(p: &Point, coefs: &[Coefs]) -> Complex32 { + let mut h = Complex32::new(1., 0.); + for c in coefs { + let (re, im) = c.response(p.c1, p.s1, p.c2, p.s2); + h *= Complex32::new(re, im); + } + h + } + /// Score and polarity for sections given as [ln fc, ln q, …]. + fn score(&self, x: &[f32]) -> (f32, bool) { + let mut coefs = [Coefs { a1: 0., a2: 0. }; MAX_SECTIONS]; + let (k, outside) = self.coefs(x, &mut coefs); + let coefs = &coefs[..k]; + let mut j = 0f32; + let mut cross = [0f32; FINE]; + let mut power = [0f32; FINE]; + for p in &self.points { + let h = Self::response(p, coefs); + j += p.w * (p.fit * h).re; + cross[p.fine] += (p.all * h).re; + power[p.fine] += p.power; + } + let flip = j < 0.; + let sign = if flip { -1. } else { 1. }; + let mut penalty = outside * outside; + for b in 0..FINE { + let before = self.before_fine[b]; + if before <= 0. { + continue; + } + let after = (power[b] + 2. * sign * cross[b]).max(before * 1e-6); + let loss = 10. * (before / after).log10(); + if loss > LOSS_FREE_DB { + penalty += LOSS_WEIGHT * (loss - LOSS_FREE_DB).powi(2); + } + } + (j.abs() / self.norm - penalty, flip) + } + fn candidate(&self, x: &[f32]) -> Candidate { + let (score, flip) = self.score(x); + let mut fit = Fit { + flip, + count: x.len() / 2, + ..Fit::NONE + }; + for (i, s) in fit.sections[..fit.count].iter_mut().enumerate() { + *s = Section { + fc: x[2 * i].exp().clamp(MIN_FC, max_fc(self.rate)), + q: x[2 * i + 1].exp().clamp(MIN_Q, MAX_Q), + }; + } + Candidate { fit, score } + } + /// Level of the sum per display band with `fit` applied (sign included). + pub fn bands(&self, fit: &Fit) -> [f32; BANDS] { + let mut coefs = [Coefs { a1: 0., a2: 0. }; MAX_SECTIONS]; + for (c, s) in coefs.iter_mut().zip(fit.active()) { + *c = Coefs::allpass(s.fc, s.q, self.rate); + } + let coefs = &coefs[..fit.active().len()]; + let sign = if fit.flip { -1. } else { 1. }; + let mut out = [0f32; BANDS]; + for p in &self.points { + out[p.band] += p.power + 2. * sign * (p.all * Self::response(p, coefs)).re; + } + out + } + /// Level per display band of a perfectly aligned sum. + pub fn ideal_bands(&self) -> [f32; BANDS] { + let mut out = [0f32; BANDS]; + for p in &self.points { + out[p.band] += p.power + 2. * p.all.norm(); + } + out + } + /// Mean change in dB over the bands that carry signal, from `from` to `to`. Every + /// band counts the same, so a hollow octave in the mids weighs as much as the sub. + pub fn mean_gain(&self, from: &[f32; BANDS], to: &[f32; BANDS]) -> f32 { + let total: f32 = self.before.iter().sum(); + let (mut sum, mut count) = (0f32, 0); + for b in 0..BANDS { + if self.before[b] > total * 1e-4 && from[b] > 0. { + sum += 10. * (to[b].max(from[b] * 1e-6) / from[b]).log10(); + count += 1; + } + } + if count > 0 { + sum / count as f32 + } else { + 0. + } + } + pub fn before_bands(&self) -> [f32; BANDS] { + self.before + } + /// The frequency where the current sections leave the most cross-energy unaligned, + /// smoothed over one third of an octave. + fn worst_frequency(&self, x: &[f32]) -> f32 { + let mut coefs = [Coefs { a1: 0., a2: 0. }; MAX_SECTIONS]; + let (k, _) = self.coefs(x, &mut coefs); + let (_, flip) = self.score(x); + let sign = if flip { -1. } else { 1. }; + let mut buckets = [0f32; 48]; + let mut first = None; + for p in &self.points { + let h = Self::response(p, &coefs[..k]); + let residual = p.w * (p.fit.norm() - sign * (p.fit * h).re); + let f = 1. / p.w; + let low = *first.get_or_insert(f); + let i = ((3. * (f / low).log2()) as usize).min(47); + buckets[i] += residual; + } + let low = first.unwrap_or(100.); + let best = (0..48) + .max_by(|&a, &b| buckets[a].total_cmp(&buckets[b])) + .unwrap_or(0); + low * 2f32.powf((best as f32 + 0.5) / 3.) + } +} + +/// Searches 0..=`max_sections` sections, warm-started from `warm`. Deterministic. +pub(crate) fn search(problem: &Problem, max_sections: usize, warm: &Fit) -> Candidate { + let max_sections = max_sections.min(MAX_SECTIONS); + let mut best: Vec = Vec::with_capacity(max_sections + 1); + best.push(problem.candidate(&[])); + let mut previous: Vec = Vec::new(); + for k in 1..=max_sections { + let mut seeds: Vec> = Vec::new(); + let worst = problem.worst_frequency(&previous); + for f in [worst, worst * 0.4, worst * 2.5] { + let mut x = previous.clone(); + x.extend([f.ln(), 0.7f32.ln()]); + seeds.push(x); + } + if k == 1 { + // Low-Q sections high up act like a short delay below their centre. + for (f, q) in [ + (120f32, 0.7f32), + (400., 0.7), + (1500., 0.7), + (3000., 0.5), + (6000., 0.5), + ] { + seeds.push(vec![f.ln(), q.ln()]); + } + } + if warm.count == k { + seeds.push( + warm.active() + .iter() + .flat_map(|s| [s.fc.ln(), s.q.ln()]) + .collect(), + ); + } + let mut top: Option<(Vec, f32)> = None; + for seed in seeds { + let (x, value) = nelder_mead(|x| -problem.score(x).0, &seed, 0.35, 120 * k * 2); + if top.as_ref().is_none_or(|(_, v)| value < *v) { + top = Some((x, value)); + } + } + let (x, _) = top.expect("at least one seed"); + let candidate = problem.candidate(&x); + let top = best.iter().map(|c| c.score).fold(f32::MIN, f32::max); + let gained = candidate.score - top; + let slack = MODEL_FLOOR.max((1. - MODEL_SHARE) * (top - best[0].score)); + best.push(candidate); + previous = x; + // A section that adds next to nothing is not worth it; more of them rarely are. + if k >= 2 && gained < slack { + break; + } + } + let top = best.iter().map(|c| c.score).fold(f32::MIN, f32::max); + let slack = MODEL_FLOOR.max((1. - MODEL_SHARE) * (top - best[0].score)); + *best + .iter() + .find(|c| c.score >= top - slack) + .expect("the best model qualifies") +} + +/// Downhill simplex. Returns the best point and its value. +fn nelder_mead( + mut f: impl FnMut(&[f32]) -> f32, + x0: &[f32], + step: f32, + max_evals: usize, +) -> (Vec, f32) { + let n = x0.len(); + let mut simplex: Vec<(Vec, f32)> = Vec::with_capacity(n + 1); + simplex.push((x0.to_vec(), f(x0))); + for i in 0..n { + let mut x = x0.to_vec(); + x[i] += step; + let v = f(&x); + simplex.push((x, v)); + } + let mut evals = n + 1; + let point = |a: &[f32], b: &[f32], t: f32| -> Vec { + a.iter().zip(b).map(|(a, b)| a + t * (b - a)).collect() + }; + while evals < max_evals { + simplex.sort_by(|a, b| a.1.total_cmp(&b.1)); + let spread = simplex[n].1 - simplex[0].1; + let size = simplex[1..] + .iter() + .flat_map(|(x, _)| x.iter().zip(&simplex[0].0).map(|(a, b)| (a - b).abs())) + .fold(0f32, f32::max); + if spread < 1e-7 && size < 1e-3 { + break; + } + let mut centroid = vec![0f32; n]; + for (x, _) in &simplex[..n] { + for (c, v) in centroid.iter_mut().zip(x) { + *c += v / n as f32; + } + } + let worst = simplex[n].clone(); + let reflected = point(¢roid, &worst.0, -1.); + let fr = f(&reflected); + evals += 1; + if fr < simplex[0].1 { + let expanded = point(¢roid, &worst.0, -2.); + let fe = f(&expanded); + evals += 1; + simplex[n] = if fe < fr { + (expanded, fe) + } else { + (reflected, fr) + }; + } else if fr < simplex[n - 1].1 { + simplex[n] = (reflected, fr); + } else { + let contracted = if fr < worst.1 { + point(¢roid, &reflected, 0.5) + } else { + point(¢roid, &worst.0, 0.5) + }; + let fc = f(&contracted); + evals += 1; + if fc < worst.1.min(fr) { + simplex[n] = (contracted, fc); + } else { + let best = simplex[0].0.clone(); + for (x, v) in &mut simplex[1..] { + *x = point(&best, x, 0.5); + *v = f(x); + } + evals += n; + } + } + } + simplex.sort_by(|a, b| a.1.total_cmp(&b.1)); + simplex.swap_remove(0) +} diff --git a/plugins/weld/core/src/lib.rs b/plugins/weld/core/src/lib.rs index c240a6e..1ab6093 100644 --- a/plugins/weld/core/src/lib.rs +++ b/plugins/weld/core/src/lib.rs @@ -1,9 +1,79 @@ //! Weld engine: aligns the phase of the main layer to a sidechain layer so the two sum -//! instead of cancel. Allocation-free after `new`; zero latency. +//! instead of cancelling. The audio thread runs polarity plus an allpass chain and hands +//! mono blocks to a worker thread, which measures the cross-spectrum and fits the chain. +//! Allocation-free on the audio thread after `new`; zero latency. +mod analyzer; +mod chain; +mod fit; +mod report; mod settings; -pub use settings::{Listen, Settings}; +mod worker; +pub use analyzer::{Analyzer, Update}; +pub use chain::{Chain, Fit, Section, MAX_SECTIONS}; +pub use report::{ + band_edge, curve_frequency, Band, CurvePoint, Report, Status, BANDS, CURVE, DISPLAY_HIGH, + DISPLAY_LOW, +}; +pub use settings::{Listen, Mode, Settings}; +pub use worker::Worker; -/// Levels for the editor, published a few times per second. +use crossbeam_queue::ArrayQueue; +use std::sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, +}; + +/// Samples per block handed to the worker. +pub const BLOCK: usize = 256; +/// Blocks the queue holds: about 340 ms at 48 kHz. +const QUEUE_BLOCKS: usize = 64; +/// A new fit runs silently this long before it is faded in, so its filters settle. +const WARM_S: f32 = 0.04; +/// Crossfade time from the old fit to the new one. +const FADE_S: f32 = 0.04; + +/// Mono blocks of both layers, before Weld's processing. +#[derive(Clone, Copy)] +pub struct Block { + pub a: [f32; BLOCK], + pub b: [f32; BLOCK], +} + +/// Lock-free queues between one engine and its worker. +pub struct Link { + pub(crate) blocks: ArrayQueue, + pub(crate) fits: ArrayQueue, + pub(crate) settings: ArrayQueue, + dropped: AtomicUsize, +} +impl Link { + fn new() -> Self { + Self { + blocks: ArrayQueue::new(QUEUE_BLOCKS), + fits: ArrayQueue::new(4), + settings: ArrayQueue::new(1), + dropped: AtomicUsize::new(0), + } + } + /// Blocks dropped because the worker fell behind. + pub fn dropped(&self) -> usize { + self.dropped.load(Ordering::Relaxed) + } + /// Takes the next block, as the worker does. For tests and offline tools. + pub fn pop_block(&self) -> Option { + self.blocks.pop() + } + /// Takes the latest settings, as the worker does. For tests and offline tools. + pub fn pop_settings(&self) -> Option { + self.settings.pop() + } + /// Hands a fit to the engine, as the worker does. For tests and offline tools. + pub fn push_fit(&self, fit: Fit) { + self.fits.force_push(fit); + } +} + +/// Levels for the editor, published a few times per second by the audio thread. #[derive(Debug, Clone, Copy, PartialEq)] pub struct AnalyzerFrame { /// RMS of the main input in dBFS since the last frame. @@ -14,6 +84,8 @@ pub struct AnalyzerFrame { pub side_peak_db: f32, /// True once any sidechain sample above −90 dBFS arrived since activation. pub side_seen: bool, + /// Blocks the worker could not take in time, since activation. + pub dropped: usize, pub sample_rate: f32, } impl Default for AnalyzerFrame { @@ -23,6 +95,7 @@ impl Default for AnalyzerFrame { side_db: -120., side_peak_db: -120., side_seen: false, + dropped: 0, sample_rate: 48000., } } @@ -46,16 +119,34 @@ struct Levels { count: usize, } +#[derive(Debug, Clone, Copy, PartialEq)] +enum Phase { + Steady, + /// The next chain runs unheard for this many more samples. + Warm(usize), + /// The next chain fades in over this many more samples. + Fade(usize), +} + pub struct Engine { rate: f32, channels: usize, settings: Settings, + sent: Option, gain: f32, target_gain: f32, gain_coef: f32, fresh: bool, levels: Levels, side_seen: bool, + current: Chain, + next: Chain, + phase: Phase, + warm: usize, + fade: usize, + link: Arc, + block: Block, + fill: usize, } impl Engine { pub fn new(rate: f32, channels: usize) -> Result { @@ -69,12 +160,24 @@ impl Engine { rate, channels, settings: Settings::default(), + sent: None, gain: 1., target_gain: 1., gain_coef: 1. - (-1. / (0.02 * rate)).exp(), fresh: true, levels: Levels::default(), side_seen: false, + current: Chain::new(Fit::NONE, rate), + next: Chain::new(Fit::NONE, rate), + phase: Phase::Steady, + warm: ((WARM_S * rate) as usize).max(1), + fade: ((FADE_S * rate) as usize).max(1), + link: Arc::new(Link::new()), + block: Block { + a: [0.; BLOCK], + b: [0.; BLOCK], + }, + fill: 0, }) } pub const fn latency_samples(&self) -> u32 { @@ -83,9 +186,28 @@ impl Engine { pub fn channels(&self) -> usize { self.channels } + pub fn sample_rate(&self) -> f32 { + self.rate + } pub fn settings(&self) -> &Settings { &self.settings } + /// The queues for this engine's worker. + pub fn link(&self) -> Arc { + self.link.clone() + } + /// The alignment heard now (the target one during a crossfade). + pub fn fit(&self) -> Fit { + match self.phase { + Phase::Steady => *self.current.fit(), + _ => *self.next.fit(), + } + } + /// Applies `fit` at once, without a crossfade. For restoring saved state. + pub fn set_fit(&mut self, fit: Fit) { + self.current.set(fit, self.rate); + self.phase = Phase::Steady; + } pub fn set_settings(&mut self, settings: Settings) -> Result<(), &'static str> { settings.validate()?; self.settings = settings; @@ -94,11 +216,22 @@ impl Engine { self.gain = self.target_gain; self.fresh = false; } + if self.sent != Some(settings) { + self.link.settings.force_push(settings); + self.sent = Some(settings); + } Ok(()) } pub fn reset(&mut self) { self.gain = self.target_gain; self.levels = Levels::default(); + self.current.reset(); + if self.phase != Phase::Steady { + std::mem::swap(&mut self.current, &mut self.next); + self.current.reset(); + self.phase = Phase::Steady; + } + self.fill = 0; } /// One frame of main input `x` and sidechain input `side`. Mono uses index 0 only. #[inline] @@ -118,22 +251,66 @@ impl Engine { if s.abs() > 3e-5 { self.side_seen = true; } + self.feed(m, s); + if self.phase == Phase::Steady { + if let Some(fit) = self.link.fits.pop() { + self.next.set(fit, self.rate); + self.phase = Phase::Warm(self.warm); + } + } + let mut aligned = [0f32; 2]; + let mix = match self.phase { + Phase::Steady => None, + Phase::Warm(_) => Some(0.), + Phase::Fade(left) => Some(1. - left as f32 / self.fade as f32), + }; + for c in 0..n { + let a = self.current.process(c, x[c]); + aligned[c] = match mix { + None => a, + Some(t) => { + let b = self.next.process(c, x[c]); + a + t * (b - a) + } + }; + } + self.phase = match self.phase { + Phase::Steady => Phase::Steady, + Phase::Warm(1) => Phase::Fade(self.fade), + Phase::Warm(left) => Phase::Warm(left - 1), + Phase::Fade(1) => { + std::mem::swap(&mut self.current, &mut self.next); + Phase::Steady + } + Phase::Fade(left) => Phase::Fade(left - 1), + }; self.gain += (self.target_gain - self.gain) * self.gain_coef; if self.settings.bypass { return x; } let mut out = [0.; 2]; for c in 0..n { - let layer = x[c]; out[c] = self.gain * match self.settings.listen { - Listen::Layer => layer, - Listen::Sum => layer + side[c], + Listen::Layer => aligned[c], + Listen::Sum => aligned[c] + side[c], Listen::SumDry => x[c] + side[c], }; } out } + #[inline] + fn feed(&mut self, a: f32, b: f32) { + self.block.a[self.fill] = a; + self.block.b[self.fill] = b; + self.fill += 1; + if self.fill == BLOCK { + self.fill = 0; + if self.link.blocks.push(self.block).is_err() { + self.link.dropped.fetch_add(1, Ordering::Relaxed); + } + } + } /// Levels since the previous call. pub fn take_analyzer(&mut self) -> AnalyzerFrame { let n = self.levels.count.max(1) as f64; @@ -142,6 +319,7 @@ impl Engine { side_db: db((self.levels.side / n) as f32, true), side_peak_db: db(self.levels.peak, false), side_seen: self.side_seen, + dropped: self.link.dropped(), sample_rate: self.rate, }; self.levels = Levels::default(); diff --git a/plugins/weld/core/src/report.rs b/plugins/weld/core/src/report.rs new file mode 100644 index 0000000..b9a57cf --- /dev/null +++ b/plugins/weld/core/src/report.rs @@ -0,0 +1,108 @@ +//! What the analysis tells the editor. Plain `Copy` data, so it crosses threads through +//! a lock-free queue without allocating. +use crate::chain::Fit; + +/// Display bands, equally wide on a log axis from `DISPLAY_LOW` to `DISPLAY_HIGH`. +pub const BANDS: usize = 10; +/// Points of the phase curve. +pub const CURVE: usize = 96; +pub const DISPLAY_LOW: f32 = 30.; +pub const DISPLAY_HIGH: f32 = 12000.; + +/// Index of the display band that contains `f` (clamped to the outer bands). +pub fn band_of(f: f32) -> usize { + let t = (f / DISPLAY_LOW).ln() / (DISPLAY_HIGH / DISPLAY_LOW).ln(); + ((t * BANDS as f32).max(0.) as usize).min(BANDS - 1) +} +/// Lower edge of display band `i`; `band_edge(BANDS)` is the top edge. +pub fn band_edge(i: usize) -> f32 { + DISPLAY_LOW * (DISPLAY_HIGH / DISPLAY_LOW).powf(i as f32 / BANDS as f32) +} +/// Frequency of curve point `i`. +pub fn curve_frequency(i: usize) -> f32 { + DISPLAY_LOW * (DISPLAY_HIGH / DISPLAY_LOW).powf(i as f32 / (CURVE - 1) as f32) +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub enum Status { + /// Not enough measured audio yet. + #[default] + Waiting, + /// The sidechain carries no signal. + NoSidechain, + /// Both layers play, but one of them is under the gate. + Quiet, + /// The layers are not related closely enough to align (free-running oscillators, + /// different notes, noise). + Loose, + /// This layer is later than the sidechain layer. An allpass cannot move it earlier. + Late, + /// Measuring and aligning. + Aligned, +} + +#[derive(Debug, Clone, Copy, PartialEq, Default)] +pub struct Band { + /// Level of the unprocessed sum relative to a perfectly aligned sum, in dB (≤ 0). + pub before: f32, + /// Level of the sum with Weld's alignment, relative to a perfect one, in dB (≤ 0). + pub after: f32, + /// Power-weighted coherence of the two layers, 0..1. + pub coherence: f32, + /// False when the band carries no measured signal. + pub valid: bool, +} + +#[derive(Debug, Clone, Copy, PartialEq, Default)] +pub struct CurvePoint { + /// Phase of the main layer relative to the sidechain layer, degrees, ±180. + pub before: f32, + /// The same with Weld's alignment applied. + pub after: f32, + /// Phase of Weld's chain, degrees, ±180. + pub chain: f32, + pub coherence: f32, + pub valid: bool, +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Report { + pub status: Status, + /// The alignment currently applied (or about to be). + pub fit: Fit, + /// True when this report comes with a newly accepted fit. + pub changed: bool, + pub bands: [Band; BANDS], + pub curve: [CurvePoint; CURVE], + /// What the cancellation costs between Low and High: aligned sum over unprocessed sum. + pub cost_db: f32, + /// What Weld wins back between Low and High: aligned-by-Weld sum over unprocessed sum. + pub recovered_db: f32, + /// Time offset of this layer against the sidechain layer, ms. Positive: this layer + /// is late. + pub lag_ms: f32, + /// How clear the time offset is, 0..1. + pub lag_strength: f32, + /// Measured frames in the current average. + pub frames: u32, + pub low: f32, + pub high: f32, +} +impl Default for Report { + fn default() -> Self { + Self { + status: Status::Waiting, + fit: Fit::NONE, + changed: false, + bands: [Band::default(); BANDS], + curve: [CurvePoint::default(); CURVE], + cost_db: 0., + recovered_db: 0., + lag_ms: 0., + lag_strength: 0., + frames: 0, + low: 60., + high: 6000., + } + } +} diff --git a/plugins/weld/core/src/settings.rs b/plugins/weld/core/src/settings.rs index c229c31..ac747a3 100644 --- a/plugins/weld/core/src/settings.rs +++ b/plugins/weld/core/src/settings.rs @@ -1,4 +1,4 @@ -//! Host-independent controls. Gains in dB. +//! Host-independent controls. Gains in dB, frequencies in Hz, times in seconds. use serde::{Deserialize, Serialize}; /// What the plugin outputs. @@ -13,9 +13,33 @@ pub enum Listen { SumDry, } +/// How the alignment is found. +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)] +pub enum Mode { + /// Keep measuring and refit when a clearly better alignment shows up. + #[default] + Follow, + /// Measure from the moment Learn starts, with equal weight; the result is applied + /// when Learn ends. + Learn, + /// Keep the current alignment. The display keeps measuring. + Hold, +} + #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)] #[serde(deny_unknown_fields)] pub struct Settings { + pub mode: Mode, + /// Averaging time of the measurement in Follow mode. + pub response: f32, + /// Lowest frequency the fit listens to. + pub low: f32, + /// Highest frequency the fit listens to. + pub high: f32, + /// Largest number of allpass sections the fit may use. + pub sections: usize, + /// Frames quieter than this (dBFS RMS) on either layer are not measured. + pub gate: f32, pub listen: Listen, pub output: f32, pub bypass: bool, @@ -23,6 +47,12 @@ pub struct Settings { impl Default for Settings { fn default() -> Self { Self { + mode: Mode::Follow, + response: 1.5, + low: 60., + high: 6000., + sections: 4, + gate: -50., listen: Listen::Layer, output: 0., bypass: false, @@ -31,10 +61,17 @@ impl Default for Settings { } impl Settings { pub fn validate(&self) -> Result<(), &'static str> { - if !self.output.is_finite() { + let values = [self.response, self.low, self.high, self.gate, self.output]; + if values.iter().any(|v| !v.is_finite()) { return Err("non-finite setting"); } - if (-12.0..=12.).contains(&self.output) { + if (0.3..=5.).contains(&self.response) + && (30.0..=400.).contains(&self.low) + && (1000.0..=12000.).contains(&self.high) + && (1..=crate::chain::MAX_SECTIONS).contains(&self.sections) + && (-70.0..=-30.).contains(&self.gate) + && (-12.0..=12.).contains(&self.output) + { Ok(()) } else { Err("setting out of range") diff --git a/plugins/weld/core/src/worker.rs b/plugins/weld/core/src/worker.rs new file mode 100644 index 0000000..c907c2b --- /dev/null +++ b/plugins/weld/core/src/worker.rs @@ -0,0 +1,77 @@ +//! The worker thread: takes blocks from the audio thread, runs the analyzer and sends +//! accepted fits back. Started and stopped outside the audio thread. +use crate::analyzer::Analyzer; +use crate::chain::Fit; +use crate::report::Report; +use crate::Link; +use crossbeam_queue::ArrayQueue; +use std::{ + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, + thread::JoinHandle, + time::Duration, +}; + +pub struct Worker { + stop: Arc, + thread: Option>, +} +impl Worker { + /// Starts the worker for one engine. `on_fit` runs on the worker thread for every + /// accepted fit, for example to store it in the plugin state. + pub fn spawn( + link: Arc, + rate: f32, + fit: Fit, + reports: Arc>, + mut on_fit: impl FnMut(Fit) + Send + 'static, + ) -> std::io::Result { + let stop = Arc::new(AtomicBool::new(false)); + let flag = stop.clone(); + let thread = std::thread::Builder::new() + .name("weld-analysis".into()) + .spawn(move || { + let mut analyzer = Analyzer::new(rate, fit); + while !flag.load(Ordering::Relaxed) { + let mut idle = true; + while let Some(settings) = link.settings.pop() { + analyzer.set_settings(settings); + } + while let Some(block) = link.blocks.pop() { + idle = false; + analyzer.push(&block.a, &block.b); + if let Some(update) = analyzer.poll() { + if let Some(fit) = update.fit { + link.fits.force_push(fit); + on_fit(fit); + } + reports.force_push(update.report); + } + if flag.load(Ordering::Relaxed) { + break; + } + } + if idle { + std::thread::sleep(Duration::from_millis(5)); + } + } + })?; + Ok(Self { + stop, + thread: Some(thread), + }) + } + pub fn stop(&mut self) { + self.stop.store(true, Ordering::Relaxed); + if let Some(thread) = self.thread.take() { + let _ = thread.join(); + } + } +} +impl Drop for Worker { + fn drop(&mut self) { + self.stop(); + } +} diff --git a/plugins/weld/core/tests/dsp.rs b/plugins/weld/core/tests/dsp.rs index e71d936..78e379e 100644 --- a/plugins/weld/core/tests/dsp.rs +++ b/plugins/weld/core/tests/dsp.rs @@ -95,3 +95,244 @@ fn invalid_settings_and_rates_are_rejected() { Settings::default() ); } + +use weld_core::{Analyzer, Chain, Fit, Mode, Section, Status}; + +fn random_fit(seed: u64) -> Fit { + let mut s = seed; + let mut rand = move || { + s = s + .wrapping_mul(6364136223846793005) + .wrapping_add(1442695040888963407); + (s >> 11) as f32 / (1u64 << 53) as f32 + }; + let mut fit = Fit { + flip: rand() > 0.5, + count: 1 + (rand() * 3.99) as usize, + ..Fit::NONE + }; + for section in &mut fit.sections { + *section = Section { + fc: 30. * 600f32.powf(rand()), + q: 0.2 * 20f32.powf(rand()), + }; + } + fit +} + +#[test] +fn every_chain_is_flat_in_magnitude() { + for seed in 1..40 { + let fit = random_fit(seed); + for i in 0..200 { + let f = 20. * 1000f32.powf(i as f32 / 199.); + let (re, im) = fit.response(f, 48000.); + let db = 10. * (re * re + im * im).log10(); + assert!(db.abs() < 0.01, "{fit:?} at {f} Hz: {db}"); + } + } +} + +#[test] +fn the_running_chain_matches_its_response() { + let fit = random_fit(3); + for f in [50f32, 333., 2100., 9000.] { + let mut chain = Chain::new(fit, 48000.); + let (mut i_sum, mut q_sum, mut energy) = (0f64, 0f64, 0f64); + let n = 96000; + for k in 0..n { + let t = k as f64 / 48000.; + let w = std::f64::consts::TAU * f as f64 * t; + let y = chain.process(0, w.cos() as f32) as f64; + if k > n / 2 { + i_sum += y * w.cos(); + q_sum += -y * w.sin(); + energy += y * y; + } + } + let m = (n / 2 - 1) as f64; + let (re, im) = fit.response(f, 48000.); + let measured = (2. * i_sum / m, 2. * q_sum / m); + assert!( + (measured.0 - re as f64).abs() < 0.01, + "{f}: {measured:?} vs {re},{im}" + ); + assert!( + (measured.1 - im as f64).abs() < 0.01, + "{f}: {measured:?} vs {re},{im}" + ); + assert!((2. * energy / m - 1.).abs() < 0.01); + } +} + +#[test] +fn the_impulse_response_starts_at_sample_zero() { + let mut chain = Chain::new(random_fit(5), 48000.); + let first = chain.process(0, 1.); + assert!(first.abs() > 0.01, "{first}"); +} + +#[test] +fn a_new_fit_crossfades_without_clicks() { + let mut engine = Engine::new(48000., 2).unwrap(); + engine.set_settings(Settings::default()).unwrap(); + let link = engine.link(); + let x = |n: usize| signal(n, 97.) + 0.3 * signal(n, 1210.); + let max_input_step = (1..48000) + .map(|n| (x(n) - x(n - 1)).abs()) + .fold(0f32, f32::max); + let mut previous = [0f32; 2]; + let mut largest = 0f32; + for n in 0..48000 { + if n == 4800 { + link.push_fit(Fit { + flip: true, + count: 2, + sections: [ + Section { fc: 300., q: 0.7 }, + Section { fc: 2000., q: 2. }, + Section::default(), + Section::default(), + ], + }); + } + let out = engine.process_sample([x(n), x(n)], [0.; 2]); + if n > 0 { + largest = largest.max((out[0] - previous[0]).abs()); + } + previous = out; + } + assert!( + largest <= 1.5 * max_input_step, + "{largest} vs {max_input_step}" + ); + assert!(engine.fit().flip); + assert_eq!(engine.fit().count, 2); +} + +#[test] +fn sum_uses_the_aligned_layer_and_sum_dry_the_unprocessed_one() { + for listen in [Listen::Sum, Listen::SumDry] { + let mut engine = Engine::new(48000., 1).unwrap(); + engine + .set_settings(Settings { + listen, + ..Settings::default() + }) + .unwrap(); + engine.set_fit(Fit { + flip: true, + ..Fit::NONE + }); + let out = engine.process_sample([0.5, 0.], [0.2, 0.]); + let expected = if listen == Listen::Sum { -0.3 } else { 0.7 }; + assert!((out[0] - expected).abs() < 1e-6, "{listen:?} {out:?}"); + } +} + +#[test] +fn blocks_reach_the_worker_and_drops_are_counted() { + let mut engine = Engine::new(48000., 2).unwrap(); + engine.set_settings(Settings::default()).unwrap(); + let link = engine.link(); + assert!(link.pop_settings().is_some()); + for n in 0..256 * 70 { + engine.process_sample([signal(n, 100.); 2], [signal(n, 100.); 2]); + } + assert_eq!(link.dropped(), 6); + let block = link.pop_block().unwrap(); + assert!((block.a[10] - signal(10, 100.)).abs() < 1e-6); + assert_eq!(block.a, block.b); +} + +#[test] +fn silence_holds_the_fit_and_produces_no_nan() { + let flipped = Fit { + flip: true, + ..Fit::NONE + }; + let mut analyzer = Analyzer::new(48000., flipped); + analyzer.set_settings(Settings::default()); + let zeros = [0f32; 256]; + let mut last = None; + for _ in 0..400 { + analyzer.push(&zeros, &zeros); + if let Some(update) = analyzer.poll() { + assert!(update.fit.is_none()); + last = Some(update.report); + } + } + let report = last.unwrap(); + assert_eq!(report.status, Status::NoSidechain); + assert_eq!(report.fit, flipped); + assert!(report.cost_db.is_finite() && report.recovered_db.is_finite()); + assert!(report + .bands + .iter() + .all(|b| b.before.is_finite() && b.after.is_finite())); +} + +#[test] +fn follow_mode_finds_the_flip_and_then_holds_still() { + let mut analyzer = Analyzer::new(48000., Fit::NONE); + analyzer.set_settings(Settings { + mode: Mode::Follow, + ..Settings::default() + }); + let a: Vec = (0..48000 * 6) + .map(|n| signal(n, 87.3) + 0.3 * signal(n, 261.9) + 0.1 * signal(n, 611.)) + .collect(); + let b: Vec = a.iter().map(|v| -0.7 * v).collect(); + let mut accepted = Vec::new(); + let mut last = None; + for (x, y) in a.chunks(256).zip(b.chunks(256)) { + analyzer.push(x, y); + if let Some(update) = analyzer.poll() { + if let Some(fit) = update.fit { + accepted.push(fit); + } + last = Some(update.report); + } + } + assert_eq!(accepted.len(), 1, "{accepted:?}"); + assert!(accepted[0].flip); + let report = last.unwrap(); + assert_eq!(report.status, Status::Aligned); + assert!(report.recovered_db > 10., "{}", report.recovered_db); + assert!((report.recovered_db - report.cost_db).abs() < 0.2); +} + +#[test] +fn learn_measures_and_applies_on_exit() { + let mut analyzer = Analyzer::new(48000., Fit::NONE); + let learn = Settings { + mode: Mode::Learn, + ..Settings::default() + }; + analyzer.set_settings(learn); + let a: Vec = (0..48000 * 2) + .map(|n| signal(n, 87.3) + 0.3 * signal(n, 261.9)) + .collect(); + let b: Vec = a.iter().map(|v| -v).collect(); + for (x, y) in a.chunks(256).zip(b.chunks(256)) { + analyzer.push(x, y); + let _ = analyzer.poll(); + } + analyzer.set_settings(Settings { + mode: Mode::Hold, + ..learn + }); + let update = analyzer.poll().expect("leaving Learn reports at once"); + assert!(analyzer.current().flip); + // Learn may already have applied the flip while measuring. + assert!(update.fit.is_none_or(|f| f.flip)); + // Hold never refits, even when the layers change. + let c: Vec = a.clone(); + for (x, y) in a.chunks(256).zip(c.chunks(256)) { + analyzer.push(x, y); + if let Some(update) = analyzer.poll() { + assert!(update.fit.is_none()); + } + } + assert!(analyzer.current().flip); +} diff --git a/plugins/weld/core/tests/fit.rs b/plugins/weld/core/tests/fit.rs new file mode 100644 index 0000000..be9bb4a --- /dev/null +++ b/plugins/weld/core/tests/fit.rs @@ -0,0 +1,294 @@ +//! Fit quality on a synthetic saw bass, with targets taken from the prototype. +use rustfft::{num_complex::Complex64, FftPlanner}; +use weld_core::{Analyzer, Chain, Fit, Mode, Settings, Status}; + +const RATE: f32 = 48000.; + +/// Two seconds of an F1 saw (60 harmonics, fixed random phases) plus a little noise. +fn bass() -> Vec { + let n = 2 * RATE as usize; + let mut seed = 1u64; + let mut rand = move || { + seed = seed + .wrapping_mul(6364136223846793005) + .wrapping_add(1442695040888963407); + (seed >> 11) as f64 / (1u64 << 53) as f64 + }; + let phases: Vec = (0..60).map(|_| rand() * std::f64::consts::TAU).collect(); + (0..n) + .map(|i| { + let t = i as f64 / RATE as f64; + let saw: f64 = (1..60) + .map(|k| (std::f64::consts::TAU * k as f64 * 87.3 * t + phases[k]).sin() / k as f64) + .sum(); + (0.3 * saw + 0.015 * (rand() - 0.5)) as f32 + }) + .collect() +} +fn noise(n: usize, seed: u64) -> Vec { + let mut s = seed; + (0..n) + .map(|_| { + s = s + .wrapping_mul(6364136223846793005) + .wrapping_add(1442695040888963407); + ((s >> 11) as f64 / (1u64 << 53) as f64 - 0.5) as f32 * 0.5 + }) + .collect() +} +fn delay(x: &[f32], samples: isize) -> Vec { + (0..x.len() as isize) + .map(|i| { + let j = i - samples; + if j >= 0 && (j as usize) < x.len() { + x[j as usize] + } else { + 0. + } + }) + .collect() +} +/// Direct-form biquad, b and a normalised by a0. +fn biquad(x: &[f32], b: [f64; 3], a: [f64; 3]) -> Vec { + let (mut x1, mut x2, mut y1, mut y2) = (0f64, 0f64, 0f64, 0f64); + x.iter() + .map(|&v| { + let v = v as f64; + let y = (b[0] * v + b[1] * x1 + b[2] * x2 - a[1] * y1 - a[2] * y2) / a[0]; + (x2, x1, y2, y1) = (x1, v, y1, y); + y as f32 + }) + .collect() +} +fn allpass(x: &[f32], fc: f64, q: f64) -> Vec { + let w = std::f64::consts::TAU * fc / RATE as f64; + let alpha = w.sin() / (2. * q); + let a = [1. + alpha, -2. * w.cos(), 1. - alpha]; + biquad(x, [a[2], a[1], a[0]], a) +} +fn highpass(x: &[f32], fc: f64) -> Vec { + let w = std::f64::consts::TAU * fc / RATE as f64; + let alpha = w.sin() / (2. * std::f64::consts::FRAC_1_SQRT_2); + let c = w.cos(); + biquad( + x, + [(1. + c) / 2., -(1. + c), (1. + c) / 2.], + [1. + alpha, -2. * c, 1. - alpha], + ) +} +fn power_db(x: &[f32]) -> f64 { + let skip = x.len() / 4; + let p: f64 = + x[skip..].iter().map(|&v| (v as f64).powi(2)).sum::() / (x.len() - skip) as f64; + 10. * p.max(1e-30).log10() +} +fn sum(a: &[f32], b: &[f32]) -> Vec { + a.iter().zip(b).map(|(a, b)| a + b).collect() +} +fn apply(fit: Fit, x: &[f32]) -> Vec { + let mut chain = Chain::new(fit, RATE); + x.iter().map(|&v| chain.process(0, v)).collect() +} +/// Level of A + B when every bin of A is rotated onto B: the best any phase-only +/// processing could reach. +fn ideal_db(a: &[f32], b: &[f32]) -> f64 { + let n = a.len(); + let mut planner = FftPlanner::::new(); + let fft = planner.plan_fft_forward(n); + let ifft = planner.plan_fft_inverse(n); + let mut fa: Vec = a.iter().map(|&v| Complex64::new(v as f64, 0.)).collect(); + let mut fb: Vec = b.iter().map(|&v| Complex64::new(v as f64, 0.)).collect(); + fft.process(&mut fa); + fft.process(&mut fb); + let mut y: Vec = fa + .iter() + .zip(&fb) + .map(|(a, b)| { + let unit = if b.norm() > 1e-12 { + b / b.norm() + } else { + Complex64::new(1., 0.) + }; + unit * a.norm() + b + }) + .collect(); + ifft.process(&mut y); + let out: Vec = y.iter().map(|v| (v.re / n as f64) as f32).collect(); + power_db(&out) +} +/// Power of A+B in octave bands from 60 Hz to 7.7 kHz, after minus before, in dB. +fn octave_changes(a: &[f32], aligned: &[f32], b: &[f32]) -> Vec { + let n = a.len(); + let mut planner = FftPlanner::::new(); + let fft = planner.plan_fft_forward(n); + let spectrum = |x: Vec| { + let mut v: Vec = x.iter().map(|&s| Complex64::new(s as f64, 0.)).collect(); + fft.process(&mut v); + v + }; + let before = spectrum(sum(a, b)); + let after = spectrum(sum(aligned, b)); + (0..7) + .map(|o| { + let lo = 60. * 2f64.powi(o); + let range = |s: &Vec| -> f64 { + (0..n / 2) + .filter(|&k| { + let f = k as f64 * RATE as f64 / n as f64; + f >= lo && f < 2. * lo + }) + .map(|k| s[k].norm_sqr()) + .sum() + }; + 10. * (range(&after) / range(&before)).log10() + }) + .collect() +} + +struct Outcome { + accepted: Option, + status: Status, + lag_ms: f32, + before: f64, + after: f64, + ideal: f64, + octaves: Vec, +} +fn run(a: &[f32], b: &[f32]) -> Outcome { + let mut analyzer = Analyzer::new(RATE, Fit::NONE); + analyzer.set_settings(Settings { + mode: Mode::Learn, + low: 40., + high: 8000., + ..Settings::default() + }); + for (x, y) in a.chunks(256).zip(b.chunks(256)) { + analyzer.push(x, y); + } + let update = analyzer.analyse(Some(0.1)); + let fit = *analyzer.current(); + let aligned = apply(fit, a); + Outcome { + accepted: update.fit, + status: update.report.status, + lag_ms: update.report.lag_ms, + before: power_db(&sum(a, b)), + after: power_db(&sum(&aligned, b)), + ideal: ideal_db(a, b), + octaves: octave_changes(a, &aligned, b), + } +} +fn show(name: &str, o: &Outcome) { + eprintln!( + "{name}: before {:.2} after {:.2} ideal {:.2} fit {:?} status {:?} lag {:.3} octaves {:?}", + o.before, + o.after, + o.ideal, + o.accepted.map(|f| (f.flip, f.active().to_vec())), + o.status, + o.lag_ms, + o.octaves + .iter() + .map(|v| (v * 10.).round() / 10.) + .collect::>() + ); +} + +#[test] +fn polarity_flip_needs_no_sections() { + let a = bass(); + let b: Vec = a.iter().map(|v| -v).collect(); + let o = run(&a, &b); + show("flip", &o); + let fit = o.accepted.expect("a fit"); + assert!(fit.flip); + assert_eq!(fit.count, 0); + assert!((o.after - o.ideal).abs() < 0.05); +} + +#[test] +fn an_allpass_is_found_again() { + let a = bass(); + let b = allpass(&a, 400., 0.7); + let o = run(&a, &b); + show("allpass400", &o); + let fit = o.accepted.expect("a fit"); + assert!(!fit.flip); + assert_eq!(fit.count, 1, "{fit:?}"); + let s = fit.sections[0]; + assert!((s.fc / 400. - 1.).abs() < 0.05, "{s:?}"); + assert!((s.q / 0.7 - 1.).abs() < 0.1, "{s:?}"); + assert!(o.ideal - o.after < 0.1); +} + +#[test] +fn a_late_sidechain_layer_is_caught_up() { + let a = bass(); + for ms in [0.3, 1.0] { + let b = delay(&a, (ms * 48.) as isize); + let o = run(&a, &b); + show(&format!("delay {ms}"), &o); + assert!(o.ideal - o.after < 0.3, "{ms}"); + assert!(o.after > o.before); + assert_ne!(o.status, Status::Late); + // A delay leaves a comb in the sum: notches at odd multiples of 1/(2·delay). + // A few allpass sections cannot follow a delay's phase all the way up, so they + // move the notches rather than remove them; one octave may lose some level + // while its neighbours gain more. + let mean = o.octaves.iter().sum::() / o.octaves.len() as f64; + assert!(mean > 0.4, "{ms} ms: mean octave change {mean}"); + for (i, v) in o.octaves.iter().enumerate() { + assert!(*v > -2., "{ms} ms, octave {i}: {v}"); + } + } +} + +#[test] +fn an_inverted_highpassed_layer_is_aligned() { + let a = bass(); + let b: Vec = highpass(&a, 300.).iter().map(|v| -v).collect(); + let o = run(&a, &b); + show("inverted hp300", &o); + assert!(o.ideal - o.after < 0.3); +} + +#[test] +fn unrelated_layers_are_left_alone() { + let a = bass(); + let b = noise(a.len(), 7); + let o = run(&a, &b); + show("unrelated", &o); + assert!(o.accepted.is_none()); + assert_eq!(o.status, Status::Loose); +} + +#[test] +fn a_late_main_layer_is_reported_and_not_made_worse() { + let a = bass(); + let b = delay(&a, -14); + let o = run(&a, &b); + show("ahead", &o); + assert_eq!(o.status, Status::Late); + assert!((o.lag_ms - 14. / 48.).abs() < 0.03, "{}", o.lag_ms); + assert!(o.after > o.before - 0.05); +} + +#[test] +fn one_fit_is_fast() { + let a = bass(); + let b = delay(&a, 48); + let mut analyzer = Analyzer::new(RATE, Fit::NONE); + analyzer.set_settings(Settings { + mode: Mode::Learn, + ..Settings::default() + }); + for (x, y) in a.chunks(256).zip(b.chunks(256)) { + analyzer.push(x, y); + } + let start = std::time::Instant::now(); + analyzer.analyse(Some(0.)); + let elapsed = start.elapsed(); + eprintln!("fit took {elapsed:?}"); + let limit = if cfg!(debug_assertions) { 1000 } else { 50 }; + assert!(elapsed.as_millis() < limit, "{elapsed:?}"); +} diff --git a/plugins/weld/core/tests/realtime.rs b/plugins/weld/core/tests/realtime.rs new file mode 100644 index 0000000..759422f --- /dev/null +++ b/plugins/weld/core/tests/realtime.rs @@ -0,0 +1,70 @@ +use std::{ + alloc::{GlobalAlloc, Layout, System}, + cell::Cell, +}; +use weld_core::{Engine, Fit, Listen, Section, Settings}; + +struct CheckedAllocator; +thread_local! { + static CHECK:Cell=const {Cell::new(false)}; + static OPERATIONS:Cell=const {Cell::new(0)}; +} +fn record() { + if CHECK.try_with(Cell::get).unwrap_or(false) { + let _ = OPERATIONS.try_with(|n| n.set(n.get() + 1)); + } +} +unsafe impl GlobalAlloc for CheckedAllocator { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + record(); + unsafe { System.alloc(layout) } + } + unsafe fn dealloc(&self, pointer: *mut u8, layout: Layout) { + record(); + unsafe { System.dealloc(pointer, layout) } + } + unsafe fn realloc(&self, pointer: *mut u8, layout: Layout, size: usize) -> *mut u8 { + record(); + unsafe { System.realloc(pointer, layout, size) } + } +} +#[global_allocator] +static ALLOCATOR: CheckedAllocator = CheckedAllocator; + +#[test] +fn processing_fits_crossfades_and_a_full_queue_do_not_allocate_or_free() { + let mut engine = Engine::new(48000., 2).unwrap(); + let mut settings = Settings::default(); + engine.set_settings(settings).unwrap(); + let link = engine.link(); + let fits: Vec = (0..4) + .map(|k| Fit { + flip: k % 2 == 1, + count: k, + sections: [Section { + fc: 200. * (k + 1) as f32, + q: 0.7, + }; 4], + }) + .collect(); + CHECK.with(|v| v.set(true)); + // Nothing takes blocks, so the queue fills up and later blocks are dropped. + for i in 0..96000 { + if i % 4800 == 0 { + settings.output = ((i / 4800) % 5) as f32 - 2.; + settings.listen = [Listen::Layer, Listen::Sum, Listen::SumDry][(i / 4800) % 3]; + engine.set_settings(settings).unwrap(); + link.push_fit(fits[(i / 4800) % 4]); + } + let t = i as f32 / 48000.; + let x = (std::f32::consts::TAU * 55. * t).sin() * 0.5; + std::hint::black_box(engine.process_sample([x, -x], [x * 0.5, x])); + if i % 1600 == 0 { + std::hint::black_box(engine.take_analyzer()); + } + } + engine.reset(); + CHECK.with(|v| v.set(false)); + assert_eq!(OPERATIONS.with(Cell::get), 0); + assert!(link.dropped() > 0); +} diff --git a/plugins/weld/plugin/Cargo.lock b/plugins/weld/plugin/Cargo.lock index 6f45f9c..d9d67e3 100644 --- a/plugins/weld/plugin/Cargo.lock +++ b/plugins/weld/plugin/Cargo.lock @@ -1074,12 +1074,39 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + [[package]] name = "num-conv" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" +[[package]] +name = "num-integer" +version = "0.1.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "num_enum" version = "0.7.6" @@ -1454,6 +1481,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a6394b9e965e73d0a289ee54f589087e2c676aedf60885baf52c76b771e4958" +[[package]] +name = "primal-check" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc0d895b311e3af9902528fbb8f928688abbd95872819320517cc24ca6b2bd08" +dependencies = [ + "num-integer", +] + [[package]] name = "proc-macro-crate" version = "3.5.0" @@ -1547,6 +1583,20 @@ dependencies = [ "semver", ] +[[package]] +name = "rustfft" +version = "6.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21db5f9893e91f41798c88680037dba611ca6674703c1a18601b01a72c8adb89" +dependencies = [ + "num-complex", + "num-integer", + "num-traits", + "primal-check", + "strength_reduce", + "transpose", +] + [[package]] name = "rustix" version = "0.38.44" @@ -1695,6 +1745,12 @@ dependencies = [ "serde", ] +[[package]] +name = "strength_reduce" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82" + [[package]] name = "syn" version = "1.0.109" @@ -1864,6 +1920,16 @@ version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +[[package]] +name = "transpose" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad61aed86bc3faea4300c7aee358b4c6d0c8d6ccc36524c96e4c92ccf26e77e" +dependencies = [ + "num-integer", + "strength_reduce", +] + [[package]] name = "ttf-parser" version = "0.25.1" @@ -2005,6 +2071,8 @@ dependencies = [ name = "weld-core" version = "0.1.0" dependencies = [ + "crossbeam-queue", + "rustfft", "serde", ] diff --git a/plugins/weld/plugin/src/lib.rs b/plugins/weld/plugin/src/lib.rs index 073194d..ea8e886 100644 --- a/plugins/weld/plugin/src/lib.rs +++ b/plugins/weld/plugin/src/lib.rs @@ -1,20 +1,23 @@ -//! CLAP audio effect with a sidechain input. All DSP state is owned by the processing thread. +//! CLAP audio effect with a sidechain input. The audio thread owns the engine; a worker +//! thread, started in `initialize`, measures and fits. mod params; use crossbeam_queue::ArrayQueue; use nih_plug::prelude::*; use nih_plug_egui::{create_egui_editor, egui}; -use params::WeldParams; +use params::{WeldParams, GESTURES}; use std::sync::Arc; -use weld_core::{AnalyzerFrame, Engine}; +use weld_core::{AnalyzerFrame, Engine, Report, Worker}; struct EditorData { ui: weld_ui::State, - gestures: [bool; 1], + gestures: [bool; GESTURES], } pub struct Weld { params: Arc, analyzer: Arc>, + reports: Arc>, engine: Option, + worker: Option, publish_count: usize, publish_interval: usize, } @@ -23,7 +26,9 @@ impl Default for Weld { Self { params: Arc::new(WeldParams::default()), analyzer: Arc::new(ArrayQueue::new(2)), + reports: Arc::new(ArrayQueue::new(2)), engine: None, + worker: None, publish_count: 0, publish_interval: 1600, } @@ -58,17 +63,21 @@ impl Plugin for Weld { fn editor(&mut self, _: AsyncExecutor) -> Option> { let params = self.params.clone(); let analyzer = self.analyzer.clone(); + let reports = self.reports.clone(); create_egui_editor( params.editor.clone(), EditorData { ui: weld_ui::State::default(), - gestures: [false; 1], + gestures: [false; GESTURES], }, |ctx, _| weld_ui::style(ctx), move |ctx, setter, data| { while let Some(frame) = analyzer.pop() { data.ui.accept_frame(frame); } + while let Some(report) = reports.pop() { + data.ui.accept_report(report); + } let before = params.settings(); let mut edited = before; nih_plug_egui::resizable_window::ResizableWindow::new("weld-editor") @@ -106,11 +115,37 @@ impl Plugin for Weld { if engine.set_settings(self.params.settings()).is_err() { return false; } + // A restored project starts with its saved alignment, without a crossfade. + let fit = self.params.stored_fit().sanitized(config.sample_rate); + engine.set_fit(fit); + if let Some(mut old) = self.worker.take() { + old.stop(); + } + let store = self.params.fit.clone(); + let Ok(worker) = Worker::spawn( + engine.link(), + config.sample_rate, + fit, + self.reports.clone(), + move |fit| { + if let Ok(mut stored) = store.write() { + *stored = fit; + } + }, + ) else { + return false; + }; + self.worker = Some(worker); context.set_latency_samples(engine.latency_samples()); self.publish_interval = (config.sample_rate / 30.) as usize; self.engine = Some(engine); true } + fn deactivate(&mut self) { + if let Some(mut worker) = self.worker.take() { + worker.stop(); + } + } fn reset(&mut self) { if let Some(e) = self.engine.as_mut() { e.reset(); diff --git a/plugins/weld/plugin/src/params.rs b/plugins/weld/plugin/src/params.rs index 899c3d1..ed83a02 100644 --- a/plugins/weld/plugin/src/params.rs +++ b/plugins/weld/plugin/src/params.rs @@ -1,8 +1,17 @@ use nih_plug::prelude::*; use nih_plug_egui::EguiState; -use std::sync::Arc; -use weld_core::{Listen, Settings}; +use std::sync::{Arc, RwLock}; +use weld_core::{Fit, Listen, Mode, Settings, MAX_SECTIONS}; +#[derive(Enum, Debug, PartialEq, Eq, Clone, Copy)] +pub enum ModeParam { + #[id = "follow"] + Follow, + #[id = "learn"] + Learn, + #[id = "hold"] + Hold, +} #[derive(Enum, Debug, PartialEq, Eq, Clone, Copy)] pub enum ListenParam { #[id = "layer"] @@ -14,12 +23,27 @@ pub enum ListenParam { SumDry, } /// Continuous controls, in the order of the gesture slots. -const FLOATS: usize = 1; +const FLOATS: usize = 5; #[derive(Params)] pub struct WeldParams { #[persist = "editor"] pub editor: Arc, + /// The applied alignment, so a project reopens with it. + #[persist = "weld-fit"] + pub fit: Arc>, + #[id = "mode"] + pub mode: EnumParam, + #[id = "response"] + pub response: FloatParam, + #[id = "low"] + pub low: FloatParam, + #[id = "high"] + pub high: FloatParam, + #[id = "sections"] + pub sections: IntParam, + #[id = "gate"] + pub gate: FloatParam, #[id = "listen"] pub listen: EnumParam, #[id = "output"] @@ -27,11 +51,68 @@ pub struct WeldParams { #[id = "bypass"] pub bypass: BoolParam, } +fn hz(value: f32) -> String { + if value >= 1000. { + format!("{:.2} kHz", value / 1000.) + } else { + format!("{value:.0} Hz") + } +} impl Default for WeldParams { fn default() -> Self { let d = Settings::default(); Self { editor: EguiState::from_size(weld_ui::WIDTH, weld_ui::HEIGHT), + fit: Arc::new(RwLock::new(Fit::NONE)), + mode: EnumParam::new("Mode", ModeParam::Follow), + response: FloatParam::new( + "Response", + d.response, + FloatRange::Skewed { + min: 0.3, + max: 5., + factor: FloatRange::skew_factor(-1.), + }, + ) + .with_unit(" s") + .with_value_to_string(Arc::new(|v| format!("{v:.2}"))), + low: FloatParam::new( + "Low", + d.low, + FloatRange::Skewed { + min: 30., + max: 400., + factor: FloatRange::skew_factor(-1.5), + }, + ) + .with_value_to_string(Arc::new(hz)), + high: FloatParam::new( + "High", + d.high, + FloatRange::Skewed { + min: 1000., + max: 12000., + factor: FloatRange::skew_factor(-1.), + }, + ) + .with_value_to_string(Arc::new(hz)), + sections: IntParam::new( + "Sections", + d.sections as i32, + IntRange::Linear { + min: 1, + max: MAX_SECTIONS as i32, + }, + ), + gate: FloatParam::new( + "Gate", + d.gate, + FloatRange::Linear { + min: -70., + max: -30., + }, + ) + .with_unit(" dB"), listen: EnumParam::new("Listen", ListenParam::Layer), output: FloatParam::new( "Output", @@ -46,9 +127,33 @@ impl Default for WeldParams { } } } +fn mode_param(mode: Mode) -> ModeParam { + match mode { + Mode::Follow => ModeParam::Follow, + Mode::Learn => ModeParam::Learn, + Mode::Hold => ModeParam::Hold, + } +} +fn listen_param(listen: Listen) -> ListenParam { + match listen { + Listen::Layer => ListenParam::Layer, + Listen::Sum => ListenParam::Sum, + Listen::SumDry => ListenParam::SumDry, + } +} impl WeldParams { pub fn settings(&self) -> Settings { Settings { + mode: match self.mode.value() { + ModeParam::Follow => Mode::Follow, + ModeParam::Learn => Mode::Learn, + ModeParam::Hold => Mode::Hold, + }, + response: self.response.value(), + low: self.low.value(), + high: self.high.value(), + sections: self.sections.value().clamp(1, MAX_SECTIONS as i32) as usize, + gate: self.gate.value(), listen: match self.listen.value() { ListenParam::Layer => Listen::Layer, ListenParam::Sum => Listen::Sum, @@ -58,8 +163,17 @@ impl WeldParams { bypass: self.bypass.value(), } } + pub fn stored_fit(&self) -> Fit { + self.fit.read().map(|f| *f).unwrap_or(Fit::NONE) + } fn floats(&self) -> [&FloatParam; FLOATS] { - [&self.output] + [ + &self.response, + &self.low, + &self.high, + &self.gate, + &self.output, + ] } /// Pushes every changed editor value to the host. Continuous controls hold one gesture /// while the pointer is down; switches are set in a single gesture. @@ -71,7 +185,13 @@ impl WeldParams { dragging: bool, open: &mut [bool; FLOATS], ) { - let values = [(old.output, new.output)]; + let values = [ + (old.response, new.response), + (old.low, new.low), + (old.high, new.high), + (old.gate, new.gate), + (old.output, new.output), + ]; for (i, (p, (a, b))) in self.floats().into_iter().zip(values).enumerate() { if a != b { if !open[i] { @@ -85,20 +205,23 @@ impl WeldParams { open[i] = false; } } + fn switch(setter: &ParamSetter, p: &P, v: P::Plain) { + setter.begin_set_parameter(p); + setter.set_parameter(p, v); + setter.end_set_parameter(p); + } if new.bypass != old.bypass { - setter.begin_set_parameter(&self.bypass); - setter.set_parameter(&self.bypass, new.bypass); - setter.end_set_parameter(&self.bypass); + switch(setter, &self.bypass, new.bypass); + } + if new.mode != old.mode { + switch(setter, &self.mode, mode_param(new.mode)); } if new.listen != old.listen { - let v = match new.listen { - Listen::Layer => ListenParam::Layer, - Listen::Sum => ListenParam::Sum, - Listen::SumDry => ListenParam::SumDry, - }; - setter.begin_set_parameter(&self.listen); - setter.set_parameter(&self.listen, v); - setter.end_set_parameter(&self.listen); + switch(setter, &self.listen, listen_param(new.listen)); + } + if new.sections != old.sections { + switch(setter, &self.sections, new.sections as i32); } } } +pub const GESTURES: usize = FLOATS; diff --git a/plugins/weld/plugin/tests/host.rs b/plugins/weld/plugin/tests/host.rs index 214d2ec..27f7dfe 100644 --- a/plugins/weld/plugin/tests/host.rs +++ b/plugins/weld/plugin/tests/host.rs @@ -111,6 +111,20 @@ fn sidechain_port_state_latency_and_audio_lifecycle() { envelope .params .insert("listen".into(), ParamValue::String("sum".into())); + envelope + .params + .insert("mode".into(), ParamValue::String("hold".into())); + envelope + .params + .insert("sections".into(), ParamValue::I32(2)); + // A saved alignment that flips polarity. + let flipped = weld_core::Fit { + flip: true, + ..weld_core::Fit::NONE + }; + envelope + .fields + .insert("weld-fit".into(), serde_json::to_string(&flipped).unwrap()); let encoded = serde_json::to_vec(&envelope).unwrap(); let mut framed = (encoded.len() as u64).to_le_bytes().to_vec(); framed.extend_from_slice(&encoded); @@ -126,13 +140,16 @@ fn sidechain_port_state_latency_and_audio_lifecycle() { serde_json::to_value(&restored.params).unwrap(), serde_json::to_value(&envelope.params).unwrap() ); - // Listen = Sum: a sidechain equal to the main input doubles the output (+6 dB). - // A silent sidechain leaves the main input alone. CLAP hosts pass every port, so - // the test always passes both (nih_plug reads one port past a short count). + let saved: weld_core::Fit = serde_json::from_str(&restored.fields["weld-fit"]).unwrap(); + assert_eq!(saved, flipped); + // Listen = Sum with the restored flip: a sidechain equal to the main input cancels + // it from the first sample on. A silent sidechain leaves the level alone. CLAP hosts + // pass every port, so the test always passes both (nih_plug reads one port past a + // short count). for (rate, with_side, expected_db) in [ - (44100., true, 6.02), - (48000., false, 0.), - (96000., true, 6.02), + (44100., true, None), + (48000., false, Some(0.)), + (96000., true, None), ] { assert!(p.activate.unwrap()(plugin, rate, 1, 512)); assert_eq!((*latency).get.unwrap()(plugin), 0); @@ -213,10 +230,10 @@ fn sidechain_port_state_latency_and_audio_lifecycle() { position += frames; } let ratio_db = 10. * (energy_out / energy_in).log10(); - assert!( - (ratio_db - expected_db).abs() < 0.2, - "{rate} {with_side} {ratio_db}" - ); + match expected_db { + Some(db) => assert!((ratio_db - db).abs() < 0.2, "{rate} {with_side} {ratio_db}"), + None => assert!(ratio_db < -60., "{rate} {with_side} {ratio_db}"), + } p.stop_processing.unwrap()(plugin); p.deactivate.unwrap()(plugin); } diff --git a/plugins/weld/ui/src/lib.rs b/plugins/weld/ui/src/lib.rs index 271c527..e943c5a 100644 --- a/plugins/weld/ui/src/lib.rs +++ b/plugins/weld/ui/src/lib.rs @@ -1,14 +1,23 @@ //! Weld editor, shared by the CLAP wrapper and the headless renderer. use common_ui::*; -use egui::{self, FontId, RichText}; -use weld_core::{AnalyzerFrame, Listen, Settings}; +use egui::{self, Color32, FontId, Pos2, Rect, RichText, Sense, Stroke, Vec2}; +use weld_core::{ + band_edge, curve_frequency, AnalyzerFrame, Listen, Mode, Report, Settings, Status, CURVE, + DISPLAY_HIGH, DISPLAY_LOW, MAX_SECTIONS, +}; pub use common_ui::style; pub const WIDTH: u32 = 1040; pub const HEIGHT: u32 = 650; +const GRID: Color32 = Color32::from_rgb(24, 26, 30); +const LOSS: Color32 = Color32::from_rgb(170, 40, 52); +const LOSS_FLOOR: f32 = -24.; +const TOP: f32 = 318.; + pub struct State { pub analyzer: AnalyzerFrame, + pub report: Report, /// True while a pointer button is held, so knob drags become one host gesture. pub gesture_active: bool, pub help: String, @@ -17,6 +26,7 @@ impl Default for State { fn default() -> Self { Self { analyzer: AnalyzerFrame::default(), + report: Report::default(), gesture_active: false, help: DEFAULT_HELP.into(), } @@ -33,12 +43,43 @@ impl State { self.analyzer.side_db = frame.side_db.max(old.side_db - 3.); self.analyzer.side_peak_db = frame.side_peak_db.max(old.side_peak_db - 3.); } + pub fn accept_report(&mut self, report: Report) { + self.report = report; + } } fn hint(r: &egui::Response, state: &mut State, text: &str) { if r.hovered() || r.has_focus() { state.help = text.into(); } } +#[allow(clippy::too_many_arguments)] +fn knob( + ui: &mut egui::Ui, + state: &mut State, + name: &str, + value: &mut f32, + range: std::ops::RangeInclusive, + default: f32, + unit: &str, + help: &str, +) { + ui.vertical_centered(|ui| { + let r = rotary(ui, name, value, range, default, unit, 58., ACCENT); + hint(&r, state, help); + }); +} +fn big(text: impl Into, size: f32, color: Color32) -> RichText { + RichText::new(text) + .font(FontId::new(size, egui::FontFamily::Name("medium".into()))) + .color(color) +} +fn hz(f: f32) -> String { + if f >= 1000. { + format!("{:.1} kHz", f / 1000.) + } else { + format!("{f:.0} Hz") + } +} pub fn show(ui: &mut egui::Ui, settings: &mut Settings, state: &mut State) { backdrop(ui); @@ -52,64 +93,446 @@ pub fn show(ui: &mut egui::Ui, settings: &mut Settings, state: &mut State) { if r.clicked() { settings.bypass = !settings.bypass; } - }); - ui.add_space(6.); - let frame = state.analyzer; - panel(ui, |ui| { - ui.set_width(ui.available_width()); - ui.set_min_height(380.); - ui.vertical(|ui| { - caption(ui, "SIDECHAIN"); - let (text, color) = if frame.side_db > -90. { - (format!("sidechain: {:.0} dBFS", frame.side_db), TEXT) - } else if frame.side_seen { - ("sidechain: silent".to_string(), MUTED) - } else { - ("no sidechain signal".to_string(), DIM) - }; - ui.label( - RichText::new(text) - .font(FontId::new(40., egui::FontFamily::Name("medium".into()))) - .color(color), - ); - ui.add_space(12.); - meter(ui, "MAIN", frame.main_db, STEEL, 520.); - meter(ui, "SIDECHAIN", frame.side_db, ACCENT, 520.); - meter(ui, "SIDE PEAK", frame.side_peak_db, FOCUS, 520.); - ui.add_space(18.); - ui.label( - RichText::new( - "FL Studio: on the other layer's mixer track, right-click the route arrow \ - to this track and choose \"Sidechain to this track\". Then open Weld's \ - wrapper settings and set the sidechain input to that track.", - ) - .size(13.) - .color(MUTED), - ); - }); - }); - ui.add_space(6.); - panel(ui, |ui| { - ui.set_width(ui.available_width()); - ui.horizontal(|ui| { - caption(ui, "LISTEN"); - for (listen, label, help) in [ - (Listen::Layer, "LAYER", "Output the main layer only. Use this in the mix."), - (Listen::Sum, "SUM", "Output main + sidechain layer. Mute the other layer's own send while listening."), - (Listen::SumDry, "SUM DRY", "Output the unprocessed main + sidechain layer, to compare with SUM."), - ] { - let r = ui.selectable_label(settings.listen == listen, RichText::new(label).size(13.)); - if r.clicked() { - settings.listen = listen; - } - hint(&r, state, help); + ui.separator(); + for (listen, label, help) in [ + (Listen::SumDry, "SUM DRY", "Output this layer unprocessed plus the sidechain layer, to compare with SUM."), + (Listen::Sum, "SUM", "Output this layer aligned plus the sidechain layer. Mute the other layer's own send while you listen."), + (Listen::Layer, "LAYER", "Output this layer only, aligned. Use this in the mix."), + ] { + let r = ui.selectable_label(settings.listen == listen, RichText::new(label).size(13.)); + if r.clicked() { + settings.listen = listen; } - ui.add_space(40.); - let d = Settings::default(); - let r = rotary(ui, "Output", &mut settings.output, -12. ..=12., d.output, " dB", 54., ACCENT); - hint(&r, state, "Output trim."); + hint(&r, state, help); + } + caption(ui, "LISTEN"); + }); + ui.add_space(6.); + ui.horizontal_top(|ui| { + panel(ui, |ui| { + ui.set_width(250.); + ui.set_min_height(TOP); + ui.vertical(|ui| summary(ui, settings, state)); }); + ui.add_space(4.); + panel(ui, |ui| { + ui.set_width(ui.available_width()); + ui.vertical(|ui| { + let report = state.report; + band_plot(ui, &report, 190.); + ui.add_space(4.); + phase_plot(ui, &report, TOP - 194.); + }); + }); + }); + ui.add_space(6.); + panel(ui, |ui| { + ui.set_width(ui.available_width()); + ui.vertical(|ui| controls(ui, settings, state)); }); ui.separator(); ui.label(RichText::new(&state.help).color(MUTED)); } + +fn status_text(report: &Report, frame: &AnalyzerFrame, mode: Mode) -> (String, Color32) { + if !frame.side_seen && frame.side_db < -90. { + return ( + "No sidechain signal. Route the other layer to Weld's sidechain input.".into(), + DIM, + ); + } + match report.status { + Status::NoSidechain => ("The sidechain is silent.".into(), MUTED), + Status::Waiting => ("Listening…".into(), MUTED), + Status::Quiet => ("One layer is under the gate.".into(), MUTED), + Status::Loose => ( + "Layers unrelated: free-running oscillators or different notes. Enable retrigger.".into(), + FOCUS, + ), + Status::Late => ( + format!( + "This layer is {:.2} ms late. Weld cannot move it earlier: put Weld on the other layer.", + report.lag_ms + ), + ACCENT, + ), + Status::Aligned => match mode { + Mode::Follow => ("Following.".into(), STEEL), + Mode::Learn => (format!("Learning… {} frames", report.frames), ACCENT), + Mode::Hold => ("Holding the alignment.".into(), STEEL), + }, + } +} + +fn summary(ui: &mut egui::Ui, settings: &Settings, state: &mut State) { + let report = state.report; + let frame = state.analyzer; + caption(ui, "RECOVERED"); + let measured = matches!( + report.status, + Status::Aligned | Status::Late | Status::Loose + ); + ui.label(big( + if measured { + format!("{:+.1} dB", report.recovered_db) + } else { + "—".into() + }, + 44., + if measured { TEXT } else { DIM }, + )); + ui.label( + RichText::new(if measured { + format!( + "Cancellation cost {:.1} dB, {}–{}", + report.cost_db, + hz(report.low), + hz(report.high) + ) + } else { + "Average per band, between Low and High".into() + }) + .size(12.) + .color(MUTED), + ); + ui.add_space(10.); + caption(ui, "ALIGNMENT"); + let fit = report.fit; + ui.label( + RichText::new(if fit.flip { + "Polarity flipped" + } else { + "Polarity normal" + }) + .size(14.) + .color(if fit.flip { ACCENT } else { STEEL }), + ); + if fit.count == 0 { + ui.label(RichText::new("No allpass sections").size(13.).color(MUTED)); + } + for s in fit.active() { + ui.label( + RichText::new(format!("{:>9} Q {:.2}", hz(s.fc), s.q)) + .monospace() + .size(13.) + .color(STEEL), + ); + } + ui.add_space(10.); + let (text, color) = status_text(&report, &frame, settings.mode); + ui.label(RichText::new(text).size(13.).color(color)); + ui.add_space(10.); + meter(ui, "MAIN", frame.main_db, STEEL, 150.); + meter(ui, "SIDECHAIN", frame.side_db, ACCENT, 150.); + if frame.dropped > 0 { + ui.label( + RichText::new(format!("{} blocks skipped (CPU busy)", frame.dropped)) + .size(11.) + .color(DIM), + ); + } +} + +fn freq_x(rect: Rect, f: f32) -> f32 { + let t = (f / DISPLAY_LOW).ln() / (DISPLAY_HIGH / DISPLAY_LOW).ln(); + rect.left() + rect.width() * t.clamp(0., 1.) +} +fn frequency_grid(p: &egui::Painter, plot: Rect, labels: bool) { + for f in [50f32, 100., 200., 500., 1000., 2000., 5000., 10000.] { + let x = freq_x(plot, f); + p.line_segment( + [Pos2::new(x, plot.top()), Pos2::new(x, plot.bottom())], + Stroke::new(1.0_f32, GRID), + ); + if labels { + p.text( + Pos2::new(x, plot.bottom() + 3.), + egui::Align2::CENTER_TOP, + if f >= 1000. { + format!("{}k", f / 1000.) + } else { + format!("{f}") + }, + FontId::proportional(10.), + DIM, + ); + } + } +} +fn fit_range(p: &egui::Painter, plot: Rect, report: &Report) { + let (a, b) = (freq_x(plot, report.low), freq_x(plot, report.high)); + p.rect_filled( + Rect::from_min_max(Pos2::new(a, plot.top()), Pos2::new(b, plot.bottom())), + 0., + Color32::from_rgba_unmultiplied(255, 255, 255, 5), + ); +} + +/// Per band: what the unprocessed sum loses against a perfect alignment (red), and the +/// part of it Weld wins back (accent). +fn band_plot(ui: &mut egui::Ui, report: &Report, height: f32) { + let (outer, _) = + ui.allocate_exact_size(Vec2::new(ui.available_width(), height), Sense::hover()); + let p = ui.painter_at(outer); + p.rect_filled(outer, 2., SCREEN); + let plot = Rect::from_min_max( + outer.left_top() + Vec2::new(34., 22.), + outer.right_bottom() - Vec2::new(10., 16.), + ); + p.text( + outer.left_top() + Vec2::new(8., 5.), + egui::Align2::LEFT_TOP, + "SUM LEVEL AGAINST A PERFECT ALIGNMENT", + FontId::proportional(10.), + MUTED, + ); + let legend = [(LOSS, "lost to cancellation"), (ACCENT, "won back by Weld")]; + let mut x = outer.right() - 10.; + for (color, label) in legend.iter().rev() { + let galley = p.layout_no_wrap(label.to_string(), FontId::proportional(10.), MUTED); + x -= galley.size().x; + p.galley(Pos2::new(x, outer.top() + 5.), galley, MUTED); + x -= 14.; + p.rect_filled( + Rect::from_min_size(Pos2::new(x, outer.top() + 7.), Vec2::splat(8.)), + 1., + *color, + ); + x -= 14.; + } + fit_range(&p, plot, report); + let y = |db: f32| plot.top() + plot.height() * (db / LOSS_FLOOR).clamp(0., 1.); + for db in [0., -6., -12., -18., -24.] { + let yy = y(db); + p.line_segment( + [Pos2::new(plot.left(), yy), Pos2::new(plot.right(), yy)], + Stroke::new(1.0_f32, if db == 0. { EDGE } else { GRID }), + ); + p.text( + Pos2::new(outer.left() + 6., yy), + egui::Align2::LEFT_CENTER, + if db == 0. { + "0".into() + } else { + format!("{db:.0}") + }, + FontId::proportional(10.), + if db == 0. { MUTED } else { DIM }, + ); + } + frequency_grid(&p, plot, true); + for (i, band) in report.bands.iter().enumerate() { + if !band.valid { + continue; + } + let (a, b) = ( + freq_x(plot, band_edge(i)) + 2., + freq_x(plot, band_edge(i + 1)) - 2., + ); + let top = y(0.); + let before = y(band.before); + let after = y(band.after.max(band.before)); + let loose = band.coherence < 0.3; + // The whole loss in red, then the recovered part over it in accent. + p.rect_filled( + Rect::from_min_max(Pos2::new(a, top), Pos2::new(b, before.max(top + 1.))), + 1., + if loose { + LOSS.gamma_multiply(0.35) + } else { + LOSS + }, + ); + if after > top + 0.5 { + p.rect_filled( + Rect::from_min_max(Pos2::new(a, after), Pos2::new(b, before)), + 0., + if loose { + ACCENT.gamma_multiply(0.35) + } else { + ACCENT + }, + ); + } else if before > top + 0.5 { + p.rect_filled( + Rect::from_min_max(Pos2::new(a, top), Pos2::new(b, before)), + 1., + if loose { + ACCENT.gamma_multiply(0.35) + } else { + ACCENT + }, + ); + } + p.line_segment( + [Pos2::new(a, after), Pos2::new(b, after)], + Stroke::new(1.5_f32, TEXT), + ); + if loose { + // Hatching marks bands where the layers do not hold a stable phase. + let mut x = a; + while x < b { + p.line_segment( + [ + Pos2::new(x, plot.bottom()), + Pos2::new((x + 8.).min(b), plot.bottom() - 8.), + ], + Stroke::new(1.0_f32, DIM), + ); + x += 5.; + } + p.text( + Pos2::new((a + b) / 2., plot.bottom() - 12.), + egui::Align2::CENTER_BOTTOM, + "loose", + FontId::proportional(10.), + DIM, + ); + } + } +} + +/// Phase of this layer against the sidechain layer, before and after, plus the chain's own +/// phase. +fn phase_plot(ui: &mut egui::Ui, report: &Report, height: f32) { + let (outer, _) = + ui.allocate_exact_size(Vec2::new(ui.available_width(), height), Sense::hover()); + let p = ui.painter_at(outer); + p.rect_filled(outer, 2., SCREEN); + let plot = Rect::from_min_max( + outer.left_top() + Vec2::new(34., 20.), + outer.right_bottom() - Vec2::new(10., 6.), + ); + p.text( + outer.left_top() + Vec2::new(8., 4.), + egui::Align2::LEFT_TOP, + "PHASE AGAINST THE SIDECHAIN LAYER", + FontId::proportional(10.), + MUTED, + ); + let legend = [(DIM, "before"), (ACCENT, "after"), (FOCUS, "Weld's chain")]; + let mut x = outer.right() - 10.; + for (color, label) in legend.iter().rev() { + let galley = p.layout_no_wrap(label.to_string(), FontId::proportional(10.), MUTED); + x -= galley.size().x; + p.galley(Pos2::new(x, outer.top() + 4.), galley, MUTED); + x -= 16.; + p.line_segment( + [ + Pos2::new(x, outer.top() + 10.), + Pos2::new(x + 10., outer.top() + 10.), + ], + Stroke::new(2.0_f32, *color), + ); + x -= 12.; + } + fit_range(&p, plot, report); + let y = |deg: f32| plot.center().y - plot.height() / 2. * (deg / 180.).clamp(-1., 1.); + for deg in [-180f32, -90., 0., 90., 180.] { + let yy = y(deg); + p.line_segment( + [Pos2::new(plot.left(), yy), Pos2::new(plot.right(), yy)], + Stroke::new(1.0_f32, if deg == 0. { EDGE } else { GRID }), + ); + if deg.abs() < 180. { + p.text( + Pos2::new(outer.left() + 6., yy), + egui::Align2::LEFT_CENTER, + format!("{deg:.0}°"), + FontId::proportional(10.), + if deg == 0. { MUTED } else { DIM }, + ); + } + } + frequency_grid(&p, plot, false); + let curve = |pick: fn(&weld_core::CurvePoint) -> f32, + color: Color32, + width: f32, + fade: bool| { + let mut previous: Option<(Pos2, f32)> = None; + for i in 0..CURVE { + let c = report.curve[i]; + if !c.valid { + previous = None; + continue; + } + let v = pick(&c); + let pos = Pos2::new(freq_x(plot, curve_frequency(i)), y(v)); + if let Some((last, last_v)) = previous { + // Do not draw the jump where the phase wraps. + if (v - last_v).abs() < 180. { + let alpha = if fade { 0.25 + 0.75 * c.coherence } else { 1. }; + p.line_segment([last, pos], Stroke::new(width, color.gamma_multiply(alpha))); + } + } + previous = Some((pos, v)); + } + }; + curve(|c| c.chain, FOCUS, 1.5, false); + curve(|c| c.before, MUTED, 1.5, true); + curve(|c| c.after, ACCENT, 2., true); +} + +fn controls(ui: &mut egui::Ui, settings: &mut Settings, state: &mut State) { + let d = Settings::default(); + ui.horizontal(|ui| { + caption(ui, "MODE"); + for (mode, label, help) in [ + (Mode::Follow, "FOLLOW", "Keep measuring and refit when a clearly better alignment shows up."), + (Mode::Learn, "LEARN", "Measure from now with equal weight, for example over one drop. Switch to HOLD to keep the result."), + (Mode::Hold, "HOLD", "Keep the current alignment. The display keeps measuring. The alignment is saved with the project."), + ] { + let r = ui.selectable_label(settings.mode == mode, RichText::new(label).size(14.)); + if r.clicked() { + settings.mode = if mode == Mode::Learn && settings.mode == Mode::Learn { + Mode::Hold + } else { + mode + }; + } + hint(&r, state, help); + } + ui.add_space(30.); + caption(ui, "SECTIONS"); + let minus = ui.button(RichText::new("−").size(14.)); + if minus.clicked() && settings.sections > 1 { + settings.sections -= 1; + } + ui.label(RichText::new(format!("{}", settings.sections)).monospace().size(14.).color(TEXT)); + let plus = ui.button(RichText::new("+").size(14.)); + if plus.clicked() && settings.sections < MAX_SECTIONS { + settings.sections += 1; + } + let help = "Most allpass sections the fit may use. Fewer is gentler; Weld uses fewer when they are enough."; + hint(&minus, state, help); + hint(&plus, state, help); + }); + ui.add_space(4.); + ui.columns(6, |c| { + knob(&mut c[0], state, "Response", &mut settings.response, 0.3..=5., d.response, " s", + "Averaging time in FOLLOW. Longer is steadier; shorter follows switch-ups faster."); + knob(&mut c[1], state, "Low", &mut settings.low, 30. ..=400., d.low, " Hz", + "Lowest frequency the fit listens to. Leave the sub to Anvil."); + knob(&mut c[2], state, "High", &mut settings.high, 1000. ..=12000., d.high, " Hz", + "Highest frequency the fit listens to."); + knob(&mut c[3], state, "Gate", &mut settings.gate, -70. ..=-30., d.gate, " dB", + "Moments where either layer is quieter than this are not measured."); + knob(&mut c[4], state, "Output", &mut settings.output, -12. ..=12., d.output, " dB", + "Output trim."); + let report = state.report; + c[5].vertical(|ui| { + caption(ui, "TIME OFFSET"); + let clear = report.lag_strength > 0.3; + ui.label(big( + if clear { format!("{:+.2} ms", report.lag_ms) } else { "—".into() }, + 22., + if clear { TEXT } else { DIM }, + )); + ui.label( + RichText::new("Positive: this layer is late. Weld can only delay it, so a late layer needs Weld on the other one.") + .size(11.) + .color(DIM), + ); + }); + }); +} diff --git a/tools/editor-check/Cargo.lock b/tools/editor-check/Cargo.lock index 35838ae..d719d79 100644 --- a/tools/editor-check/Cargo.lock +++ b/tools/editor-check/Cargo.lock @@ -648,6 +648,8 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" name = "weld-core" version = "0.1.0" dependencies = [ + "crossbeam-queue", + "rustfft", "serde", ] diff --git a/tools/editor-check/src/main.rs b/tools/editor-check/src/main.rs index cbc672d..f735f97 100644 --- a/tools/editor-check/src/main.rs +++ b/tools/editor-check/src/main.rs @@ -10,7 +10,11 @@ fn main() { .find(|a| a[0] == "--plugin") .map(|a| a[1].as_str()) .unwrap_or("rivet"); - if !["rivet", "patina", "anvil", "crucible", "lathe", "temper", "weld"].contains(&plugin) { + if ![ + "rivet", "patina", "anvil", "crucible", "lathe", "temper", "weld", + ] + .contains(&plugin) + { eprintln!("Use --plugin rivet, patina, anvil, crucible, lathe, temper or weld"); std::process::exit(2); } @@ -145,16 +149,41 @@ fn main() { let mut weld_settings = weld_core::Settings::default(); let mut weld_state = weld_ui::State::default(); if weld { - // Two layers of an F1 saw bass; the sidechain layer is polarity-flipped. + // Two layers of an F1 saw bass. The sidechain layer is polarity-flipped and runs + // through an allpass at 400 Hz, as an EQ in its chain would leave it. The real + // analyzer measures them and its fits go back to the engine, as on the worker. let mut engine = weld_core::Engine::new(48000., 2).unwrap(); engine.set_settings(weld_settings).unwrap(); + let link = engine.link(); + let mut analyzer = weld_core::Analyzer::new(48000., weld_core::Fit::NONE); + let mut other = weld_core::Chain::new( + weld_core::Fit { + flip: true, + count: 1, + sections: [weld_core::Section { fc: 400., q: 0.7 }; weld_core::MAX_SECTIONS], + }, + 48000., + ); let mut saw = common_dsp::Saw::new(48000., 43.65); - for i in 0..36000 { + for i in 0..48000 * 4 { let x = (saw.sample() * 1.6).tanh() * 0.4; - engine.process_sample([x, x], [-0.7 * x, -0.7 * x]); + let b = 0.8 * other.process(0, x); + engine.process_sample([x, x], [b, b]); if i % 1600 == 0 { weld_state.accept_frame(engine.take_analyzer()); } + while let Some(settings) = link.pop_settings() { + analyzer.set_settings(settings); + } + while let Some(block) = link.pop_block() { + analyzer.push(&block.a, &block.b); + if let Some(update) = analyzer.poll() { + if let Some(fit) = update.fit { + link.push_fit(fit); + } + weld_state.accept_report(update.report); + } + } } weld_state.accept_frame(engine.take_analyzer()); }