Files
banban/talkingq-url/utils/audio_denoiser.py
2026-04-16 21:13:06 +08:00

118 lines
4.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import noisereduce as nr
import soundfile as sf
import librosa
import numpy as np
import os
def normalize_audio_volume(audio, original_rms=None, target_dbfs=-20):
"""
对音频进行音量归一化
参数:
audio: 音频数据
original_rms: 原始音频的RMS值可选
target_dbfs: 目标音量级别dBFS
返回:
归一化后的音频
"""
# 计算当前音频的RMS
current_rms = np.sqrt(np.mean(audio**2))
# 方法1恢复到原始音量水平
if original_rms is not None and original_rms > 0:
scale_factor = original_rms / current_rms
normalized_audio = audio * scale_factor
print(f"音量恢复: 缩放因子 {scale_factor:.2f}")
# 方法2基于目标dBFS的音量归一化
else:
# 计算当前音频的dBFS
current_dbfs = 20 * np.log10(current_rms / (np.max(np.abs(audio)) + 1e-8))
# 计算需要的增益
gain_db = target_dbfs - current_dbfs
gain_linear = 10 ** (gain_db / 20)
normalized_audio = audio * gain_linear
print(f"音量归一化: 增益 {gain_db:.1f} dB, 缩放因子 {gain_linear:.2f}")
# 防止削波clipping
max_val = np.max(np.abs(normalized_audio))
if max_val > 0.95: # 如果接近最大值1.0
normalized_audio = normalized_audio * 0.95 / max_val
print("已应用防削波处理")
return normalized_audio
def reduce_background_noise(audio_path, output_path=None, use_default_noise=True,
noise_path='assets/audio/noise_sample.wav', noise_start=0.1, noise_end=0.5,
normalize_volume=True, target_dbfs=-20):
"""
使用noisereduce库去除背景噪声
参数:
audio_path: 音频文件路径
output_path: 输出文件路径(可选)
use_default_noise: 是否使用默认噪声样本
noise_path: 默认噪声样本路径
noise_start: 噪声样本开始时间仅当use_default_noise=False时使用
noise_end: 噪声样本结束时间仅当use_default_noise=False时使用
"""
# 加载音频
y, sr = librosa.load(audio_path, sr=None)
print(f"音频信息: 时长 {len(y)/sr:.2f}秒, 采样率 {sr}Hz")
# 提取或加载噪声样本
if use_default_noise:
try:
noise_path = os.path.join("assets", "audio", "noise_sample.wav")
noise_clip, noise_sr = librosa.load(noise_path, sr=None)
# 检查采样率是否匹配
if noise_sr != sr:
print(f"警告: 噪声样本采样率({noise_sr}Hz)与音频采样率({sr}Hz)不匹配")
# 重采样噪声样本以匹配音频采样率
noise_clip = librosa.resample(noise_clip, orig_sr=noise_sr, target_sr=sr)
print("已对噪声样本进行重采样以匹配音频采样率")
except FileNotFoundError as e:
print(f"错误: {e}")
print("将使用当前音频提取噪声样本")
use_default_noise = False
if not use_default_noise:
# 从当前音频提取噪声样本
noise_start_sample = int(noise_start * sr)
noise_end_sample = int(noise_end * sr)
noise_clip = y[noise_start_sample:noise_end_sample]
print(f"使用当前音频的 {noise_end-noise_start:.2f}秒 噪声样本进行降噪")
else:
print(f"使用默认噪声样本进行降噪,噪声长度: {len(noise_clip)/sr:.2f}")
# 应用降噪
reduced_noise = nr.reduce_noise(
y=y,
sr=sr,
y_noise=noise_clip,
prop_decrease=0.95, # 降噪比例
n_fft=1024,
win_length=1024,
hop_length=256,
n_std_thresh_stationary=1.5,
stationary=True
)
# 记录原始音频的音量RMS
original_rms = np.sqrt(np.mean(y**2))
print(f"原始音频RMS: {original_rms:.4f}")
# 音量归一化处理
if normalize_volume:
normalize_audio_volume(reduced_noise, original_rms, target_dbfs)
# 保存结果
if output_path:
sf.write(output_path, reduced_noise, sr)
print(f"降噪后的音频已保存至: {output_path}")
if __name__ == "__main__":
reduce_background_noise('test/TalkingQ_XQSN00001004_eba844b5.mp3', 'test/denoised2.mp3')