cocktailpeanut/midi-composer
0
1const MIDI_OUTPUT_BATCH_SIZE=4;2//Do not change MIDI_OUTPUT_BATCH_SIZE. It will be automatically replaced.3 4/**5 * 自动绕过 shadowRoot 的 querySelector6 * @param {string} selector - 要查询的 CSS 选择器7 * @returns {Element|null} - 匹配的元素或 null 如果未找到8 */9function deepQuerySelector(selector) {10 /**11 * 在指定的根元素或文档对象下深度查询元素12 * @param {Element|Document} root - 要开始搜索的根元素或文档对象13 * @param {string} selector - 要查询的 CSS 选择器14 * @returns {Element|null} - 匹配的元素或 null 如果未找到15 */16 function deepSearch(root, selector) {17 // 在当前根元素下查找18 let element = root.querySelector(selector);19 if (element) {20 return element;21 }22 23 // 如果未找到,递归检查 shadow DOM24 const shadowHosts = root.querySelectorAll('*');25 26 for (let i = 0; i < shadowHosts.length; i++) {27 const host = shadowHosts[i];28 29 // 检查当前元素是否有 shadowRoot30 if (host.shadowRoot) {31 element = deepSearch(host.shadowRoot, selector);32 if (element) {33 return element;34 }35 }36 }37 // 未找到元素38 return null;39 }40 41 return deepSearch(this, selector);42}43 44Element.prototype.deepQuerySelector = deepQuerySelector;45Document.prototype.deepQuerySelector = deepQuerySelector;46 47function gradioApp() {48 const elems = document.getElementsByTagName('gradio-app')49 const gradioShadowRoot = elems.length == 0 ? null : elems[0].shadowRoot50 return !!gradioShadowRoot ? gradioShadowRoot : document;51}52 53uiUpdateCallbacks = []54msgReceiveCallbacks = []55 56function onUiUpdate(callback){57 uiUpdateCallbacks.push(callback)58}59 60function onMsgReceive(callback){61 msgReceiveCallbacks.push(callback)62}63 64function runCallback(x, m){65 try {66 x(m)67 } catch (e) {68 (console.error || console.log).call(console, e.message, e);69 }70}71function executeCallbacks(queue, m) {72 queue.forEach(function(x){runCallback(x, m)})73}74 75document.addEventListener("DOMContentLoaded", function() {76 var mutationObserver = new MutationObserver(function(m){77 executeCallbacks(uiUpdateCallbacks, m);78 });79 mutationObserver.observe( gradioApp(), { childList:true, subtree:true })80});81 82function HSVtoRGB(h, s, v) {83 let r, g, b, i, f, p, q, t;84 i = Math.floor(h * 6);85 f = h * 6 - i;86 p = v * (1 - s);87 q = v * (1 - f * s);88 t = v * (1 - (1 - f) * s);89 switch (i % 6) {90 case 0: r = v; g = t; b = p; break;91 case 1: r = q; g = v; b = p; break;92 case 2: r = p; g = v; b = t; break;93 case 3: r = p; g = q; b = v; break;94 case 4: r = t; g = p; b = v; break;95 case 5: r = v; g = p; b = q; break;96 }97 return {98 r: Math.round(r * 255),99 g: Math.round(g * 255),100 b: Math.round(b * 255)101 };102}103 104function isMobile(){105 return /(iPhone|iPad|iPod|iOS|Android|Windows Phone)/i.test(navigator.userAgent);106}107 108const number2patch = ['Acoustic Grand', 'Bright Acoustic', 'Electric Grand', 'Honky-Tonk', 'Electric Piano 1', 'Electric Piano 2', 'Harpsichord', 'Clav', 'Celesta', 'Glockenspiel', 'Music Box', 'Vibraphone', 'Marimba', 'Xylophone', 'Tubular Bells', 'Dulcimer', 'Drawbar Organ', 'Percussive Organ', 'Rock Organ', 'Church Organ', 'Reed Organ', 'Accordion', 'Harmonica', 'Tango Accordion', 'Acoustic Guitar(nylon)', 'Acoustic Guitar(steel)', 'Electric Guitar(jazz)', 'Electric Guitar(clean)', 'Electric Guitar(muted)', 'Overdriven Guitar', 'Distortion Guitar', 'Guitar Harmonics', 'Acoustic Bass', 'Electric Bass(finger)', 'Electric Bass(pick)', 'Fretless Bass', 'Slap Bass 1', 'Slap Bass 2', 'Synth Bass 1', 'Synth Bass 2', 'Violin', 'Viola', 'Cello', 'Contrabass', 'Tremolo Strings', 'Pizzicato Strings', 'Orchestral Harp', 'Timpani', 'String Ensemble 1', 'String Ensemble 2', 'SynthStrings 1', 'SynthStrings 2', 'Choir Aahs', 'Voice Oohs', 'Synth Voice', 'Orchestra Hit', 'Trumpet', 'Trombone', 'Tuba', 'Muted Trumpet', 'French Horn', 'Brass Section', 'SynthBrass 1', 'SynthBrass 2', 'Soprano Sax', 'Alto Sax', 'Tenor Sax', 'Baritone Sax', 'Oboe', 'English Horn', 'Bassoon', 'Clarinet', 'Piccolo', 'Flute', 'Recorder', 'Pan Flute', 'Blown Bottle', 'Skakuhachi', 'Whistle', 'Ocarina', 'Lead 1 (square)', 'Lead 2 (sawtooth)', 'Lead 3 (calliope)', 'Lead 4 (chiff)', 'Lead 5 (charang)', 'Lead 6 (voice)', 'Lead 7 (fifths)', 'Lead 8 (bass+lead)', 'Pad 1 (new age)', 'Pad 2 (warm)', 'Pad 3 (polysynth)', 'Pad 4 (choir)', 'Pad 5 (bowed)', 'Pad 6 (metallic)', 'Pad 7 (halo)', 'Pad 8 (sweep)', 'FX 1 (rain)', 'FX 2 (soundtrack)', 'FX 3 (crystal)', 'FX 4 (atmosphere)', 'FX 5 (brightness)', 'FX 6 (goblins)', 'FX 7 (echoes)', 'FX 8 (sci-fi)', 'Sitar', 'Banjo', 'Shamisen', 'Koto', 'Kalimba', 'Bagpipe', 'Fiddle', 'Shanai', 'Tinkle Bell', 'Agogo', 'Steel Drums', 'Woodblock', 'Taiko Drum', 'Melodic Tom', 'Synth Drum', 'Reverse Cymbal', 'Guitar Fret Noise', 'Breath Noise', 'Seashore', 'Bird Tweet', 'Telephone Ring', 'Helicopter', 'Applause', 'Gunshot']109const number2drum_kits = {0: "Standard", 8: "Room", 16: "Power", 24: "Electric", 25: "TR-808", 32: "Jazz", 40: "Blush", 48: "Orchestra"}110 111class MidiVisualizer extends HTMLElement{112 constructor() {113 super();114 this.midiEvents = [];115 this.activeNotes = [];116 this.midiTimes = [];117 this.trackMap = new Map()118 this.patches = [];119 for (let i=0;i<16;i++){120 this.patches.push([[0,0]])121 }122 this.container = null;123 this.trackList = null124 this.pianoRoll = null;125 this.svg = null;126 this.timeLine = null;127 this.config = {128 noteHeight : 4,129 beatWidth: 32130 }131 if (isMobile()){132 this.config.noteHeight = 1;133 this.config.beatWidth = 16;134 }135 this.timePreBeat = 16136 this.svgWidth = 0;137 this.t1 = 0;138 this.totalTimeMs = 0139 this.playTime = 0140 this.playTimeMs = 0141 this.lastUpdateTime = 0142 this.colorMap = new Map();143 this.playing = false;144 this.timer = null;145 this.version = "v2"146 this.init();147 }148 149 init(){150 this.innerHTML=''151 const shadow = this.attachShadow({mode: 'open'});152 const style = document.createElement("style");153 style.textContent = ".note.active {stroke: black;stroke-width: 0.75;stroke-opacity: 0.75;}";154 const container = document.createElement('div');155 container.style.display="flex";156 container.style.height=`${this.config.noteHeight*128 + 25}px`;157 const trackListContainer = document.createElement('div');158 trackListContainer.style.width = "260px";159 trackListContainer.style.minWidth = "260px";160 trackListContainer.style.height = "100%";161 trackListContainer.style.display="flex";162 trackListContainer.style.flexDirection="column";163 const trackList = document.createElement('div');164 trackList.style.width = "100%";165 trackList.style.height = "100%";166 trackList.style.overflowY= "scroll";167 trackList.style.display="flex";168 trackList.style.flexDirection="column";169 trackList.style.flexGrow="1";170 const trackControls = document.createElement('div');171 trackControls.style.display="flex";172 trackControls.style.flexDirection="row";173 trackControls.style.width = "100%";174 trackControls.style.height = "50px";175 trackControls.style.minHeight = "50px";176 const allTrackBtn = document.createElement('button');177 allTrackBtn.textContent = "All";178 allTrackBtn.style.width = "50%";179 allTrackBtn.style.height = "100%";180 allTrackBtn.style.backgroundColor = "rgba(200, 200, 200, 0.3)";181 allTrackBtn.style.color = 'inherit';182 allTrackBtn.style.border = "none";183 allTrackBtn.style.cursor = 'pointer';184 let self = this;185 allTrackBtn.onclick = function (){186 self.trackMap.forEach((track, id) => {187 track.setChecked(true);188 })189 };190 const noneTrackBtn = document.createElement('button');191 noneTrackBtn.textContent = "None";192 noneTrackBtn.style.width = "50%";193 noneTrackBtn.style.height = "100%";194 noneTrackBtn.style.backgroundColor = "rgba(200, 200, 200, 0.3)";195 noneTrackBtn.style.color = 'inherit';196 noneTrackBtn.style.border = "none";197 noneTrackBtn.style.cursor = 'pointer';198 noneTrackBtn.onclick = function (){199 self.trackMap.forEach((track, id) => {200 track.setChecked(false);201 });202 };203 const pianoRoll = document.createElement('div');204 pianoRoll.style.overflowX= "scroll";205 pianoRoll.style.flexGrow="1";206 const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');207 svg.style.height = `${this.config.noteHeight*128}px`;208 svg.style.width = `${this.svgWidth}px`;209 const timeLine = document.createElementNS('http://www.w3.org/2000/svg', 'line');210 timeLine.style.stroke = "green"211 timeLine.style.strokeWidth = "2";212 213 if (isMobile()){214 trackListContainer.style.display = "none";215 timeLine.style.strokeWidth = "1";216 }217 shadow.appendChild(style)218 shadow.appendChild(container);219 container.appendChild(trackListContainer);220 trackListContainer.appendChild(trackList);221 trackListContainer.appendChild(trackControls);222 trackControls.appendChild(allTrackBtn);223 trackControls.appendChild(noneTrackBtn);224 container.appendChild(pianoRoll);225 pianoRoll.appendChild(svg);226 svg.appendChild(timeLine)227 this.container = container;228 this.trackList = trackList;229 this.pianoRoll = pianoRoll;230 this.svg = svg;231 this.timeLine= timeLine;232 for(let i = 0; i < 128 ; i++){233 this.colorMap.set(i, HSVtoRGB(i / 128, 1, 1))234 }235 this.setPlayTime(0);236 }237 238 addTrack(id, tr, cl, name, color){239 const track = {id, tr, cl, name, color, empty: true,240 lastCC: new Map(),241 instrument: cl===9?"Standard Drum":"Acoustic Grand",242 svg: document.createElementNS('http://www.w3.org/2000/svg', 'g'),243 ccPaths: new Map()244 }245 this.svg.appendChild(track.svg)246 const trackItem = this.createTrackItem(track);247 this.trackList.appendChild(trackItem);248 this.trackMap.set(id, track);249 return track;250 }251 252 getTrack(tr, cl){253 const id = tr * 16 + cl254 let track = this.trackMap.get(id)255 if (!!track){256 return track257 }258 let color = this.colorMap.get((this.trackMap.size*53)%128)259 return this.addTrack(id, tr, cl, `Track ${tr}, Channel ${cl}`, color)260 }261 262 createTrackItem(track) {263 const trackItem = document.createElement('div');264 trackItem.style.display = 'flex';265 trackItem.style.alignItems = 'center';266 trackItem.style.width = '100%';267 trackItem.style.position = 'relative';268 269 const colorBar = document.createElement('div');270 colorBar.style.width = '5%';271 colorBar.style.height = '100%';272 colorBar.style.position = 'absolute';273 colorBar.style.left = '0';274 colorBar.style.top = '0';275 let color = track.color;276 colorBar.style.backgroundColor = `rgb(${color.r}, ${color.g}, ${color.b})`;277 trackItem.appendChild(colorBar);278 279 const content = document.createElement('div');280 content.style.paddingLeft = '30px';281 content.style.flexGrow = '1';282 content.style.color = "grey"283 content.innerHTML = `<p>${track.name}<br>${track.instrument}</p>`;284 trackItem.appendChild(content);285 track.updateInstrument = function (instrument){286 track.instrument = instrument;287 content.innerHTML = `<p>${track.name}<br>${track.instrument}</p>`;288 }289 track.setEmpty = function (empty){290 if (empty!==track.empty){291 content.style.color = empty?"grey":"inherit";292 }293 }294 295 const toggleSwitch = document.createElement('input');296 toggleSwitch.type = 'checkbox';297 toggleSwitch.checked = true;298 toggleSwitch.style.marginLeft = 'auto';299 toggleSwitch.style.marginRight = '10px';300 toggleSwitch.style.width = '20px';301 toggleSwitch.style.height = '20px';302 toggleSwitch.style.cursor = 'pointer';303 304 toggleSwitch.onchange = function () {305 track.svg.setAttribute('visibility',toggleSwitch.checked? "visible" : "hidden")306 };307 track.setChecked = function (checked){308 toggleSwitch.checked = checked;309 track.svg.setAttribute('visibility',toggleSwitch.checked? "visible" : "hidden")310 }311 trackItem.appendChild(toggleSwitch);312 return trackItem;313 }314 315 clearMidiEvents(){316 this.pause()317 this.midiEvents = [];318 this.activeNotes = [];319 this.midiTimes = [];320 this.trackMap = new Map()321 this.patches = [];322 for (let i=0;i<16;i++){323 this.patches.push([[0,0]])324 }325 this.t1 = 0326 this.setPlayTime(0);327 this.totalTimeMs = 0;328 this.playTimeMs = 0329 this.lastUpdateTime = 0330 this.trackList.innerHTML = ''331 this.svgWidth = 0332 this.svg.innerHTML = ''333 this.svg.style.width = `${this.svgWidth}px`;334 this.svg.appendChild(this.timeLine)335 }336 337 appendMidiEvent(midiEvent){338 if(midiEvent instanceof Array && midiEvent.length > 0){339 340 this.t1 += midiEvent[1]341 let t = this.t1*this.timePreBeat + midiEvent[2]342 midiEvent = [midiEvent[0], t].concat(midiEvent.slice(3))343 if(midiEvent[0] === "note"){344 let track = midiEvent[2]345 let duration = 0346 let channel = 0347 let pitch = 0348 let velocity = 0349 if(this.version === "v1"){350 duration = midiEvent[3]351 channel = midiEvent[4]352 pitch = midiEvent[5]353 velocity = midiEvent[6]354 }else if (this.version === "v2"){355 channel = midiEvent[3]356 pitch = midiEvent[4]357 velocity = midiEvent[5]358 duration = midiEvent[6]359 }360 let vis_track = this.getTrack(track, channel);361 vis_track.setEmpty(false);362 let x = (t/this.timePreBeat)*this.config.beatWidth363 let y = (127 - pitch)*this.config.noteHeight364 let w = (duration/this.timePreBeat)*this.config.beatWidth365 let h = this.config.noteHeight366 this.svgWidth = Math.ceil(Math.max(x + w, this.svgWidth))367 let opacity = Math.min(1, velocity/127 + 0.1).toFixed(2)368 let rect = this.drawNote(vis_track, x,y,w,h, opacity)369 midiEvent.push(rect);370 this.setPlayTime(t);371 this.pianoRoll.scrollTo(this.svgWidth - this.pianoRoll.offsetWidth, this.pianoRoll.scrollTop)372 }else if(midiEvent[0] === "patch_change"){373 let track = midiEvent[2];374 let channel = midiEvent[3];375 this.patches[channel].push([t, midiEvent[4]]);376 this.patches[channel].sort((a, b) => a[0] - b[0]);377 this.getTrack(track, channel);378 }else if(midiEvent[0] === "control_change"){379 let track = midiEvent[2];380 let channel = midiEvent[3];381 let controller = midiEvent[4];382 let value = midiEvent[5];383 let vis_track = this.getTrack(track, channel);384 this.drawCC(vis_track, t, controller, value);385 this.setPlayTime(t);386 }387 this.midiEvents.push(midiEvent);388 this.svg.style.width = `${this.svgWidth}px`;389 }390 391 }392 393 drawNote(track, x, y, w, h, opacity) {394 if (!track.svg) {395 return null;396 }397 const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');398 rect.classList.add('note');399 const color = track.color;400 rect.setAttribute('fill', `rgba(${color.r}, ${color.g}, ${color.b}, ${opacity})`);401 // Round values to the nearest integer to avoid partially filled pixels.402 rect.setAttribute('x', `${Math.round(x)}`);403 rect.setAttribute('y', `${Math.round(y)}`);404 rect.setAttribute('width', `${Math.round(w)}`);405 rect.setAttribute('height', `${Math.round(h)}`);406 track.svg.appendChild(rect);407 return rect408 }409 410 drawCC(track, t, controller, value){411 if (!track.svg) {412 return null;413 }414 let path = track.ccPaths.get(controller);415 let x = (t/this.timePreBeat)*this.config.beatWidth416 let y = (127 - value)*this.config.noteHeight417 if (!path){418 path = document.createElementNS('http://www.w3.org/2000/svg', 'path');419 path.setAttribute('visibility',"hidden");420 path.setAttribute('fill', "transparent");421 const color = track.color;422 path.setAttribute('stroke', `rgba(${color.r}, ${color.g}, ${color.b}, 0.6)`);423 path.setAttribute('stroke-width', "1");424 path.setAttribute('d',425 t===0?`M ${x} ${y}`:`M 0 ${127*this.config.noteHeight} H ${x} V ${y}`);426 track.svg.appendChild(path);427 track.ccPaths.set(controller, path);428 track.lastCC.set(controller, value);429 return path;430 }431 let lastVal = track.lastCC.get(controller);432 if(lastVal !== value){433 path.removeAttribute('visibility');434 }435 let d = path.getAttribute("d");436 d += `H ${x} V ${y}`437 path.setAttribute('d', d);438 return path439 }440 441 finishAppendMidiEvent(){442 this.pause()443 let midiEvents = this.midiEvents.sort((a, b)=>a[1]-b[1])444 let tempo = (60 / 120) * 10 ** 3445 let ms = 0446 let lastT = 0447 this.midiTimes.push({ms:ms, t: 0, tempo: tempo})448 midiEvents.forEach((midiEvent)=>{449 let t = midiEvent[1]450 ms += ((t- lastT) / this.timePreBeat) * tempo451 if(midiEvent[0]==="set_tempo"){452 tempo = (60 / midiEvent[3]) * 10 ** 3453 this.midiTimes.push({ms:ms, t: t, tempo: tempo})454 }455 if(midiEvent[0]==="note"){456 this.totalTimeMs = Math.max(this.totalTimeMs, ms + (midiEvent[3]/ this.timePreBeat)*tempo)457 }else{458 this.totalTimeMs = Math.max(this.totalTimeMs, ms);459 }460 lastT = t;461 })462 let x = (lastT/this.timePreBeat)*this.config.beatWidth;463 this.trackMap.forEach((track, id)=>{464 track.ccPaths.forEach((path, controller)=>{465 let d = path.getAttribute("d");466 d += `H ${x}`467 path.setAttribute('d', d);468 })469 })470 }471 472 setPlayTime(t){473 this.playTime = t474 let x = Math.round((t/this.timePreBeat)*this.config.beatWidth)475 this.timeLine.setAttribute('x1', `${x}`);476 this.timeLine.setAttribute('y1', '0');477 this.timeLine.setAttribute('x2', `${x}`);478 this.timeLine.setAttribute('y2', `${this.config.noteHeight*128}`);479 480 this.pianoRoll.scrollTo(Math.max(0, x - this.pianoRoll.offsetWidth/2), this.pianoRoll.scrollTop)481 482 this.trackMap.forEach((track, id)=>{483 let instrument = track.instrument484 let cl = track.cl;485 let patches = this.patches[cl]486 let p = 0487 for (let i = 0; i < patches.length ; i++){488 let tp = patches[i]489 if (t < tp[0])490 break491 p = tp[1]492 }493 if (cl === 9){494 let drumKit = number2drum_kits[`${p}`];495 if (!!drumKit)496 instrument = drumKit + " Drum";497 }else{498 instrument = number2patch[p]499 }500 if (instrument !== track.instrument)501 track.updateInstrument(instrument)502 });503 504 let dt = Date.now() - this.lastUpdateTime; // limit the update rate of ActiveNotes505 if(this.playing && dt > 50){506 let activeNotes = []507 this.removeActiveNotes(this.activeNotes)508 this.midiEvents.forEach((midiEvent)=>{509 if(midiEvent[0] === "note"){510 let time = midiEvent[1]511 let duration = this.version==="v1"? midiEvent[3]:midiEvent[6]512 let note = midiEvent[midiEvent.length - 1]513 if(time <=this.playTime && time+duration>= this.playTime){514 activeNotes.push(note)515 }516 }517 });518 this.addActiveNotes(activeNotes)519 this.lastUpdateTime = Date.now();520 }521 522 }523 524 setPlayTimeMs(ms){525 this.playTimeMs = ms526 let playTime = 0527 for(let i =0;i<this.midiTimes.length;i++){528 let midiTime = this.midiTimes[i]529 if(midiTime.ms>=ms){530 break;531 }532 playTime = midiTime.t + (ms-midiTime.ms) * this.timePreBeat / midiTime.tempo533 }534 this.setPlayTime(playTime)535 }536 537 addActiveNotes(notes){538 notes.forEach((note)=>{539 this.activeNotes.push(note)540 note.classList.add('active');541 });542 }543 544 removeActiveNotes(notes){545 notes.forEach((note)=>{546 let idx = this.activeNotes.indexOf(note)547 if(idx>-1)548 this.activeNotes.splice(idx, 1);549 note.classList.remove('active');550 });551 }552 553 play(){554 this.playing = true;555 }556 557 pause(){558 this.removeActiveNotes(this.activeNotes)559 this.playing = false;560 }561 562 563 bindAudioPlayer(audio){564 this.pause()565 audio.addEventListener("play", (event)=>{566 this.play()567 })568 audio.addEventListener("pause", (event)=>{569 this.pause()570 })571 audio.addEventListener("loadedmetadata", (event)=>{572 //I don't know why the calculated totalTimeMs is different from audio.duration*10**3573 this.totalTimeMs = audio.duration*10**3;574 })575 }576 577 bindWaveformCursor(cursor){578 let self = this;579 const callback = function(mutationsList, observer) {580 for(let mutation of mutationsList) {581 if (mutation.type === 'attributes' && mutation.attributeName === 'style') {582 let progress = parseFloat(mutation.target.style.left.slice(0,-1))*0.01;583 if(!isNaN(progress)){584 self.setPlayTimeMs(progress*self.totalTimeMs);585 }586 }587 }588 };589 const observer = new MutationObserver(callback);590 observer.observe(cursor, {591 attributes: true,592 attributeFilter: ['style']593 });594 }595}596 597customElements.define('midi-visualizer', MidiVisualizer);598 599(()=>{600 function midi_visualizer_setup(idx, midi_visualizer){601 let midi_visualizer_container_inited = null602 let midi_audio_audio_inited = null;603 let midi_audio_cursor_inited = null;604 onUiUpdate((m)=>{605 let app = gradioApp()606 let midi_visualizer_container = app.querySelector(`#midi_visualizer_container_${idx}`);607 if(!!midi_visualizer_container && midi_visualizer_container_inited!== midi_visualizer_container){608 midi_visualizer_container.appendChild(midi_visualizer)609 midi_visualizer_container_inited = midi_visualizer_container;610 }611 let midi_audio = app.querySelector(`#midi_audio_${idx}`);612 if (!!midi_audio){613 let midi_audio_cursor = midi_audio.deepQuerySelector(".cursor");614 if(!!midi_audio_cursor && midi_audio_cursor_inited!==midi_audio_cursor){615 midi_visualizer.bindWaveformCursor(midi_audio_cursor)616 midi_audio_cursor_inited = midi_audio_cursor617 }618 let midi_audio_audio = midi_audio.deepQuerySelector("audio");619 if(!!midi_audio_audio && midi_audio_audio_inited!==midi_audio_audio){620 midi_visualizer.bindAudioPlayer(midi_audio_audio)621 midi_audio_audio_inited = midi_audio_audio622 }623 }624 });625 }626 627 let midi_visualizers = []628 for (let i = 0; i < MIDI_OUTPUT_BATCH_SIZE ; i++){629 let midi_visualizer = document.createElement('midi-visualizer');630 midi_visualizers.push(midi_visualizer);631 midi_visualizer_setup(i, midi_visualizer)632 }633 634 let hasProgressBar = false;635 let output_tabs_inited = null;636 onUiUpdate((m)=>{637 let app = gradioApp()638 let output_tabs = app.querySelector("#output_tabs");639 if(!!output_tabs && output_tabs_inited!== output_tabs){640 output_tabs_inited = output_tabs;641 }642 });643 644 function createProgressBar(progressbarContainer){645 let parentProgressbar = progressbarContainer.parentNode;646 let divProgress = document.createElement('div');647 divProgress.className='progressDiv';648 let rect = progressbarContainer.getBoundingClientRect();649 divProgress.style.width = rect.width + "px";650 divProgress.style.background = "#b4c0cc";651 divProgress.style.borderRadius = "8px";652 let divInner = document.createElement('div');653 divInner.className='progress';654 divInner.style.color = "white";655 divInner.style.background = "#0060df";656 divInner.style.textAlign = "right";657 divInner.style.fontWeight = "bold";658 divInner.style.borderRadius = "8px";659 divInner.style.height = "20px";660 divInner.style.lineHeight = "20px";661 divInner.style.paddingRight = "8px"662 divInner.style.width = "0%";663 divProgress.appendChild(divInner);664 parentProgressbar.insertBefore(divProgress, progressbarContainer);665 hasProgressBar = true;666 }667 668 function removeProgressBar(progressbarContainer){669 let parentProgressbar = progressbarContainer.parentNode;670 let divProgress = parentProgressbar.querySelector(".progressDiv");671 parentProgressbar.removeChild(divProgress);672 hasProgressBar = false;673 }674 675 function setProgressBar(progress, total){676 if (!hasProgressBar)677 createProgressBar(output_tabs_inited)678 if (hasProgressBar && total === 0){679 removeProgressBar(output_tabs_inited)680 return681 }682 let parentProgressbar = output_tabs_inited.parentNode;683 // let divProgress = parentProgressbar.querySelector(".progressDiv");684 let divInner = parentProgressbar.querySelector(".progress");685 if(total===0)686 total = 1;687 divInner.style.width = `${(progress/total)*100}%`;688 divInner.textContent = `${progress}/${total}`;689 }690 691 onMsgReceive((msgs)=>{692 for(let msg of msgs){693 if(msg instanceof Array){694 msg.forEach((o)=>{handleMsg(o)});695 }else{696 handleMsg(msg);697 }698 }699 })700 function handleMsg(msg){701 let idx;702 switch (msg.name) {703 case "visualizer_clear":704 idx = msg.data[0];705 let ver = msg.data[1];706 midi_visualizers[idx].clearMidiEvents(false);707 midi_visualizers[idx].version = ver;708 break;709 case "visualizer_append":710 idx = msg.data[0];711 let events = msg.data[1];712 events.forEach( value => {713 midi_visualizers[idx].appendMidiEvent(value);714 })715 break;716 case "visualizer_end":717 idx = msg.data;718 midi_visualizers[idx].finishAppendMidiEvent()719 midi_visualizers[idx].setPlayTime(0);720 break;721 case "progress":722 let progress = msg.data[0]723 let total = msg.data[1]724 setProgressBar(progress, total)725 break;726 default:727 }728 }729})();730 