harp-dev Claude Opus 5.5 commited on
Commit
9039c7a
·
verified ·
1 Parent(s): 3ded493

Load the model and soundfont in the worker, and check the MIDI

Browse files

Loads the model and soundfont in the worker alone, moves the model card first, and raises an unreadable MIDI file as gr.Error. It also imports spaces plainly, since it is always installed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Files changed (1) hide show
  1. app.py +31 -45
app.py CHANGED
@@ -1,22 +1,9 @@
 
 
1
  import sys
2
 
3
  sys.stdout.reconfigure(line_buffering=True)
4
 
5
- try:
6
- import spaces
7
- except ImportError:
8
- # keep @spaces.GPU usable as a no-op; ZeroGPU requires this exact name.
9
- class spaces:
10
- class GPU:
11
- def __init__(self, func=None, duration=60):
12
- self.func = func
13
-
14
- def __call__(self, *args, **kwargs):
15
- if self.func is not None:
16
- return self.func(*args, **kwargs)
17
- func = args[0]
18
- return func
19
-
20
 
21
  import urllib.request
22
  from pathlib import Path
@@ -37,6 +24,20 @@ import music_x_transformers
37
  import representation
38
  import utils as mmt_utils
39
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
41
  CKPT_DIR = Path(__file__).parent / "checkpoint"
42
 
