From a37b12235fbc6f70dcabdba7baa6a750d9f1542d Mon Sep 17 00:00:00 2001 From: kijai <40791699+kijai@users.noreply.github.com> Date: Wed, 27 Aug 2025 20:51:08 +0300 Subject: [PATCH] Add loudness norm node --- nodes_utility.py | 45 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/nodes_utility.py b/nodes_utility.py index 9af2016..bb4e408 100644 --- a/nodes_utility.py +++ b/nodes_utility.py @@ -409,6 +409,45 @@ class WanVideoSigmaToStep: def convert(self, sigma): return (sigma,) +class NormalizeAudioLoudness: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "audio": ("AUDIO",), + "lufs": ("FLOAT", {"default": -23.0, "min": -100.0, "max": 0.0, "step": 0.1, "tool": "Loudness Units relative to Full Scale, higher LUFS values (closer to 0) mean louder audio. Lower LUFS values (more negative) mean quieter audio."}), + }, + } + + RETURN_TYPES = ("AUDIO", ) + RETURN_NAMES = ("audio", ) + FUNCTION = "normalize" + CATEGORY = "WanVideoWrapper" + + def normalize(self, audio, lufs): + audio_input = audio["waveform"] + sample_rate = audio["sample_rate"] + if audio_input.dim() == 3: + audio_input = audio_input.squeeze(0) + audio_input_np = audio_input.detach().transpose(0, 1).numpy().astype(np.float32) + audio_input_np = np.ascontiguousarray(audio_input_np) + normalized_audio = self.loudness_norm(audio_input_np, sr=sample_rate, lufs=lufs) + + out_audio = {"waveform": torch.from_numpy(normalized_audio).transpose(0, 1).unsqueeze(0).float(), "sample_rate": sample_rate} + + return (out_audio, ) + + def loudness_norm(self, audio_array, sr=16000, lufs=-23): + try: + import pyloudnorm + except: + raise ImportError("pyloudnorm package is not installed") + meter = pyloudnorm.Meter(sr) + loudness = meter.integrated_loudness(audio_array) + if abs(loudness) > 100: + return audio_array + normalized_audio = pyloudnorm.normalize.loudness(audio_array, loudness, lufs) + return normalized_audio + NODE_CLASS_MAPPINGS = { "WanVideoImageResizeToClosest": WanVideoImageResizeToClosest, "WanVideoVACEStartToEndFrame": WanVideoVACEStartToEndFrame, @@ -417,7 +456,8 @@ NODE_CLASS_MAPPINGS = { "DummyComfyWanModelObject": DummyComfyWanModelObject, "WanVideoLatentReScale": WanVideoLatentReScale, "CreateScheduleFloatList": CreateScheduleFloatList, - "WanVideoSigmaToStep": WanVideoSigmaToStep + "WanVideoSigmaToStep": WanVideoSigmaToStep, + "NormalizeAudioLoudness": NormalizeAudioLoudness } NODE_DISPLAY_NAME_MAPPINGS = { "WanVideoImageResizeToClosest": "WanVideo Image Resize To Closest", @@ -427,5 +467,6 @@ NODE_DISPLAY_NAME_MAPPINGS = { "DummyComfyWanModelObject": "Dummy Comfy Wan Model Object", "WanVideoLatentReScale": "WanVideo Latent ReScale", "CreateScheduleFloatList": "Create Schedule Float List", - "WanVideoSigmaToStep": "WanVideo Sigma To Step" + "WanVideoSigmaToStep": "WanVideo Sigma To Step", + "NormalizeAudioLoudness": "Normalize Audio Loudness" } \ No newline at end of file