pachet commited on
Commit
7566162
·
1 Parent(s): 7c526ae

trying end phrase detection and processing with python

Browse files
Files changed (2) hide show
  1. app.py +327 -4
  2. phrase_catching.py +10 -0
app.py CHANGED
@@ -1,7 +1,330 @@
1
  import gradio as gr
 
 
2
 
3
- def greet(name):
4
- return "Hello " + name + "!!"
5
 
6
- demo = gr.Interface(fn=greet, inputs="text", outputs="text")
7
- demo.launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import gradio as gr
2
+ from fastapi import FastAPI, Request
3
+ import json
4
 
5
+ # ✅ Create FastAPI App
6
+ app = FastAPI()
7
 
8
+
9
+ # ✅ MIDI Processing Function in Python
10
+ @app.post("/midi_input")
11
+ async def process_midi(request: Request):
12
+ try:
13
+ midi_data = await request.json()
14
+ note = midi_data["note"]
15
+ velocity = midi_data["velocity"]
16
+
17
+ print(f"🎹 Received MIDI Note: {note}, Velocity: {velocity}")
18
+
19
+ # 🚀 Process MIDI data (example: Transpose + Generate New Notes)
20
+ generated_note = (note + 5) % 128 # Transpose up by 3 semitones
21
+ generated_velocity = min(velocity + 10, 127) # Increase velocity slightly
22
+
23
+ # ✅ Send MIDI Response Back to Client
24
+ return {
25
+ "status": "success",
26
+ "generated_note": generated_note,
27
+ "generated_velocity": generated_velocity,
28
+ "original_note": note
29
+ }
30
+
31
+ except Exception as e:
32
+ print(f"🚨 Error processing MIDI: {str(e)}")
33
+ return {"status": "error", "message": str(e)}
34
+
35
+
36
+ @app.post("/midi_phrase")
37
+ async def process_midi_phrase(request: Request):
38
+ try:
39
+ data = await request.json()
40
+ phrase = data["phrase"] # List of MIDI notes
41
+
42
+ print(f"🎹 Received MIDI Phrase ({len(phrase)} notes)")
43
+
44
+ # 🚀 Process Phrase: Example - Transpose Each Note Up by 3 Semitones
45
+ generated_phrase = [
46
+ {
47
+ "note": (note["note"] + 3) % 128,
48
+ "velocity": note["velocity"],
49
+ "duration": note["duration"], # ✅ Keep original duration
50
+ "inter_onset": note["inter_onset"] # ✅ Keep inter-onset time
51
+ }
52
+ for note in phrase
53
+ ]
54
+
55
+ return {"status": "success", "generated_phrase": generated_phrase}
56
+
57
+ except Exception as e:
58
+ print(f"🚨 Error processing MIDI phrase: {str(e)}")
59
+ return {"status": "error", "message": str(e)}
60
+
61
+
62
+ # ✅ JavaScript to Capture and Send MIDI Data
63
+ midi_js = """
64
+ <script>
65
+
66
+ let style = document.createElement("style");
67
+ style.innerHTML = `
68
+ * {
69
+ font-family: Arial, sans-serif !important;
70
+ }
71
+ `;
72
+ document.head.appendChild(style);
73
+ console.log("✅ Applied fallback font to prevent 404 error.");
74
+
75
+ let midiAccess = null;
76
+ let selectedInput = null;
77
+ let selectedOutput = null;
78
+
79
+ // ✅ Request MIDI Access
80
+ navigator.requestMIDIAccess()
81
+ .then(access => {
82
+ console.log("✅ MIDI Access Granted!");
83
+ midiAccess = access;
84
+ updateMIDIDevices();
85
+ midiAccess.onstatechange = updateMIDIDevices;
86
+ })
87
+ .catch(err => console.error("🚨 MIDI API Error:", err));
88
+
89
+ // ✅ Update MIDI Input & Output Menus
90
+ function updateMIDIDevices() {
91
+ let inputSelect = document.getElementById("midiInput");
92
+ let outputSelect = document.getElementById("midiOutput");
93
+
94
+ if (!inputSelect || !outputSelect) {
95
+ console.error("❌ MIDI dropdowns not found!");
96
+ return;
97
+ }
98
+
99
+ // Clear existing options
100
+ inputSelect.innerHTML = '<option value="">Select MIDI Input</option>';
101
+ outputSelect.innerHTML = '<option value="">Select MIDI Output</option>';
102
+
103
+ // Populate MIDI Input Devices
104
+ midiAccess.inputs.forEach((input, key) => {
105
+ let option = document.createElement("option");
106
+ option.value = key;
107
+ option.textContent = input.name || `MIDI Input ${key}`;
108
+ inputSelect.appendChild(option);
109
+ });
110
+
111
+ // Populate MIDI Output Devices
112
+ midiAccess.outputs.forEach((output, key) => {
113
+ let option = document.createElement("option");
114
+ option.value = key;
115
+ option.textContent = output.name || `MIDI Output ${key}`;
116
+ outputSelect.appendChild(option);
117
+ });
118
+
119
+ console.log("🎛 Updated MIDI Input & Output devices.");
120
+ }
121
+
122
+ // ✅ Handle MIDI Input Selection
123
+ function selectMIDIInput() {
124
+ let inputSelect = document.getElementById("midiInput");
125
+ let inputId = inputSelect.value;
126
+
127
+ if (selectedInput) {
128
+ selectedInput.onmidimessage = null;
129
+ }
130
+
131
+ if (midiAccess.inputs.has(inputId)) {
132
+ selectedInput = midiAccess.inputs.get(inputId);
133
+ selectedInput.onmidimessage = handleMIDIMessage;
134
+ console.log(`🎤 MIDI Input Selected: ${selectedInput.name}`);
135
+ }
136
+ }
137
+
138
+ // ✅ Handle MIDI Output Selection
139
+ function selectMIDIOutput() {
140
+ let outputSelect = document.getElementById("midiOutput");
141
+ let outputId = outputSelect.value;
142
+
143
+ if (midiAccess.outputs.has(outputId)) {
144
+ selectedOutput = midiAccess.outputs.get(outputId);
145
+ console.log(`🎹 MIDI Output Selected: ${selectedOutput.name}`);
146
+ }
147
+ }
148
+
149
+
150
+ // ✅ Play a MIDI Note Sent Back from Python
151
+ function playMIDINote(note, velocity) {
152
+ if (!selectedOutput) {
153
+ console.warn("⚠️ No MIDI output selected.");
154
+ return;
155
+ }
156
+
157
+ let noteOnMessage = [0x90, note, velocity]; // Note On
158
+ let noteOffMessage = [0x80, note, 0]; // Note Off
159
+
160
+ console.log(`🎵 Playing Generated MIDI Note ${note}, Velocity ${velocity}`);
161
+
162
+ try {
163
+ selectedOutput.send(noteOnMessage);
164
+ setTimeout(() => {
165
+ selectedOutput.send(noteOffMessage);
166
+ console.log(`🔇 Note Off ${note}`);
167
+ }, 500);
168
+ } catch (error) {
169
+ console.error("🚨 Error playing MIDI note:", error);
170
+ }
171
+ }
172
+
173
+ // ✅ Send MIDI Data to Python
174
+ // ✅ Handle Incoming MIDI Messages and Send to Python
175
+
176
+ let midiPhrase = []; // ✅ Buffer to store incoming notes
177
+ let activeNotes = new Map(); // ✅ Track active notes with start times
178
+ let phraseTimeout = null; // ✅ Timer to detect phrase end
179
+ let lastNoteOnTime = null; // ✅ Track previous note-on time for inter-onset calculation
180
+
181
+ // ✅ Handle Incoming MIDI Messages (Phrase Buffering)
182
+ function handleMIDIMessage(event) {
183
+ let command = event.data[0] & 0xf0; // Extract MIDI command
184
+ let note = event.data[1];
185
+ let velocity = event.data[2];
186
+ let timestamp = performance.now(); // ✅ Get precise timing
187
+
188
+ if (command === 0x90 && velocity > 0) {
189
+ // ✅ Note On: Store start time and inter-onset interval
190
+ let interOnsetTime = lastNoteOnTime ? timestamp - lastNoteOnTime : 0;
191
+ lastNoteOnTime = timestamp;
192
+ console.log(interOnsetTime);
193
+
194
+ activeNotes.set(note, timestamp); // ✅ Store note start time
195
+
196
+ midiPhrase.push({ note, velocity, start_time: timestamp, duration: null, inter_onset: interOnsetTime });
197
+
198
+ console.log(`🎤 Note ON: ${note}, Velocity ${velocity}, IOT ${interOnsetTime}ms`);
199
+ }
200
+ else if (command === 0x80 || (command === 0x90 && velocity === 0)) {
201
+ // ✅ Note Off: Calculate duration and update phrase
202
+ if (activeNotes.has(note)) {
203
+ let startTime = activeNotes.get(note);
204
+ let duration = timestamp - startTime;
205
+ activeNotes.delete(note);
206
+
207
+ // ✅ Find the note in the phrase and update duration
208
+ let noteIndex = midiPhrase.findIndex(n => n.note === note && n.duration === null);
209
+ if (noteIndex !== -1) {
210
+ midiPhrase[noteIndex].duration = duration;
211
+ console.log(`🎹 Note OFF: ${note}, Duration ${duration}ms`);
212
+ }
213
+ }
214
+
215
+ // ✅ If no active notes, start phrase timeout (1s delay before sending)
216
+ if (activeNotes.size === 0) {
217
+ if (phraseTimeout) clearTimeout(phraseTimeout);
218
+ phraseTimeout = setTimeout(sendPhraseToPython, 1000);
219
+ }
220
+ }
221
+ }
222
+
223
+ // ✅ Send the Phrase to Python After 1 Second of Silence
224
+ function sendPhraseToPython() {
225
+ if (midiPhrase.length === 0 || activeNotes.size > 0) return; // ✅ Do not send if notes are still active
226
+
227
+ console.log("📨 Sending MIDI phrase to Python:", midiPhrase);
228
+
229
+ fetch("/midi_phrase", {
230
+ method: "POST",
231
+ headers: { "Content-Type": "application/json" },
232
+ body: JSON.stringify({ phrase: midiPhrase })
233
+ })
234
+ .then(response => response.json())
235
+ .then(data => {
236
+ console.log("📩 Python Response:", data);
237
+ if (data.generated_phrase) {
238
+ playGeneratedPhrase(data.generated_phrase);
239
+ }
240
+ })
241
+ .catch(error => console.error("🚨 Error sending MIDI phrase:", error));
242
+
243
+ // ✅ Clear the phrase buffer
244
+ midiPhrase = [];
245
+ lastNoteOnTime = null;
246
+
247
+ }
248
+
249
+ // ✅ Play Generated MIDI Phrase from Python (Keeping Timing)
250
+ function playGeneratedPhrase(phrase) {
251
+ if (!selectedOutput) {
252
+ console.warn("⚠️ No MIDI output selected.");
253
+ return;
254
+ }
255
+
256
+ console.log("🎵 Playing Generated Phrase:", phrase);
257
+
258
+ phrase.forEach((noteData, index) => {
259
+ let delay = noteData.inter_onset; // ✅ Use stored inter-onset time
260
+ setTimeout(() => {
261
+ let noteOnMessage = [0x90, noteData.note, noteData.velocity];
262
+ let noteOffMessage = [0x80, noteData.note, 0];
263
+
264
+ selectedOutput.send(noteOnMessage);
265
+ setTimeout(() => selectedOutput.send(noteOffMessage), noteData.duration); // ✅ Use stored duration
266
+ }, delay);
267
+ });
268
+ }
269
+
270
+
271
+ // ✅ Attach Generate Button Event
272
+ function attachButtonEvent() {
273
+ let generateButton = document.getElementById("generateButton");
274
+
275
+ if (generateButton) {
276
+ console.log("✅ Generate button found! Attaching event listener...");
277
+
278
+ generateButton.addEventListener("click", function () {
279
+ console.log("🎹 Generate button clicked.");
280
+
281
+ if (!selectedOutput) {
282
+ alert("⚠️ Please select a MIDI Output first!");
283
+ return;
284
+ }
285
+
286
+ let randomNote = 60 + Math.floor(Math.random() * 12); // Random note from C4 to B4
287
+ console.log(`🎵 Generating MIDI Note: ${randomNote}`);
288
+ playMIDINote(randomNote, 100);
289
+ });
290
+
291
+ } else {
292
+ console.log("⏳ Waiting for button to be available...");
293
+ setTimeout(attachButtonEvent, 500); // Try again in 500ms
294
+ }
295
+ }
296
+
297
+ // ✅ Ensure the Button and Menus Are Loaded
298
+ window.onload = function() {
299
+ console.log("✅ Page fully loaded. Checking for elements...");
300
+ updateMIDIDevices();
301
+ attachButtonEvent();
302
+ };
303
+ </script>
304
+
305
+ <!-- 🎛 MIDI Input & Output Selection -->
306
+ <div>
307
+ <label for="midiInput">MIDI Input: </label>
308
+ <select id="midiInput" onchange="selectMIDIInput()"></select>
309
+
310
+ <label for="midiOutput">MIDI Output: </label>
311
+ <select id="midiOutput" onchange="selectMIDIOutput()"></select>
312
+ </div>
313
+
314
+ <!-- 🎶 "Generate MIDI" Button -->
315
+ <button id="generateButton">🎵 Generate MIDI Note</button>
316
+
317
+ """
318
+
319
+ # ✅ Inject JavaScript and HTML
320
+ with gr.Blocks() as demo:
321
+ gr.HTML(midi_js)
322
+
323
+ # ✅ Mount FastAPI with Gradio
324
+ app = gr.mount_gradio_app(app, demo, path="/")
325
+
326
+ if __name__ == "__main__":
327
+ import uvicorn
328
+
329
+ print("🚀 Starting FastAPI with Gradio...")
330
+ uvicorn.run(app, host="0.0.0.0", port=7860)
phrase_catching.py CHANGED
@@ -62,6 +62,16 @@ async def process_midi_phrase(request: Request):
62
  # ✅ JavaScript to Capture and Send MIDI Data
63
  midi_js = """
64
  <script>
 
 
 
 
 
 
 
 
 
 
65
  let midiAccess = null;
66
  let selectedInput = null;
67
  let selectedOutput = null;
 
62
  # ✅ JavaScript to Capture and Send MIDI Data
63
  midi_js = """
64
  <script>
65
+
66
+ let style = document.createElement("style");
67
+ style.innerHTML = `
68
+ * {
69
+ font-family: Arial, sans-serif !important;
70
+ }
71
+ `;
72
+ document.head.appendChild(style);
73
+ console.log("✅ Applied fallback font to prevent 404 error.");
74
+
75
  let midiAccess = null;
76
  let selectedInput = null;
77
  let selectedOutput = null;