Spaces:
Running on Zero
Running on Zero
Build the models at startup, check inputs before reserving the GPU, bound the retries, and reserve 170 s rather than 300.
Browse files
app.py
CHANGED
|
@@ -42,7 +42,9 @@ MAX_CFG = 5.0 # classifier-free guidance ceiling (default: 5.0, per pap
|
|
| 42 |
MIN_CFG = 1.0 # classifier-free guidance floor, annealed down to this (default: 1.0, per paper)
|
| 43 |
RESCORE_WEIGHT = 0.5 # MAGNeT/MusicGen rescoring interpolation weight (default: 0.5, per paper)
|
| 44 |
HINT_DURATION = 30.0 # seconds of MusicGen hint audio generated for beat detection (default: 30.0, per repo)
|
| 45 |
-
|
|
|
|
|
|
|
| 46 |
|
| 47 |
beat_checkpoint_path = None
|
| 48 |
beat_checkpoint_ready = threading.Event()
|
|
@@ -51,7 +53,6 @@ beat_checkpoint_error = None
|
|
| 51 |
musicgen = None
|
| 52 |
loopgen = None
|
| 53 |
file2beats = None
|
| 54 |
-
model_lock = threading.Lock()
|
| 55 |
|
| 56 |
|
| 57 |
def download_beat_checkpoint():
|
|
@@ -84,6 +85,14 @@ _loader.start()
|
|
| 84 |
if __name__ != "__main__":
|
| 85 |
_loader.join()
|
| 86 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 87 |
|
| 88 |
model_card = ModelCard(
|
| 89 |
name="LoopGen",
|
|
@@ -167,33 +176,25 @@ def _beat_aligned_length(path, min_length, max_length, unit_size=0.02, length_re
|
|
| 167 |
return length
|
| 168 |
|
| 169 |
|
| 170 |
-
@spaces.GPU(duration=300)
|
| 171 |
-
@torch.inference_mode()
|
| 172 |
def process_fn(description: str, min_length: float, max_length: float, seed: int) -> str:
|
| 173 |
"""Generates a seamlessly loopable music clip: a MusicGen hint is used to
|
| 174 |
find a beat-aligned loop length (retrying up to MAX_RETRIES times), then
|
| 175 |
MAGNeT generates the final loop as a continuation of that hint. Adapted
|
| 176 |
-
from pipelines.py's loopgen_loop.
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
background-download thread above: LoopGen/MusicGen.get_pretrained()
|
| 180 |
-
bakes device="cuda" in immediately, and ZeroGPU only allows CUDA calls
|
| 181 |
-
made inside an @spaces.GPU-decorated call, not from a background thread."""
|
| 182 |
-
global musicgen, loopgen, file2beats
|
| 183 |
-
|
| 184 |
-
if not beat_checkpoint_ready.wait(timeout=90):
|
| 185 |
-
raise gr.Error("Beat-tracking checkpoint is still downloading, please try again shortly.")
|
| 186 |
if beat_checkpoint_error is not None:
|
| 187 |
raise gr.Error(f"Beat-tracking checkpoint failed to download: {beat_checkpoint_error}")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 188 |
|
| 189 |
-
with model_lock:
|
| 190 |
-
if musicgen is None:
|
| 191 |
-
musicgen = MusicGen.get_pretrained(MUSICGEN_MODEL, device=DEVICE)
|
| 192 |
-
if loopgen is None:
|
| 193 |
-
loopgen = LoopGen.get_pretrained(MAGNET_MODEL, device=DEVICE)
|
| 194 |
-
if file2beats is None:
|
| 195 |
-
file2beats = File2Beats(checkpoint_path=beat_checkpoint_path, device=DEVICE)
|
| 196 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 197 |
torch.manual_seed(seed)
|
| 198 |
np.random.seed(seed)
|
| 199 |
|
|
@@ -268,7 +269,7 @@ if __name__ == "__main__":
|
|
| 268 |
info="Random seed for generation (default: 42, per repo)."),
|
| 269 |
]
|
| 270 |
output_components = [
|
| 271 |
-
gr.Audio(type="filepath", label="
|
| 272 |
]
|
| 273 |
|
| 274 |
build_endpoint(
|
|
|
|
| 42 |
MIN_CFG = 1.0 # classifier-free guidance floor, annealed down to this (default: 1.0, per paper)
|
| 43 |
RESCORE_WEIGHT = 0.5 # MAGNeT/MusicGen rescoring interpolation weight (default: 0.5, per paper)
|
| 44 |
HINT_DURATION = 30.0 # seconds of MusicGen hint audio generated for beat detection (default: 30.0, per repo)
|
| 45 |
+
# Beat-alignment retries before falling back to a fixed length. The repo's default is 5, but each
|
| 46 |
+
# hint costs about 46 s of the caller's GPU allowance, and two keep a request inside its duration
|
| 47 |
+
MAX_RETRIES = 2
|
| 48 |
|
| 49 |
beat_checkpoint_path = None
|
| 50 |
beat_checkpoint_ready = threading.Event()
|
|
|
|
| 53 |
musicgen = None
|
| 54 |
loopgen = None
|
| 55 |
file2beats = None
|
|
|
|
| 56 |
|
| 57 |
|
| 58 |
def download_beat_checkpoint():
|
|
|
|
| 85 |
if __name__ != "__main__":
|
| 86 |
_loader.join()
|
| 87 |
|
| 88 |
+
# The worker, which runs process_fn, builds the models on CUDA here, at module level on its main
|
| 89 |
+
# thread, where ZeroGPU's emulation records the placement, rather than inside every call,
|
| 90 |
+
# which ZeroGPU runs in a fresh fork. The server never runs process_fn, so it builds none.
|
| 91 |
+
if beat_checkpoint_error is None:
|
| 92 |
+
musicgen = MusicGen.get_pretrained(MUSICGEN_MODEL, device=DEVICE)
|
| 93 |
+
loopgen = LoopGen.get_pretrained(MAGNET_MODEL, device=DEVICE)
|
| 94 |
+
file2beats = File2Beats(checkpoint_path=beat_checkpoint_path, device=DEVICE)
|
| 95 |
+
|
| 96 |
|
| 97 |
model_card = ModelCard(
|
| 98 |
name="LoopGen",
|
|
|
|
| 176 |
return length
|
| 177 |
|
| 178 |
|
|
|
|
|
|
|
| 179 |
def process_fn(description: str, min_length: float, max_length: float, seed: int) -> str:
|
| 180 |
"""Generates a seamlessly loopable music clip: a MusicGen hint is used to
|
| 181 |
find a beat-aligned loop length (retrying up to MAX_RETRIES times), then
|
| 182 |
MAGNeT generates the final loop as a continuation of that hint. Adapted
|
| 183 |
+
from pipelines.py's loopgen_loop."""
|
| 184 |
+
# Checked before the GPU is reserved, so a request that cannot run spends none of the
|
| 185 |
+
# caller's ZeroGPU allowance
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 186 |
if beat_checkpoint_error is not None:
|
| 187 |
raise gr.Error(f"Beat-tracking checkpoint failed to download: {beat_checkpoint_error}")
|
| 188 |
+
if musicgen is None or loopgen is None or file2beats is None:
|
| 189 |
+
raise gr.Error("The models are still loading, please try again shortly.")
|
| 190 |
+
|
| 191 |
+
return generate_loop(description, min_length, max_length, seed)
|
| 192 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 193 |
|
| 194 |
+
# About 46 s per hint, at most MAX_RETRIES of them, then about 61 s for MAGNeT's loop
|
| 195 |
+
@spaces.GPU(duration=170)
|
| 196 |
+
@torch.inference_mode()
|
| 197 |
+
def generate_loop(description: str, min_length: float, max_length: float, seed: int) -> str:
|
| 198 |
torch.manual_seed(seed)
|
| 199 |
np.random.seed(seed)
|
| 200 |
|
|
|
|
| 269 |
info="Random seed for generation (default: 42, per repo)."),
|
| 270 |
]
|
| 271 |
output_components = [
|
| 272 |
+
gr.Audio(type="filepath", label="Loop").set_info("Generated loopable music clip."),
|
| 273 |
]
|
| 274 |
|
| 275 |
build_endpoint(
|