voice-matching

Making one voice sit next to another in a documentary or podcast mix: long-term average spectrum (LTAS) EQ matching with opendubstage ltasmatch, bridging a TTS or cloned voice into a location recordin

SKILL.md

GitHub

voice-matching

Dialogue editors match voices with an EQ match (FabFilter Pro-Q "EQ Match", iZotope Ozone / Neutron "Match EQ", RX "Dialogue Match"): compare the long-term average spectrum of a reference voice with the voice that sounds different and apply a smoothed, gain-limited difference curve. OpenDubStage does this offline: opendubstage ltasmatch (opendubstage.match.ltasmatch), numpy plus an ffmpeg binary.

1. Decide the reference, then match down

  • The reference is the film's main voice (the interview, the field recording, the archive), not the cleanest source. Match the clean or synthetic voice down to the recording: band-limit it, give it the room, lay the same floor under it. Lifting the reference up to the clean voice destroys what makes it a document (NPR §5.8; Podcast Engineering School on phone matching: "reduce the frequency range of someone recording directly to more closely match the telephone caller").
  • Match per line, not per source, when sources vary within themselves: Phil Heywood EQ'd each veteran's line with narrow cuts "so one guy sounded the same as the next" (They Shall Not Grow Old, Mix). Practically: one chain per source class, then a per-passage loudness match to the anchor and a per-passage 2-band tilt toward the median spectrum.
  • Sync sound from a camera stays "the other room": it is the world, the narration is the storyteller (Murch's "colour"). Match it at a small amount (0.25) and keep both at the same dialogue loudness (±2 LU short-term) so the ear is not jolted. Minding the Gap keeps home video centre and dry; only the score gets width.
  • Two eras of a voice are never matched to each other (Moonage Daydream, 32 Sounds): equal loudness (−24 ± 1), timbre deliberately different, LTAS differing ≥ 6 dB above 6 kHz; the listener must always know which year is talking.

2. The LTAS method and its numbers

opendubstage ltasmatch REFERENCE SOURCE [--gmax 6] [--half-octave] [--protect-below HZ] [--json]
Step Setting Why
Speech-active frames energy VAD (energy_vad: frame RMS within 20 dB of the 95th percentile and ≥ 12 dB above the 10th) or word timings (interval_mask); drop the first/last 2 frames of each run the match must compare speech with speech, not room with room
LTAS 1/3-octave bands 80 Hz–12.5 kHz (THIRD_OCTAVE), STFT 4096 / hop 1024 at 48 kHz, power mean over active frames, dB 1/3-octave is coarse enough not to copy formants
Smoothing 1/6-octave Gaussian on a log axis (one smooth pass) matching at fine resolution copies the other voice's formants and sounds phasey
Correction curve G(f) = amount × (P_ref − P_src), amount 0.3–0.5 (RX docs: "10–40 % is often enough"; more when the sources differ more than lav vs boom); zero-mean in 300 Hz–3 kHz so loudness normalisation is untouched partial matches sound like the same room; full matches sound like a filter
Gain limit ±6 dB per band (--gmax), ±5 dB above 8 kHz; never boost above the source's real bandwidth (an 11 kHz-bandwidth source has no 12 kHz to boost; boosting adds hiss) large boosts lift the noise floor
Filter least-squares fit of octave-spaced (OCTAVE_EQ, Q 1.4) or half-octave (HALF_OCTAVE_EQ, Q 2.1) RBJ peaking bands → an ffmpeg equalizer chain (eq_string); alternatively firwin2 → minimum_phase → afir plain filter strings, reproducible, cacheable
Pass criterion residual |LTAS_src′ − LTAS_ref| ≤ 3 dB in every band 200 Hz–6.3 kHz (deviation); RMS < 2 dB measured in practice: ±3 dB reads as "same room", ±4 as "a different voice"

Suggested amounts: synthetic bridge → main recording 0.4–0.45; camera speech → main recording 0.25; a second speaker inside the same recording → main speaker 0.5.

3. Bridging a TTS or cloned voice into a recording

The synthetic line must be degraded to the recording's bandwidth and given its room; the recording is never cleaned up to meet it (NPR §5.8; TSNGO's "glue" of old crowd libraries under new actor lines).

