Spaces:
Running
on
Zero
Running
on
Zero
Update app.py
Browse files
app.py
CHANGED
|
@@ -12,7 +12,7 @@ except Exception:
|
|
| 12 |
return deco
|
| 13 |
spaces = _DummySpaces()
|
| 14 |
|
| 15 |
-
#
|
| 16 |
@spaces.GPU(duration=10)
|
| 17 |
def gpu_probe(a: int = 1, b: int = 1):
|
| 18 |
return a + b
|
|
@@ -43,12 +43,24 @@ WEIGHTS_FILE = "model.safetensors"
|
|
| 43 |
CACHE_DIR = SPACE_ROOT / "weights"
|
| 44 |
CACHE_DIR.mkdir(parents=True, exist_ok=True)
|
| 45 |
|
| 46 |
-
# ================
|
| 47 |
-
|
| 48 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
def get_weights_path(progress: Optional[gr.Progress] = None) -> Path:
|
| 51 |
-
"""Download/resolve weights lazily."""
|
| 52 |
global _weights_path
|
| 53 |
if _weights_path is None:
|
| 54 |
if progress: progress(0.10, desc="Downloading model weights (first run)")
|
|
@@ -63,23 +75,9 @@ def get_weights_path(progress: Optional[gr.Progress] = None) -> Path:
|
|
| 63 |
_weights_path = Path(wp)
|
| 64 |
return _weights_path
|
| 65 |
|
| 66 |
-
def ensure_repo(progress: Optional[gr.Progress] = None) -> Path:
|
| 67 |
-
"""Clone the repo lazily and add to sys.path."""
|
| 68 |
-
global _repo_ready
|
| 69 |
-
if not _repo_ready:
|
| 70 |
-
if not REPO_DIR.exists():
|
| 71 |
-
if progress: progress(0.18, desc="Cloning SonicMaster repo (first run)")
|
| 72 |
-
subprocess.run(
|
| 73 |
-
["git", "clone", "--depth", "1", REPO_URL, REPO_DIR.as_posix()],
|
| 74 |
-
check=True,
|
| 75 |
-
)
|
| 76 |
-
if REPO_DIR.as_posix() not in sys.path:
|
| 77 |
-
sys.path.append(REPO_DIR.as_posix())
|
| 78 |
-
_repo_ready = True
|
| 79 |
-
return REPO_DIR
|
| 80 |
-
|
| 81 |
# ================== Helpers ==================
|
| 82 |
def save_temp_wav(wav: np.ndarray, sr: int, path: Path):
|
|
|
|
| 83 |
if wav.ndim == 2 and wav.shape[0] < wav.shape[1]:
|
| 84 |
wav = wav.T
|
| 85 |
if wav.dtype == np.float64:
|
|
@@ -93,11 +91,15 @@ def read_audio(path: str) -> Tuple[np.ndarray, int]:
|
|
| 93 |
return wav, sr
|
| 94 |
|
| 95 |
def _candidate_commands(py: str, script: Path, ckpt: Path, inp: Path, prompt: str, out: Path) -> List[List[str]]:
|
| 96 |
-
|
|
|
|
|
|
|
|
|
|
| 97 |
return [
|
| 98 |
-
[py, script.as_posix(), "--ckpt",
|
| 99 |
-
|
| 100 |
-
[py, script.as_posix(), "--
|
|
|
|
| 101 |
]
|
| 102 |
|
| 103 |
def run_sonicmaster_cli(
|
|
@@ -106,40 +108,37 @@ def run_sonicmaster_cli(
|
|
| 106 |
out_path: Path,
|
| 107 |
progress: Optional[gr.Progress] = None,
|
| 108 |
) -> Tuple[bool, str]:
|
| 109 |
-
"""Run inference
|
| 110 |
if progress: progress(0.14, desc="Preparing inference")
|
| 111 |
ckpt = get_weights_path(progress=progress)
|
| 112 |
-
repo = ensure_repo(progress=progress)
|
| 113 |
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
return False, "No inference script found in the repo (expected infer_single.py or similar)."
|
| 118 |
|
| 119 |
py = sys.executable or "python3"
|
| 120 |
env = os.environ.copy()
|
| 121 |
|
| 122 |
last_err = ""
|
| 123 |
-
for
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
if
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
last_err = f"Unexpected error: {e}\n{traceback.format_exc()}"
|
| 139 |
return False, last_err or "All candidate commands failed."
|
| 140 |
|
| 141 |
# ============ GPU path (ZeroGPU) ============
|
| 142 |
-
@spaces.GPU(duration=60) #
|
| 143 |
def enhance_on_gpu(input_path: str, prompt: str, output_path: str) -> Tuple[bool, str]:
|
| 144 |
try:
|
| 145 |
import torch # noqa: F401
|
|
@@ -155,7 +154,7 @@ def _has_cuda() -> bool:
|
|
| 155 |
except Exception:
|
| 156 |
return False
|
| 157 |
|
| 158 |
-
# ================== Examples
|
| 159 |
PROMPTS_10 = [
|
| 160 |
"Increase the clarity of this song by emphasizing treble frequencies.",
|
| 161 |
"Make this song sound more boomy by amplifying the low end bass frequencies.",
|
|
@@ -169,36 +168,17 @@ PROMPTS_10 = [
|
|
| 169 |
"Please, dereverb this audio.",
|
| 170 |
]
|
| 171 |
|
| 172 |
-
def
|
| 173 |
-
"""
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
|
|
|
|
|
|
|
|
|
| 178 |
|
| 179 |
-
|
| 180 |
-
"""
|
| 181 |
-
Returns (dropdown_update, paths:list[str], status:str)
|
| 182 |
-
"""
|
| 183 |
-
paths = list_example_files(progress=progress)
|
| 184 |
-
if not paths:
|
| 185 |
-
return gr.Dropdown.update(choices=[], value=None), [], "No sample .wav files found in repo/samples/inputs."
|
| 186 |
-
labels = [f"{i+1:02d} — {Path(p).name}" for i, p in enumerate(paths)]
|
| 187 |
-
# Auto-select first item for convenience
|
| 188 |
-
return gr.Dropdown.update(choices=labels, value=labels[0]), paths, f"Loaded {len(paths)} sample audios."
|
| 189 |
-
|
| 190 |
-
def set_example_selection(idx_label: str, paths: List[str]) -> Tuple[str, str]:
|
| 191 |
-
"""When user picks an example, set the audio path + a suggested prompt."""
|
| 192 |
-
if not idx_label or not paths:
|
| 193 |
-
return "", ""
|
| 194 |
-
try:
|
| 195 |
-
idx = int(idx_label.split()[0]) - 1 # "01 — file.wav" -> 0
|
| 196 |
-
except Exception:
|
| 197 |
-
idx = 0
|
| 198 |
-
idx = max(0, min(idx, len(paths)-1))
|
| 199 |
-
audio_path = paths[idx]
|
| 200 |
-
prompt = PROMPTS_10[idx] if idx < len(PROMPTS_10) else PROMPTS_10[-1]
|
| 201 |
-
return audio_path, prompt
|
| 202 |
|
| 203 |
# ================== Main callback ==================
|
| 204 |
def enhance_audio_ui(
|
|
@@ -249,52 +229,29 @@ def enhance_audio_ui(
|
|
| 249 |
with gr.Blocks(title="SonicMaster – Text-Guided Restoration & Mastering", fill_height=True) as _demo:
|
| 250 |
gr.Markdown(
|
| 251 |
"## 🎧 SonicMaster\n"
|
| 252 |
-
"Upload audio or
|
|
|
|
| 253 |
)
|
| 254 |
with gr.Row():
|
| 255 |
with gr.Column(scale=1):
|
| 256 |
-
# Sample loader (lazy)
|
| 257 |
-
with gr.Accordion("Sample audios (10)", open=False):
|
| 258 |
-
load_btn = gr.Button("📥 Load 10 sample audios")
|
| 259 |
-
samples_dropdown = gr.Dropdown(
|
| 260 |
-
choices=[], value=None, # no default until choices are set
|
| 261 |
-
label="Pick a sample",
|
| 262 |
-
interactive=True,
|
| 263 |
-
)
|
| 264 |
-
samples_state = gr.State([]) # holds absolute paths
|
| 265 |
-
|
| 266 |
in_audio = gr.Audio(label="Input Audio", type="filepath")
|
| 267 |
prompt = gr.Textbox(label="Text Prompt", placeholder="e.g., Reduce reverb and brighten vocals.")
|
| 268 |
run_btn = gr.Button("🚀 Enhance", variant="primary")
|
| 269 |
|
| 270 |
-
|
| 271 |
-
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
|
| 275 |
-
|
| 276 |
-
|
| 277 |
-
|
| 278 |
-
|
| 279 |
|
| 280 |
with gr.Column(scale=1):
|
| 281 |
out_audio = gr.Audio(label="Enhanced Audio (output)")
|
| 282 |
status = gr.Textbox(label="Status / Messages", interactive=False, lines=8)
|
| 283 |
|
| 284 |
-
# Load samples (3 outputs directly; no .then needed)
|
| 285 |
-
load_btn.click(
|
| 286 |
-
fn=load_examples,
|
| 287 |
-
inputs=None,
|
| 288 |
-
outputs=[samples_dropdown, samples_state, status],
|
| 289 |
-
)
|
| 290 |
-
|
| 291 |
-
# When a sample is chosen, set audio path + suggested prompt
|
| 292 |
-
samples_dropdown.change(
|
| 293 |
-
fn=set_example_selection,
|
| 294 |
-
inputs=[samples_dropdown, samples_state],
|
| 295 |
-
outputs=[in_audio, prompt],
|
| 296 |
-
)
|
| 297 |
-
|
| 298 |
run_btn.click(
|
| 299 |
fn=enhance_audio_ui,
|
| 300 |
inputs=[in_audio, prompt],
|
|
@@ -309,4 +266,4 @@ app = demo
|
|
| 309 |
|
| 310 |
# Local debugging only
|
| 311 |
if __name__ == "__main__":
|
| 312 |
-
demo.launch(server_name="0.0.0.0", server_port=7860)
|
|
|
|
| 12 |
return deco
|
| 13 |
spaces = _DummySpaces()
|
| 14 |
|
| 15 |
+
# Public probes so ZeroGPU supervisor can detect them
|
| 16 |
@spaces.GPU(duration=10)
|
| 17 |
def gpu_probe(a: int = 1, b: int = 1):
|
| 18 |
return a + b
|
|
|
|
| 43 |
CACHE_DIR = SPACE_ROOT / "weights"
|
| 44 |
CACHE_DIR.mkdir(parents=True, exist_ok=True)
|
| 45 |
|
| 46 |
+
# ================ Repo clone AT STARTUP (per your request) ================
|
| 47 |
+
def ensure_repo() -> Path:
|
| 48 |
+
if not REPO_DIR.exists():
|
| 49 |
+
subprocess.run(
|
| 50 |
+
["git", "clone", "--depth", "1", REPO_URL, REPO_DIR.as_posix()],
|
| 51 |
+
check=True,
|
| 52 |
+
)
|
| 53 |
+
if REPO_DIR.as_posix() not in sys.path:
|
| 54 |
+
sys.path.append(REPO_DIR.as_posix())
|
| 55 |
+
return REPO_DIR
|
| 56 |
|
| 57 |
+
# Clone now so examples are available immediately
|
| 58 |
+
ensure_repo()
|
| 59 |
+
|
| 60 |
+
# ================ Weights: still lazy (download at first run) ================
|
| 61 |
+
_weights_path: Optional[Path] = None
|
| 62 |
def get_weights_path(progress: Optional[gr.Progress] = None) -> Path:
|
| 63 |
+
"""Download/resolve weights lazily (keeps startup fast)."""
|
| 64 |
global _weights_path
|
| 65 |
if _weights_path is None:
|
| 66 |
if progress: progress(0.10, desc="Downloading model weights (first run)")
|
|
|
|
| 75 |
_weights_path = Path(wp)
|
| 76 |
return _weights_path
|
| 77 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 78 |
# ================== Helpers ==================
|
| 79 |
def save_temp_wav(wav: np.ndarray, sr: int, path: Path):
|
| 80 |
+
# Ensure shape (samples, channels)
|
| 81 |
if wav.ndim == 2 and wav.shape[0] < wav.shape[1]:
|
| 82 |
wav = wav.T
|
| 83 |
if wav.dtype == np.float64:
|
|
|
|
| 91 |
return wav, sr
|
| 92 |
|
| 93 |
def _candidate_commands(py: str, script: Path, ckpt: Path, inp: Path, prompt: str, out: Path) -> List[List[str]]:
|
| 94 |
+
"""
|
| 95 |
+
Only support infer_single.py variants.
|
| 96 |
+
Expected primary flags: --ckpt --input --prompt --output
|
| 97 |
+
"""
|
| 98 |
return [
|
| 99 |
+
[py, script.as_posix(), "--ckpt", ckpt.as_posix(), "--input", inp.as_posix(), "--prompt", prompt, "--output", out.as_posix()],
|
| 100 |
+
# A couple common alternates some repos use:
|
| 101 |
+
[py, script.as_posix(), "--weights", ckpt.as_posix(), "--input", inp.as_posix(), "--text", prompt, "--out", out.as_posix()],
|
| 102 |
+
[py, script.as_posix(), "--ckpt", ckpt.as_posix(), "--input", inp.as_posix(), "--text", prompt, "--output", out.as_posix()],
|
| 103 |
]
|
| 104 |
|
| 105 |
def run_sonicmaster_cli(
|
|
|
|
| 108 |
out_path: Path,
|
| 109 |
progress: Optional[gr.Progress] = None,
|
| 110 |
) -> Tuple[bool, str]:
|
| 111 |
+
"""Run inference via subprocess; returns (ok, message). Uses ONLY infer_single.py."""
|
| 112 |
if progress: progress(0.14, desc="Preparing inference")
|
| 113 |
ckpt = get_weights_path(progress=progress)
|
|
|
|
| 114 |
|
| 115 |
+
script = REPO_DIR / "infer_single.py"
|
| 116 |
+
if not script.exists():
|
| 117 |
+
return False, "infer_single.py not found in the SonicMaster repo."
|
|
|
|
| 118 |
|
| 119 |
py = sys.executable or "python3"
|
| 120 |
env = os.environ.copy()
|
| 121 |
|
| 122 |
last_err = ""
|
| 123 |
+
for cidx, cmd in enumerate(_candidate_commands(py, script, ckpt, input_wav_path, prompt, out_path), 1):
|
| 124 |
+
try:
|
| 125 |
+
if progress:
|
| 126 |
+
progress(min(0.25 + 0.10 * cidx, 0.70), desc=f"Running infer_single.py (try {cidx})")
|
| 127 |
+
res = subprocess.run(cmd, capture_output=True, text=True, check=True, env=env)
|
| 128 |
+
if out_path.exists() and out_path.stat().st_size > 0:
|
| 129 |
+
if progress: progress(0.88, desc="Post-processing output")
|
| 130 |
+
return True, (res.stdout or "Inference completed.").strip()
|
| 131 |
+
last_err = "infer_single.py finished but produced no output file."
|
| 132 |
+
except subprocess.CalledProcessError as e:
|
| 133 |
+
snippet = "\n".join(filter(None, [e.stdout or "", e.stderr or ""])).strip()
|
| 134 |
+
last_err = snippet if snippet else f"infer_single.py failed with return code {e.returncode}."
|
| 135 |
+
except Exception as e:
|
| 136 |
+
import traceback
|
| 137 |
+
last_err = f"Unexpected error with infer_single.py: {e}\n{traceback.format_exc()}"
|
|
|
|
| 138 |
return False, last_err or "All candidate commands failed."
|
| 139 |
|
| 140 |
# ============ GPU path (ZeroGPU) ============
|
| 141 |
+
@spaces.GPU(duration=60) # safe cap for ZeroGPU tiers
|
| 142 |
def enhance_on_gpu(input_path: str, prompt: str, output_path: str) -> Tuple[bool, str]:
|
| 143 |
try:
|
| 144 |
import torch # noqa: F401
|
|
|
|
| 154 |
except Exception:
|
| 155 |
return False
|
| 156 |
|
| 157 |
+
# ================== Examples @ STARTUP ==================
|
| 158 |
PROMPTS_10 = [
|
| 159 |
"Increase the clarity of this song by emphasizing treble frequencies.",
|
| 160 |
"Make this song sound more boomy by amplifying the low end bass frequencies.",
|
|
|
|
| 168 |
"Please, dereverb this audio.",
|
| 169 |
]
|
| 170 |
|
| 171 |
+
def build_startup_examples() -> List[List[Any]]:
|
| 172 |
+
"""Build 10 (audio_path, prompt) pairs from repo at import time."""
|
| 173 |
+
wav_dir = REPO_DIR / "samples" / "inputs"
|
| 174 |
+
wav_paths = sorted(p for p in wav_dir.glob("*.wav") if p.is_file())
|
| 175 |
+
ex = []
|
| 176 |
+
for i, p in enumerate(wav_paths[:10]):
|
| 177 |
+
pr = PROMPTS_10[i] if i < len(PROMPTS_10) else PROMPTS_10[-1]
|
| 178 |
+
ex.append([p.as_posix(), pr])
|
| 179 |
+
return ex
|
| 180 |
|
| 181 |
+
STARTUP_EXAMPLES = build_startup_examples()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 182 |
|
| 183 |
# ================== Main callback ==================
|
| 184 |
def enhance_audio_ui(
|
|
|
|
| 229 |
with gr.Blocks(title="SonicMaster – Text-Guided Restoration & Mastering", fill_height=True) as _demo:
|
| 230 |
gr.Markdown(
|
| 231 |
"## 🎧 SonicMaster\n"
|
| 232 |
+
"Upload audio or pick an example, write a prompt, then click **Enhance**.\n"
|
| 233 |
+
"- First run downloads model weights (progress will show).\n"
|
| 234 |
)
|
| 235 |
with gr.Row():
|
| 236 |
with gr.Column(scale=1):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 237 |
in_audio = gr.Audio(label="Input Audio", type="filepath")
|
| 238 |
prompt = gr.Textbox(label="Text Prompt", placeholder="e.g., Reduce reverb and brighten vocals.")
|
| 239 |
run_btn = gr.Button("🚀 Enhance", variant="primary")
|
| 240 |
|
| 241 |
+
# Show 10 audio+prompt examples immediately at startup
|
| 242 |
+
if STARTUP_EXAMPLES:
|
| 243 |
+
gr.Examples(
|
| 244 |
+
examples=STARTUP_EXAMPLES,
|
| 245 |
+
inputs=[in_audio, prompt],
|
| 246 |
+
label="Sample Inputs (10)",
|
| 247 |
+
)
|
| 248 |
+
else:
|
| 249 |
+
gr.Markdown("> ⚠️ No sample .wav files found in `samples/inputs/`.")
|
| 250 |
|
| 251 |
with gr.Column(scale=1):
|
| 252 |
out_audio = gr.Audio(label="Enhanced Audio (output)")
|
| 253 |
status = gr.Textbox(label="Status / Messages", interactive=False, lines=8)
|
| 254 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 255 |
run_btn.click(
|
| 256 |
fn=enhance_audio_ui,
|
| 257 |
inputs=[in_audio, prompt],
|
|
|
|
| 266 |
|
| 267 |
# Local debugging only
|
| 268 |
if __name__ == "__main__":
|
| 269 |
+
demo.launch(server_name="0.0.0.0", server_port=7860)
|