multimodalart HF Staff commited on
Commit
44e4078
·
verified ·
1 Parent(s): 89f6edb

Upload folder using huggingface_hub

Browse files
Files changed (6) hide show
  1. .gitattributes +2 -0
  2. README.md +25 -7
  3. app.py +164 -0
  4. example_melody.wav +3 -0
  5. example_piano.wav +3 -0
  6. requirements.txt +6 -0
.gitattributes CHANGED
@@ -33,3 +33,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ example_melody.wav filter=lfs diff=lfs merge=lfs -text
37
+ example_piano.wav filter=lfs diff=lfs merge=lfs -text
README.md CHANGED
@@ -1,13 +1,31 @@
1
  ---
2
- title: Muscriptor Music Transcription
3
- emoji: 🔥
4
- colorFrom: gray
5
- colorTo: green
6
  sdk: gradio
7
  sdk_version: 6.20.0
8
- python_version: '3.12'
9
  app_file: app.py
10
- pinned: false
 
 
11
  ---
12
 
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: MuScriptor Music Transcription
3
+ emoji: 🎵
4
+ colorFrom: red
5
+ colorTo: blue
6
  sdk: gradio
7
  sdk_version: 6.20.0
 
8
  app_file: app.py
9
+ short_description: Convert music audio to MIDI with MuScriptor
10
+ python_version: "3.12"
11
+ startup_duration_timeout: 30m
12
  ---
13
 
