Spaces:
Running on Zero
Running on Zero
| from __future__ import annotations | |
| import uuid | |
| from pathlib import Path | |
| import gradio as gr | |
| import soundfile as sf | |
| try: | |
| import spaces | |
| except ImportError: | |
| class spaces: | |
| class GPU: | |
| def __init__(self, func=None, duration=60): | |
| self.func = func | |
| def __call__(self, *args, **kwargs): | |
| if self.func is not None: | |
| return self.func(*args, **kwargs) | |
| return args[0] | |
| from game_runtime import transcribe as transcribe_with_game | |
| from pyharp import ModelCard, build_endpoint | |
| MAX_AUDIO_SECONDS = 30 | |
| OUTPUT_ROOT = Path("/tmp/game_outputs") | |
| LANGUAGE_CODES = { | |
| "Automatic": "", | |
| "English": "en", | |
| "Japanese": "ja", | |
| "Cantonese": "yue", | |
| "Mandarin Chinese": "zh", | |
| } | |
| model_card = ModelCard( | |
| name="GAME", | |
| description=( | |
| "Transcribe a monophonic singing voice recording " | |
| "into editable MIDI and detailed note data." | |
| ), | |
| author="OpenVPI", | |
| tags=[ | |
| "music-information-retrieval", | |
| "singing-voice", | |
| "transcription", | |
| "audio-to-midi", | |
| ], | |
| ) | |
| def process_fn( | |
| audio_path: str | None, | |
| language: str, | |
| steps: str, | |
| ) -> tuple[str, str]: | |
| if not audio_path: | |
| raise gr.Error("Please upload a singing voice recording.") | |
| try: | |
| duration = sf.info(audio_path).duration | |
| except Exception as exc: | |
| raise gr.Error(f"Could not read the uploaded audio: {exc}") from exc | |
| if duration > MAX_AUDIO_SECONDS: | |
| raise gr.Error( | |
| f"Audio must be no longer than {MAX_AUDIO_SECONDS} seconds. " | |
| f"Received {duration:.1f} seconds." | |
| ) | |
| output_dir = OUTPUT_ROOT / uuid.uuid4().hex | |
| output_dir.mkdir(parents=True, exist_ok=True) | |
| try: | |
| transcribe_with_game( | |
| audio_path=Path(audio_path), | |
| output_dir=output_dir, | |
| language_code=LANGUAGE_CODES[language], | |
| steps=int(steps), | |
| ) | |
| except Exception as exc: | |
| raise gr.Error(f"GAME inference failed: {exc}") from exc | |
| midi_files = sorted(output_dir.glob("*.mid")) | |
| csv_files = sorted(output_dir.glob("*.csv")) | |
| if not midi_files or not csv_files: | |
| raise gr.Error("GAME did not produce the expected MIDI and CSV files.") | |
| return str(midi_files[0]), str(csv_files[0]) | |
| with gr.Blocks(title="GAME Singing to MIDI") as demo: | |
| input_components = [ | |
| gr.Audio( | |
| type="filepath", | |
| label="Singing Voice", | |
| ) | |
| .harp_required(True) | |
| .set_info( | |
| "A monophonic singing voice recording, up to 30 seconds long." | |
| ), | |
| gr.Dropdown( | |
| choices=list(LANGUAGE_CODES), | |
| value="Automatic", | |
| label="Language", | |
| info="Use Automatic when the singing language is unknown.", | |
| ), | |
| gr.Dropdown( | |
| choices=["1", "2", "4", "8"], | |
| value="2", | |
| label="D3PM Steps", | |
| info=( | |
| "More steps may improve transcription at the cost " | |
| "of longer processing time." | |
| ), | |
| ), | |
| ] | |
| output_components = [ | |
| gr.File( | |
| type="filepath", | |
| file_types=[".mid", ".midi"], | |
| label="Transcribed MIDI", | |
| ).set_info("Editable MIDI transcription."), | |
| gr.File( | |
| type="filepath", | |
| file_types=[".csv"], | |
| label="Detailed Note Data", | |
| ).set_info("Note timing and pitch data generated by GAME."), | |
| ] | |
| 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, | |
| ) | |