Restore / app.py
ChillD1's picture
Upload app.py
4774e93 verified
Raw
History Blame Contribute Delete
5.84 kB
"""
app.py — Gradio front-end for tape_restore.py
Deploy: put this file, tape_restore.py, requirements.txt, and README.md
in the root of a Hugging Face Space with SDK = Gradio.
"""
import os
import tempfile
import traceback
import gradio as gr
import numpy as np
import tape_restore as tr
class Args:
"""Lightweight stand-in for the argparse.Namespace tape_restore.restore() expects."""
pass
def run_restoration(
audio_path,
use_noise_sample,
noise_start,
noise_end,
hiss_strength,
click_threshold,
hum_freq,
wow_flutter,
do_declick,
do_dehum,
do_dehiss,
do_eq,
do_dynamics,
progress=gr.Progress(track_tqdm=False),
):
if audio_path is None:
raise gr.Error("Upload an audio file first.")
try:
progress(0.05, desc="Loading audio...")
audio, sr = tr.load_audio(audio_path)
duration = len(audio) / sr
if duration > 20 * 60:
raise gr.Error(
f"File is {duration/60:.1f} minutes long. This demo Space caps "
f"input at 20 minutes to avoid CPU timeouts — for longer masters, "
f"run tape_restore.py locally instead."
)
args = Args()
args.no_declick = not do_declick
args.click_threshold = click_threshold
args.no_hum = not do_dehum
args.hum_freq = float(hum_freq)
args.no_hiss = not do_dehiss
args.hiss_strength = hiss_strength
args.noise_sample = (
[noise_start, noise_end]
if use_noise_sample and noise_end > noise_start
else None
)
args.wow_flutter = wow_flutter
args.no_eq = not do_eq
args.no_dynamics = not do_dynamics
progress(0.2, desc="Restoring (this can take a while on CPU)...")
restored = tr.restore(audio, sr, args)
progress(0.9, desc="Writing output...")
out_path = os.path.join(
tempfile.gettempdir(), f"restored_{next(tempfile._get_candidate_names())}.wav"
)
tr.save_audio(out_path, restored, sr)
progress(1.0, desc="Done")
return out_path, "Restoration complete."
except gr.Error:
raise
except Exception as e:
traceback.print_exc()
raise gr.Error(f"Restoration failed: {e}")
with gr.Blocks(title="Tape Restoration") as demo:
gr.Markdown(
"""
# Analog Tape Restoration
Upload audio digitized from a degraded analog master (soundtrack cue,
film master, etc.) and restore it: de-click, de-hum, de-hiss, EQ
rebuild, and gentle dynamics restoration.
**Tip:** for the biggest quality gain, mark a 1–2 second region of
pure tape hiss / room tone (no music) below — usually found at the
very head or tail of the reel — so de-hiss learns *this tape's*
actual noise floor instead of guessing.
Long files run slowly on free CPU hardware; this demo caps input at
20 minutes. For longer reels, run `tape_restore.py` locally.
"""
)
with gr.Row():
with gr.Column():
audio_in = gr.Audio(label="Input audio", type="filepath")
gr.Markdown("### Noise sample (optional but recommended)")
use_noise_sample = gr.Checkbox(
label="Use a noise-only region to profile hiss", value=False
)
with gr.Row():
noise_start = gr.Number(label="Start (seconds)", value=0.0)
noise_end = gr.Number(label="End (seconds)", value=2.0)
gr.Markdown("### Stages")
with gr.Row():
do_declick = gr.Checkbox(label="De-click / de-pop", value=True)
do_dehum = gr.Checkbox(label="De-hum", value=True)
with gr.Row():
do_dehiss = gr.Checkbox(label="De-hiss", value=True)
do_eq = gr.Checkbox(label="EQ restoration", value=True)
do_dynamics = gr.Checkbox(label="Dynamics restoration", value=True)
gr.Markdown("### Parameters")
hiss_strength = gr.Slider(
0.0, 1.0, value=0.75, step=0.05,
label="De-hiss strength",
info="Higher = more aggressive noise reduction, but can thin out highs/room tone",
)
click_threshold = gr.Slider(
4.0, 40.0, value=15.0, step=1.0,
label="Click sensitivity threshold",
info="Lower = catches more clicks but risks false positives on transients",
)
hum_freq = gr.Radio(
choices=["60", "50"], value="60",
label="Mains hum frequency",
info="60 Hz = US/NA tapes, 50 Hz = EU/most of the rest of the world",
)
wow_flutter = gr.Slider(
0.0, 0.5, value=0.0, step=0.05,
label="Wow/flutter smoothing",
info="Mild envelope-based warble reduction. 0 = off. For real pitch "
"correction, use a dedicated tool like Capstan or iZotope RX.",
)
run_btn = gr.Button("Restore", variant="primary")
with gr.Column():
audio_out = gr.Audio(label="Restored audio", type="filepath")
status = gr.Textbox(label="Status", interactive=False)
run_btn.click(
fn=run_restoration,
inputs=[
audio_in,
use_noise_sample,
noise_start,
noise_end,
hiss_strength,
click_threshold,
hum_freq,
wow_flutter,
do_declick,
do_dehum,
do_dehiss,
do_eq,
do_dynamics,
],
outputs=[audio_out, status],
)
if __name__ == "__main__":
demo.launch()