14
+ # MuScriptor Music Transcription
15
+
16
+ This Space demonstrates [MuScriptor](https://huggingface.co/MuScriptor/muscriptor-medium), a ~300M parameter multi-instrument automatic music transcription (AMT) model developed by Mirelo x Kyutai.
17
+
18
+ Upload a music recording (any genre, multiple instruments) and the model will transcribe it into a downloadable MIDI file with per-note onset, offset, pitch, and instrument information.
19
+
20
+ ## How it works
21
+
22
+ 1. Upload an audio file (WAV, MP3, FLAC, etc.) or record from microphone
23
+ 2. Optionally select which instruments are present to improve accuracy
24
+ 3. Click "Transcribe" to get a downloadable MIDI file
25
+
26
+ ## Model details
27
+
28
+ - **Architecture**: Decoder-only Transformer (dim=1024, 16 heads, 24 layers)
29
+ - **Input**: 16 kHz mono audio → mel-spectrogram (512 mel bins)
30
+ - **Output**: MIDI-like token sequence using the MT3_FULL_PLUS taxonomy (36 instrument subgroups)
31
+ - **License**: CC-BY-NC 4.0 (weights) / MIT (code)
app.py ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import spaces # MUST come before any torch / CUDA-touching import
2
+ import torch
3
+ import gradio as gr
4
+ import tempfile
5
+ import os
6
+ import time
7
+ import sys
8
+ import io
9
+
10
+ from muscriptor.transcription_model import TranscriptionModel
11
+ from muscriptor.events import NoteStartEvent, NoteEndEvent, ProgressEvent
12
+ from muscriptor.tokenizer.mt3 import MT3_FULL_PLUS_GROUP_NAMES
13
+
14
+ # Load the model at module scope — ZeroGPU intercepts .to("cuda") and packs
15
+ # weights to disk, streaming them into VRAM on the first @spaces.GPU call.
16
+ print("[muscriptor-space] Loading model...", file=sys.stderr, flush=True)
17
+ t0 = time.perf_counter()
18
+ model = TranscriptionModel.load_model("medium", device="cuda")
19
+ print(f"[muscriptor-space] Model loaded in {time.perf_counter() - t0:.1f}s", file=sys.stderr, flush=True)
20
+
21
+ # Build the instrument choices list (sorted by group ID for stable ordering)
22
+ INSTRUMENT_CHOICES = sorted(MT3_FULL_PLUS_GROUP_NAMES.keys(), key=lambda k: MT3_FULL_PLUS_GROUP_NAMES[k])
23
+
24
+ CSS = """
25
+ #col-container { max-width: 1100px; margin: 0 auto; }
26
+ .dark .gradio-container { color: var(--body-text-color); }
27
+ """
28
+
29
+
30
+ @spaces.GPU(duration=120)
31
+ def transcribe_audio(
32
+ audio_path: str,
33
+ instruments: list[str] | None,
34
+ use_sampling: bool,
35
+ temperature: float,
36
+ progress=gr.Progress(track_tqdm=True),
37
+ ):
38
+ """Transcribe an audio recording into a downloadable MIDI file.
39
+
40
+ Upload any music recording (multi-instrument, any genre) and MuScriptor
41
+ will convert it into a MIDI file with per-note onset, offset, pitch, and
42
+ instrument information.
43
+
44
+ Args:
45
+ audio_path: Path to the uploaded audio file (wav, mp3, flac, etc.).
46
+ instruments: Optional list of instrument group names to condition the
47
+ transcription (improves coherence when you know which instruments
48
+ are present). Leave empty for automatic (unconditioned) transcription.
49
+ use_sampling: If True, use stochastic sampling instead of greedy decoding.
50
+ temperature: Sampling temperature (only used when use_sampling is True).
51
+ """
52
+ if audio_path is None:
53
+ return None, "Please upload an audio file first."
54
+
55
+ t0 = time.perf_counter()
56
+
57
+ # Run the transcription — returns MIDI bytes
58
+ try:
59
+ midi_bytes = model.transcribe_to_midi(
60
+ audio_path,
61
+ instruments=instruments if instruments else None,
62
+ use_sampling=use_sampling,
63
+ temperature=temperature,
64
+ )
65
+ except Exception as e:
66
+ return None, f"Transcription failed: {e}"
67
+
68
+ elapsed = time.perf_counter() - t0
69
+
70
+ # Write to a temporary file for download
71
+ tmp = tempfile.NamedTemporaryFile(suffix=".mid", delete=False)
72
+ tmp.write(midi_bytes)
73
+ tmp.close()
74
+
75
+ # Also collect note statistics for the summary
76
+ note_count = 0
77
+ instrument_set = set()
78
+ try:
79
+ from mido import MidiFile
80
+ midi = MidiFile(tmp.name)
81
+ for track in midi.tracks:
82
+ for msg in track:
83
+ if msg.type == "note_on" and msg.velocity > 0:
84
+ note_count += 1
85
+ instrument_set.add(msg.program if not msg.is_meta else 0)
86
+ except Exception:
87
+ pass
88
+
89
+ summary = (
90
+ f"Transcription complete in {elapsed:.1f}s. "
91
+ f"Found {note_count} notes across {len(instrument_set)} instrument program(s). "
92
+ f"Download the MIDI file below."
93
+ )
94
+
95
+ return tmp.name, summary
96
+
97
+
98
+ with gr.Blocks(theme=gr.themes.Citrus(), css=CSS) as demo:
99
+ with gr.Column(elem_id="col-container"):
100
+ gr.Markdown(
101
+ """
102
+ # MuScriptor — Music Transcription (Audio → MIDI)
103
+
104
+ Upload a music recording and get a downloadable MIDI file with transcribed notes.
105
+ MuScriptor is a ~300M parameter multi-instrument automatic music transcription model
106
+ developed by [Mirelo](https://www.mirelo.ai/) x [Kyutai](https://kyutai.org/).
107
+
108
+ [Model card](https://huggingface.co/MuScriptor/muscriptor-medium) · [Code](https://github.com/muscriptor/muscriptor) · [Audio samples](https://muscriptor.github.io)
109
+ """
110
+ )
111
+
112
+ with gr.Row():
113
+ audio_input = gr.Audio(
114
+ label="Upload or record audio",
115
+ type="filepath",
116
+ sources=["upload", "microphone"],
117
+ )
118
+
119
+ with gr.Accordion("Advanced settings", open=False):
120
+ instrument_checkbox = gr.CheckboxGroup(
121
+ choices=INSTRUMENT_CHOICES,
122
+ value=[],
123
+ label="Instrument conditioning (optional)",
124
+ info="Select instruments present in the audio to improve transcription accuracy. Leave empty for automatic detection.",
125
+ )
126
+ use_sampling = gr.Checkbox(
127
+ label="Use sampling (stochastic decoding)",
128
+ value=False,
129
+ info="If enabled, uses temperature-based sampling instead of greedy decoding.",
130
+ )
131
+ temperature = gr.Slider(
132
+ label="Temperature",
133
+ minimum=0.1,
134
+ maximum=2.0,
135
+ value=1.0,
136
+ step=0.1,
137
+ info="Sampling temperature (only used when sampling is enabled).",
138
+ )
139
+
140
+ transcribe_btn = gr.Button("Transcribe", variant="primary")
141
+
142
+ midi_output = gr.File(label="Download MIDI file")
143
+ summary_output = gr.Textbox(label="Summary", interactive=False)
144
+
145
+ transcribe_btn.click(
146
+ fn=transcribe_audio,
147
+ inputs=[audio_input, instrument_checkbox, use_sampling, temperature],
148
+ outputs=[midi_output, summary_output],
149
+ api_name="transcribe",
150
+ )
151
+
152
+ gr.Examples(
153
+ examples=[
154
+ ["example_piano.wav", [], False, 1.0],
155
+ ["example_melody.wav", [], False, 1.0],
156
+ ],
157
+ inputs=[audio_input, instrument_checkbox, use_sampling, temperature],
158
+ outputs=[midi_output, summary_output],
159
+ fn=transcribe_audio,
160
+ cache_examples=True,
161
+ cache_mode="lazy",
162
+ )
163
+
164
+ demo.launch(mcp_server=True)
example_melody.wav ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ac1baf0ca3cd75e9e91fff3fa03980eb2696def24ac36dd78786beffc9d87960
3
+ size 256044
example_piano.wav ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:5c69639c8efc102188d0f55cbc7b05528339e5a4b38062dccf7a99bb71d5efac
3
+ size 160044
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ git+https://github.com/muscriptor/muscriptor.git
2
+ numpy>=1.24
3
+ einops>=0.4
4
+ mido>=1.3
5
+ safetensors>=0.4
6
+ soundfile>=0.14.0