penguinfish1688's picture
Release standalone duplex TTS with XML interleaver, voice presets and examples
4123b95 verified
Raw History Blame Contribute Delete
1.86 kB
"""Constant-gain volume normalization preserving shared timing."""
import math
import numpy as np
def normalize_for_listening(wave, sample_rate, target_dbfs=-20., peak_dbfs=-1., max_boost_db=40.):
wave = np.asarray(wave, dtype=np.float32)
if wave.ndim != 1 or not np.isfinite(wave).all() or sample_rate <= 0:
raise ValueError('Expected finite mono audio and a positive sample rate')
if not all(math.isfinite(x) for x in (target_dbfs, peak_dbfs, max_boost_db)) or peak_dbfs >= 0 or max_boost_db < 0:
raise ValueError('Invalid normalization target/ceiling')
peak = float(np.max(np.abs(wave))) if len(wave) else 0.
if peak == 0:
return wave.copy(), dict(gain_db=0., input_peak=0., output_peak=0., active_rms=0., silent=True)
frame = max(1, round(sample_rate*.02))
padded = np.pad(wave.astype(np.float64), (0, (-len(wave)) % frame))
powers = np.square(padded.reshape(-1, frame)).mean(axis=1)
# Select active frames for measuring level only; waveform samples remain intact.
active = powers >= max(float(powers.max())*10**(-35/10), 1e-20)
rms = float(np.sqrt(powers[active].mean())) if active.any() else 0.
if rms == 0:
return wave.copy(), dict(gain_db=0., input_peak=peak, output_peak=peak, active_rms=0., silent=True)
desired = 10**(target_dbfs/20)/rms
peak_gain = 10**(peak_dbfs/20)/peak
maximum = 10**(max_boost_db/20)
gain = min(desired, peak_gain, maximum)
normalized = wave*gain
return normalized, dict(gain_db=20*math.log10(gain), input_peak=peak,
output_peak=float(np.max(np.abs(normalized))), active_rms=rms,
output_active_rms=rms*gain, target_dbfs=target_dbfs, peak_ceiling_dbfs=peak_dbfs,
peak_limited=peak_gain < desired and peak_gain <= maximum,
boost_limited=maximum < desired and maximum < peak_gain, silent=False)