Spaces:
Running on Zero
Running on Zero
File size: 3,551 Bytes
f55264b 4bf5da8 f55264b fb903a9 f55264b 105e04d f55264b 459123a f55264b 3cdf6fd ae41f86 f55264b 459123a f55264b d4636df f55264b d4636df fb903a9 f55264b 5074382 154d1e1 5074382 ae41f86 199d6c3 5074382 ce8de21 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | from __future__ import annotations
import spaces # imported first, as ZeroGPU requires
from pathlib import Path
from tempfile import mkdtemp
import gradio as gr
from pyharp import ModelCard, build_endpoint
from pyharp.tags import Subcategory
from runtime import EXAMPLE, load_model, render
model_card = ModelCard(
name="DExter",
description=(
"Render an expressive piano MIDI performance from a MusicXML score. "
"Repository: https://github.com/anusfoil/DExter Paper: "
"https://arxiv.org/abs/2406.14850"
),
author="Huan Zhang, Shreyan Chowdhury, Carlos Eduardo Cancino-Chacón, et al.",
tags=[Subcategory.PERFORMANCE_RENDERING, 'piano'],
)
# pyharp runs process_fn in a worker, which imports this file as __mp_main__ and starts with the
# server. The model is built and placed there alone, where ZeroGPU's emulation records the CUDA
# placement. ZeroGPU runs each request in a process of its own, so a model first built inside the
# GPU call was built again, inside the reservation, every time.
if __name__ != "__main__":
load_model()
def process_fn(xml_path: str | None, use_example: bool, seed: int) -> str:
# Checked before the GPU is reserved, so a request without a score spends none of the
# caller's ZeroGPU allowance
if not xml_path and not use_example:
raise gr.Error("Upload a MusicXML score or select the example.")
return perform(xml_path or str(EXAMPLE), int(seed))
@spaces.GPU(duration=75)
def perform(source: str, seed: int) -> str:
# mkdtemp, not a context manager: that would delete the directory on exit,
# taking the rendered file with it before the caller can read it, which is
# what the block-cache call here was working around. That also reached for
# output_components, which exists only under __main__ and so is undefined
# in the worker process that imports this module.
output = Path(mkdtemp(prefix="dexter-")) / "performance.mid"
try:
render(source, output, int(seed))
except Exception as exc:
raise gr.Error(f"DExter inference failed: {exc}") from exc
return str(output)
if __name__ == "__main__":
with gr.Blocks(title="DExter", delete_cache=(3600, 86400)) as demo:
inputs = [
gr.File(type="filepath", file_types=[".xml", ".musicxml"], label="Piano MusicXML score").set_info(
"Uncompressed MusicXML, up to 2 MB and 400 notes. Upload takes priority over the example."
),
gr.Checkbox(value=True, label="Use bundled Schubert example if no file is uploaded",
info=(
"Runs on a bundled Schubert excerpt when you have not uploaded a "
"score."
)),
gr.Number(value=13, precision=0, minimum=0, maximum=2147483647, label="Random seed",
info=(
"Changes the performance the model samples. The same seed and score "
"give the same performance."
)),
]
output_components = [gr.File(type="filepath", label="Expressive MIDI performance", file_types=[".mid", ".midi"]).set_info((
"The score played with timing, dynamics and pedalling the model "
"chose."
))]
build_endpoint(
model_card=model_card,
input_components=inputs,
output_components=output_components,
process_fn=process_fn,
)
demo.queue(default_concurrency_limit=1, max_size=4).launch(show_error=True, pwa=True, max_file_size="2mb")
|