Spaces:
Sleeping
Sleeping
File size: 6,331 Bytes
a95f6c0 ee2c2c5 a95f6c0 ee2c2c5 a95f6c0 928845d a95f6c0 928845d a95f6c0 5560f6c a95f6c0 ee2c2c5 a95f6c0 928845d 0b8b6f4 ee2c2c5 a95f6c0 ee2c2c5 a95f6c0 928845d c408069 a95f6c0 e92a2ea a95f6c0 ee2c2c5 a95f6c0 ee2c2c5 a95f6c0 ee2c2c5 a95f6c0 ee2c2c5 a95f6c0 ee2c2c5 a95f6c0 ee2c2c5 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | import sys
sys.stdout.reconfigure(line_buffering=True) # real-time logs in HF Spaces
try:
import spaces
def gpu_decorator(func): return spaces.GPU(func)
except ImportError:
def gpu_decorator(func): return func
import contextlib
import tempfile
import threading
import traceback
import soundfile as sf
import torch
import torchaudio
from omegaconf import OmegaConf
import gradio as gr
from pyharp import ModelCard, build_endpoint
# ---- Paths and device ----
CKPT_PATH = "pretrained/VCTK_16k_4s_time-190000.pt"
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
# ---- Inference config ----
ARGS = OmegaConf.load("config/inference.yaml")
OP_HP = OmegaConf.load("config/operator.yaml")
SAMPLE_RATE = ARGS.exp.sample_rate
AUDIO_LEN = ARGS.exp.audio_len
# ---- Model loading ----
sampler = None
model_loading = True
model_error = None
model_ready = False # has the network been moved onto the GPU yet?
def load_model():
global sampler, model_loading, model_error
try:
print(f"Loading checkpoint from {CKPT_PATH}...")
# imported here (not at top) so the Gradio server starts before these load
from networks.ncsnpp import NCSNppTime
from diff_params.edm import EDM
from utils.training_utils import load_state_dict
from testing.EulerHeunSamplerDPS import EulerHeunSamplerDPS
network_cfg = OmegaConf.load("config/network.yaml")
# stft stays as OmegaConf — NCSNppTime uses dot access on it (stft_kwargs.n_fft)
stft_cfg = network_cfg.pop("stft")
# built on CPU — ZeroGPU only intercepts CUDA calls inside an
# @spaces.GPU-decorated call, not from this background thread
network = NCSNppTime(stft=stft_cfg, **OmegaConf.to_container(network_cfg))
# load_state_dict tries multiple key strategies ('ema', 'model', etc.)
# to handle checkpoints saved in different formats
state_dict = torch.load(CKPT_PATH, map_location="cpu", weights_only=False)
load_state_dict(state_dict, ema=network)
network.eval()
diff_params_cfg = OmegaConf.load("config/diff_params.yaml")
# sde_hp stays as OmegaConf — EDM uses dot access on it (sde_hp.sigma_data)
sde_hp = diff_params_cfg.pop("sde_hp")
diff_params = EDM(sde_hp=sde_hp, **OmegaConf.to_container(diff_params_cfg))
sampler = EulerHeunSamplerDPS(network, diff_params, ARGS)
print("Model ready.")
except Exception:
model_error = traceback.format_exc()
print(f"Error loading model:\n{model_error}")
finally:
model_loading = False
# Load in background so the Gradio server starts immediately
threading.Thread(target=load_model, daemon=True).start()
# ---- pyharp model card ----
model_card = ModelCard(
name="BUDDy - Blind Dereverberation",
description="Removes room reverberation from a speech recording. No room measurements needed — the model estimates the room acoustics automatically.",
author="Lemercier, Moliner, Welker, Välimäki, Gerkmann (2024)",
tags=["speech", "dereverberation", "effect removal"],
)
# ---- Inference ----
@gpu_decorator
def process_fn(input_audio_path: str, num_steps: int):
global model_ready
if model_loading:
raise gr.Error("Model is still loading, please wait a moment and try again.")
if sampler is None:
raise gr.Error(f"Model failed to load: {model_error}")
if not model_ready:
sampler.model.to(DEVICE) # only safe here, inside @spaces.GPU
model_ready = True
from testing.operators.subband_filtering import BlindSubbandFiltering
# Update step count from slider — also update args so get_gamma() uses the right T
sampler.T = num_steps
# using soundfile directly — torchaudio.load/save need torchcodec, which isn't installed
data, sr = sf.read(input_audio_path)
waveform = torch.tensor(data.T if data.ndim > 1 else data[None]).float() # (channels, samples)
# resampling to 16kHz
if sr != SAMPLE_RATE:
waveform = torchaudio.functional.resample(waveform, sr, SAMPLE_RATE)
# converting to mono
if waveform.shape[0] > 1:
waveform = waveform.mean(dim=0, keepdim=True)
# trimming/padding to exactly AUDIO_LEN
if waveform.shape[-1] > AUDIO_LEN:
waveform = waveform[..., :AUDIO_LEN]
elif waveform.shape[-1] < AUDIO_LEN:
waveform = torch.nn.functional.pad(waveform, (0, AUDIO_LEN - waveform.shape[-1]))
y = waveform.squeeze(0).to(DEVICE) # (AUDIO_LEN,)
# Normalize to match sigma_data of the training set (0.05)
y = ARGS.tester.posterior_sampling.warm_initialization.scaling_factor * y / (y.std() + 1e-8)
y = y.unsqueeze(0) # (1, AUDIO_LEN) — sampler expects a batch dimension
# tester.py line 147
operator = BlindSubbandFiltering(OP_HP, sample_rate=SAMPLE_RATE)
with torch.no_grad():
operator.update_H(use_noise=True)
# Run the DPS sampler. No torch.inference_mode() here because the DPS
# likelihood gradient uses torch.autograd.grad() on intermediate tensors.
with contextlib.nullcontext():
pred = sampler.predict_conditional(y, operator, shape=(1, AUDIO_LEN), blind=True)
pred = pred.detach().cpu()
if pred.dim() > 1:
pred = pred.squeeze(0)
with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as f:
out_path = f.name
sf.write(out_path, pred.numpy(), SAMPLE_RATE)
return out_path
# ---- Gradio UI ----
with gr.Blocks() as demo:
input_components = [
gr.Audio(type="filepath", label="Reverberant Audio").harp_required(True),
gr.Slider(
minimum=30,
maximum=400,
step=1,
value=201,
label="Processing Steps",
info="More steps = higher quality but slower. Paper default: 201. Max: 400.",
),
]
output_components = [
gr.Audio(type="filepath", label="Dereverberated Audio").set_info(
"Clean speech with room reverb removed."
),
]
build_endpoint(
model_card=model_card,
input_components=input_components,
output_components=output_components,
process_fn=process_fn,
)
if __name__ == "__main__":
demo.queue().launch(share=True, show_error=True, pwa=True)
|