PageSourceSearch

https://ditto-tts.github.io/js/main.js

js ditto-tts.github.io collected 2026-10-03 09:00:58 UTC 23,852 bytes, 582 lines download raw bytes

1// Draw the full waveform of the audio
2function drawWaveform(data, canvas, currentTime, totalTime, promptTime = 3) {
3  const ctx = canvas.getContext("2d");
4  // console.log((new Date()).getTime());
5
6  const step = Math.ceil(data.length / canvas.width);
7  const amp = canvas.height / 2;
8  const currentRatio = currentTime / totalTime;
9  const promptRatio = promptTime / totalTime;
10
11  ctx.clearRect(0, 0, canvas.width, canvas.height);
12  ctx.fillStyle = "transparent"; // Background color
13  ctx.fillRect(0, 0, canvas.width, canvas.height);
14
15  const barWidth = 2; //4;      // Width of each bar
16  const gap = 1; // Space between bars
17
18  let maxVal = 0;
19  let sumArray = [];
20  for (let i = 0; i < canvas.width; i += barWidth + gap) {
21    let sum = 0;
22    for (let j = 0; j < step; j++) {
23      // const datum = data[(i * step / (barWidth + gap)) + j];
24      const datum = Math.abs(data[i * step + j]);
25      sum += datum;
26    }
27    sumArray.push(sum);
28    maxVal = Math.max(sum, maxVal);
29  }
30
31  for (let i = 0; i < canvas.width; i += barWidth + gap) {
32    let mean = sumArray[Math.round(i / (barWidth + gap))] / maxVal;
33    mean = Math.pow(mean, 0.8);
34    mean = Math.max(mean, 0.05);
35    // console.log(maxVal, mean);
36    if (i < currentRatio * canvas.width) {
37      if (i < promptRatio * canvas.width) {
38        ctx.fillStyle = "#8FBDDF"; //"#F06A8A";
39      } else {
40        ctx.fillStyle = "#9FDE83"; //"#8FBDDF";
41      }
42    } else {
43      ctx.fillStyle = "#858585";
44    }
45    ctx.beginPath();
46    // ctx.fillRect(i, (1 + min) * amp, barWidth, (max - min) * amp);
47    ctx.roundRect(i, (1 - mean) * amp, barWidth, 2 * mean * amp, [100]);
48    // ctx.stroke();
49    ctx.fill();
50  }
51}
52
53function audioVisualizer(audio, canvas, promptTime = 3) {
54  let audioData = null;
55
56  // Set up AudioContext and Analyzer
57  const audioContext = new (window.AudioContext || window.webkitAudioContext)();
58  const analyser = audioContext.createAnalyser();
59  const source = audioContext.createMediaElementSource(audio);
60  source.connect(analyser);
61  analyser.connect(audioContext.destination);
62
63  // Fetch and decode the audio file data
64  fetch(audio.src)
65    .then((response) => response.arrayBuffer())
66    .then((data) => audioContext.decodeAudioData(data))
67    .then((buffer) => {
68      audioData = buffer.getChannelData(0);
69      drawWaveform(audioData, canvas, 0, audio.duration, promptTime);
70    });
71
72  audio.addEventListener("play", function () {
73    if (audioContext.state === "suspended") {
74      audioContext.resume();
75    }
76  });
77
78  let userInteracted = false;
79  audio.addEventListener("seeking", function () {
80    if (audioContext.state === "suspended") {
81      audioContext.resume();
82    }
83    userInteracted = true;
84  });
85
86  // Update waveform display during audio playback
87  setInterval(function () {
88    if (audio.paused != true || userInteracted) {
89      drawWaveform(
90        audioData,
91        canvas,
92        audio.currentTime,
93        audio.duration,
94        promptTime,
95      );
96      userInteracted = false;
97    }
98  }, 50);
99}
100
101function divBuilderLibriSpeech(id, data) {
102  const fragment = document.createDocumentFragment();
103
104  for (let i = 0; i < data["text_list"].length; i++) {
105    const copiedNode = document.importNode(
106      document.querySelector("#prompt-template").content,
107      true,
108    );
109
110    // inject values
111    const promptText = copiedNode.querySelector(".ditto-prompt-text > span");
112    promptText.innerText = data["prompt_text_list"][i];
113    const text = copiedNode.querySelector(".ditto-text > span");
114    text.innerText = data["text_list"][i];
115
116    const audioOursAll = copiedNode.querySelector(".ditto-audioviz audio");
117    audioOursAll.setAttribute(
118      "src",
119      data["path_template_list"][i].replace("{}", "ours_all"),
120    );
121
122    const sampleAudioList = copiedNode.querySelectorAll(
123      ".ditto-sample-box audio",
124    );
125    sampleAudioList[0].setAttribute(
126      "src",
127      data["path_template_list"][i].replace("{}", "prompt"),
128    );
129    sampleAudioList[1].setAttribute(
130      "src",
131      data["path_template_list"][i].replace("{}", "gt"),
132    );
133    sampleAudioList[2].setAttribute(
134      "src",
135      data["path_template_list"][i].replace("{}", "yourtts"),
136    );
137    sampleAudioList[3].setAttribute(
138      "src",
139      data["path_template_list"][i].replace("{}", "vall-e"),
140    );
141    sampleAudioList[4].setAttribute(
142      "src",
143      data["path_template_list"][i].replace("{}", "clam"),
144    );
145
146    const card = copiedNode.querySelectorAll(".ditto-sample-box table tr")
147    card.forEach((elm, idx) => {
148      elm.removeChild(elm.children[5]);
149    });
150
151    // inject functions
152    const canvas = copiedNode.querySelector(".ditto-audioviz canvas");
153    audioVisualizer(audioOursAll, canvas, data["prompt_time"][i]);
154
155    const toggle = copiedNode.querySelector(".ditto-toggle");
156    const sampleBox = copiedNode.querySelector(".ditto-sample-box");
157    toggle.children[0].onclick = function () {
158      toggle.children[0].classList.remove("show");
159      toggle.children[1].classList.add("show");
160      sampleBox.classList.add("show");
161    };
162    toggle.children[1].onclick = function () {
163      toggle.children[0].classList.add("show");
164      toggle.children[1].classList.remove("show");
165      sampleBox.classList.remove("show");
166    };
167    const openToggle = copiedNode.querySelector(
168      ".ditto-toggle div:nth-child(1)",
169    );
170    const closeToggle = copiedNode.querySelector(
171      ".ditto-toggle div:nth-child(2)",
172    );
173
174    fragment.appendChild(copiedNode);
175  }
176
177  const footnote1 = document.createElement("span");
178  footnote1.innerHTML =
179    '<sup id="footnote1-1">1</sup><a href="https://edresson.github.io/YourTTS/">https://edresson.github.io/YourTTS/</a><br>';
180  const footnote2 = document.createElement("span");
181  footnote2.innerHTML =
182    '<sup id="footnote1-2">2</sup>
182<a href="https://www.microsoft.com/en-us/research/project/vall-e-x/vall-e/">https://www.microsoft.com/en-us/research/project/vall-e-x/vall-e/</a><br>';
183  const footnote3 = document.createElement("span");
184  footnote3.innerHTML =
185    '<sup id="footnote1-3">3</sup><a href="https://clam-tts.github.io/">https://clam-tts.github.io/</a>';
186
187  fragment.appendChild(footnote1);
188  fragment.appendChild(footnote2);
189  fragment.appendChild(footnote3);
190
191  const root = document.querySelector(id);
192  root.appendChild(fragment);
193}
194
195function divBuilder(id, data) {
196  const fragment = document.createDocumentFragment();
197
198  for (let i = 0; i < data["text_list"].length; i++) {
199    const copiedNode = document.importNode(
200      document.querySelector("#prompt-template").content,
201      true,
202    );
203
204    // inject values
205    const promptText = copiedNode.querySelector(".ditto-prompt-text > span");
206    promptText.innerText = data["prompt_text_list"][i];
207    const text = copiedNode.querySelector(".ditto-text > span");
208    text.innerText = data["text_list"][i];
209
210    const audioOursAll = copiedNode.querySelector(".ditto-audioviz audio");
211    audioOursAll.setAttribute(
212      "src",
213      data["path_template_list"][i].replace("{}", "ours_all"),
214    );
215
216    const sampleAudioList = copiedNode.querySelectorAll(
217      ".ditto-sample-box audio",
218    );
219    sampleAudioList[0].setAttribute(
220      "src",
221      data["path_template_list"][i].replace("{}", "prompt"),
222    );
223    sampleAudioList[1].setAttribute(
224      "src",
225      data["path_template_list"][i].replace("{}", "gt"),
226    );
227    sampleAudioList[2].setAttribute(
228      "src",
229      data["path_template_list"][i].replace("{}", "yourtts"),
230    );
231    sampleAudioList[3].setAttribute(
232      "src",
233      data["path_template_list"][i].replace("{}", "vall-e"),
234    );
235    sampleAudioList[4].setAttribute(
236      "src",
237      data["path_template_list"][i].replace("{}", "clam"),
238    );
239    sampleAudioList[5].setAttribute(
240      "src",
241      data["path_template_list"][i].replace("{}", "megatts"),
242    );
243
244    // inject functions
245    const canvas = copiedNode.querySelector(".ditto-audioviz canvas");
246    audioVisualizer(audioOursAll, canvas, data["prompt_time"][i]);
247
248    const toggle = copiedNode.querySelector(".ditto-toggle");
249    const sampleBox = copiedNode.querySelector(".ditto-sample-box");
250    toggle.children[0].onclick = function () {
251      toggle.children[0].classList.remove("show");
252      toggle.children[1].classList.add("show");
253      sampleBox.classList.add("show");
254    };
255    toggle.children[1].onclick = function () {
256      toggle.children[0].classList.add("show");
257      toggle.children[1].classList.remove("show");
258      sampleBox.classList.remove("show");
259    };
260    const openToggle = copiedNode.querySelector(
261      ".ditto-toggle div:nth-child(1)",
262    );
263    const closeToggle = copiedNode.querySelector(
264      ".ditto-toggle div:nth-child(2)",
265    );
266
267    fragment.appendChild(copiedNode);
268  }
269
270  const root = document.querySelector(id);
271  root.appendChild(fragment);
272}
273
274function divBuilderMLS(id, data) {
275  divBuilder(id, data);
276
277  const cards = document.querySelectorAll(id + " .ditto-card");
278  for (let i = 0; i < data["text_list"].length; i++) {
279    cards[i].querySelector("div:nth-child(1)").innerHTML =
280      "<p><strong>Prompt + Text (" +
281      data["lang_list"][i] +
282      ")</strong>: " +
283      data["text_list"][i] +
284      "</p>";
285
286    cards[i]
287      .querySelectorAll(".ditto-sample-box table tr")
288      .forEach((elm, idx) => {
289        elm.removeChild(elm.children[5]);
290        elm.removeChild(elm.children[3]);
291        elm.removeChild(elm.children[2]);
292      });
293  }
294
295  const fragment = document.createDocumentFragment();
296  const footnote1 = document.createElement("span");
297  footnote1.innerHTML =
298    '<sup id="footnote2-1">1</sup><a href="https://clam-tts.github.io/">https://clam-tts.github.io/</a>';
299
300  fragment.appendChild(footnote1);
301
302  const root = document.querySelector(id);
303  root.appendChild(fragment);
304}
305
306function divBuilderCeleb(id, data) {
307  divBuilder(id, data);
308
309  const cards = document.querySelectorAll(id + " .ditto-card");
310  for (let i = 0; i < data["text_list"].length; i++) {
311    cards[i].querySelector("div:nth-child(1)").innerHTML =
312      "<p style='font-weight: bold;'>Celeb: " +
313      data["name_list"][i] +
314      "</p>" +
315      cards[i].querySelector("div:nth-child(1)").innerHTML;
316    cards[i]
317      .querySelectorAll(".ditto-sample-box table tr")
318      .forEach((elm, idx) => {
319        elm.removeChild(elm.children[5]);
320        elm.removeChild(elm.children[3]);
321        elm.removeChild(elm.children[2]);
322        elm.removeChild(elm.children[1]);
323      });
324  }
325
326  const fragment = document.createDocumentFragment();
327  const footnote1 = document.createElement("span");
328  footnote1.innerHTML =
329    '<sup id="footnote3-1">1</sup><a href="https://clam-tts.github.io/">https://clam-tts.github.io/</a>';
330
331  fragment.appendChild(footnote1);
332
333  const root = document.querySelector(id);
334  root.appendChild(fragment);
335}
336
337function divBuilderAnime(id, data) {
338  divBuilder(id, data);
339
340  const cards = document.querySelectorAll(id + " .ditto-card");
341  for (let i = 0; i < data["text_list"].length; i++) {
342    cards[i].querySelector("div:nth-child(1)").innerHTML =
343      "<p style='font-weight: bold;'>Anime: " +
344      data["name_list"][i] +
345      "</p>" +
346      cards[i].querySelector("div:nth-child(1)").innerHTML;
347    cards[i]
348      .querySelectorAll(".ditto-sample-box table tr")
349      .forEach((elm, idx) => {
350        elm.removeChild(elm.children[3]);
351        elm.removeChild(elm.children[2]);
352        elm.removeChild(elm.children[1]);
353      });
354  }
355
356  const fragment = document.createDocumentFragment();
357  const footnote1 = document.createElement("span");
358  footnote1.innerHTML =
359    '<sup id="footnote4-1">1</sup><a href="https://boostprompt.github.io/boostprompt/">https://boostprompt.github.io/boostprompt/</a>';
360
361  fragment.appendChild(footnote1);
362
363  const root = document.querySelector(id);
364  root.appendChild(fragment);
365}
366
367const librispeechData = {
368  text_list: [
369    "They moved thereafter cautiously about the hut groping before and about them to find something to show that Warrenton had fulfilled his mission.",
370    "And lay me down in thy cold bed and leave my shining lot.",
371    "Number ten, fresh nelly is waiting on you, good night husband.",
372    "Yea, his honourable worship is within, but he hath a godly minister or tw
372o with him, and likewise a leech.",
373    "Instead of shoes, the old man wore boots with turnover tops, and his blue coat had wide cuffs of gold braid.",
374    "The army found the people in poverty and left them in comparative wealth.",
375    "Thus did this humane and right minded father comfort his unhappy daughter, and her mother embracing her again, did all she could to soothe her feelings.",
376    "He was in deep converse with the clerk and entered the hall holding him by the arm",
377  ],
378
379  path_template_list: [
380    "audios/librispeech/librispeech_61-70970-0024_{}.wav",
381    "audios/librispeech/librispeech_908-157963-0027_{}.wav",
382    "audios/librispeech/librispeech_1089-134686-0004_{}.wav",
383    "audios/librispeech/librispeech_1221-135767-0014_{}.wav",
384    "audios/librispeech/librispeech_1284-1180-0002_{}.wav",
385    "audios/librispeech/librispeech_4077-13754-0000_{}.wav",
386    "audios/librispeech/librispeech_5639-40744-0020_{}.wav",
387    "audios/librispeech/librispeech_61-70970-0007_{}.wav",
388  ],
389
390  prompt_text_list: [
391    "ly descended the ladder and found himself soon upon firm rock",
392    "milked cow and tames the fi",
393    "paced up and down waiting but he could wait no long",
394    "windows the wooden shutters to close over them at",
395    "he told his visitors as he lighted a pipe with a",
396    "an tribes have generally regarded the religion of the latter day sa",
397    "ings were showered upon him by all who saw him",
398    "urs passed wearily by and movement could yet be heard about",
399  ],
400  prompt_time: Array(8).fill(3),
401};
402
403const mlsData = {
404  text_list: [
405    "o god our hands are few and faint our hope rests all with thee lend us thy hand in this sore strait and thine the glory be",
406    "angelica broke in in her energetic way if you're going to be a duke i won't be left plain miss hamilton wells you couldn't be plain miss anything",
407    "같은 시간 에프조의 멕시코와 스웨덴 역시 조별리그 삼차전을 치른다.",
408    "실현할 기회를 얻지 못하다 미국의 전기회사를 방문해 계획을 말했습니다.",
409    "und grinst nun erst beginnt die praktische übung bin ich nicht schon allzu erschöpft durch das theoretische wohl allzu erschöpft das gehört zu meinem schicksal trotzdem greife ich so gut ich kann nach der hingereichten flasche",
410    "wenn nicht mr osborne ein bescheidener fußgänger gewesen wäre noch ist der beifall in der luft da lösen ihn plötzlich andere töne ab der marquis von blandford",
411    "en toen jammerde het ook in het schrijfboek van hjalmar o dat was vreselijk om aan te horen op iedere bladzijde stonden van boven naar beneden de grote letters en naast iedere grote letter stond een kleine dat was het voorbeeld",
412    "neen niet op de achterste rij ook niet naast jantje kroeze dat gaat geen twee dagen goed vooraan dan maar nee die bank is te klein voor hem en daar zit hij ook de kleinere jongens achter hem in de weg",
413    "au lieu de répondre à ces questions le jeune homme se mit à pleurer amèrement que la fortune est inconstante s'écria-t-il elle se plaît à abaisser les hommes qu'elle a élevés",
414    "et de se savoir ainsi chantée par un poète cela lui fit oublier toute modestie elle allongea son cou derrière ses feuilles tourna vaniteusement sa tête à gauche et à droite et se mira avec complaisance dans une grosse goutte de rosée qui était restée pendue à un brin d'herbe",
415    "lo quise decir a vuestra merced es que es fama en este pueblo que no hay gente más mala que las placeras porque todas son desvergonzadas desalmadas y atrevidas y yo así lo creo por las que he visto en otros pueblos",
416    "en otro cajón el valor de treinta y seis libras esterlinas en moneda de europa y del brasil parte en oro parte en plata y entre otras algunas piezas de a ocho a la vista de aquel dinero me sonreí sardónicamente metal miserable exclamé de qué puedes servirme",
417    "molto impensierito della diffusione del malcostume per opera della stampa quotidiana proibisce alla moglie e alla figliuola la lettura dei giornali la piccola medea è stata educata secondo le rigide massime di condotta che a lui",
418    "però disse il maestro se tu tronchi qualche fraschetta d'una d'este piante e pensier c'hai si faran tutti monchi",
419    "o homem ao destacar-se do último elo da cadeia dos seres sentiu-se forte e senhor da terra a natureza oferecia-lhe por toda a parte seus pe
419itos uberantes e este regozijo de harmonia ligava a sua existência a vida panteística do universo",
420    "a influência manifesta do cristianismo é a comuna o abraço dos povos pelo trabalho do comércio e da indústria eis o segredo das riquezas de pisa grand veneza genova bruges e florença ao pé da barbárie dos estados feudais",
421    "choćby nawet z kamyków ułożony niema dla niego żadnej wagi bo gatunkowo nie jest ani cięższym ani lżejszym od wody gdyby było inaczej to jest gdyby był cięższym męczyłby bezpotrzebnie właściciela w przeciwnym zaś razie unosiłby go łatwo w górę i utrudniał pełzanie po dnie",
422    "nie mają one zupełnie zwierzchników jeśli w ulu matka pszczół jest tylko matką ale nie królową jak to niektórzy badacze mylnie twierdzili to tem bardziej w mrowisku nie może być mowy o królowych gdzie matek jest zwykle kilka przy jednowładztwie zresztą musi być przymus tymczasem mrówki nie podlegają żadnemu zgoła przymusowi",
423  ],
424
425  path_template_list: [
426    "audios/mls/mls_english_10611_10308_000071_{}.wav",
427    "audios/mls/mls_english_12278_12446_000057_{}.wav",
428    "audios/multi/0006_G2A3E7_KYH_001279_wav_c_2_{}.wav",
429    "audios/multi/0001_G1A3E6S0C0_PSB_000046_wav_c_0_{}.wav",
430    "audios/mls/mls_german_2252_1599_000090_{}.wav",
431    "audios/mls/mls_german_1844_931_000001_{}.wav",
432    "audios/mls/mls_dutch_3034_2211_000142_{}.wav",
433    "audios/mls/mls_dutch_3798_4626_000595_{}.wav",
434    "audios/mls/mls_french_296_1028_000040_{}.wav",
435    "audios/mls/mls_french_10179_11051_000033_{}.wav",
436    "audios/mls/mls_spanish_3471_1378_000155_{}.wav",
437    "audios/mls/mls_spanish_8585_9503_000072_{}.wav",
438    "audios/mls/mls_italian_8582_7877_000066_{}.wav",
439    "audios/mls/mls_italian_280_529_000089_{}.wav",
440    "audios/mls/mls_portuguese_13063_13511_000009_{}.wav",
441    "audios/mls/mls_portuguese_13063_13511_000045_{}.wav",
442    "audios/mls/mls_polish_9098_8338_000009_{}.wav",
443    "audios/mls/mls_polish_8758_8338_000023_{}.wav",
444  ],
445  lang_list: [
446    "English",
447    "English",
448    "Korean",
449    "Korean",
450    "German",
451    "German",
452    "Dutch",
453    "Dutch",
454    "French",
455    "French",
456    "Spanish",
457    "Spanish",
458    "Italian",
459    "Italian",
460    "Portuguese",
461    "Portuguese",
462    "Polish",
463    "Polish",
464  ],
465  prompt_text_list: new Array(18),
466  prompt_time: Array(18).fill(3),
467};
468
469const celebData = {
470  text_list: [
471    "We must unite and harness our strengths, for the fate of our world hangs in the balance.",
472    "However, if you choose to stay, know that the truth I unveil may forever alter the course of your journey.",
473    "So here we are, trying to catch up, and hoping this day turns around soon.",
474    "And sometimes, in both realms, it's not just about shining the brightest, but enduring the longest.",
475    "But to those who knew her well, it was a symbol of her unwavering determination and spirit.",
476    "We have the responsibility to ensure power and technology are used for the greater good.",
477    "Our goal is to bridge communication gaps and preserve the richness of these unique languages.",
478  ],
479
480  path_template_list: [
481    "audios/famous/optimusprime_{}.wav",
482    "audios/famous/sherlock_{}.wav",
483    "audios/famous/jessie_{}.wav",
484    "audios/famous/caine_{}.wav",
485    "audios/famous/rachel_{}.wav",
486    "audios/famous/robert_{}.wav",
487    "audios/famous/zuck_{}.wav",
488  ],
489  name_list: [
490    "Optimus Prime",
491    "Benedict Cumberbatch",
492    "Jessie Eisenberg",
493    "Michael Caine",
494    "Rachel McAdams",
495    "Robert Downey Jr.",
496    "Mark Zuckerberg",
497  ],
498  prompt_text_list: [
499    "Perhaps, in any case, we had better see some improvement, or this battle may be lost before it has truly begun.",
500    "So maybe, that you would prefer to forgo my secret rather than consent to becoming a prisoner here for what might be several days.",
501    "I'm starting this, twelve minutes late which is annoying and not my fault. Rachel needed my help and Ziggy would not stop crying.",
502    "What you need to be a star in movies is not that different from what you need to be a star in the other universe. It just takes a little more luck.",
503    "So far, the ordinary observer, an extraordinary observer might have seen that the chin was very pointed and pronounced. ",
504    "They, say the best weapon is one you never have to fire. I respectfully disagree! I prefer, the weapon you only have to fire once! That's how dad did it! that's how America does it! and it's worked out pretty well so far.",
505    "Alright so our team developed the first speech to speech AI translation system, that works for languages that are only spoken and not written like Hokkien.",
506  ],
507  // prompt_time: Array(7).fill(3),
508  prompt_time: [8.731, 7.43, 6.873, 8.266, 7.152, 16.718, 8.638],
509};
510
511const animeData = {
512  text_list: [
513    "Let's go drink until we can't feel feelings anymore.",
514    "Uh, it's not like the internet to go crazy about something small and stupid.",
515    "Then I would never talk to that person about boa constrictors, or primeval forests, or stars. I would bring myself down to his level.",
516    "In what a disgraceful light might it not strike so vain a man!",
517  ],
518
519  path_template_list: [
520    "audios/anime/spongebob_{}.wav",
521    "audios/anime/petergriffin_{}.wav",
522    "audios/anime/rick_{}.wav",
523    "audios/anime/morty_{}.wav",
524  ],
525  name_list: [
526    "Sponge Bob",
527    "Peter Griffin",
528    "Rick",
529    "Morty",
530  ],
531  prompt_text_list: [
532    "My name is Spongebob Squarepants. And I’m gonna tell you about paying.",
533    "Well, I, I’m getting something really special too and by special, I don’t mean special like that Kleinman boy down the street. More special, like, like special K the serial.",
534    "Yeah, that’s the difference between you and me morty. I never go back to the carpet store.",
535    "I’m being serious. Ok?",
536  ],
537  // prompt_time: Array(7).fill(3),
538  prompt_time: [3.239, 9.850, 4.350, 1.750],
539};
540
541divBuilderCeleb("#celeb-box", celebData);
542//divBuilder("#librispeech-box", librispeechData);
543//divBuilder("#vctk-box", vctkData);
544//divBuilderMLS("#mls-box", mlsData);
545let librispeechFlag = false;
546let mlsFlag = false;
547let animeFlag = false;
548
549document
550  .querySelector(
551    'button[data-bs-toggle="tab"][data-bs-target="#librispeech-box"]',
552  )
553  .addEventListener("shown.bs.tab", function (event) {
554    if (!librispeechFlag) {
555      librispeechFlag = true;
556      divBuilderLibriSpeech("#librispeech-box", librispeechData);
557    }
558  });
559document
560  .querySelector('button[data-bs-toggle="tab"][data-bs-target="#mls-box"]')
561  .addEventListener("shown.bs.tab", function (event) {
562    if (!mlsFlag) {
563      mlsFlag = true;
564      divBuilderMLS("#mls-box", mlsData);
565    }
566  });
567document
568  .querySelector('button[data-bs-toggle="tab"][data-bs-target="#anime-box"]')
569  .addEventListener("shown.bs.tab", function (event) {
570    if (!animeFlag) {
571      animeFlag = true;
572      divBuilderAnime("#anime-box", animeData);
573    }
574  });
575
576addEventListener("scroll", (event) => {
577  if (document.querySelector("#myTab").offsetTop < window.scrollY) {
578    document.querySelector("#fab").classList.add("show");
579  } else {
580    document.querySelector("#fab").classList.remove("show");
581  }
582});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.