harp-dev commited on
Commit
795f903
·
verified ·
1 Parent(s): 72ad407

Build the models at startup, check inputs before reserving the GPU, bound the retries, and reserve 170 s rather than 300.

Browse files
Files changed (1) hide show
  1. app.py +23 -22
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
- MAX_RETRIES = 5 # beat-alignment retries before falling back to a fixed length (default: 5, per repo)
 
 
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
- Model construction happens here, inside the GPU call, rather than in the
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="Output Audio").set_info("Generated loopable music clip."),
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(