Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| from fastapi import FastAPI, Request | |
| import json | |
| # THIS VERSION WORKS | |
| # AND SENDS BACK A NOTE A FIFTH HIGHER THAN THE INPUT NOT ON THE SELECTED PORT | |
| # | |
| # ✅ Create FastAPI App | |
| app = FastAPI() | |
| # ✅ MIDI Processing Function in Python | |
| async def process_midi(request: Request): | |
| try: | |
| midi_data = await request.json() | |
| note = midi_data["note"] | |
| velocity = midi_data["velocity"] | |
| print(f"🎹 Received MIDI Note: {note}, Velocity: {velocity}") | |
| # 🚀 Process MIDI data (example: Transpose + Generate New Notes) | |
| generated_note = (note + 5) % 128 # Transpose up by 3 semitones | |
| generated_velocity = min(velocity + 10, 127) # Increase velocity slightly | |
| # ✅ Send MIDI Response Back to Client | |
| return { | |
| "status": "success", | |
| "generated_note": generated_note, | |
| "generated_velocity": generated_velocity, | |
| "original_note": note | |
| } | |
| except Exception as e: | |
| print(f"🚨 Error processing MIDI: {str(e)}") | |
| return {"status": "error", "message": str(e)} | |
| # ✅ JavaScript to Capture and Send MIDI Data | |
| midi_js = """ | |
| <script> | |
| let midiAccess = null; | |
| let selectedInput = null; | |
| let selectedOutput = null; | |
| // ✅ Request MIDI Access | |
| navigator.requestMIDIAccess() | |
| .then(access => { | |
| console.log("✅ MIDI Access Granted!"); | |
| midiAccess = access; | |
| updateMIDIDevices(); | |
| midiAccess.onstatechange = updateMIDIDevices; | |
| }) | |
| .catch(err => console.error("🚨 MIDI API Error:", err)); | |
| // ✅ Update MIDI Input & Output Menus | |
| function updateMIDIDevices() { | |
| let inputSelect = document.getElementById("midiInput"); | |
| let outputSelect = document.getElementById("midiOutput"); | |
| if (!inputSelect || !outputSelect) { | |
| console.error("❌ MIDI dropdowns not found!"); | |
| return; | |
| } | |
| // Clear existing options | |
| inputSelect.innerHTML = '<option value="">Select MIDI Input</option>'; | |
| outputSelect.innerHTML = '<option value="">Select MIDI Output</option>'; | |
| // Populate MIDI Input Devices | |
| midiAccess.inputs.forEach((input, key) => { | |
| let option = document.createElement("option"); | |
| option.value = key; | |
| option.textContent = input.name || `MIDI Input ${key}`; | |
| inputSelect.appendChild(option); | |
| }); | |
| // Populate MIDI Output Devices | |
| midiAccess.outputs.forEach((output, key) => { | |
| let option = document.createElement("option"); | |
| option.value = key; | |
| option.textContent = output.name || `MIDI Output ${key}`; | |
| outputSelect.appendChild(option); | |
| }); | |
| console.log("🎛 Updated MIDI Input & Output devices."); | |
| } | |
| // ✅ Handle MIDI Input Selection | |
| function selectMIDIInput() { | |
| let inputSelect = document.getElementById("midiInput"); | |
| let inputId = inputSelect.value; | |
| if (selectedInput) { | |
| selectedInput.onmidimessage = null; | |
| } | |
| if (midiAccess.inputs.has(inputId)) { | |
| selectedInput = midiAccess.inputs.get(inputId); | |
| selectedInput.onmidimessage = handleMIDIMessage; | |
| console.log(`🎤 MIDI Input Selected: ${selectedInput.name}`); | |
| } | |
| } | |
| // ✅ Handle MIDI Output Selection | |
| function selectMIDIOutput() { | |
| let outputSelect = document.getElementById("midiOutput"); | |
| let outputId = outputSelect.value; | |
| if (midiAccess.outputs.has(outputId)) { | |
| selectedOutput = midiAccess.outputs.get(outputId); | |
| console.log(`🎹 MIDI Output Selected: ${selectedOutput.name}`); | |
| } | |
| } | |
| // ✅ Play a MIDI Note Sent Back from Python | |
| function playMIDINote(note, velocity) { | |
| if (!selectedOutput) { | |
| console.warn("⚠️ No MIDI output selected."); | |
| return; | |
| } | |
| let noteOnMessage = [0x90, note, velocity]; // Note On | |
| let noteOffMessage = [0x80, note, 0]; // Note Off | |
| console.log(`🎵 Playing Generated MIDI Note ${note}, Velocity ${velocity}`); | |
| try { | |
| selectedOutput.send(noteOnMessage); | |
| setTimeout(() => { | |
| selectedOutput.send(noteOffMessage); | |
| console.log(`🔇 Note Off ${note}`); | |
| }, 500); | |
| } catch (error) { | |
| console.error("🚨 Error playing MIDI note:", error); | |
| } | |
| } | |
| // ✅ Send MIDI Data to Python | |
| // ✅ Handle Incoming MIDI Messages and Send to Python | |
| function handleMIDIMessage(event) { | |
| let originalNote = event.data[1]; | |
| let velocity = event.data[2]; | |
| let midiData = { | |
| note: originalNote, | |
| velocity: velocity | |
| }; | |
| console.log(`🎤 MIDI Input: Note ${originalNote}, Velocity ${velocity}`); | |
| // ✅ Send MIDI data to Python backend | |
| fetch("/midi_input", { | |
| method: "POST", | |
| headers: { "Content-Type": "application/json" }, | |
| body: JSON.stringify(midiData) | |
| }) | |
| .then(response => response.json()) | |
| .then(data => { | |
| console.log("📨 MIDI sent to Python. Response:", data); | |
| // ✅ Play the generated MIDI response | |
| if (data.status === "success") { | |
| playMIDINote(data.generated_note, data.generated_velocity); | |
| } | |
| }) | |
| .catch(error => console.error("🚨 Error sending MIDI data:", error)); | |
| } | |
| // ✅ Attach Generate Button Event | |
| function attachButtonEvent() { | |
| let generateButton = document.getElementById("generateButton"); | |
| if (generateButton) { | |
| console.log("✅ Generate button found! Attaching event listener..."); | |
| generateButton.addEventListener("click", function () { | |
| console.log("🎹 Generate button clicked."); | |
| if (!selectedOutput) { | |
| alert("⚠️ Please select a MIDI Output first!"); | |
| return; | |
| } | |
| let randomNote = 60 + Math.floor(Math.random() * 12); // Random note from C4 to B4 | |
| console.log(`🎵 Generating MIDI Note: ${randomNote}`); | |
| playMIDINote(randomNote, 100); | |
| }); | |
| } else { | |
| console.log("⏳ Waiting for button to be available..."); | |
| setTimeout(attachButtonEvent, 500); // Try again in 500ms | |
| } | |
| } | |
| // ✅ Ensure the Button and Menus Are Loaded | |
| window.onload = function() { | |
| console.log("✅ Page fully loaded. Checking for elements..."); | |
| updateMIDIDevices(); | |
| attachButtonEvent(); | |
| }; | |
| </script> | |
| <!-- 🎛 MIDI Input & Output Selection --> | |
| <div> | |
| <label for="midiInput">MIDI Input: </label> | |
| <select id="midiInput" onchange="selectMIDIInput()"></select> | |
| <label for="midiOutput">MIDI Output: </label> | |
| <select id="midiOutput" onchange="selectMIDIOutput()"></select> | |
| </div> | |
| <!-- 🎶 "Generate MIDI" Button --> | |
| <button id="generateButton">🎵 Generate MIDI Note</button> | |
| """ | |
| # ✅ Inject JavaScript and HTML | |
| with gr.Blocks() as demo: | |
| gr.HTML(midi_js) | |
| # ✅ Mount FastAPI with Gradio | |
| app = gr.mount_gradio_app(app, demo, path="/") | |
| # ✅ Run the app | |
| if __name__ == "__main__": | |
| import uvicorn | |
| uvicorn.run(app, host="0.0.0.0", port=7860) |