Spaces:
Sleeping
Sleeping
Load the model and soundfont in the worker, and check the MIDI
Browse filesLoads 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>
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.
|
| 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 |
-
#
|
| 97 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 = [
|