pachet commited on
Commit
4fa7ba3
·
1 Parent(s): 9f4af53

added the style fix to the js

Browse files
Files changed (1) hide show
  1. app.py +169 -19
app.py CHANGED
@@ -5,8 +5,9 @@ import json
5
  # ✅ Create FastAPI App
6
  app = FastAPI()
7
 
 
8
  # ✅ MIDI Processing Function in Python
9
- @app.post("/api/midi_input")
10
  async def process_midi(request: Request):
11
  try:
12
  midi_data = await request.json()
@@ -15,11 +16,16 @@ async def process_midi(request: Request):
15
 
16
  print(f"🎹 Received MIDI Note: {note}, Velocity: {velocity}")
17
 
18
- # 🚀 Process MIDI data (example: simple echo response)
 
 
 
 
19
  return {
20
  "status": "success",
21
- "received_note": note,
22
- "received_velocity": velocity,
 
23
  }
24
 
25
  except Exception as e:
@@ -27,11 +33,44 @@ async def process_midi(request: Request):
27
  return {"status": "error", "message": str(e)}
28
 
29
 
30
- # ✅ JavaScript for MIDI Input/Output Management and Sending MIDI Notes
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  midi_js = """
32
  <script>
33
 
34
- console.log("✅ JavaScript is running");
 
 
 
 
 
 
 
35
 
36
  let midiAccess = null;
37
  let selectedInput = null;
@@ -107,6 +146,7 @@ function selectMIDIOutput() {
107
  }
108
  }
109
 
 
110
  // ✅ Play a MIDI Note Sent Back from Python
111
  function playMIDINote(note, velocity) {
112
  if (!selectedOutput) {
@@ -130,34 +170,144 @@ function playMIDINote(note, velocity) {
130
  }
131
  }
132
 
133
- // ✅ Send MIDI Data to Python Backend
134
- function sendMIDI() {
135
- let note = 60; // Middle C (C4)
136
- let velocity = 100;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
 
138
- console.log("🎹 Sending Test MIDI Note");
 
 
139
 
140
- fetch("/api/midi_input", {
 
 
141
  method: "POST",
142
  headers: { "Content-Type": "application/json" },
143
- body: JSON.stringify({ note, velocity })
144
  })
145
  .then(response => response.json())
146
  .then(data => {
147
  console.log("📩 Python Response:", data);
148
- if (data.received_note) {
149
- playMIDINote(data.received_note, data.received_velocity);
150
  }
151
  })
152
- .catch(error => console.error("🚨 Error sending MIDI:", error));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153
  }
154
 
155
  // ✅ Ensure the Button and Menus Are Loaded
156
  window.onload = function() {
157
  console.log("✅ Page fully loaded. Checking for elements...");
158
  updateMIDIDevices();
 
159
  };
160
-
161
  </script>
162
 
163
  <!-- 🎛 MIDI Input & Output Selection -->
@@ -169,8 +319,8 @@ window.onload = function() {
169
  <select id="midiOutput" onchange="selectMIDIOutput()"></select>
170
  </div>
171
 
172
- <!-- 🎶 "Send Test MIDI" Button -->
173
- <button id="sendMIDIButton" onclick="sendMIDI()">🎹 Send Test MIDI Note</button>
174
 
175
  """
176
 
 
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()
 
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:
 
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;
 
146
  }
147
  }
148
 
149
+
150
  // ✅ Play a MIDI Note Sent Back from Python
151
  function playMIDINote(note, velocity) {
152
  if (!selectedOutput) {
 
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
+ let accumulatedDelay = 0; // ✅ Track total delay from phrase start
259
+ phrase.forEach((noteData, index) => {
260
+ accumulatedDelay += noteData.inter_onset; // ✅ Accumulate inter-onset time
261
+
262
+ setTimeout(() => {
263
+ let noteOnMessage = [0x90, noteData.note, noteData.velocity];
264
+ let noteOffMessage = [0x80, noteData.note, 0];
265
+
266
+ selectedOutput.send(noteOnMessage);
267
+ console.log(`🎶 Note ON: ${noteData.note}, Velocity: ${noteData.velocity}, Start Delay: ${accumulatedDelay}ms`);
268
+
269
+ setTimeout(() => {
270
+ selectedOutput.send(noteOffMessage);
271
+ console.log(`🔇 Note OFF: ${noteData.note}`);
272
+ }, noteData.duration); // ✅ Use stored duration
273
+
274
+ }, accumulatedDelay); // ✅ Use accumulated delay for precise timing
275
+ });
276
+ }
277
+
278
+
279
+ // ✅ Attach Generate Button Event
280
+ function attachButtonEvent() {
281
+ let generateButton = document.getElementById("generateButton");
282
+
283
+ if (generateButton) {
284
+ console.log("✅ Generate button found! Attaching event listener...");
285
+
286
+ generateButton.addEventListener("click", function () {
287
+ console.log("🎹 Generate button clicked.");
288
+
289
+ if (!selectedOutput) {
290
+ alert("⚠️ Please select a MIDI Output first!");
291
+ return;
292
+ }
293
+
294
+ let randomNote = 60 + Math.floor(Math.random() * 12); // Random note from C4 to B4
295
+ console.log(`🎵 Generating MIDI Note: ${randomNote}`);
296
+ playMIDINote(randomNote, 100);
297
+ });
298
+
299
+ } else {
300
+ console.log("⏳ Waiting for button to be available...");
301
+ setTimeout(attachButtonEvent, 500); // Try again in 500ms
302
+ }
303
  }
304
 
305
  // ✅ Ensure the Button and Menus Are Loaded
306
  window.onload = function() {
307
  console.log("✅ Page fully loaded. Checking for elements...");
308
  updateMIDIDevices();
309
+ attachButtonEvent();
310
  };
 
311
  </script>
312
 
313
  <!-- 🎛 MIDI Input & Output Selection -->
 
319
  <select id="midiOutput" onchange="selectMIDIOutput()"></select>
320
  </div>
321
 
322
+ <!-- 🎶 "Generate MIDI" Button -->
323
+ <button id="generateButton">🎵 Generate MIDI Note</button>
324
 
325
  """
326