Spaces:
Running on Zero
Running on Zero
| from __future__ import annotations | |
| import threading | |
| from pathlib import Path | |
| import torch | |
| from midigpt import Score | |
| from midigpt.inference import ( | |
| GenerationRequest, | |
| InferenceConfig, | |
| InferenceEngine, | |
| TrackPrompt, | |
| ) | |
| MODEL_REPOSITORY = "Metacreation/MIDI-GPT" | |
| MODEL_NAME = "yellow_small" | |
| MODEL_FILENAME = "yellow_small-final.safetensors" | |
| _engine: InferenceEngine | None = None | |
| _engine_lock = threading.Lock() | |
| def _patch_searchsorted_device_mismatch() -> None: | |
| if getattr(torch.searchsorted, "_midigpt_device_compat", False): | |
| return | |
| original = torch.searchsorted | |
| def device_safe_searchsorted(sorted_sequence, values, *args, **kwargs): | |
| if isinstance(values, torch.Tensor) and values.device != sorted_sequence.device: | |
| values = values.to(sorted_sequence.device) | |
| return original(sorted_sequence, values, *args, **kwargs) | |
| device_safe_searchsorted._midigpt_device_compat = True | |
| torch.searchsorted = device_safe_searchsorted | |
| _patch_searchsorted_device_mismatch() | |
| def _get_engine() -> InferenceEngine: | |
| global _engine | |
| if _engine is not None: | |
| return _engine | |
| with _engine_lock: | |
| if _engine is None: | |
| device = "cuda" if torch.cuda.is_available() else "cpu" | |
| _engine = InferenceEngine.from_pretrained( | |
| MODEL_REPOSITORY, | |
| filename=MODEL_FILENAME, | |
| device=device, | |
| ) | |
| return _engine | |
| def rewrite_bars( | |
| input_path: Path, | |
| output_path: Path, | |
| track_index: int, | |
| start_bar: int, | |
| bar_count: int, | |
| temperature: float, | |
| top_p: float, | |
| seed: int, | |
| polyphony_limit: int, | |
| ) -> dict: | |
| score = Score.from_midi(str(input_path)) | |
| track_count = len(score.tracks) | |
| if track_count == 0: | |
| raise ValueError("The MIDI file does not contain any tracks.") | |
| if track_index < 0 or track_index >= track_count: | |
| raise ValueError( | |
| f"Track index {track_index} is out of range. " | |
| f"This file has {track_count} tracks (0-{track_count - 1})." | |
| ) | |
| bar_counts = [len(track.bars) for track in score.tracks] | |
| if len(set(bar_counts)) != 1: | |
| raise ValueError( | |
| "MIDI-GPT requires every track to span the same number of bars." | |
| ) | |
| total_bars = bar_counts[0] | |
| if total_bars < 4: | |
| raise ValueError( | |
| f"MIDI-GPT requires at least 4 bars. This file has {total_bars}." | |
| ) | |
| if start_bar < 0 or start_bar + bar_count > total_bars: | |
| raise ValueError( | |
| f"Bars {start_bar}-{start_bar + bar_count - 1} are outside " | |
| f"the available range 0-{total_bars - 1}." | |
| ) | |
| target_bars = list(range(start_bar, start_bar + bar_count)) | |
| original_notes = [ | |
| [ | |
| ( | |
| note.pitch, | |
| note.velocity, | |
| note.onset_ticks, | |
| note.duration_ticks, | |
| ) | |
| for note in score.tracks[track_index].bars[index].notes | |
| ] | |
| for index in target_bars | |
| ] | |
| prompts = [] | |
| for index in range(track_count): | |
| prompts.append( | |
| TrackPrompt( | |
| id=index, | |
| bars=target_bars if index == track_index else [], | |
| ) | |
| ) | |
| model_dim = 4 if total_bars < 8 else 8 | |
| request = GenerationRequest( | |
| tracks=prompts, | |
| config=InferenceConfig( | |
| temperature=temperature, | |
| top_p=top_p, | |
| seed=seed, | |
| model_dim=model_dim, | |
| mask_mode="attention", | |
| bars_per_step=1, | |
| polyphony_hard_limit=polyphony_limit, | |
| novelty_check=False, | |
| ), | |
| ) | |
| result = _get_engine().session(score, request).run() | |
| output_path.parent.mkdir(parents=True, exist_ok=True) | |
| result.to_midi(str(output_path)) | |
| generated_notes = sum( | |
| len(result.tracks[track_index].bars[index].notes) | |
| for index in target_bars | |
| ) | |
| generated_content = [ | |
| [ | |
| ( | |
| note.pitch, | |
| note.velocity, | |
| note.onset_ticks, | |
| note.duration_ticks, | |
| ) | |
| for note in result.tracks[track_index].bars[index].notes | |
| ] | |
| for index in target_bars | |
| ] | |
| return { | |
| "model": MODEL_NAME, | |
| "track_count": track_count, | |
| "total_bars": total_bars, | |
| "rewritten_track": track_index, | |
| "rewritten_bars": target_bars, | |
| "generated_notes": generated_notes, | |
| "content_changed": generated_content != original_notes, | |
| "seed": seed, | |
| } | |