pachet commited on
Commit
3aa7092
·
1 Parent(s): 643e116

trying end phrase detection and processing with python

Browse files
Files changed (2) hide show
  1. app.py +73 -15
  2. working_interaction.py +229 -0
app.py CHANGED
@@ -31,6 +31,28 @@ async def process_midi(request: Request):
31
  print(f"🚨 Error processing MIDI: {str(e)}")
32
  return {"status": "error", "message": str(e)}
33
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  # ✅ JavaScript to Capture and Send MIDI Data
35
  midi_js = """
36
  <script>
@@ -134,35 +156,71 @@ function playMIDINote(note, velocity) {
134
 
135
  // ✅ Send MIDI Data to Python
136
  // ✅ Handle Incoming MIDI Messages and Send to Python
 
 
 
 
137
  function handleMIDIMessage(event) {
138
- let originalNote = event.data[1];
139
  let velocity = event.data[2];
 
 
 
 
 
 
 
 
 
 
 
140
 
141
- let midiData = {
142
- note: originalNote,
143
- velocity: velocity
144
- };
145
 
146
- console.log(`🎤 MIDI Input: Note ${originalNote}, Velocity ${velocity}`);
147
 
148
- // ✅ Send MIDI data to Python backend
149
- fetch("/midi_input", {
150
  method: "POST",
151
  headers: { "Content-Type": "application/json" },
152
- body: JSON.stringify(midiData)
153
  })
154
  .then(response => response.json())
155
  .then(data => {
156
- console.log("📨 MIDI sent to Python. Response:", data);
157
-
158
- // ✅ Play the generated MIDI response
159
- if (data.status === "success") {
160
- playMIDINote(data.generated_note, data.generated_velocity);
161
  }
162
  })
163
- .catch(error => console.error("🚨 Error sending MIDI data:", error));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
  }
165
 
 
166
  // ✅ Attach Generate Button Event
167
  function attachButtonEvent() {
168
  let generateButton = document.getElementById("generateButton");
 
31
  print(f"🚨 Error processing MIDI: {str(e)}")
32
  return {"status": "error", "message": str(e)}
33
 
34
+
35
+ @app.post("/midi_phrase")
36
+ async def process_midi_phrase(request: Request):
37
+ try:
38
+ data = await request.json()
39
+ phrase = data["phrase"] # List of MIDI notes
40
+
41
+ print(f"🎹 Received MIDI Phrase ({len(phrase)} notes)")
42
+
43
+ # 🚀 Example Processing: Transpose Each Note Up by 3 Semitones
44
+ generated_phrase = [
45
+ {"note": (note["note"] + 3) % 128, "velocity": note["velocity"]}
46
+ for note in phrase
47
+ ]
48
+
49
+ return {"status": "success", "generated_phrase": generated_phrase}
50
+
51
+ except Exception as e:
52
+ print(f"🚨 Error processing MIDI phrase: {str(e)}")
53
+ return {"status": "error", "message": str(e)}
54
+
55
+
56
  # ✅ JavaScript to Capture and Send MIDI Data
57
  midi_js = """
58
  <script>
 
156
 
157
  // ✅ Send MIDI Data to Python
158
  // ✅ Handle Incoming MIDI Messages and Send to Python
159
+ let midiPhrase = []; // ✅ Buffer to store incoming notes
160
+ let phraseTimeout = null; // ✅ Timer to detect phrase end
161
+
162
+ // ✅ Handle Incoming MIDI Messages (Phrase Buffering)
163
  function handleMIDIMessage(event) {
164
+ let note = event.data[1];
165
  let velocity = event.data[2];
166
+ let timestamp = event.timeStamp;
167
+
168
+ // ✅ Store note in buffer
169
+ midiPhrase.push({ note, velocity, timestamp });
170
+
171
+ console.log(`🎤 MIDI Input: Note ${note}, Velocity ${velocity} (Stored in phrase)`);
172
+
173
+ // ✅ Reset phrase timeout (wait for 1 second of silence)
174
+ if (phraseTimeout) clearTimeout(phraseTimeout);
175
+ phraseTimeout = setTimeout(sendPhraseToPython, 1000);
176
+ }
177
 
178
+ // Send the Phrase to Python After 1 Second of Silence
179
+ function sendPhraseToPython() {
180
+ if (midiPhrase.length === 0) return; // If no phrase, do nothing
 
181
 
182
+ console.log("📨 Sending MIDI phrase to Python:", midiPhrase);
183
 
184
+ fetch("/midi_phrase", {
 
185
  method: "POST",
186
  headers: { "Content-Type": "application/json" },
187
+ body: JSON.stringify({ phrase: midiPhrase })
188
  })
189
  .then(response => response.json())
190
  .then(data => {
191
+ console.log("📩 Python Response:", data);
192
+ if (data.generated_phrase) {
193
+ playGeneratedPhrase(data.generated_phrase);
 
 
194
  }
195
  })
196
+ .catch(error => console.error("🚨 Error sending MIDI phrase:", error));
197
+
198
+ // ✅ Clear the phrase buffer
199
+ midiPhrase = [];
200
+ }
201
+
202
+ // ✅ Play Generated MIDI Phrase from Python
203
+ function playGeneratedPhrase(phrase) {
204
+ if (!selectedOutput) {
205
+ console.warn("⚠️ No MIDI output selected.");
206
+ return;
207
+ }
208
+
209
+ console.log("🎵 Playing Generated Phrase:", phrase);
210
+
211
+ phrase.forEach((noteData, index) => {
212
+ let delay = index * 200; // Add slight delay between notes
213
+ setTimeout(() => {
214
+ let noteOnMessage = [0x90, noteData.note, noteData.velocity];
215
+ let noteOffMessage = [0x80, noteData.note, 0];
216
+
217
+ selectedOutput.send(noteOnMessage);
218
+ setTimeout(() => selectedOutput.send(noteOffMessage), 300);
219
+ }, delay);
220
+ });
221
  }
