import streamlit as st
import streamlit.components.v1 as components
import rtmidi
import json
from pathlib import Path
import time
def create_midi_output():
"""Initialize MIDI output"""
midi_out = rtmidi.MidiOut()
available_ports = midi_out.get_ports()
if available_ports:
st.sidebar.write("Available MIDI ports:", available_ports)
selected_port = st.sidebar.selectbox("Select MIDI Output", range(len(available_ports)),
format_func=lambda x: available_ports[x])
midi_out.open_port(selected_port)
else:
st.sidebar.warning("No MIDI output ports available")
midi_out.open_virtual_port("Virtual MIDI Output")
return midi_out
def get_piano_html():
"""Return the HTML content for the piano keyboard"""
return """
"""
def main():
st.title("MIDI Piano Keyboard")
st.write("Click keys or use your computer keyboard (A-K and W-U for white and black keys)")
# Initialize MIDI output
midi_out = create_midi_output()
# Create a placeholder for MIDI messages
midi_message_placeholder = st.empty()
# Display the piano keyboard
components.html(
get_piano_html(),
height=200,
scrolling=False
)
# Handle MIDI events from JavaScript
if 'midi_events' not in st.session_state:
st.session_state.midi_events = []
def handle_midi_event(event):
if event['type'] == 'noteOn':
midi_out.send_message([0x90, event['note'], event['velocity']])
midi_message_placeholder.write(f"Note On: {event['note']}")
elif event['type'] == 'noteOff':
midi_out.send_message([0x80, event['note'], event['velocity']])
midi_message_placeholder.write(f"Note Off: {event['note']}")
# JavaScript callback handler
components.html(
"""
""",
height=0
)
# Add a loop to continuously check for new MIDI events
while True:
time.sleep(0.1) # Small delay to prevent excessive CPU usage
# Process any pending MIDI events
for event in st.session_state.midi_events:
handle_midi_event(event)
st.session_state.midi_events = []
if __name__ == "__main__":
main()