"""Build a browser player payload from the exported MIDI and its exact piano render.""" import base64 import json from pathlib import Path import mido import soundfile as sf UI = Path(__file__).parent / 'ui' def player_payload(directory, stem="refined"): directory = Path(directory) clock = 0.0 active = {} notes = [] for message in mido.MidiFile(directory / f'{stem}.mid'): clock += message.time if message.type == 'note_on' and message.velocity: active[(message.channel, message.note)] = (clock, message.velocity) elif message.type == 'note_off' or (message.type == 'note_on' and not message.velocity): start, velocity = active.pop((message.channel, message.note)) notes.append(dict(pitch=message.note, start=start, duration=clock-start, velocity=velocity)) if active: raise ValueError('MIDI contains unterminated notes') wav = directory / f'{stem}.wav' return json.dumps(dict(notes=notes, duration=sf.info(wav).duration, audio='data:audio/wav;base64,' + base64.b64encode(wav.read_bytes()).decode())) def player_component(gr): return gr.HTML(value='', html_template=(UI/'player.html').read_text(), css_template=(UI/'player.css').read_text(), js_on_load=(UI/'player.js').read_text(), elem_id='midi-player')