speaking-midi / player.py
k-l-lambda's picture
Deploy dense piano fitting in both Space input modes
f97fef1
Raw History Blame Contribute Delete
1.38 kB
"""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')