Spaces:
Running
Running
feat: add optional DSP post-processing for audio clarification and normalization
Browse files
app.py
CHANGED
|
@@ -120,19 +120,38 @@ def ensure_wav(filepath: str) -> str:
|
|
| 120 |
return filepath
|
| 121 |
|
| 122 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
def demo_fn(
|
| 124 |
speech_upl: str,
|
| 125 |
noise_type: str,
|
| 126 |
snr: int,
|
| 127 |
atten_lim_db: float,
|
| 128 |
wet_dry_mix: float,
|
|
|
|
| 129 |
mic_input: Optional[str] = None,
|
| 130 |
):
|
| 131 |
if mic_input:
|
| 132 |
speech_upl = mic_input
|
| 133 |
|
| 134 |
sr = config("sr", 48000, int, section="df")
|
| 135 |
-
logger.info(f"Got parameters speech_upl: {speech_upl}, noise: {noise_type}, snr: {snr}, atten_lim_db: {atten_lim_db}, wet_dry_mix: {wet_dry_mix}")
|
| 136 |
snr = int(snr)
|
| 137 |
noise_fn = NOISES[noise_type]
|
| 138 |
meta = AudioMetaData(-1, -1, -1, -1, "")
|
|
@@ -177,8 +196,12 @@ def demo_fn(
|
|
| 177 |
alpha = wet_dry_mix / 100.0
|
| 178 |
enhanced = alpha * enhanced + (1 - alpha) * sample
|
| 179 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 180 |
lim = torch.linspace(0.0, 1.0, int(sr * 0.15)).unsqueeze(0)
|
| 181 |
-
lim = torch.cat((lim, torch.ones(1, enhanced.shape[1] - lim.shape[1])), dim=1)
|
| 182 |
enhanced = enhanced * lim
|
| 183 |
|
| 184 |
if meta.sample_rate != sr:
|
|
@@ -368,6 +391,10 @@ with gr.Blocks() as demo:
|
|
| 368 |
value=90,
|
| 369 |
label="Voice Naturalness Mix (%) - 100% is fully denoised, 90% blends back some original voice",
|
| 370 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
| 371 |
mic_input,
|
| 372 |
]
|
| 373 |
btn = gr.Button("Generate")
|
|
@@ -384,10 +411,10 @@ with gr.Blocks() as demo:
|
|
| 384 |
radio.change(toggle, radio, [mic_input, audio_file])
|
| 385 |
gr.Examples(
|
| 386 |
[
|
| 387 |
-
["./samples/p232_013_clean.wav", "Kitchen", "10", 15, 90, None],
|
| 388 |
-
["./samples/p232_013_clean.wav", "Cafe", "10", 15, 90, None],
|
| 389 |
-
["./samples/p232_019_clean.wav", "Cafe", "10", 15, 90, None],
|
| 390 |
-
["./samples/p232_019_clean.wav", "River", "10", 15, 90, None],
|
| 391 |
],
|
| 392 |
fn=demo_fn,
|
| 393 |
inputs=inputs,
|
|
|
|
| 120 |
return filepath
|
| 121 |
|
| 122 |
|
| 123 |
+
import torchaudio.functional as F_audio
|
| 124 |
+
|
| 125 |
+
def post_process_audio(waveform: Tensor, sr: int) -> Tensor:
|
| 126 |
+
"""Apply professional DSP filters to make voice audio crisp and clear."""
|
| 127 |
+
# 1. High-pass filter at 80 Hz to cut muddy sub-bass rumble and AC hums
|
| 128 |
+
waveform = F_audio.highpass_biquad(waveform, sample_rate=sr, cutoff_freq=80.0)
|
| 129 |
+
|
| 130 |
+
# 2. Treble peaking equalizer at 6000 Hz (+3.0 dB) to add air, presence, and vocal crispness
|
| 131 |
+
waveform = F_audio.equalizer_biquad(waveform, sample_rate=sr, center_freq=6000.0, gain=3.0, Q=0.707)
|
| 132 |
+
|
| 133 |
+
# 3. Normalize peak to -1.0 dBFS (0.9 max amplitude) to maximize volume without clipping
|
| 134 |
+
max_val = waveform.abs().max()
|
| 135 |
+
if max_val > 0:
|
| 136 |
+
waveform = waveform * (0.9 / max_val)
|
| 137 |
+
|
| 138 |
+
return waveform
|
| 139 |
+
|
| 140 |
+
|
| 141 |
def demo_fn(
|
| 142 |
speech_upl: str,
|
| 143 |
noise_type: str,
|
| 144 |
snr: int,
|
| 145 |
atten_lim_db: float,
|
| 146 |
wet_dry_mix: float,
|
| 147 |
+
post_process: bool,
|
| 148 |
mic_input: Optional[str] = None,
|
| 149 |
):
|
| 150 |
if mic_input:
|
| 151 |
speech_upl = mic_input
|
| 152 |
|
| 153 |
sr = config("sr", 48000, int, section="df")
|
| 154 |
+
logger.info(f"Got parameters speech_upl: {speech_upl}, noise: {noise_type}, snr: {snr}, atten_lim_db: {atten_lim_db}, wet_dry_mix: {wet_dry_mix}, post_process: {post_process}")
|
| 155 |
snr = int(snr)
|
| 156 |
noise_fn = NOISES[noise_type]
|
| 157 |
meta = AudioMetaData(-1, -1, -1, -1, "")
|
|
|
|
| 196 |
alpha = wet_dry_mix / 100.0
|
| 197 |
enhanced = alpha * enhanced + (1 - alpha) * sample
|
| 198 |
|
| 199 |
+
# Apply professional post-processing (Low-cut filter + presence boost EQ)
|
| 200 |
+
if post_process:
|
| 201 |
+
enhanced = post_process_audio(enhanced, sr)
|
| 202 |
+
|
| 203 |
lim = torch.linspace(0.0, 1.0, int(sr * 0.15)).unsqueeze(0)
|
| 204 |
+
lim = torch.cat((lim, torch.ones(1, enhanced.shape[-1] - lim.shape[1])), dim=1)
|
| 205 |
enhanced = enhanced * lim
|
| 206 |
|
| 207 |
if meta.sample_rate != sr:
|
|
|
|
| 391 |
value=90,
|
| 392 |
label="Voice Naturalness Mix (%) - 100% is fully denoised, 90% blends back some original voice",
|
| 393 |
),
|
| 394 |
+
gr.Checkbox(
|
| 395 |
+
label="Post-Process (80Hz Low-Cut & Presence Boost)",
|
| 396 |
+
value=True,
|
| 397 |
+
),
|
| 398 |
mic_input,
|
| 399 |
]
|
| 400 |
btn = gr.Button("Generate")
|
|
|
|
| 411 |
radio.change(toggle, radio, [mic_input, audio_file])
|
| 412 |
gr.Examples(
|
| 413 |
[
|
| 414 |
+
["./samples/p232_013_clean.wav", "Kitchen", "10", 15, 90, True, None],
|
| 415 |
+
["./samples/p232_013_clean.wav", "Cafe", "10", 15, 90, True, None],
|
| 416 |
+
["./samples/p232_019_clean.wav", "Cafe", "10", 15, 90, True, None],
|
| 417 |
+
["./samples/p232_019_clean.wav", "River", "10", 15, 90, True, None],
|
| 418 |
],
|
| 419 |
fn=demo_fn,
|
| 420 |
inputs=inputs,
|