Spaces:
Running
Running
add app.py (AudioSR CPU + motyw Lektora)
Browse files
app.py
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""AudioSR — wzbogacanie krótkich próbek głosu (HF Space, CPU/DARMOWY). UI w kolorystyce
|
| 2 |
+
Lektor AI (ciemny + cyan #00ffff). Pole do wrzucenia próbki → odtwarzacz + pobieranie WAV.
|
| 3 |
+
|
| 4 |
+
Działa bez PRO (CPU basic). UWAGA: na darmowym CPU 1 klip ≈ ~2 min (to dyfuzja).
|
| 5 |
+
Domyślnie model „basic", 24 kroki (sprawdzone do Twojego głosu — patrz sr_dataset.py)."""
|
| 6 |
+
import os
|
| 7 |
+
import tempfile
|
| 8 |
+
import numpy as np
|
| 9 |
+
import soundfile as sf
|
| 10 |
+
import gradio as gr
|
| 11 |
+
import torch
|
| 12 |
+
from audiosr import build_model, super_resolution
|
| 13 |
+
|
| 14 |
+
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
|
| 15 |
+
OUTPUT_SR = 48000
|
| 16 |
+
_CACHE = {}
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def _model(model_name: str):
|
| 20 |
+
if model_name not in _CACHE:
|
| 21 |
+
_CACHE[model_name] = build_model(model_name=model_name, device=DEVICE)
|
| 22 |
+
return _CACHE[model_name]
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def enhance(audio_path, model_name, ddim_steps, guidance_scale, seed):
|
| 26 |
+
if not audio_path:
|
| 27 |
+
raise gr.Error("Najpierw wrzuć plik audio.")
|
| 28 |
+
seed = int(seed or 0)
|
| 29 |
+
if seed == 0:
|
| 30 |
+
seed = int.from_bytes(os.urandom(4), "big") % (2 ** 31)
|
| 31 |
+
wav = super_resolution(
|
| 32 |
+
_model(model_name), audio_path,
|
| 33 |
+
seed=seed, guidance_scale=float(guidance_scale),
|
| 34 |
+
ddim_steps=int(ddim_steps), latent_t_per_second=12.8,
|
| 35 |
+
)
|
| 36 |
+
wav = np.asarray(wav).squeeze()
|
| 37 |
+
if wav.ndim > 1:
|
| 38 |
+
wav = wav.T
|
| 39 |
+
# przytnij padding AudioSR (zaokrągla do 5.12 s + cisza) do długości wejścia
|
| 40 |
+
info = sf.info(audio_path)
|
| 41 |
+
exp = int(round(info.frames / info.samplerate * OUTPUT_SR))
|
| 42 |
+
if exp and wav.shape[0] > exp:
|
| 43 |
+
wav = wav[:exp]
|
| 44 |
+
# wyrównaj poziom (RMS) do oryginału — AudioSR normalizuje per-call
|
| 45 |
+
src, _ = sf.read(audio_path, dtype="float32")
|
| 46 |
+
if getattr(src, "ndim", 1) > 1:
|
| 47 |
+
src = src.mean(axis=1)
|
| 48 |
+
r_in = float(np.sqrt((src ** 2).mean())) + 1e-9
|
| 49 |
+
r_out = float(np.sqrt((wav ** 2).mean())) + 1e-9
|
| 50 |
+
wav = wav * (r_in / r_out)
|
| 51 |
+
peak = float(np.abs(wav).max()) or 1.0
|
| 52 |
+
if peak > 0.99:
|
| 53 |
+
wav = wav / peak * 0.99
|
| 54 |
+
# zapis do pliku — żeby był odtwarzacz I pobieranie czystego WAV
|
| 55 |
+
base = os.path.splitext(os.path.basename(audio_path))[0]
|
| 56 |
+
out_path = os.path.join(tempfile.gettempdir(), f"{base}_48k.wav")
|
| 57 |
+
sf.write(out_path, wav.astype(np.float32), OUTPUT_SR)
|
| 58 |
+
return out_path, gr.DownloadButton(value=out_path, interactive=True)
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
# ── Motyw Lektor AI (ciemny + cyan) ──────────────────────────────────────────
|
| 62 |
+
CYAN, PANEL, BG, BG2, TXT, MUTED, BORDER = (
|
| 63 |
+
"#00ffff", "#0a0a0f", "#0d0d14", "#15151e", "#cfcfe0", "#8a8aa0", "#222230")
|
| 64 |
+
|
| 65 |
+
CSS = f"""
|
| 66 |
+
.gradio-container, .gradio-container .dark, body {{
|
| 67 |
+
background:{PANEL} !important; color:{TXT} !important;
|
| 68 |
+
font-family:'Segoe UI', system-ui, -apple-system, sans-serif !important;
|
| 69 |
+
}}
|
| 70 |
+
.gradio-container {{ max-width:920px !important; margin:0 auto !important; }}
|
| 71 |
+
#lk-menu {{
|
| 72 |
+
display:flex; align-items:center; gap:14px;
|
| 73 |
+
padding:14px 18px; margin:0 0 16px 0; border-radius:12px;
|
| 74 |
+
background:linear-gradient(180deg,{BG2},{BG});
|
| 75 |
+
border:1px solid {BORDER}; border-bottom:2px solid {CYAN};
|
| 76 |
+
box-shadow:0 10px 28px -16px {CYAN};
|
| 77 |
+
}}
|
| 78 |
+
#lk-menu .lk-title {{ font-size:20px; font-weight:700; letter-spacing:.4px; color:{TXT}; line-height:1.1; }}
|
| 79 |
+
#lk-menu .lk-title b {{ color:{CYAN}; text-shadow:0 0 10px rgba(0,255,255,.45); }}
|
| 80 |
+
#lk-menu .lk-sub {{ font-size:12px; color:{MUTED}; margin-top:2px; }}
|
| 81 |
+
.block, .form, .gr-box, fieldset, .gr-panel, .panel {{
|
| 82 |
+
background:{BG} !important; border:1px solid {BORDER} !important; border-radius:10px !important;
|
| 83 |
+
}}
|
| 84 |
+
label, label span, .gr-input-label, span[data-testid="block-info"] {{ color:{MUTED} !important; }}
|
| 85 |
+
input, textarea, select, .gr-text-input {{
|
| 86 |
+
background:{BG2} !important; color:{TXT} !important; border:1px solid {BORDER} !important;
|
| 87 |
+
}}
|
| 88 |
+
input:focus, textarea:focus, select:focus {{ border-color:{CYAN} !important; box-shadow:0 0 0 1px {CYAN} !important; }}
|
| 89 |
+
input[type=range] {{ accent-color:{CYAN} !important; }}
|
| 90 |
+
.gr-button-primary, button.primary, .primary {{
|
| 91 |
+
background:{CYAN} !important; color:#06141c !important; border:none !important;
|
| 92 |
+
font-weight:700 !important; letter-spacing:.6px !important;
|
| 93 |
+
}}
|
| 94 |
+
.gr-button-primary:hover, button.primary:hover {{ box-shadow:0 0 16px rgba(0,255,255,.55) !important; }}
|
| 95 |
+
.gr-button-secondary, button.secondary {{
|
| 96 |
+
background:transparent !important; color:{CYAN} !important; border:1px solid {CYAN} !important;
|
| 97 |
+
}}
|
| 98 |
+
.gradio-container a {{ color:{CYAN} !important; }}
|
| 99 |
+
footer {{ display:none !important; }}
|
| 100 |
+
"""
|
| 101 |
+
|
| 102 |
+
ICON = (f"<svg width='30' height='30' viewBox='0 0 24 24' fill='none' stroke='{CYAN}' "
|
| 103 |
+
f"stroke-width='2.2' stroke-linecap='round'>"
|
| 104 |
+
f"<line x1='4' y1='15' x2='4' y2='9'/><line x1='9' y1='19' x2='9' y2='5'/>"
|
| 105 |
+
f"<line x1='14' y1='16' x2='14' y2='8'/><line x1='19' y1='13' x2='19' y2='11'/></svg>")
|
| 106 |
+
MENU = (f"<div id='lk-menu'>{ICON}<div>"
|
| 107 |
+
f"<div class='lk-title'>Audio<b>SR</b> · Lektor AI</div>"
|
| 108 |
+
f"<div class='lk-sub'>Wzbogacanie pr��bek głosu — super-rozdzielczość do 48 kHz</div>"
|
| 109 |
+
f"</div></div>")
|
| 110 |
+
|
| 111 |
+
with gr.Blocks(title="AudioSR · Lektor AI", css=CSS, theme=gr.themes.Base()) as demo:
|
| 112 |
+
gr.HTML(MENU)
|
| 113 |
+
with gr.Row():
|
| 114 |
+
with gr.Column(scale=1):
|
| 115 |
+
audio = gr.Audio(sources=["upload", "microphone"], type="filepath",
|
| 116 |
+
label="Próbka głosu — wrzuć plik (krótki klip)")
|
| 117 |
+
model = gr.Radio(["basic", "speech"], value="basic",
|
| 118 |
+
label="Model (basic = sprawdzony do Twojego głosu)")
|
| 119 |
+
steps = gr.Slider(10, 100, value=24, step=1, label="DDIM steps (24 = jakość/czas)")
|
| 120 |
+
guid = gr.Slider(1.0, 10.0, value=3.5, step=0.5, label="Guidance scale")
|
| 121 |
+
seed = gr.Number(value=0, label="Seed (0 = losowy)")
|
| 122 |
+
btn = gr.Button("WZBOGAĆ", variant="primary")
|
| 123 |
+
with gr.Column(scale=1):
|
| 124 |
+
out = gr.Audio(type="filepath", label="Wzbogacone — 48 kHz (odtwarzacz)", interactive=False)
|
| 125 |
+
dl = gr.DownloadButton("Pobierz WAV (48 kHz)", variant="secondary", interactive=False)
|
| 126 |
+
gr.HTML(f"<div style='color:{MUTED};font-size:12px;margin-top:6px'>"
|
| 127 |
+
f"Na darmowym CPU 1 klip ≈ 2 min (dyfuzja). Model „basic”, padding przycięty, "
|
| 128 |
+
f"poziom wyrównany do oryginału.</div>")
|
| 129 |
+
btn.click(enhance, [audio, model, steps, guid, seed], [out, dl])
|
| 130 |
+
|
| 131 |
+
if __name__ == "__main__":
|
| 132 |
+
demo.queue(max_size=8).launch()
|