222
 
223
+
224
  // ✅ Attach Generate Button Event
225
  function attachButtonEvent() {
226
  let generateButton = document.getElementById("generateButton");
working_interaction.py ADDED
@@ -0,0 +1,229 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from fastapi import FastAPI, Request
3
+ import json
4
+ # THIS VERSION WORKS
5
+ # AND SENDS BACK A NOTE A FIFTH HIGHER THAN THE INPUT NOT ON THE SELECTED PORT
6
+ #
7
+ # ✅ Create FastAPI App
8
+ app = FastAPI()
9
+
10
+
11
+ # ✅ MIDI Processing Function in Python
12
+ @app.post("/midi_input")
13
+ async def process_midi(request: Request):
14
+ try:
15
+ midi_data = await request.json()
16
+ note = midi_data["note"]
17
+ velocity = midi_data["velocity"]
18
+
19
+ print(f"🎹 Received MIDI Note: {note}, Velocity: {velocity}")
20
+
21
+ # 🚀 Process MIDI data (example: Transpose + Generate New Notes)
22
+ generated_note = (note + 5) % 128 # Transpose up by 3 semitones
23
+ generated_velocity = min(velocity + 10, 127) # Increase velocity slightly
24
+
25
+ # ✅ Send MIDI Response Back to Client
26
+ return {
27
+ "status": "success",
28
+ "generated_note": generated_note,
29
+ "generated_velocity": generated_velocity,
30
+ "original_note": note
31
+ }
32
+
33
+ except Exception as e:
34
+ print(f"🚨 Error processing MIDI: {str(e)}")
35
+ return {"status": "error", "message": str(e)}
36
+
37
+
38
+ # ✅ JavaScript to Capture and Send MIDI Data
39
+ midi_js = """
40
+ <script>
41
+ let midiAccess = null;
42
+ let selectedInput = null;
43
+ let selectedOutput = null;
44
+
45
+ // ✅ Request MIDI Access
46
+ navigator.requestMIDIAccess()
47
+ .then(access => {
48
+ console.log("✅ MIDI Access Granted!");
49
+ midiAccess = access;
50
+ updateMIDIDevices();
51
+ midiAccess.onstatechange = updateMIDIDevices;
52
+ })
53
+ .catch(err => console.error("🚨 MIDI API Error:", err));
54
+
55
+ // ✅ Update MIDI Input & Output Menus
56
+ function updateMIDIDevices() {
57
+ let inputSelect = document.getElementById("midiInput");
58
+ let outputSelect = document.getElementById("midiOutput");
59
+
60
+ if (!inputSelect || !outputSelect) {
61
+ console.error("❌ MIDI dropdowns not found!");
62
+ return;
63
+ }
64
+
65
+ // Clear existing options
66
+ inputSelect.innerHTML = '<option value="">Select MIDI Input</option>';
67
+ outputSelect.innerHTML = '<option value="">Select MIDI Output</option>';
68
+
69
+ // Populate MIDI Input Devices
70
+ midiAccess.inputs.forEach((input, key) => {
71
+ let option = document.createElement("option");
72
+ option.value = key;
73
+ option.textContent = input.name || `MIDI Input ${key}`;
74
+ inputSelect.appendChild(option);
75
+ });
76
+
77
+ // Populate MIDI Output Devices
78
+ midiAccess.outputs.forEach((output, key) => {
79
+ let option = document.createElement("option");
80
+ option.value = key;
81
+ option.textContent = output.name || `MIDI Output ${key}`;
82
+ outputSelect.appendChild(option);
83
+ });
84
+
85
+ console.log("🎛 Updated MIDI Input & Output devices.");
86
+ }
87
+
88
+ // ✅ Handle MIDI Input Selection
89
+ function selectMIDIInput() {
90
+ let inputSelect = document.getElementById("midiInput");
91
+ let inputId = inputSelect.value;
92
+
93
+ if (selectedInput) {
94
+ selectedInput.onmidimessage = null;
95
+ }
96
+
97
+ if (midiAccess.inputs.has(inputId)) {
98
+ selectedInput = midiAccess.inputs.get(inputId);
99
+ selectedInput.onmidimessage = handleMIDIMessage;
100
+ console.log(`🎤 MIDI Input Selected: ${selectedInput.name}`);
101
+ }
102
+ }
103
+
104
+ // ✅ Handle MIDI Output Selection
105
+ function selectMIDIOutput() {
106
+ let outputSelect = document.getElementById("midiOutput");
107
+ let outputId = outputSelect.value;
108
+
109
+ if (midiAccess.outputs.has(outputId)) {
110
+ selectedOutput = midiAccess.outputs.get(outputId);
111
+ console.log(`🎹 MIDI Output Selected: ${selectedOutput.name}`);
112
+ }
113
+ }
114
+
115
+
116
+ // ✅ Play a MIDI Note Sent Back from Python
117
+ function playMIDINote(note, velocity) {
118
+ if (!selectedOutput) {
119
+ console.warn("⚠️ No MIDI output selected.");
120
+ return;
121
+ }
122
+
123
+ let noteOnMessage = [0x90, note, velocity]; // Note On
124
+ let noteOffMessage = [0x80, note, 0]; // Note Off
125
+
126
+ console.log(`🎵 Playing Generated MIDI Note ${note}, Velocity ${velocity}`);
127
+
128
+ try {
129
+ selectedOutput.send(noteOnMessage);
130
+ setTimeout(() => {
131
+ selectedOutput.send(noteOffMessage);
132
+ console.log(`🔇 Note Off ${note}`);
133
+ }, 500);
134
+ } catch (error) {
135
+ console.error("🚨 Error playing MIDI note:", error);
136
+ }
137
+ }
138
+
139
+ // ✅ Send MIDI Data to Python
140
+ // ✅ Handle Incoming MIDI Messages and Send to Python
141
+ function handleMIDIMessage(event) {
142
+ let originalNote = event.data[1];
143
+ let velocity = event.data[2];
144
+
145
+ let midiData = {
146
+ note: originalNote,
147
+ velocity: velocity
148
+ };
149
+
150
+ console.log(`🎤 MIDI Input: Note ${originalNote}, Velocity ${velocity}`);
151
+
152
+ // ✅ Send MIDI data to Python backend
153
+ fetch("/midi_input", {
154
+ method: "POST",
155
+ headers: { "Content-Type": "application/json" },
156
+ body: JSON.stringify(midiData)
157
+ })
158
+ .then(response => response.json())
159
+ .then(data => {
160
+ console.log("📨 MIDI sent to Python. Response:", data);
161
+
162
+ // ✅ Play the generated MIDI response
163
+ if (data.status === "success") {
164
+ playMIDINote(data.generated_note, data.generated_velocity);
165
+ }
166
+ })
167
+ .catch(error => console.error("🚨 Error sending MIDI data:", error));
168
+ }
169
+
170
+ // ✅ Attach Generate Button Event
171
+ function attachButtonEvent() {
172
+ let generateButton = document.getElementById("generateButton");
173
+
174
+ if (generateButton) {
175
+ console.log("✅ Generate button found! Attaching event listener...");
176
+
177
+ generateButton.addEventListener("click", function () {
178
+ console.log("🎹 Generate button clicked.");
179
+
180
+ if (!selectedOutput) {
181
+ alert("⚠️ Please select a MIDI Output first!");
182
+ return;
183
+ }
184
+
185
+ let randomNote = 60 + Math.floor(Math.random() * 12); // Random note from C4 to B4
186
+ console.log(`🎵 Generating MIDI Note: ${randomNote}`);
187
+ playMIDINote(randomNote, 100);
188
+ });
189
+
190
+ } else {
191
+ console.log("⏳ Waiting for button to be available...");
192
+ setTimeout(attachButtonEvent, 500); // Try again in 500ms
193
+ }
194
+ }
195
+
196
+ // ✅ Ensure the Button and Menus Are Loaded
197
+ window.onload = function() {
198
+ console.log("✅ Page fully loaded. Checking for elements...");
199
+ updateMIDIDevices();
200
+ attachButtonEvent();
201
+ };
202
+ </script>
203
+
204
+ <!-- 🎛 MIDI Input & Output Selection -->
205
+ <div>
206
+ <label for="midiInput">MIDI Input: </label>
207
+ <select id="midiInput" onchange="selectMIDIInput()"></select>
208
+
209
+ <label for="midiOutput">MIDI Output: </label>
210
+ <select id="midiOutput" onchange="selectMIDIOutput()"></select>
211
+ </div>
212
+
213
+ <!-- 🎶 "Generate MIDI" Button -->
214
+ <button id="generateButton">🎵 Generate MIDI Note</button>
215
+
216
+ """
217
+
218
+ # ✅ Inject JavaScript and HTML
219
+ with gr.Blocks() as demo:
220
+ gr.HTML(midi_js)
221
+
222
+ # ✅ Mount FastAPI with Gradio
223
+ app = gr.mount_gradio_app(app, demo, path="/")
224
+
225
+ # ✅ Run the app
226
+ if __name__ == "__main__":
227
+ import uvicorn
228
+
229
+ uvicorn.run(app, host="0.0.0.0", port=7860)