feat(weld): phase alignment for layered bass, release 0.1.0
Check and Windows preview / check (anvil) (push) Successful in 2m0s
Check and Windows preview / check (crucible) (push) Successful in 1m29s
Check and Windows preview / check (lathe) (push) Successful in 1m27s
Check and Windows preview / check (patina) (push) Successful in 1m53s
Check and Windows preview / check (rivet) (push) Successful in 1m29s
Check and Windows preview / check (temper) (push) Successful in 1m29s
Check and Windows preview / check (weld) (push) Successful in 2m38s
Check and Windows preview / previews (push) Successful in 1m28s
Independent plugin release / plan (push) Successful in 8s
Check and Windows preview / windows (push) Successful in 3m14s
Independent plugin release / build (push) Successful in 3m50s
Independent plugin release / publish (push) Successful in 9s

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.
This commit is contained in:
nvrl committed 2026-11-06 08:31:41 +01:00
1 parent 0a0917ac56
commit 9b3ddeaf56
26 files changed
+2898 -144

No files matched your search

+1 -1
View File
@@ -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
+4 -5
View File
@@ -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)
+1 -1
View File
@@ -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
+12 -4
View File
@@ -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.
+100 -22
View File
@@ -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.
Binary file not shown.

After

Width:  |  Height:  |  Size: 258 KiB