Nodes/Castcut nodes/Castcut Audio Match Level
ComfyUI Node

Castcut Audio Match Level

Duck the room bed under the voice without riding the gain slider

By doodersrage·Created 29 days ago·Updated 3 days ago· 2
Castcut Audio Match Level
  • audio
  • reference
  • audio
  • gain_db
◄offset_db-15.0►

Audio is the layer ComfyUI bolted on last. You can make a voice with a TTS pack, make a room loop with Stable Audio or ACE-Step, and drive a talking head with InfiniteTalk - then you hit the part nobody writes tutorials about: the bed is either louder than the voice or so quiet it may as well not be there, and you find out by rendering and listening again. Community corpus search for "audio levels": one thread. For the video models sitting on top of it: thousands.

Castcut Audio Match Level is a one-job fix for that. You hand it two audio inputs - the thing you want to sit underneath, and the voice you want to sit under - plus a number, and it returns the first one scaled so its level lands a fixed distance below the second one's spoken level. No slider-riding, no render-three-times-and-pick. It measures and it multiplies.

It's the odd one out in the castcut-nodes pack - everything else there is a post-render still check for the Castcut app - and the only node in it with no extra dependencies.

How it decides the gain

The mechanism is about twenty lines in castcut_nodes.py. Both inputs get mixed down to mono, chopped into 50 ms windows, and each window gets a dB figure.

Which windows get counted is the whole trick. For the reference (your voice) it takes the mean of the loudest 30% of windows, so pauses between words don't drag the measured "spoken level" down. For the audio being scaled it energy-averages over all windows - the whole bed counts.

Then:

gain_db = reference_spoken_db + offset_db - bed_db

clamped to ±40 dB. The multiply is one scalar, 10 ** (gain_db / 20), applied to the waveform - channels and sample rate untouched, so the output keeps the input's rate. No resampling, no limiting, no EBU R128 loudness claim. It's a heuristic that gets you in the ballpark on the first run instead of the fourth, and a bed measuring under −70 dB comes back untouched, gain 0 - a near-silent bed is never pumped up from nothing.

The inputs that matter, and the two outputs

  • audio (AUDIO) - the bed you want turned down: room tone, ambience, a music loop, foley.
  • reference (AUDIO) - the voice you're matching against. This one should actually contain speech; nothing here detects a voice, it just measures the loudest parts of whatever you give it.
  • offset_db (FLOAT, default −15, range −60 to 0, step 0.5) - how far under the voice to sit. −15 dB is a firm duck; go toward −25 if you want the bed felt rather than heard. Think twice before 0: that's level-matched, as loud as the voice, not under it.

Outputs are audio (AUDIO) - into SaveAudio, PreviewAudio, or whatever muxes audio into your video - and gain_db (FLOAT), the gain actually applied. The second is more useful than it sounds: send it to a text display and you can see whether your "quiet" ambience got pulled down 4 dB or pushed up 12.

Installing it

The pack is published to the Comfy Registry, so ComfyUI Manager → Custom Nodes Manager → search Castcut nodes → Install, or from a shell:

comfy node install castcut-nodes

A git-URL install works too but Manager 3.41+ gates it behind allow_git_url_install = true under [default] in ComfyUI/user/__manager/config.ini, and ComfyUI listening on 127.0.0.1 - a plain clone is simpler:

cd ComfyUI/custom_nodes && git clone https://github.com/doodersrage/castcut-nodes

Restart ComfyUI, then check http://127.0.0.1:8188/object_info/CastcutAudioMatchLevel returns it; the description ends with [castcut-nodes 1.7.0], which is how the Castcut app reads the installed version. No model downloads, nothing heavy: requirements.txt is numpy and pillow, both already in ComfyUI. The pack's other nodes need comfyui_controlnet_aux for DWPose and ComfyUI_FaceAnalysis with insightface; this one needs neither.

Where people get burned

One loud event drags the whole bed down. A door slam six seconds in raises the bed's energy average, so the quiet rest of it gets turned down more than you'd expect. Trim spikes you don't want counted.

The cap, and the missing limiter. Gain is capped at ±40 dB, and there's no ceiling after it - the node just multiplies. Push a very quiet bed up hard and you can sail past 0 dBFS, where the clipping happens in whatever encodes the file. If gain_db is pinned at ±40, the measurement and the audio disagree and something upstream is wrong.

It's a relative tool, not a mastering chain. No normalization to a target, no compression, no noise reduction, no sample-rate conversion. Chain a few of these for voice plus room plus music and you're building a mixer in a graph editor. For the one job it names - an ambience bed under a talking clip - it's genuinely fine.

CategoryCastcut

Inputs (3)

NameTypeDefaultDescription
audioAUDIO—
referenceAUDIO—
offset_dbFLOAT-15.0-60–0—

Outputs (2)

NameTypeDescription
audioAUDIO—
gain_dbFLOAT—