Next commited on
Commit
4e9d326
1 Parent(s): 46cbb18

Rename app.py to app.js

Browse files
Files changed (2) hide show
  1. app.js +398 -0
  2. app.py +0 -41
app.js ADDED
@@ -0,0 +1,398 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * 自动绕过 shadowRoot 的 querySelector
3
+ * @param {string} selector - 要查询的 CSS 选择器
4
+ * @returns {Element|null} - 匹配的元素或 null 如果未找到
5
+ */
6
+ function deepQuerySelector(selector) {
7
+ /**
8
+ * 在指定的根元素或文档对象下深度查询元素
9
+ * @param {Element|Document} root - 要开始搜索的根元素或文档对象
10
+ * @param {string} selector - 要查询的 CSS 选择器
11
+ * @returns {Element|null} - 匹配的元素或 null 如果未找到
12
+ */
13
+ function deepSearch(root, selector) {
14
+ let element = root.querySelector(selector);
15
+ if (element) {
16
+ return element;
17
+ }
18
+
19
+ const shadowHosts = root.querySelectorAll('*');
20
+ for (let i = 0; i < shadowHosts.length; i++) {
21
+ const host = shadowHosts[i];
22
+ if (host.shadowRoot) {
23
+ element = deepSearch(host.shadowRoot, selector);
24
+ if (element) {
25
+ return element;
26
+ }
27
+ }
28
+ }
29
+ return null;
30
+ }
31
+
32
+ return deepSearch(this, selector);
33
+ }
34
+
35
+ Element.prototype.deepQuerySelector = deepQuerySelector;
36
+ Document.prototype.deepQuerySelector = deepQuerySelector;
37
+
38
+ function gradioApp() {
39
+ const elems = document.getElementsByTagName('gradio-app');
40
+ const gradioShadowRoot = elems.length === 0 ? null : elems[0].shadowRoot;
41
+ return !!gradioShadowRoot ? gradioShadowRoot : document;
42
+ }
43
+
44
+ let uiUpdateCallbacks = [];
45
+ let msgReceiveCallbacks = [];
46
+
47
+ function onUiUpdate(callback) {
48
+ uiUpdateCallbacks.push(callback);
49
+ }
50
+
51
+ function onMsgReceive(callback) {
52
+ msgReceiveCallbacks.push(callback);
53
+ }
54
+
55
+ function runCallback(x, m) {
56
+ try {
57
+ x(m);
58
+ } catch (e) {
59
+ (console.error || console.log).call(console, e.message, e);
60
+ }
61
+ }
62
+
63
+ function executeCallbacks(queue, m) {
64
+ queue.forEach(function(x) {
65
+ runCallback(x, m);
66
+ });
67
+ }
68
+
69
+ document.addEventListener("DOMContentLoaded", function() {
70
+ const mutationObserver = new MutationObserver(function(m) {
71
+ executeCallbacks(uiUpdateCallbacks, m);
72
+ });
73
+ mutationObserver.observe(gradioApp(), { childList: true, subtree: true });
74
+ });
75
+
76
+ function HSVtoRGB(h, s, v) {
77
+ let r, g, b, i, f, p, q, t;
78
+ i = Math.floor(h * 6);
79
+ f = h * 6 - i;
80
+ p = v * (1 - s);
81
+ q = v * (1 - f * s);
82
+ t = v * (1 - (1 - f) * s);
83
+ switch (i % 6) {
84
+ case 0:
85
+ r = v;
86
+ g = t;
87
+ b = p;
88
+ break;
89
+ case 1:
90
+ r = q;
91
+ g = v;
92
+ b = p;
93
+ break;
94
+ case 2:
95
+ r = p;
96
+ g = v;
97
+ b = t;
98
+ break;
99
+ case 3:
100
+ r = p;
101
+ g = q;
102
+ b = v;
103
+ break;
104
+ case 4:
105
+ r = t;
106
+ g = p;
107
+ b = v;
108
+ break;
109
+ case 5:
110
+ r = v;
111
+ g = p;
112
+ b = q;
113
+ break;
114
+ }
115
+ return {
116
+ r: Math.round(r * 255),
117
+ g: Math.round(g * 255),
118
+ b: Math.round(b * 255)
119
+ };
120
+ }
121
+
122
+ class MidiVisualizer extends HTMLElement {
123
+ constructor() {
124
+ super();
125
+ this.midiEvents = [];
126
+ this.activeNotes = [];
127
+ this.midiTimes = [];
128
+ this.wrapper = null;
129
+ this.svg = null;
130
+ this.timeLine = null;
131
+ this.config = {
132
+ noteHeight: 4,
133
+ beatWidth: 32
134
+ };
135
+ this.timePreBeat = 16;
136
+ this.svgWidth = 0;
137
+ this.t1 = 0;
138
+ this.totalTimeMs = 0;
139
+ this.playTime = 0;
140
+ this.playTimeMs = 0;
141
+ this.colorMap = new Map();
142
+ this.playing = false;
143
+ this.timer = null;
144
+ this.init();
145
+ }
146
+
147
+ init() {
148
+ this.innerHTML = '';
149
+ const shadow = this.attachShadow({ mode: 'open' });
150
+ const style = document.createElement('style');
151
+ const wrapper = document.createElement('div');
152
+ style.textContent = ".note.active {stroke: black;stroke-width: 0.75;stroke-opacity: 0.75;}";
153
+ wrapper.style.overflowX = "scroll";
154
+ const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
155
+ svg.style.height = `${this.config.noteHeight * 128}px`;
156
+ svg.style.width = `${this.svgWidth}px`;
157
+ const timeLine = document.createElementNS('http://www.w3.org/2000/svg', 'line');
158
+ timeLine.style.stroke = "green";
159
+ timeLine.style.strokeWidth = 2;
160
+ shadow.appendChild(style);
161
+ shadow.appendChild(wrapper);
162
+ wrapper.appendChild(svg);
163
+ svg.appendChild(timeLine);
164
+ this.wrapper = wrapper;
165
+ this.svg = svg;
166
+ this.timeLine = timeLine;
167
+ this.setPlayTime(0);
168
+ }
169
+
170
+ clearMidiEvents() {
171
+ this.pause();
172
+ this.midiEvents = [];
173
+ this.activeNotes = [];
174
+ this.midiTimes = [];
175
+ this.t1 = 0;
176
+ this.colorMap.clear();
177
+ this.setPlayTime(0);
178
+ this.totalTimeMs = 0;
179
+ this.playTimeMs = 0;
180
+ this.svgWidth = 0;
181
+ this.svg.innerHTML = '';
182
+ this.svg.style.width = `${this.svgWidth}px`;
183
+ this.svg.appendChild(this.timeLine);
184
+ }
185
+
186
+ appendMidiEvent(midiEvent) {
187
+ if (Array.isArray(midiEvent) && midiEvent.length > 0) {
188
+ this.t1 += midiEvent[1];
189
+ let t = this.t1 * this.timePreBeat + midiEvent[2];
190
+ midiEvent = [midiEvent[0], t].concat(midiEvent.slice(3));
191
+ if (midiEvent[0] === "note") {
192
+ let track = midiEvent[2];
193
+ let duration = midiEvent[3];
194
+ let channel = midiEvent[4];
195
+ let pitch = midiEvent[5];
196
+ let velocity = midiEvent[6];
197
+ let x = (t / this.timePreBeat) * this.config.beatWidth;
198
+ let y = (127 - pitch) * this.config.noteHeight;
199
+ let w = (duration / this.timePreBeat) * this.config.beatWidth;
200
+ let h = this.config.noteHeight;
201
+ this.svgWidth = Math.ceil(Math.max(x + w, this.svgWidth));
202
+ let color = this.getColor(track, channel);
203
+ let opacity = Math.min(1, velocity / 127 + 0.1).toFixed(2);
204
+ let rect = this.drawNote(x, y, w, h, `rgba(${color.r}, ${color.g}, ${color.b}, ${opacity})`);
205
+ midiEvent.push(rect);
206
+ this.setPlayTime(t);
207
+ this.wrapper.scrollTo(this.svgWidth - this.wrapper.offsetWidth, 0);
208
+ }
209
+ this.midiEvents.push(midiEvent);
210
+ this.svg.style.width = `${this.svgWidth}px`;
211
+ }
212
+ }
213
+
214
+ getColor(track, channel) {
215
+ let key = `${track},${channel}`;
216
+ let color = this.colorMap.get(key);
217
+ if (color) {
218
+ return color;
219
+ }
220
+ color = HSVtoRGB(Math.random(), Math.random() * 0.5 + 0.5, 1);
221
+ this.colorMap.set(key, color);
222
+ return color;
223
+ }
224
+
225
+ drawNote(x, y, w, h, fill) {
226
+ if (!this.svg) {
227
+ return null;
228
+ }
229
+ const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
230
+ rect.classList.add('note');
231
+ rect.setAttribute('fill', fill);
232
+ rect.setAttribute('x', `${Math.round(x)}`);
233
+ rect.setAttribute('y', `${Math.round(y)}`);
234
+ rect.setAttribute('width', `${Math.round(w)}`);
235
+ rect.setAttribute('height', `${Math.round(h)}`);
236
+ this.svg.appendChild(rect);
237
+ return rect;
238
+ }
239
+
240
+ finishAppendMidiEvent() {
241
+ this.pause();
242
+ let midiEvents = this.midiEvents.sort((a, b) => a[1] - b[1]);
243
+ let tempo = (60 / 120) * 10 ** 3;
244
+ let ms = 0;
245
+ for (let i = 0; i < midiEvents.length; i++) {
246
+ let midiEvent = midiEvents[i];
247
+ if (i === 0) {
248
+ ms = 0;
249
+ } else {
250
+ ms += (midiEvent[1] - midiEvents[i - 1][1]) * tempo / this.timePreBeat;
251
+ }
252
+ midiEvent.push(ms);
253
+ this.midiTimes.push(ms);
254
+ }
255
+ this.totalTimeMs = ms;
256
+ }
257
+
258
+ getDuration() {
259
+ return this.totalTimeMs;
260
+ }
261
+
262
+ setPlayTime(t) {
263
+ if (this.timeLine) {
264
+ this.playTime = t;
265
+ let x = (t / this.timePreBeat) * this.config.beatWidth;
266
+ this.timeLine.setAttribute('x1', x);
267
+ this.timeLine.setAttribute('x2', x);
268
+ this.timeLine.setAttribute('y1', 0);
269
+ this.timeLine.setAttribute('y2', this.config.noteHeight * 128);
270
+ this.wrapper.scrollTo(Math.max(x - this.wrapper.offsetWidth / 2, 0), 0);
271
+ }
272
+ }
273
+
274
+ setPlayTimeMs(ms) {
275
+ if (ms < 0 || ms > this.totalTimeMs) {
276
+ return;
277
+ }
278
+ let i = this.midiTimes.findIndex((t) => t > ms);
279
+ if (i === -1) {
280
+ i = this.midiTimes.length;
281
+ }
282
+ this.playTimeMs = ms;
283
+ this.setPlayTime(this.midiEvents[i - 1][1]);
284
+ let notes = this.activeNotes.filter((note) => {
285
+ note[7].classList.remove('active');
286
+ if (note[1] + note[3] * this.timePreBeat > this.playTime) {
287
+ return true;
288
+ }
289
+ return false;
290
+ });
291
+ this.activeNotes = notes;
292
+ let actives = this.midiEvents.filter((event) => event[1] <= this.playTime && this.playTime < event[1] + event[3] * this.timePreBeat);
293
+ actives.forEach((event) => {
294
+ event[7].classList.add('active');
295
+ });
296
+ this.activeNotes.push(...actives);
297
+ }
298
+
299
+ isPlaying() {
300
+ return this.playing;
301
+ }
302
+
303
+ play() {
304
+ if (this.playing) {
305
+ return;
306
+ }
307
+ this.playing = true;
308
+ let t0 = this.playTimeMs;
309
+ let start = performance.now();
310
+ const step = (now) => {
311
+ this.timer = requestAnimationFrame(step);
312
+ this.setPlayTimeMs(t0 + now - start);
313
+ if (t0 + now - start >= this.totalTimeMs) {
314
+ this.pause();
315
+ }
316
+ };
317
+ this.timer = requestAnimationFrame(step);
318
+ }
319
+
320
+ pause() {
321
+ if (this.timer) {
322
+ cancelAnimationFrame(this.timer);
323
+ this.timer = null;
324
+ }
325
+ this.playing = false;
326
+ }
327
+ }
328
+
329
+ window.customElements.define('midi-visualizer', MidiVisualizer);
330
+
331
+ function getProgressBar() {
332
+ const progressbar = gradioApp().deepQuerySelector('span#prompt_progress span');
333
+ if (progressbar !== null) {
334
+ return progressbar;
335
+ }
336
+ return { innerHTML: '' };
337
+ }
338
+
339
+ function getPromptResult() {
340
+ const promptResult = gradioApp().deepQuerySelector('div#prompt_result');
341
+ if (promptResult !== null) {
342
+ return promptResult;
343
+ }
344
+ return { innerHTML: '' };
345
+ }
346
+
347
+ function updateProgressBarHTML(html) {
348
+ const bar = getProgressBar();
349
+ if (bar.innerHTML !== html) {
350
+ bar.innerHTML = html;
351
+ }
352
+ }
353
+
354
+ function updateProgressBar(text, value, time) {
355
+ let percent = !text.includes('%') ? `${Math.round(value * 100)}%` : '';
356
+ updateProgressBarHTML(`${text} ${percent}`);
357
+ const result = getPromptResult();
358
+ result.innerHTML += `<br>${text} ${percent}`;
359
+ }
360
+
361
+ onUiUpdate(function() {
362
+ const bar = getProgressBar();
363
+ if (bar.innerHTML === '') {
364
+ return;
365
+ }
366
+ let str = bar.innerHTML;
367
+ if (str.includes('Rendering') || str.includes('Generating')) {
368
+ updateProgressBar(str, 0);
369
+ } else {
370
+ const match = str.match(/([0-9.]+)%/);
371
+ if (match) {
372
+ const value = parseFloat(match[1]) / 100;
373
+ updateProgressBar(str, value);
374
+ }
375
+ }
376
+ });
377
+
378
+ function runWhenReady(fn) {
379
+ const gradioRoot = gradioApp();
380
+ if (gradioRoot !== null && gradioRoot !== undefined) {
381
+ fn();
382
+ } else {
383
+ const observer = new MutationObserver(function(mutations) {
384
+ if (gradioRoot !== null && gradioRoot !== undefined) {
385
+ observer.disconnect();
386
+ fn();
387
+ }
388
+ });
389
+ observer.observe(document, { childList: true, subtree: true });
390
+ }
391
+ }
392
+
393
+ runWhenReady(function() {
394
+ const progressbar = getProgressBar();
395
+ if (progressbar !== null) {
396
+ progressbar.innerHTML = '';
397
+ }
398
+ });
app.py DELETED
@@ -1,41 +0,0 @@
1
- import gradio as gr
2
-
3
- def play_midi(midi_file):
4
- # You would use a library like mido to handle the MIDI file
5
- # For the purpose of this example, we'll just return the MIDI file path
6
- return midi_file.name
7
-
8
- css = """
9
- <style>
10
- .note.active {
11
- stroke: black;
12
- stroke-width: 0.75;
13
- stroke-opacity: 0.75;
14
- }
15
- midi-visualizer {
16
- display: block;
17
- overflow-x: scroll;
18
- }
19
- </style>
20
- """
21
-
22
- html = """
23
- <div id="midi_visualizer_container"></div>
24
- <audio id="midi_audio" controls>
25
- <source src="{{ midi_url }}" type="audio/midi">
26
- Your browser does not support the audio element.
27
- </audio>
28
- <script>
29
- // JavaScript code for handling MIDI visualization and playback
30
- // Paste the provided JavaScript code here
31
- </script>
32
- """
33
-
34
- with gr.Blocks() as demo:
35
- midi_file = gr.File(label="Upload MIDI File", file_count="single", file_types=[".mid"])
36
- gr.Markdown(css)
37
- midi_url = gr.Variable(value="")
38
- gr.Markdown(html)
39
- midi_file.change(fn=play_midi, inputs=midi_file, outputs=midi_url)
40
-
41
- demo.launch()