Stage Setting Check
Band-limit highpass=f=110..120, lowpass=f=7500..9000 LTAS above the source's cutoff within 3 dB of the reference
Proximity warmth equalizer=f=180:t=q:w=1.2:g=1.5 if the TTS is thin; equalizer=f=2500:t=q:w=1.2:g=-2 against TTS "presence"
Early reflections early_reflections() from ltasmatch: taps at 3.1, 5.3, 7.9, 11.3, 17.2, 26.0, 39.0 ms, first tap 0.30, RT60 ≈ 110 ms (a car or small room has almost no tail; early reflections plus bandwidth sell it, a long IR puts him in a bathroom) decay_ratio_db (0–30 ms vs 30–120 ms) within 3 dB of the reference
Compression acompressor=threshold=-22dB:ratio=2:attack=10:release=200
Fitted EQ the ltasmatch curve at amount 0.4–0.45 deviation ≤ 3 dB
Level −3 dB relative to the anchor is often right for a bridge (it is a connective line, not the voice) short-term within ±2 LU of the main voice
Floor the main recording's room tone under every bridge at −46 LUFS, starting 1 s before and ending 1 s after a blind listener cannot tell the bridge from the recording by room alone
Direction of travel DNSMOS BAK of the bridge should go down (it should get dirtier, not cleaner)

Credit it. Synthetic or re-voiced lines in nonfiction go on the end card (Deaf President Now! credited the actors who voiced its interviewees; Cinema Eye and Peabody juries are alert to undisclosed synthetic voice). One line; it costs nothing and its absence costs trust.

4. The off-mic second speaker

An interviewer or partner who chimes in from beside the microphone is content (Cameraperson, Radio Diaries). Treat it as its own lane, the way a tape-sync engineer treats the phone side.

  • Find the spans: diarisation labels merge and split on a two-person recording, so confirm with pitch (median_f0, autocorrelation 70–400 Hz, voicing 0.45) and, as a fallback, "spans ≤ 12 words that sit ≥ 6 dB below the cue's median short-term loudness". Clamp spans to ≥ 0.4 s, merge gaps < 0.3 s, expand each by 0.08 s so switches land in air.
  • Ride: +2 to +5 dB per span (measured in practice: 1.7 LU under the main voice before the ride); target short-term loudness within ±2 LU of the main voice over the same cue (Byers: "every voice roughly the same volume"). Hand-ride the one or two lines that still fail.
  • EQ per span: equalizer=f=450:t=q:w=1.2:g=-3,highshelf=f=3000:g=3 as a start, or the fitted curve against the main speaker at amount 0.5.
  • Place: a level-only pan of 15–20 % toward one side (pan=stereo|c0=0.85*c0|c1=1.0*c0 style, no delay, no width tricks). Verify the mono sum of the dialogue stem differs from the stereo mid by < 0.1 dB (no cancellation). On headphones: two people in a room, not one voice with a volume ride (Mangini: "no loudspeaker can reproduce proximity").
  • Music stays down through the interjection: the span counts as speech for the envelope.

5. Phone, tape-sync and remote callers

  • Match the studio voice toward the caller for long passages; cut mud 250–450 Hz on the caller, add 2–3 kHz; put the thin source slightly louder because it is harder to understand (Podcast Engineering School).
  • Push fixes to the source where possible (NPR Training interview checklist).

6. Verify

Check Threshold
LTAS deviation per voice class vs the reference ≤ 3 dB in every 1/3-octave band 200 Hz–6.3 kHz (≤ 4 dB before matching is applied)
per-band correction no band boosted > +6 dB; nothing above the source's cutoff
loudness per class short-term (3 s) within ±2 LU of −24 across the film; per-cue sentence std ≤ 1.5 LU
second speaker spans ≥ 0.4 s; within ±2 LU of the main voice after the ride; mono sum < 0.1 dB
bridges DNSMOS BAK lower than the raw TTS; decay ratio within 3 dB of the reference; a blind A/B against the preceding line passes in both orders
tonal QC opendubstage qc tonal: 1–4 kHz presence share per class within ±4 dB of the class mean
different-era voices LTAS differs ≥ 6 dB above 6 kHz; loudness within ±1 LU

See references/ltas-method.md for the algebra and the pitfalls.