161 lines
5.0 KiB
Python
161 lines
5.0 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
Author: C0ffymachyne
|
|
License: GPLv3
|
|
Version: 1.0.0
|
|
|
|
Description:
|
|
Audio widening node
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import math
|
|
import torch
|
|
|
|
sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)), "comfy"))
|
|
|
|
|
|
from ..core.io import audio_from_comfy_2d, audio_to_comfy_3d
|
|
from ..core.loudness import lufs_normalization, get_loudness
|
|
from ..core.widening import (
|
|
StereoWidenerFrequencyBased,
|
|
DecorrelationType,
|
|
FilterbankType,
|
|
)
|
|
|
|
|
|
def interpolate(t, a, b):
|
|
if not 0.0 <= t <= 1.0:
|
|
raise ValueError("t must be in the range [0.0, 1.0]")
|
|
return a + t * (b - a)
|
|
|
|
|
|
class SignalProcessingStereoWidening:
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
"mode": (["decorrelation", "simple"],),
|
|
"audio_input": ("AUDIO",),
|
|
},
|
|
"optional": {
|
|
"width": (
|
|
"FLOAT",
|
|
{"default": 6.0, "min": 1.0, "max": 8.0, "step": 0.1},
|
|
),
|
|
},
|
|
}
|
|
|
|
RETURN_TYPES = ("AUDIO",)
|
|
RETURN_NAMES = ("widened_audio",)
|
|
CATEGORY = "Audio Processing"
|
|
FUNCTION = "process"
|
|
|
|
def process(self, mode, audio_input, width=1.2):
|
|
"""
|
|
Widen stereo audio or convert mono audio to wide stereo using the provided widening algorithm.
|
|
|
|
Parameters:
|
|
audio_input (Dict): Dictionary containing 'waveform' and 'sample_rate'.
|
|
width (float): Width factor (>1.0).
|
|
|
|
Returns:
|
|
Tuple[Dict[str, torch.Tensor]]: Dictionary with widened 'waveform' and 'sample_rate'.
|
|
"""
|
|
|
|
waveform, sample_rate = audio_from_comfy_2d(
|
|
audio_input, repeat=False, try_gpu=True
|
|
)
|
|
channels, num_samples = waveform.shape
|
|
|
|
loudness = get_loudness(waveform, sample_rate)
|
|
|
|
if mode == "simple":
|
|
|
|
if channels not in [1, 2]:
|
|
raise ValueError(
|
|
f"Unsupported number of channels: {channels}. Only mono and stereo are supported."
|
|
)
|
|
|
|
# Calculate coefficients based on the provided width parameter
|
|
width_coeff = 1.0 / max(1.0 + width, 2.0) # Scalar
|
|
|
|
coef_mid = 1.0 * width_coeff # Coefficient for mid
|
|
coef_sides = width * width_coeff # Coefficient for sides
|
|
|
|
if channels == 2:
|
|
# Stereo to Widened Stereo
|
|
L = waveform[0, :] # Left channel
|
|
R = waveform[1, :] # Right channel
|
|
|
|
# Apply the widening algorithm
|
|
mid = (L + R) * coef_mid # Mid signal
|
|
sides = (R - L) * coef_sides # Side signal
|
|
|
|
widened_L = mid - sides # New Left channel
|
|
widened_R = mid + sides # New Right channel
|
|
|
|
# Stack the widened channels back into a stereo waveform
|
|
widened_waveform = torch.stack(
|
|
(widened_L, widened_R), dim=0
|
|
) # [2, samples]
|
|
|
|
elif channels == 1:
|
|
# Mono to Wide Stereo
|
|
L = waveform[0, :].clone() # Duplicate mono channel to Left
|
|
R = waveform[0, :].clone() # Duplicate mono channel to Right
|
|
|
|
# Apply the widening algorithm
|
|
mid = (L + R) * coef_mid # Mid signal
|
|
sides = (R - L) * coef_sides # Side signal
|
|
|
|
widened_L = mid - sides # New Left channel
|
|
widened_R = mid + sides # New Right channel
|
|
|
|
# Stack the widened channels into a stereo waveform
|
|
widened_waveform = torch.stack(
|
|
(widened_L, widened_R), dim=0
|
|
) # [2, samples]
|
|
|
|
widened_waveform = lufs_normalization(
|
|
widened_waveform, sample_rate, loudness
|
|
)
|
|
|
|
return audio_to_comfy_3d(widened_waveform, sample_rate)
|
|
|
|
if mode == "decorrelation":
|
|
|
|
waveform = waveform.cpu()
|
|
|
|
decorellation_type = DecorrelationType.VELVET
|
|
filterbank_type = FilterbankType.ENERGY_PRESERVE
|
|
start_value = 0.0
|
|
end_value = math.pi / 2
|
|
|
|
if width > 1.0:
|
|
width = 1.0
|
|
|
|
beta = interpolate(width, start_value, end_value)
|
|
cutoff_frequency_hz = 22000 # sample_rate//2 # max possible
|
|
cutoff_frequency_hz = (sample_rate // 2) - 10 # max possible
|
|
|
|
stereoWidener = StereoWidenerFrequencyBased(
|
|
waveform,
|
|
sample_rate,
|
|
filterbank_type,
|
|
decorellation_type,
|
|
(beta, beta),
|
|
cutoff_frequency_hz,
|
|
)
|
|
|
|
widened_waveform = torch.from_numpy(stereoWidener.process())
|
|
widened_waveform = widened_waveform.T
|
|
|
|
widened_waveform = lufs_normalization(
|
|
widened_waveform, sample_rate, loudness
|
|
)
|
|
|
|
return audio_to_comfy_3d(widened_waveform, sample_rate)
|