import random import gradio as gr import numpy as np import rtmidi import MIDI import base64 import io from huggingface_hub import hf_hub_download from midi_synthesizer import MidiSynthesizer MAX_SEED = np.iinfo(np.int32).max class MIDIManager: def __init__(self): self.soundfont_path = hf_hub_download(repo_id="skytnt/midi-model", filename="soundfont.sf2") self.synthesizer = MidiSynthesizer(self.soundfont_path) self.loaded_midi = {} # Store uploaded MIDI files self.modified_files = [] # Track generated files self.is_playing = False self.midi_in = rtmidi.MidiIn() self.midi_in.open_port(0) if self.midi_in.get_ports() else None self.midi_in.set_callback(self.midi_callback) self.live_notes = [] def midi_callback(self, event, data=None): message, _ = event if len(message) >= 3 and message[0] & 0xF0 == 0x90: # Note On note, velocity = message[1], message[2] if velocity > 0: self.live_notes.append((note, velocity, 0)) # Time placeholder def load_midi(self, file_path): midi = MIDI.load(file_path) midi_id = f"midi_{len(self.loaded_midi)}" self.loaded_midi[midi_id] = midi return midi_id def extract_notes(self, midi): notes = [] for track in midi.tracks: for event in track.events: if event.type == 'note_on' and event.velocity > 0: notes.append((event.note, event.velocity, event.time)) return notes def generate_variation(self, midi_id, length_factor=2, variation=0.3): if midi_id not in self.loaded_midi: return None notes = self.extract_notes(self.loaded_midi[midi_id]) new_notes = [] for _ in range(int(length_factor)): for note, vel, time in notes: if random.random() < variation: new_note = min(127, max(0, note + random.randint(-2, 2))) new_vel = min(127, max(0, vel + random.randint(-10, 10))) new_notes.append((new_note, new_vel, time)) else: new_notes.append((note, vel, time)) new_midi = MIDI.MIDIFile(1) new_midi.addTrack() for note, vel, time in new_notes: new_midi.addNote(0, 0, note, time, 100, vel) output = io.BytesIO() new_midi.writeFile(output) midi_data = base64.b64encode(output.getvalue()).decode('utf-8') self.modified_files.append(midi_data) return midi_data def apply_synth_effect(self, midi_data, effect, intensity): midi = MIDI.load(io.BytesIO(base64.b64decode(midi_data))) if effect == "tempo": factor = 1 + (intensity - 0.5) * 0.4 for track in midi.tracks: for event in track.events: event.time = int(event.time * factor) output = io.BytesIO() midi.writeFile(output) midi_data = base64.b64encode(output.getvalue()).decode('utf-8') self.modified_files.append(midi_data) return midi_data def play_with_loop(self, midi_data): self.is_playing = True midi_file = MIDI.load(io.BytesIO(base64.b64decode(midi_data))) while self.is_playing: self.synthesizer.play_midi(midi_file) return "Stopped" def stop_playback(self): self.is_playing = False return "Stopping..." def save_live_midi(self): if not self.live_notes: return None midi = MIDI.MIDIFile(1) midi.addTrack() time_cum = 0 for note, vel, _ in self.live_notes: midi.addNote(0, 0, note, time_cum, 100, vel) time_cum += 100 # Simple timing output = io.BytesIO() midi.writeFile(output) midi_data = base64.b64encode(output.getvalue()).decode('utf-8') self.modified_files.append(midi_data) self.live_notes = [] # Reset after saving return midi_data midi_manager = MIDIManager() def create_download_list(): html = "