Vansh Chugh commited on
Commit
db50e80
·
1 Parent(s): 6017d6d

fail soft on unmatched instrument names, add matching-status output

Browse files
Files changed (1) hide show
  1. app.py +41 -15
app.py CHANGED
@@ -18,13 +18,14 @@ except ImportError:
18
  return func
19
 
20
  import tempfile
 
21
 
22
  import gradio as gr
23
  import torch
24
  from pyharp import ModelCard, build_endpoint
25
 
26
  from muscriptor import TranscriptionModel
27
- from muscriptor.tokenizer.mt3 import resolve_instrument_names
28
 
29
  model_card = ModelCard(
30
  name="MuScriptor",
@@ -36,6 +37,8 @@ model_card = ModelCard(
36
  tags=["transcription", "midi", "multi-instrument"],
37
  )
38
 
 
 
39
  _models: dict[str, TranscriptionModel] = {}
40
 
41
 
@@ -51,19 +54,34 @@ def _get_model(variant: str) -> TranscriptionModel:
51
  return _models[variant]
52
 
53
 
54
- def _parse_instruments(text: str) -> list[str] | None:
55
- """Turn the comma-separated instruments box into exact group names.
56
 
57
- Accepts unambiguous abbreviations, same as the CLI's --instruments.
58
- Blank input means "no restriction, let the model decide".
 
59
  """
60
  tokens = [t for t in text.split(",") if t.strip()]
61
  if not tokens:
62
- return None
63
- try:
64
- return resolve_instrument_names(tokens)
65
- except ValueError as e:
66
- raise gr.Error(str(e))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
 
68
 
69
  @spaces.GPU
@@ -74,10 +92,10 @@ def process_fn(
74
  instruments_text: str,
75
  use_sampling: bool,
76
  temperature: float,
77
- ) -> str:
78
- """Transcribe the input audio to a MIDI file and return its path."""
79
  model = _get_model(variant)
80
- instruments = _parse_instruments(instruments_text)
81
 
82
  midi_bytes = model.transcribe_to_midi(
83
  input_audio_path,
@@ -90,7 +108,12 @@ def process_fn(
90
  f.write(midi_bytes)
91
  output_midi_path = f.name
92
 
93
- return output_midi_path
 
 
 
 
 
94
 
95
 
96
  with gr.Blocks() as demo:
@@ -105,7 +128,7 @@ with gr.Blocks() as demo:
105
  gr.Textbox(
106
  value="",
107
  label="Instruments",
108
- info="Comma-separated instrument names to restrict decoding to, e.g. acoustic_piano,violin,trumpet,drums,etc. Leave blank to let the model detect instruments on its own.",
109
  ),
110
  gr.Checkbox(
111
  label="Use Sampling",
@@ -124,6 +147,9 @@ with gr.Blocks() as demo:
124
  gr.File(type="filepath", file_types=[".mid", ".midi"], label="Output MIDI").set_info(
125
  "Transcribed MIDI notes."
126
  ),
 
 
 
127
  ]
128
 
129
  build_endpoint(
 
18
  return func
19
 
20
  import tempfile
21
+ from pathlib import Path
22
 
23
  import gradio as gr
24
  import torch
25
  from pyharp import ModelCard, build_endpoint
26
 
27
  from muscriptor import TranscriptionModel
28
+ from muscriptor.tokenizer.mt3 import MT3_FULL_PLUS_GROUP_NAMES, resolve_instrument_names
29
 
30
  model_card = ModelCard(
31
  name="MuScriptor",
 
37
  tags=["transcription", "midi", "multi-instrument"],
38
  )
39
 
40
+ _VALID_INSTRUMENTS = ", ".join(MT3_FULL_PLUS_GROUP_NAMES)
41
+
42
  _models: dict[str, TranscriptionModel] = {}
43
 
44
 
 
54
  return _models[variant]
55
 
56
 
57
+ def _resolve_instruments(text: str) -> tuple[list[str] | None, str]:
58
+ """Resolve the comma-separated instruments box into exact group names.
59
 
60
+ Names that don't resolve are dropped rather than raised: HARP's client
61
+ shows generic error message, so an error here would not reach the user.
62
+ Second value returns what happened as a .txt output
63
  """
64
  tokens = [t for t in text.split(",") if t.strip()]
65
  if not tokens:
66
+ return None, "No instrument restriction requested."
67
+
68
+ resolved: list[str] = []
69
+ problems: list[str] = []
70
+ for token in tokens:
71
+ try:
72
+ resolved.extend(resolve_instrument_names([token]))
73
+ except ValueError as e:
74
+ problems.append(str(e))
75
+
76
+ if not problems:
77
+ return resolved, f"Matched: {', '.join(resolved)}."
78
+
79
+ note = "; ".join(problems)
80
+ if resolved:
81
+ note = f"Matched: {', '.join(resolved)}. Ignored: {note}"
82
+ else:
83
+ note = f"No valid instrument names found, transcribing unrestricted. {note}"
84
+ return (resolved or None), note
85
 
86
 
87
  @spaces.GPU
 
92
  instruments_text: str,
93
  use_sampling: bool,
94
  temperature: float,
95
+ ) -> tuple[str, str]:
96
+ """Transcribe the input audio to MIDI, plus a note on the Instruments field."""
97
  model = _get_model(variant)
98
+ instruments, instrument_note = _resolve_instruments(instruments_text)
99
 
100
  midi_bytes = model.transcribe_to_midi(
101
  input_audio_path,
 
108
  f.write(midi_bytes)
109
  output_midi_path = f.name
110
 
111
+ notes_text = f"{Path(input_audio_path).name}\n\nInstrument Matching\n{instrument_note}\n"
112
+ with tempfile.NamedTemporaryFile(mode="w", suffix=".txt", delete=False) as f:
113
+ f.write(notes_text)
114
+ output_notes_path = f.name
115
+
116
+ return output_midi_path, output_notes_path
117
 
118
 
119
  with gr.Blocks() as demo:
 
128
  gr.Textbox(
129
  value="",
130
  label="Instruments",
131
+ info=f"Comma-separated instrument names to restrict decoding to. Valid names: {_VALID_INSTRUMENTS}. Leave blank to let the model detect instruments on its own.",
132
  ),
133
  gr.Checkbox(
134
  label="Use Sampling",
 
147
  gr.File(type="filepath", file_types=[".mid", ".midi"], label="Output MIDI").set_info(
148
  "Transcribed MIDI notes."
149
  ),
150
+ gr.File(type="filepath", file_types=[".txt"], label="Instrument Matching").set_info(
151
+ "Which requested instrument names were matched or ignored."
152
+ ),
153
  ]
154
 
155
  build_endpoint(