@creativeorange/azure-text-to-speech 2.1.1 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8457,6 +8457,9 @@ class TextToSpeech {
8457
8457
  __publicField(this, "wordBoundaryOffset", 0);
8458
8458
  __publicField(this, "prevTextOffset", 0);
8459
8459
  __publicField(this, "url", "");
8460
+ __publicField(this, "prefetchedAudio", /* @__PURE__ */ new Map());
8461
+ __publicField(this, "prefetchPromises", /* @__PURE__ */ new Map());
8462
+ __publicField(this, "activePrefetchedAudioUrl", "");
8460
8463
  this.key = key;
8461
8464
  this.region = region;
8462
8465
  this.voice = voice;
@@ -8469,14 +8472,17 @@ class TextToSpeech {
8469
8472
  }
8470
8473
  setVoice(voice) {
8471
8474
  this.voice = voice;
8475
+ this.clearPrefetchedAudio();
8472
8476
  return this;
8473
8477
  }
8474
8478
  setRate(rate) {
8475
8479
  this.rate = rate;
8480
+ this.clearPrefetchedAudio();
8476
8481
  return this;
8477
8482
  }
8478
8483
  setPitch(pitch) {
8479
8484
  this.pitch = pitch;
8485
+ this.clearPrefetchedAudio();
8480
8486
  return this;
8481
8487
  }
8482
8488
  async registerBindings(node) {
@@ -8627,6 +8633,10 @@ class TextToSpeech {
8627
8633
  if (this.player !== void 0) {
8628
8634
  this.player.pause();
8629
8635
  }
8636
+ if (this.activePrefetchedAudioUrl !== "") {
8637
+ URL.revokeObjectURL(this.activePrefetchedAudioUrl);
8638
+ this.activePrefetchedAudioUrl = "";
8639
+ }
8630
8640
  this.player = void 0;
8631
8641
  this.highlightDiv = void 0;
8632
8642
  this.prevTextOffset = 0;
@@ -8645,6 +8655,9 @@ class TextToSpeech {
8645
8655
  this.stopPlayer();
8646
8656
  if (this.clickedNode.hasAttribute("co-tts.next")) {
8647
8657
  const nextNode = document.getElementById(this.clickedNode.getAttribute("co-tts.next"));
8658
+ if (nextNode && await this.playPrefetchedNode(nextNode)) {
8659
+ return;
8660
+ }
8648
8661
  if (nextNode && nextNode.attributes.getNamedItem("co-tts.text")) {
8649
8662
  this.handleWithoutClick(nextNode, nextNode.attributes.getNamedItem("co-tts.text"));
8650
8663
  } else if (nextNode) {
@@ -8657,6 +8670,7 @@ class TextToSpeech {
8657
8670
  this.player.onAudioStart = async () => {
8658
8671
  document.dispatchEvent(new CustomEvent("COAzureTTSStartedPlaying", {}));
8659
8672
  };
8673
+ this.prefetchNextNode(this.clickedNode);
8660
8674
  this.synthesizer.speakSsmlAsync(
8661
8675
  this.buildSSML(this.textToRead),
8662
8676
  () => {
@@ -8669,6 +8683,150 @@ class TextToSpeech {
8669
8683
  }
8670
8684
  );
8671
8685
  }
8686
+ getNodeText(node, attr) {
8687
+ var _a;
8688
+ if (attr && attr.value !== "") {
8689
+ return attr.value;
8690
+ }
8691
+ if (node.hasAttribute("co-tts.text") && node.getAttribute("co-tts.text") !== "") {
8692
+ return (_a = node.getAttribute("co-tts.text")) != null ? _a : "";
8693
+ }
8694
+ return node.innerText;
8695
+ }
8696
+ getPrefetchKey(node, text) {
8697
+ return [
8698
+ node.id,
8699
+ text,
8700
+ this.voice,
8701
+ this.rate,
8702
+ this.pitch,
8703
+ this.url
8704
+ ].join("|");
8705
+ }
8706
+ clearPrefetchedAudio() {
8707
+ this.prefetchedAudio.forEach((prefetch) => {
8708
+ if (prefetch.url) {
8709
+ URL.revokeObjectURL(prefetch.url);
8710
+ }
8711
+ });
8712
+ this.prefetchedAudio.clear();
8713
+ this.prefetchPromises.clear();
8714
+ }
8715
+ async prefetchNextNode(node) {
8716
+ var _a;
8717
+ if (!node || !node.hasAttribute("co-tts.next")) {
8718
+ return;
8719
+ }
8720
+ const nextNode = document.getElementById(node.getAttribute("co-tts.next"));
8721
+ if (!nextNode) {
8722
+ return;
8723
+ }
8724
+ const attr = (_a = nextNode.attributes.getNamedItem("co-tts.text")) != null ? _a : nextNode.attributes.getNamedItem("co-tts");
8725
+ const text = this.getNodeText(nextNode, attr);
8726
+ if (text === "") {
8727
+ return;
8728
+ }
8729
+ const key = this.getPrefetchKey(nextNode, text);
8730
+ if (this.prefetchedAudio.has(key) || this.prefetchPromises.has(key)) {
8731
+ return;
8732
+ }
8733
+ const speechConfig = SpeechConfig.fromSubscription(this.key, this.region);
8734
+ speechConfig.speechSynthesisVoiceName = `Microsoft Server Speech Text to Speech Voice (${this.voice})`;
8735
+ speechConfig.speechSynthesisOutputFormat = SpeechSynthesisOutputFormat.Audio24Khz160KBitRateMonoMp3;
8736
+ const synthesizer = new SpeechSynthesizer(speechConfig, null);
8737
+ const wordBoundryList = [];
8738
+ synthesizer.wordBoundary = (s, e) => {
8739
+ wordBoundryList.push(e);
8740
+ };
8741
+ const prefetchPromise = new Promise((resolve) => {
8742
+ synthesizer.speakSsmlAsync(
8743
+ this.buildSSML(text),
8744
+ (result) => {
8745
+ synthesizer.close();
8746
+ if (!(result == null ? void 0 : result.audioData)) {
8747
+ resolve(null);
8748
+ return;
8749
+ }
8750
+ const blob = new Blob([result.audioData], { type: "audio/mpeg" });
8751
+ const url = URL.createObjectURL(blob);
8752
+ const prefetch = {
8753
+ key,
8754
+ nodeId: nextNode.id,
8755
+ text,
8756
+ url,
8757
+ wordBoundryList
8758
+ };
8759
+ this.prefetchedAudio.set(key, prefetch);
8760
+ resolve(prefetch);
8761
+ },
8762
+ () => {
8763
+ synthesizer.close();
8764
+ resolve(null);
8765
+ }
8766
+ );
8767
+ }).finally(() => {
8768
+ this.prefetchPromises.delete(key);
8769
+ });
8770
+ this.prefetchPromises.set(key, prefetchPromise);
8771
+ }
8772
+ async playPrefetchedNode(node) {
8773
+ var _a, _b, _c;
8774
+ const attr = (_a = node.attributes.getNamedItem("co-tts.text")) != null ? _a : node.attributes.getNamedItem("co-tts");
8775
+ const text = this.getNodeText(node, attr);
8776
+ const key = this.getPrefetchKey(node, text);
8777
+ const prefetch = (_b = this.prefetchedAudio.get(key)) != null ? _b : await this.prefetchPromises.get(key);
8778
+ if (!prefetch) {
8779
+ return false;
8780
+ }
8781
+ this.prefetchedAudio.delete(key);
8782
+ this.clickedNode = node;
8783
+ this.textToRead = prefetch.text;
8784
+ this.wordBoundryList = prefetch.wordBoundryList;
8785
+ this.wordEncounters = [];
8786
+ this.previousWordBoundary = void 0;
8787
+ this.prevTextOffset = 0;
8788
+ this.currentWord = "";
8789
+ this.currentOffset = 0;
8790
+ this.wordBoundaryOffset = 0;
8791
+ if (node.hasAttribute("co-tts.highlight")) {
8792
+ if (((_c = node.attributes.getNamedItem("co-tts.highlight")) == null ? void 0 : _c.value) !== "") {
8793
+ const newReferenceDiv = document.getElementById(node.attributes.getNamedItem("co-tts.highlight").value);
8794
+ this.highlightDiv = newReferenceDiv;
8795
+ if (newReferenceDiv !== null) {
8796
+ this.originalHighlightDivInnerHTML = newReferenceDiv.innerHTML;
8797
+ }
8798
+ } else {
8799
+ this.highlightDiv = node;
8800
+ this.originalHighlightDivInnerHTML = node.innerHTML;
8801
+ }
8802
+ }
8803
+ await this.createInterval();
8804
+ const audio = new Audio(prefetch.url);
8805
+ this.activePrefetchedAudioUrl = prefetch.url;
8806
+ this.player = audio;
8807
+ audio.addEventListener("play", () => {
8808
+ document.dispatchEvent(new CustomEvent("COAzureTTSStartedPlaying", {}));
8809
+ }, { once: true });
8810
+ audio.addEventListener("ended", async () => {
8811
+ this.stopPlayer();
8812
+ if (this.clickedNode.hasAttribute("co-tts.next")) {
8813
+ const nextNode = document.getElementById(this.clickedNode.getAttribute("co-tts.next"));
8814
+ if (nextNode && await this.playPrefetchedNode(nextNode)) {
8815
+ return;
8816
+ }
8817
+ if (nextNode && nextNode.attributes.getNamedItem("co-tts.text")) {
8818
+ this.handleWithoutClick(nextNode, nextNode.attributes.getNamedItem("co-tts.text"));
8819
+ } else if (nextNode) {
8820
+ nextNode.dispatchEvent(new Event("click"));
8821
+ }
8822
+ } else {
8823
+ document.dispatchEvent(new CustomEvent("COAzureTTSFinishedPlaying", {}));
8824
+ }
8825
+ }, { once: true });
8826
+ this.prefetchNextNode(node);
8827
+ await audio.play();
8828
+ return true;
8829
+ }
8672
8830
  async clearInterval() {
8673
8831
  clearInterval(this.interval);
8674
8832
  }