@@ -47,9 +48,7 @@ _VALID_INSTRUMENTS = ", ".join(sorted(n for n in encoding["instrument_code_map"]
47
 
48
 
49
  def load_model():
50
- """Build the model on CPU and load the sod/ape checkpoint weights. Stays on
51
- CPU here even if GPU hardware is available -- ZeroGPU only allows touching
52
- CUDA from inside an @spaces.GPU call, not at module load time."""
53
  model = music_x_transformers.MusicXTransformer(
54
  dim=train_args["dim"],
55
  encoding=encoding,
@@ -71,10 +70,6 @@ def load_model():
71
  return model
72
 
73
 
74
- # Checkpoint is small (~80MB) and lives in the Space repo, so load it
75
- # synchronously at startup rather than in a background thread.
76
- model = load_model()
77
- model_ready = False # has the model been moved onto DEVICE yet?
78
 
79
 
80
  def download_musescore_soundfont():
@@ -93,8 +88,14 @@ def download_musescore_soundfont():
93
  urllib.request.urlretrieve(prefix + "MuseScore_General.sf3", sf_path)
94
 
95
 
96
- # Fetch upfront so failures show in logs, not after a request.
97
- download_musescore_soundfont()
 
 
 
 
 
 
98
 
99
 
100
  def encode_seed(midi_path, encoding, max_beat):
@@ -175,11 +176,6 @@ def generate_tokens(prefix_codes, generation_length, temperature, focus):
175
  the pipeline that touches CUDA, so it's the only part wrapped in
176
  @spaces.GPU. Everything else (MIDI decoding, audio synthesis) is
177
  CPU-bound and shouldn't burn ZeroGPU quota."""
178
- global model, model_ready
179
- if not model_ready:
180
- model = model.to(DEVICE)
181
- model_ready = True
182
-
183
  sos = encoding["type_code_map"]["start-of-song"]
184
  eos = encoding["type_code_map"]["end-of-song"]
185
 
@@ -208,7 +204,12 @@ def process_fn(input_midi_path, instruments_text, generation_length, temperature
208
  + (if requested) a rendered audio preview + a report of what happened
209
  with the requested instruments."""
210
  if input_midi_path:
211
- prefix_codes = encode_seed(input_midi_path, encoding, train_args["max_beat"])
 
 
 
 
 
212
  instrument_note = "Seed MIDI provided -- Instruments field ignored."
213
  report_name = Path(input_midi_path).name
214
  else:
@@ -235,21 +236,6 @@ def process_fn(input_midi_path, instruments_text, generation_length, temperature
235
  return midi_path, audio_path, report_path
236
 
237
 
238
- model_card = ModelCard(
239
- name="Multitrack Music Transformer",
240
- description=(
241
- "Generates symbolic multi-instrument music. Upload a MIDI seed to "
242
- "continue it, list instruments to write a fresh piece for exactly "
243
- "those, or leave both empty for a fully unconditioned generation. "
244
- "Trained on the Symbolic Orchestral Database (SOD). Repository: "
245
- "https://github.com/salu133445/mmt Paper: "
246
- "https://arxiv.org/abs/2207.06983"
247
- ),
248
- author="Hao-Wen Dong, Ke Chen, Shlomo Dubnov, Julian McAuley, Taylor Berg-Kirkpatrick",
249
- tags=[Subcategory.HOLISTIC],
250
- )
251
-
252
-
253
  if __name__ == "__main__":
254
  with gr.Blocks() as demo:
255
  input_components = [
 
1
+ import spaces # imported first, as ZeroGPU requires
2
+
3
  import sys
4
 
5
  sys.stdout.reconfigure(line_buffering=True)
6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
 
8
  import urllib.request
9
  from pathlib import Path
 
24
  import representation
25
  import utils as mmt_utils
26
 
27
+ model_card = ModelCard(
28
+ name="Multitrack Music Transformer",
29
+ description=(
30
+ "Generates symbolic multi-instrument music. Upload a MIDI seed to "
31
+ "continue it, list instruments to write a fresh piece for exactly "
32
+ "those, or leave both empty for a fully unconditioned generation. "
33
+ "Trained on the Symbolic Orchestral Database (SOD). Repository: "
34
+ "https://github.com/salu133445/mmt Paper: "
35
+ "https://arxiv.org/abs/2207.06983"
36
+ ),
37
+ author="Hao-Wen Dong, Ke Chen, Shlomo Dubnov, Julian McAuley, Taylor Berg-Kirkpatrick",
38
+ tags=[Subcategory.HOLISTIC],
39
+ )
40
+
41
  DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
42
  CKPT_DIR = Path(__file__).parent / "checkpoint"
43
 
 
48
 
49
 
50
  def load_model():
51
+ """Build the model on CPU and load the sod/ape checkpoint weights."""
 
 
52
  model = music_x_transformers.MusicXTransformer(
53
  dim=train_args["dim"],
54
  encoding=encoding,
 
70
  return model
71
 
72
 
 
 
 
 
73
 
74
 
75
  def download_musescore_soundfont():
 
88
  urllib.request.urlretrieve(prefix + "MuseScore_General.sf3", sf_path)
89
 
90
 
91
+ # pyharp runs process_fn in a worker, which imports this file as __mp_main__ and starts with the
92
+ # server, so the model is loaded and placed there alone, on its main thread, where ZeroGPU's
93
+ # emulation records the placement. The checkpoint is small (~80MB) and lives in the Space repo.
94
+ # The soundfont is fetched there too, so failures show in logs rather than after a request, and
95
+ # only one process writes it.
96
+ if __name__ != "__main__":
97
+ model = load_model().to(DEVICE)
98
+ download_musescore_soundfont()
99
 
100
 
101
  def encode_seed(midi_path, encoding, max_beat):
 
176
  the pipeline that touches CUDA, so it's the only part wrapped in
177
  @spaces.GPU. Everything else (MIDI decoding, audio synthesis) is
178
  CPU-bound and shouldn't burn ZeroGPU quota."""
 
 
 
 
 
179
  sos = encoding["type_code_map"]["start-of-song"]
180
  eos = encoding["type_code_map"]["end-of-song"]
181
 
 
204
  + (if requested) a rendered audio preview + a report of what happened
205
  with the requested instruments."""
206
  if input_midi_path:
207
+ try:
208
+ prefix_codes = encode_seed(input_midi_path, encoding, train_args["max_beat"])
209
+ except gr.Error:
210
+ raise
211
+ except Exception as exc:
212
+ raise gr.Error(f"Could not read the seed MIDI: {exc}") from exc
213
  instrument_note = "Seed MIDI provided -- Instruments field ignored."
214
  report_name = Path(input_midi_path).name
215
  else:
 
236
  return midi_path, audio_path, report_path
237
 
238
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
239
  if __name__ == "__main__":
240
  with gr.Blocks() as demo:
241
  input_components = [