File size: 4,454 Bytes
9ae6560
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
from __future__ import annotations

import uuid
from pathlib import Path
from tempfile import gettempdir

import gradio as gr

try:
    import spaces
except ImportError:
    class spaces:
        class GPU:
            def __init__(self, func=None, duration=120):
                self.func = func

            def __call__(self, *args, **kwargs):
                if self.func is not None:
                    return self.func(*args, **kwargs)
                return args[0]

from pyharp import ModelCard, build_endpoint

from midi_gpt_runtime import rewrite_bars

OUTPUT_ROOT = Path(gettempdir()) / "midi_gpt_outputs"

model_card = ModelCard(
    name="MIDI-GPT",
    description=(
        "Rewrite selected bars in a multitrack MIDI arrangement while "
        "preserving the rest of the composition."
    ),
    author="Metacreation Lab",
    tags=[
        "music-generation",
        "symbolic-music",
        "midi",
        "music-editing",
        "multitrack",
    ],
)


@spaces.GPU(duration=120)
def process_fn(
    midi_path: str | None,
    track_index: float,
    start_bar: float,
    bar_count: str,
    temperature: float,
    top_p: float,
    seed: float,
    polyphony_limit: float,
) -> tuple[str, dict]:
    if not midi_path:
        raise gr.Error("Please upload a MIDI file.")

    source = Path(midi_path)
    if source.suffix.lower() not in {".mid", ".midi"}:
        raise gr.Error("The input must be a .mid or .midi file.")

    output_dir = OUTPUT_ROOT / uuid.uuid4().hex
    output_path = output_dir / f"{source.stem}-rewritten.mid"

    try:
        details = rewrite_bars(
            input_path=source,
            output_path=output_path,
            track_index=int(track_index),
            start_bar=int(start_bar),
            bar_count=int(bar_count),
            temperature=float(temperature),
            top_p=float(top_p),
            seed=int(seed),
            polyphony_limit=int(polyphony_limit),
        )
    except Exception as exc:
        raise gr.Error(f"MIDI-GPT inference failed: {exc}") from exc

    return str(output_path), details


with gr.Blocks(title="MIDI-GPT") as demo:
    input_components = [
        gr.File(
            type="filepath",
            file_types=[".mid", ".midi"],
            label="Input MIDI",
        )
        .harp_required(True)
        .set_info("A multitrack MIDI file containing at least four bars."),
        gr.Number(
            value=0,
            minimum=0,
            precision=0,
            label="Track Index",
            info="Zero-based index of the track to rewrite.",
        ),
        gr.Number(
            value=0,
            minimum=0,
            precision=0,
            label="Start Bar",
            info="Zero-based index of the first bar to rewrite.",
        ),
        gr.Dropdown(
            choices=["1", "2", "4"],
            value="2",
            label="Number of Bars",
            info="Number of consecutive bars to rewrite.",
        ),
        gr.Slider(
            minimum=0.5,
            maximum=1.5,
            step=0.1,
            value=1.0,
            label="Temperature",
            info="Higher values produce more varied results.",
        ),
        gr.Slider(
            minimum=0.5,
            maximum=1.0,
            step=0.05,
            value=0.95,
            label="Top-p",
            info="Nucleus sampling threshold.",
        ),
        gr.Number(
            value=0,
            minimum=0,
            maximum=2_147_483_647,
            precision=0,
            label="Seed",
            info="Use the same seed and controls to reproduce a result.",
        ),
        gr.Slider(
            minimum=0,
            maximum=12,
            step=1,
            value=0,
            label="Maximum Polyphony",
            info="Maximum simultaneous notes; 0 leaves it unrestricted.",
        ),
    ]
    output_components = [
        gr.File(
            type="filepath",
            file_types=[".mid", ".midi"],
            label="Rewritten MIDI",
        ).set_info("Editable multitrack MIDI with the selected bars replaced."),
        gr.JSON(label="Generation Details"),
    ]
    build_endpoint(
        model_card=model_card,
        input_components=input_components,
        output_components=output_components,
        process_fn=process_fn,
    )


if __name__ == "__main__":
    demo.queue(default_concurrency_limit=1).launch(
        show_error=True,
        pwa=True,
    )