BABE-2 / app.py
Vansh Chugh
fix device handling: build network on target device, keep recording on CPU until fed to sampler
dc8400f
Raw
History Blame Contribute Delete
5.32 kB
import sys
sys.stdout.reconfigure(line_buffering=True)
try:
import spaces
except ImportError:
# keep @spaces.GPU usable as a no-op; ZeroGPU requires this exact name.
class spaces:
class GPU:
def __init__(self, func=None, duration=60):
self.func = func
def __call__(self, *args, **kwargs):
if self.func is not None:
return self.func(*args, **kwargs)
func = args[0]
return func
import tempfile
import gradio as gr
import soundfile as sf
import torch
import torchaudio
from huggingface_hub import hf_hub_download
from omegaconf import OmegaConf
from pyharp import ModelCard, build_endpoint
from model.config import CHECKPOINTS, CHECKPOINTS_REPO
from model.cqtdiff import Unet_CQT_oct_with_attention
from model.edm import EDM
from model.restoration import restore_audio
# one entry per architecture ("piano"/"singing"), holding everything that's
# lazily built/loaded once and reused across requests for that architecture
_state = {}
def get_arch_state(base_cfg, device):
"""Lazily builds (once per architecture) and caches the network and EDM diffusion parameters on `device`."""
key = base_cfg.architecture
if key not in _state:
net = Unet_CQT_oct_with_attention(base_cfg, device)
net.to(device)
net.eval()
_state[key] = {"network": net, "diff_params": EDM(base_cfg), "loaded_checkpoint": None, "ltas_ref": None}
return _state[key]
def load_checkpoint(label, device):
"""Downloads (if not already cached locally) and loads the checkpoint for the selected voice/instrument, reusing the shared network for its architecture."""
filename, base_cfg = CHECKPOINTS[label]
state = get_arch_state(base_cfg, device)
if state["loaded_checkpoint"] != filename:
ckpt_path = hf_hub_download(repo_id=CHECKPOINTS_REPO, filename=filename)
checkpoint = torch.load(ckpt_path, map_location="cpu", weights_only=False)
state["network"].load_state_dict(checkpoint["ema"])
state["ltas_ref"] = checkpoint["LTAS"]
state["loaded_checkpoint"] = filename
return state["network"], state["diff_params"], state["ltas_ref"], base_cfg
model_card = ModelCard(
name="BABE-2",
description="Restores degraded historical piano or singing-voice recordings with a diffusion-based generative equalizer.",
author="Eloi Moliner, Maija Turunen, Filip Elvander, Vesa Välimäki",
tags=["restoration", "equalizer", "diffusion", "historical recordings"],
)
@spaces.GPU(duration=180)
def process_fn(input_audio_path: str, checkpoint_label: str, steps: int, strength: float) -> str:
"""Restores the input recording using the selected voice/instrument model, diffusion step count, and restoration strength."""
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
net, diff_params, ltas_ref, base_cfg = load_checkpoint(checkpoint_label, device)
ltas_ref = ltas_ref.to(device)
cfg = OmegaConf.create(OmegaConf.to_container(base_cfg, resolve=True))
cfg.tester.T = int(steps)
cfg.tester.posterior_sampling.xi = float(strength)
audio, sr = sf.read(input_audio_path)
sig = torch.tensor(audio, dtype=torch.float32)
if sig.dim() > 1:
sig = sig.mean(dim=-1)
if sr != cfg.exp.sample_rate:
sig = torchaudio.functional.resample(sig, sr, cfg.exp.sample_rate)
def on_progress(fraction, desc):
print(f"[{fraction:.0%}] {desc}")
restored = restore_audio(sig, cfg, net, diff_params, ltas_ref, device, on_progress=on_progress)
out_path = tempfile.NamedTemporaryFile(suffix=".wav", delete=False).name
sf.write(out_path, restored.cpu().numpy(), cfg.exp.sample_rate)
return out_path
def update_strength_default(checkpoint_label):
"""Updates the Restoration Strength slider to the selected model's paper-recommended default."""
_, base_cfg = CHECKPOINTS[checkpoint_label]
return gr.update(value=float(base_cfg.tester.posterior_sampling.xi))
with gr.Blocks() as demo:
checkpoint_dropdown = gr.Dropdown(
choices=list(CHECKPOINTS.keys()),
value="Piano (MAESTRO)",
label="Voice / Instrument",
info="Singer-specific models give the most faithful restoration when your recording actually resembles that singer.",
)
input_components = [
gr.Audio(type="filepath", label="Input Audio").harp_required(True),
checkpoint_dropdown,
gr.Slider(minimum=10, maximum=100, step=1, value=51, label="Processing Steps",
info="More steps can improve quality at the cost of processing time."),
gr.Slider(minimum=0.0, maximum=2.0, step=0.05, value=1.0, label="Restoration Strength",
info="How strongly the output is guided to match the input recording."),
]
output_components = [
gr.Audio(type="filepath", label="Restored Audio").set_info("The restored recording."),
]
checkpoint_dropdown.change(fn=update_strength_default, inputs=checkpoint_dropdown, outputs=input_components[3])
build_endpoint(
model_card=model_card,
input_components=input_components,
output_components=output_components,
process_fn=process_fn,
)
demo.queue().launch(pwa=True)