@creativeorange/azure-text-to-speech 3.0.1 → 3.0.4
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.
- package/dist/TextToSpeech.d.ts +23 -9
- package/dist/co-azure-tts.es.js +324 -190
- package/dist/co-azure-tts.umd.js +7 -11
- package/package.json +1 -1
package/dist/co-azure-tts.es.js
CHANGED
|
@@ -8738,6 +8738,24 @@ class SpeechToText {
|
|
|
8738
8738
|
);
|
|
8739
8739
|
}
|
|
8740
8740
|
}
|
|
8741
|
+
const PUNCTUATION_MARKS = [
|
|
8742
|
+
".",
|
|
8743
|
+
",",
|
|
8744
|
+
"!",
|
|
8745
|
+
"?",
|
|
8746
|
+
"*",
|
|
8747
|
+
"(",
|
|
8748
|
+
")",
|
|
8749
|
+
"&",
|
|
8750
|
+
"\\",
|
|
8751
|
+
"/",
|
|
8752
|
+
"^",
|
|
8753
|
+
"[",
|
|
8754
|
+
"]",
|
|
8755
|
+
"<",
|
|
8756
|
+
">",
|
|
8757
|
+
":"
|
|
8758
|
+
];
|
|
8741
8759
|
class TextToSpeech {
|
|
8742
8760
|
constructor(options) {
|
|
8743
8761
|
__publicField(this, "region", "");
|
|
@@ -8753,8 +8771,6 @@ class TextToSpeech {
|
|
|
8753
8771
|
__publicField(this, "player");
|
|
8754
8772
|
__publicField(this, "synthesizer");
|
|
8755
8773
|
__publicField(this, "previousWordBoundary");
|
|
8756
|
-
__publicField(this, "interval");
|
|
8757
|
-
__publicField(this, "wordEncounters", []);
|
|
8758
8774
|
__publicField(this, "originalHighlightDivInnerHTML", "");
|
|
8759
8775
|
__publicField(this, "currentWord", "");
|
|
8760
8776
|
__publicField(this, "currentOffset", 0);
|
|
@@ -8768,6 +8784,8 @@ class TextToSpeech {
|
|
|
8768
8784
|
__publicField(this, "playbackSegments", []);
|
|
8769
8785
|
__publicField(this, "authorizationManager");
|
|
8770
8786
|
__publicField(this, "stopPromise");
|
|
8787
|
+
__publicField(this, "playbackSessionId", 0);
|
|
8788
|
+
__publicField(this, "highlightInterval");
|
|
8771
8789
|
var _a, _b, _c;
|
|
8772
8790
|
if (!options || typeof options.voice !== "string" || options.voice.trim() === "") {
|
|
8773
8791
|
throw new Error("A voice is required.");
|
|
@@ -8843,9 +8861,8 @@ class TextToSpeech {
|
|
|
8843
8861
|
}
|
|
8844
8862
|
async handleIdModifier(node, attr) {
|
|
8845
8863
|
node.addEventListener("click", async (_) => {
|
|
8846
|
-
var _a, _b, _c
|
|
8864
|
+
var _a, _b, _c;
|
|
8847
8865
|
await this.stopPlayer();
|
|
8848
|
-
await this.createInterval();
|
|
8849
8866
|
const referenceDiv = document.getElementById(attr.value);
|
|
8850
8867
|
this.clickedNode = referenceDiv;
|
|
8851
8868
|
if (!referenceDiv) {
|
|
@@ -8856,51 +8873,30 @@ class TextToSpeech {
|
|
|
8856
8873
|
} else {
|
|
8857
8874
|
this.textToRead = (_c = (_b = referenceDiv.innerText) != null ? _b : referenceDiv.textContent) != null ? _c : "";
|
|
8858
8875
|
}
|
|
8859
|
-
|
|
8860
|
-
const highlightTarget = (_e = (_d = referenceDiv.attributes.getNamedItem("co-tts.highlight")) == null ? void 0 : _d.value) != null ? _e : "";
|
|
8861
|
-
if (highlightTarget !== "") {
|
|
8862
|
-
const newReferenceDiv = document.getElementById(highlightTarget);
|
|
8863
|
-
this.highlightDiv = newReferenceDiv;
|
|
8864
|
-
this.originalHighlightDivInnerHTML = (_f = newReferenceDiv == null ? void 0 : newReferenceDiv.innerHTML) != null ? _f : "";
|
|
8865
|
-
} else {
|
|
8866
|
-
this.highlightDiv = referenceDiv;
|
|
8867
|
-
this.originalHighlightDivInnerHTML = referenceDiv.innerHTML;
|
|
8868
|
-
}
|
|
8869
|
-
}
|
|
8876
|
+
this.setHighlightTargetFromNode(referenceDiv);
|
|
8870
8877
|
await this.startSynthesizer(node, attr);
|
|
8871
8878
|
});
|
|
8872
8879
|
}
|
|
8873
8880
|
async handleAjaxModifier(node, attr) {
|
|
8874
8881
|
node.addEventListener("click", async (_) => {
|
|
8875
8882
|
await this.stopPlayer();
|
|
8876
|
-
await this.createInterval();
|
|
8877
8883
|
this.clickedNode = node;
|
|
8878
8884
|
const response = await fetch(attr.value, {
|
|
8879
8885
|
method: `GET`
|
|
8880
8886
|
});
|
|
8881
8887
|
this.textToRead = await response.text();
|
|
8888
|
+
this.setHighlightTargetFromNode(node);
|
|
8882
8889
|
await this.startSynthesizer(node, attr);
|
|
8883
8890
|
});
|
|
8884
8891
|
}
|
|
8885
8892
|
async handleDefault(node, attr) {
|
|
8886
8893
|
node.addEventListener("click", async (_) => {
|
|
8887
|
-
var _a, _b
|
|
8894
|
+
var _a, _b;
|
|
8888
8895
|
await this.stopPlayer();
|
|
8889
|
-
await this.createInterval();
|
|
8890
8896
|
this.clickedNode = node;
|
|
8891
|
-
|
|
8892
|
-
const highlightTarget = (_b = (_a = node.attributes.getNamedItem("co-tts.highlight")) == null ? void 0 : _a.value) != null ? _b : "";
|
|
8893
|
-
if (highlightTarget !== "") {
|
|
8894
|
-
const newReferenceDiv = document.getElementById(highlightTarget);
|
|
8895
|
-
this.highlightDiv = newReferenceDiv;
|
|
8896
|
-
this.originalHighlightDivInnerHTML = (_c = newReferenceDiv == null ? void 0 : newReferenceDiv.innerHTML) != null ? _c : "";
|
|
8897
|
-
} else {
|
|
8898
|
-
this.highlightDiv = node;
|
|
8899
|
-
this.originalHighlightDivInnerHTML = node.innerHTML;
|
|
8900
|
-
}
|
|
8901
|
-
}
|
|
8897
|
+
this.setHighlightTargetFromNode(node);
|
|
8902
8898
|
if (attr.value === "") {
|
|
8903
|
-
this.textToRead = (
|
|
8899
|
+
this.textToRead = (_b = (_a = node.innerText) != null ? _a : node.textContent) != null ? _b : "";
|
|
8904
8900
|
} else {
|
|
8905
8901
|
this.textToRead = attr.value;
|
|
8906
8902
|
}
|
|
@@ -8908,26 +8904,12 @@ class TextToSpeech {
|
|
|
8908
8904
|
});
|
|
8909
8905
|
}
|
|
8910
8906
|
async handleWithoutClick(node, attr) {
|
|
8911
|
-
var _a, _b
|
|
8907
|
+
var _a, _b;
|
|
8912
8908
|
await this.stopPlayer();
|
|
8913
|
-
await this.createInterval();
|
|
8914
8909
|
this.clickedNode = node;
|
|
8915
|
-
|
|
8916
|
-
if (((_a = node.attributes.getNamedItem("co-tts.highlight")) == null ? void 0 : _a.value) !== "") {
|
|
8917
|
-
const newReferenceDiv = document.getElementById(
|
|
8918
|
-
node.attributes.getNamedItem("co-tts.highlight").value
|
|
8919
|
-
);
|
|
8920
|
-
this.highlightDiv = newReferenceDiv;
|
|
8921
|
-
if (newReferenceDiv !== null) {
|
|
8922
|
-
this.originalHighlightDivInnerHTML = newReferenceDiv.innerHTML;
|
|
8923
|
-
}
|
|
8924
|
-
} else {
|
|
8925
|
-
this.highlightDiv = node;
|
|
8926
|
-
this.originalHighlightDivInnerHTML = node.innerHTML;
|
|
8927
|
-
}
|
|
8928
|
-
}
|
|
8910
|
+
this.setHighlightTargetFromNode(node);
|
|
8929
8911
|
if (attr.value === "") {
|
|
8930
|
-
this.textToRead = (
|
|
8912
|
+
this.textToRead = (_b = (_a = node.innerText) != null ? _a : node.textContent) != null ? _b : "";
|
|
8931
8913
|
} else {
|
|
8932
8914
|
this.textToRead = attr.value;
|
|
8933
8915
|
}
|
|
@@ -8944,7 +8926,7 @@ class TextToSpeech {
|
|
|
8944
8926
|
if (!this.player || typeof this.player.pause !== "function") {
|
|
8945
8927
|
return;
|
|
8946
8928
|
}
|
|
8947
|
-
|
|
8929
|
+
this.stopHighlightingInterval();
|
|
8948
8930
|
await this.player.pause();
|
|
8949
8931
|
document.dispatchEvent(new CustomEvent("COAzureTTSPausedPlaying", {}));
|
|
8950
8932
|
});
|
|
@@ -8954,7 +8936,9 @@ class TextToSpeech {
|
|
|
8954
8936
|
if (!this.player) {
|
|
8955
8937
|
return;
|
|
8956
8938
|
}
|
|
8957
|
-
|
|
8939
|
+
if (this.shouldHighlight()) {
|
|
8940
|
+
this.startHighlightingInterval(this.playbackSessionId);
|
|
8941
|
+
}
|
|
8958
8942
|
if (typeof this.player.resume === "function") {
|
|
8959
8943
|
await this.player.resume();
|
|
8960
8944
|
} else if (typeof this.player.play === "function") {
|
|
@@ -8975,8 +8959,9 @@ class TextToSpeech {
|
|
|
8975
8959
|
return this.stopPromise;
|
|
8976
8960
|
}
|
|
8977
8961
|
async performStopPlayer() {
|
|
8978
|
-
|
|
8979
|
-
|
|
8962
|
+
this.invalidatePlaybackSession();
|
|
8963
|
+
this.stopHighlightingInterval();
|
|
8964
|
+
if (this.highlightDiv) {
|
|
8980
8965
|
this.highlightDiv.innerHTML = this.originalHighlightDivInnerHTML;
|
|
8981
8966
|
}
|
|
8982
8967
|
this.resetPlaybackSegments();
|
|
@@ -8985,10 +8970,12 @@ class TextToSpeech {
|
|
|
8985
8970
|
const synthesizer = this.synthesizer;
|
|
8986
8971
|
this.textToRead = "";
|
|
8987
8972
|
this.currentWord = "";
|
|
8973
|
+
this.currentOffset = 0;
|
|
8974
|
+
this.wordBoundaryOffset = 0;
|
|
8988
8975
|
this.originalHighlightDivInnerHTML = "";
|
|
8989
8976
|
this.wordBoundryList = [];
|
|
8990
|
-
this.wordEncounters = [];
|
|
8991
8977
|
this.playbackSegments = [];
|
|
8978
|
+
this.previousWordBoundary = void 0;
|
|
8992
8979
|
this.highlightDiv = void 0;
|
|
8993
8980
|
this.prevTextOffset = 0;
|
|
8994
8981
|
this.playbackTextOffsetBase = void 0;
|
|
@@ -9028,10 +9015,18 @@ class TextToSpeech {
|
|
|
9028
9015
|
async startSynthesizer(_node, _attr) {
|
|
9029
9016
|
try {
|
|
9030
9017
|
this.speechConfig = await this.createSpeechConfig();
|
|
9031
|
-
|
|
9032
|
-
this.
|
|
9033
|
-
|
|
9034
|
-
|
|
9018
|
+
const sessionId = this.createPlaybackSession();
|
|
9019
|
+
this.resetHighlightState();
|
|
9020
|
+
const player = new SpeakerAudioDestination();
|
|
9021
|
+
const audioConfig = AudioConfig.fromSpeakerOutput(player);
|
|
9022
|
+
const synthesizer = new SpeechSynthesizer(this.speechConfig, audioConfig);
|
|
9023
|
+
this.player = player;
|
|
9024
|
+
this.audioConfig = audioConfig;
|
|
9025
|
+
this.synthesizer = synthesizer;
|
|
9026
|
+
synthesizer.wordBoundary = (_s, e) => {
|
|
9027
|
+
if (!this.isActivePlaybackSession(sessionId)) {
|
|
9028
|
+
return;
|
|
9029
|
+
}
|
|
9035
9030
|
this.wordBoundryList.push(e);
|
|
9036
9031
|
};
|
|
9037
9032
|
const playbackChain = this.collectPlaybackChain(this.clickedNode);
|
|
@@ -9041,8 +9036,11 @@ class TextToSpeech {
|
|
|
9041
9036
|
} else {
|
|
9042
9037
|
this.playbackSegments = [];
|
|
9043
9038
|
}
|
|
9044
|
-
|
|
9039
|
+
player.onAudioEnd = async () => {
|
|
9045
9040
|
var _a;
|
|
9041
|
+
if (!this.isActivePlaybackSession(sessionId)) {
|
|
9042
|
+
return;
|
|
9043
|
+
}
|
|
9046
9044
|
const clickedNode = this.clickedNode;
|
|
9047
9045
|
const wasChainedPlayback = this.playbackSegments.length > 0;
|
|
9048
9046
|
const nextNodeId = ((_a = clickedNode == null ? void 0 : clickedNode.hasAttribute) == null ? void 0 : _a.call(clickedNode, "co-tts.next")) ? clickedNode.getAttribute("co-tts.next") : null;
|
|
@@ -9066,23 +9064,39 @@ class TextToSpeech {
|
|
|
9066
9064
|
document.dispatchEvent(new CustomEvent("COAzureTTSFinishedPlaying", {}));
|
|
9067
9065
|
}
|
|
9068
9066
|
};
|
|
9069
|
-
|
|
9067
|
+
player.onAudioStart = async () => {
|
|
9068
|
+
if (!this.isActivePlaybackSession(sessionId)) {
|
|
9069
|
+
return;
|
|
9070
|
+
}
|
|
9070
9071
|
document.dispatchEvent(new CustomEvent("COAzureTTSStartedPlaying", {}));
|
|
9071
9072
|
};
|
|
9073
|
+
if (this.shouldHighlight()) {
|
|
9074
|
+
this.startHighlightingInterval(sessionId);
|
|
9075
|
+
}
|
|
9072
9076
|
if (!isChainedPlayback) {
|
|
9073
9077
|
void this.prefetchNextNode(this.clickedNode);
|
|
9074
9078
|
}
|
|
9075
|
-
this.
|
|
9076
|
-
|
|
9079
|
+
const ssml = this.buildSSML(this.textToRead);
|
|
9080
|
+
synthesizer.speakSsmlAsync(
|
|
9081
|
+
ssml,
|
|
9077
9082
|
() => {
|
|
9078
|
-
this.
|
|
9083
|
+
this.closeResource(synthesizer);
|
|
9084
|
+
if (this.isActivePlaybackSession(sessionId) && this.synthesizer === synthesizer) {
|
|
9085
|
+
this.synthesizer = void 0;
|
|
9086
|
+
}
|
|
9079
9087
|
},
|
|
9080
9088
|
(error) => {
|
|
9089
|
+
this.closeResource(synthesizer);
|
|
9090
|
+
if (!this.isActivePlaybackSession(sessionId)) {
|
|
9091
|
+
return;
|
|
9092
|
+
}
|
|
9093
|
+
if (this.synthesizer === synthesizer) {
|
|
9094
|
+
this.synthesizer = void 0;
|
|
9095
|
+
}
|
|
9081
9096
|
if (isLikelyAuthorizationError(error)) {
|
|
9082
9097
|
this.authorizationManager.clearAuthorization();
|
|
9083
9098
|
}
|
|
9084
9099
|
this.dispatchError(error, "SYNTHESIS_ERROR");
|
|
9085
|
-
this.closeSynthesizer();
|
|
9086
9100
|
void this.stopPlayer();
|
|
9087
9101
|
}
|
|
9088
9102
|
);
|
|
@@ -9129,23 +9143,16 @@ class TextToSpeech {
|
|
|
9129
9143
|
this.textToRead = chain.map((segment) => segment.text).join(" ");
|
|
9130
9144
|
this.highlightDiv = void 0;
|
|
9131
9145
|
this.originalHighlightDivInnerHTML = "";
|
|
9132
|
-
this.
|
|
9133
|
-
this.previousWordBoundary = void 0;
|
|
9134
|
-
this.prevTextOffset = 0;
|
|
9135
|
-
this.playbackTextOffsetBase = void 0;
|
|
9136
|
-
this.currentWord = "";
|
|
9137
|
-
this.currentOffset = 0;
|
|
9138
|
-
this.wordBoundaryOffset = 0;
|
|
9146
|
+
this.resetHighlightState();
|
|
9139
9147
|
}
|
|
9140
9148
|
getHighlightDivForNode(node) {
|
|
9141
|
-
var _a;
|
|
9142
|
-
if (!node.hasAttribute("co-tts.highlight")) {
|
|
9149
|
+
var _a, _b, _c, _d;
|
|
9150
|
+
if (!((_a = node == null ? void 0 : node.hasAttribute) == null ? void 0 : _a.call(node, "co-tts.highlight"))) {
|
|
9143
9151
|
return void 0;
|
|
9144
9152
|
}
|
|
9145
|
-
|
|
9146
|
-
|
|
9147
|
-
|
|
9148
|
-
);
|
|
9153
|
+
const highlightTarget = (_c = (_b = node.attributes.getNamedItem("co-tts.highlight")) == null ? void 0 : _b.value) != null ? _c : "";
|
|
9154
|
+
if (highlightTarget !== "") {
|
|
9155
|
+
return (_d = document.getElementById(highlightTarget)) != null ? _d : void 0;
|
|
9149
9156
|
}
|
|
9150
9157
|
return node;
|
|
9151
9158
|
}
|
|
@@ -9158,16 +9165,14 @@ class TextToSpeech {
|
|
|
9158
9165
|
}
|
|
9159
9166
|
updateChainedHighlight(wordBoundary) {
|
|
9160
9167
|
var _a, _b;
|
|
9161
|
-
if (~
|
|
9168
|
+
if (~PUNCTUATION_MARKS.indexOf(wordBoundary.text)) {
|
|
9162
9169
|
wordBoundary = (_a = this.previousWordBoundary) != null ? _a : void 0;
|
|
9163
9170
|
}
|
|
9164
9171
|
if (!wordBoundary) {
|
|
9165
9172
|
this.resetPlaybackSegments();
|
|
9166
9173
|
return;
|
|
9167
9174
|
}
|
|
9168
|
-
|
|
9169
|
-
this.playbackTextOffsetBase = wordBoundary.textOffset;
|
|
9170
|
-
}
|
|
9175
|
+
this.ensurePlaybackTextOffsetBase();
|
|
9171
9176
|
const normalizedTextOffset = Math.max(
|
|
9172
9177
|
0,
|
|
9173
9178
|
wordBoundary.textOffset - ((_b = this.playbackTextOffsetBase) != null ? _b : 0)
|
|
@@ -9179,21 +9184,13 @@ class TextToSpeech {
|
|
|
9179
9184
|
return;
|
|
9180
9185
|
}
|
|
9181
9186
|
const relativeTextOffset = normalizedTextOffset - segment.start;
|
|
9182
|
-
|
|
9187
|
+
this.highlightTextRange(
|
|
9188
|
+
segment.highlightDiv,
|
|
9183
9189
|
segment.originalHighlightDivInnerHTML,
|
|
9184
|
-
|
|
9185
|
-
|
|
9190
|
+
relativeTextOffset,
|
|
9191
|
+
wordBoundary.wordLength,
|
|
9192
|
+
segment.text
|
|
9186
9193
|
);
|
|
9187
|
-
if (currentOffset === Number.MAX_SAFE_INTEGER) {
|
|
9188
|
-
this.previousWordBoundary = wordBoundary;
|
|
9189
|
-
return;
|
|
9190
|
-
}
|
|
9191
|
-
const startOfString = segment.originalHighlightDivInnerHTML.substring(0, currentOffset);
|
|
9192
|
-
const endOffset = currentOffset + wordBoundary.wordLength;
|
|
9193
|
-
const endOfString = segment.originalHighlightDivInnerHTML.substring(endOffset);
|
|
9194
|
-
segment.highlightDiv.innerHTML = `
|
|
9195
|
-
${startOfString}<mark class='co-tts-highlight'>${wordBoundary.text}</mark>${endOfString}
|
|
9196
|
-
`;
|
|
9197
9194
|
this.previousWordBoundary = wordBoundary;
|
|
9198
9195
|
}
|
|
9199
9196
|
getNodeText(node, attr) {
|
|
@@ -9300,7 +9297,7 @@ class TextToSpeech {
|
|
|
9300
9297
|
this.prefetchPromises.set(key, prefetchPromise);
|
|
9301
9298
|
}
|
|
9302
9299
|
async playPrefetchedNode(node) {
|
|
9303
|
-
var _a, _b
|
|
9300
|
+
var _a, _b;
|
|
9304
9301
|
const attr = (_a = node.attributes.getNamedItem("co-tts.text")) != null ? _a : node.attributes.getNamedItem("co-tts");
|
|
9305
9302
|
const text = this.getNodeText(node, attr);
|
|
9306
9303
|
const key = this.getPrefetchKey(node, text);
|
|
@@ -9309,39 +9306,28 @@ class TextToSpeech {
|
|
|
9309
9306
|
return false;
|
|
9310
9307
|
}
|
|
9311
9308
|
this.prefetchedAudio.delete(key);
|
|
9309
|
+
const sessionId = this.createPlaybackSession();
|
|
9310
|
+
this.resetHighlightState();
|
|
9312
9311
|
this.clickedNode = node;
|
|
9313
9312
|
this.textToRead = prefetch.text;
|
|
9314
9313
|
this.wordBoundryList = prefetch.wordBoundryList;
|
|
9315
|
-
this.
|
|
9316
|
-
this.
|
|
9317
|
-
this.prevTextOffset = 0;
|
|
9318
|
-
this.currentWord = "";
|
|
9319
|
-
this.currentOffset = 0;
|
|
9320
|
-
this.wordBoundaryOffset = 0;
|
|
9321
|
-
if (node.hasAttribute("co-tts.highlight")) {
|
|
9322
|
-
if (((_c = node.attributes.getNamedItem("co-tts.highlight")) == null ? void 0 : _c.value) !== "") {
|
|
9323
|
-
const newReferenceDiv = document.getElementById(
|
|
9324
|
-
node.attributes.getNamedItem("co-tts.highlight").value
|
|
9325
|
-
);
|
|
9326
|
-
this.highlightDiv = newReferenceDiv;
|
|
9327
|
-
if (newReferenceDiv !== null) {
|
|
9328
|
-
this.originalHighlightDivInnerHTML = newReferenceDiv.innerHTML;
|
|
9329
|
-
}
|
|
9330
|
-
} else {
|
|
9331
|
-
this.highlightDiv = node;
|
|
9332
|
-
this.originalHighlightDivInnerHTML = node.innerHTML;
|
|
9333
|
-
}
|
|
9334
|
-
}
|
|
9335
|
-
await this.createInterval();
|
|
9314
|
+
this.playbackSegments = [];
|
|
9315
|
+
this.setHighlightTargetFromNode(node);
|
|
9336
9316
|
try {
|
|
9337
9317
|
const audio = new Audio(prefetch.url);
|
|
9338
9318
|
this.activePrefetchedAudioUrl = prefetch.url;
|
|
9339
9319
|
this.player = audio;
|
|
9340
9320
|
audio.addEventListener("play", () => {
|
|
9321
|
+
if (!this.isActivePlaybackSession(sessionId)) {
|
|
9322
|
+
return;
|
|
9323
|
+
}
|
|
9341
9324
|
document.dispatchEvent(new CustomEvent("COAzureTTSStartedPlaying", {}));
|
|
9342
9325
|
}, { once: true });
|
|
9343
9326
|
audio.addEventListener("ended", async () => {
|
|
9344
9327
|
var _a2;
|
|
9328
|
+
if (!this.isActivePlaybackSession(sessionId)) {
|
|
9329
|
+
return;
|
|
9330
|
+
}
|
|
9345
9331
|
const clickedNode = this.clickedNode;
|
|
9346
9332
|
const nextNodeId = ((_a2 = clickedNode == null ? void 0 : clickedNode.hasAttribute) == null ? void 0 : _a2.call(clickedNode, "co-tts.next")) ? clickedNode.getAttribute("co-tts.next") : null;
|
|
9347
9333
|
await this.stopPlayer();
|
|
@@ -9361,101 +9347,253 @@ class TextToSpeech {
|
|
|
9361
9347
|
}
|
|
9362
9348
|
}, { once: true });
|
|
9363
9349
|
audio.addEventListener("error", () => {
|
|
9350
|
+
if (!this.isActivePlaybackSession(sessionId)) {
|
|
9351
|
+
return;
|
|
9352
|
+
}
|
|
9364
9353
|
this.dispatchError(
|
|
9365
9354
|
new Error("Prefetched audio resource closed unexpectedly."),
|
|
9366
9355
|
"AUDIO_RESOURCE_ERROR"
|
|
9367
9356
|
);
|
|
9368
9357
|
void this.stopPlayer();
|
|
9369
9358
|
}, { once: true });
|
|
9359
|
+
if (this.shouldHighlight()) {
|
|
9360
|
+
this.startHighlightingInterval(sessionId);
|
|
9361
|
+
}
|
|
9370
9362
|
void this.prefetchNextNode(node);
|
|
9371
9363
|
await audio.play();
|
|
9372
9364
|
return true;
|
|
9373
9365
|
} catch (error) {
|
|
9366
|
+
this.stopHighlightingInterval();
|
|
9374
9367
|
this.dispatchError(error, "AUDIO_RESOURCE_ERROR");
|
|
9375
9368
|
await this.stopPlayer();
|
|
9376
9369
|
return false;
|
|
9377
9370
|
}
|
|
9378
9371
|
}
|
|
9379
|
-
|
|
9380
|
-
|
|
9372
|
+
createPlaybackSession() {
|
|
9373
|
+
this.playbackSessionId += 1;
|
|
9374
|
+
return this.playbackSessionId;
|
|
9381
9375
|
}
|
|
9382
|
-
|
|
9383
|
-
|
|
9384
|
-
|
|
9385
|
-
|
|
9386
|
-
|
|
9387
|
-
|
|
9388
|
-
|
|
9389
|
-
|
|
9390
|
-
|
|
9391
|
-
|
|
9392
|
-
|
|
9393
|
-
|
|
9394
|
-
|
|
9395
|
-
|
|
9396
|
-
|
|
9397
|
-
|
|
9398
|
-
|
|
9399
|
-
|
|
9400
|
-
|
|
9401
|
-
wordBoundary = (_a = this.previousWordBoundary) != null ? _a : void 0;
|
|
9402
|
-
}
|
|
9403
|
-
if (wordBoundary === void 0 || this.prevTextOffset > wordBoundary.prevTextOffset) {
|
|
9404
|
-
this.highlightDiv.innerHTML = this.originalHighlightDivInnerHTML;
|
|
9405
|
-
} else {
|
|
9406
|
-
if (!this.wordEncounters[wordBoundary.text]) {
|
|
9407
|
-
this.wordEncounters[wordBoundary.text] = 0;
|
|
9408
|
-
}
|
|
9409
|
-
this.prevTextOffset = wordBoundary.prevTextOffset;
|
|
9410
|
-
if (this.currentWord !== wordBoundary.text || this.wordBoundaryOffset !== wordBoundary.textOffset) {
|
|
9411
|
-
this.currentOffset = this.getPosition(
|
|
9412
|
-
this.originalHighlightDivInnerHTML,
|
|
9413
|
-
wordBoundary.text,
|
|
9414
|
-
wordBoundary.textOffset
|
|
9415
|
-
);
|
|
9416
|
-
this.wordEncounters[wordBoundary.text] = this.currentOffset + wordBoundary.wordLength;
|
|
9417
|
-
this.currentWord = wordBoundary.text;
|
|
9418
|
-
this.wordBoundaryOffset = wordBoundary.textOffset;
|
|
9419
|
-
}
|
|
9420
|
-
if (this.currentOffset === Number.MAX_SAFE_INTEGER) {
|
|
9421
|
-
this.highlightDiv.innerHTML = this.originalHighlightDivInnerHTML;
|
|
9422
|
-
} else {
|
|
9423
|
-
this.previousWordBoundary = wordBoundary;
|
|
9424
|
-
const startOfString = this.originalHighlightDivInnerHTML.substring(0, this.currentOffset);
|
|
9425
|
-
const endOffset = this.currentOffset + wordBoundary.wordLength;
|
|
9426
|
-
const endOfString = this.originalHighlightDivInnerHTML.substring(endOffset);
|
|
9427
|
-
this.highlightDiv.innerHTML = `
|
|
9428
|
-
${startOfString}<mark class='co-tts-highlight'>${wordBoundary.text}</mark>${endOfString}
|
|
9429
|
-
`;
|
|
9430
|
-
}
|
|
9431
|
-
}
|
|
9432
|
-
} else if (this.playbackSegments.length > 0) {
|
|
9433
|
-
this.resetPlaybackSegments();
|
|
9434
|
-
} else {
|
|
9435
|
-
this.highlightDiv.innerHTML = this.originalHighlightDivInnerHTML;
|
|
9436
|
-
}
|
|
9376
|
+
isActivePlaybackSession(sessionId) {
|
|
9377
|
+
return sessionId === this.playbackSessionId;
|
|
9378
|
+
}
|
|
9379
|
+
invalidatePlaybackSession() {
|
|
9380
|
+
this.playbackSessionId += 1;
|
|
9381
|
+
}
|
|
9382
|
+
stopHighlightingInterval() {
|
|
9383
|
+
if (!this.highlightInterval) {
|
|
9384
|
+
return;
|
|
9385
|
+
}
|
|
9386
|
+
clearInterval(this.highlightInterval);
|
|
9387
|
+
this.highlightInterval = void 0;
|
|
9388
|
+
}
|
|
9389
|
+
startHighlightingInterval(sessionId) {
|
|
9390
|
+
this.stopHighlightingInterval();
|
|
9391
|
+
this.highlightInterval = setInterval(() => {
|
|
9392
|
+
if (!this.isActivePlaybackSession(sessionId)) {
|
|
9393
|
+
this.stopHighlightingInterval();
|
|
9394
|
+
return;
|
|
9437
9395
|
}
|
|
9396
|
+
this.updateHighlightForCurrentTime(sessionId);
|
|
9438
9397
|
}, 50);
|
|
9439
9398
|
}
|
|
9440
|
-
|
|
9441
|
-
|
|
9442
|
-
|
|
9443
|
-
|
|
9444
|
-
|
|
9445
|
-
|
|
9446
|
-
|
|
9399
|
+
updateHighlightForCurrentTime(sessionId) {
|
|
9400
|
+
var _a, _b;
|
|
9401
|
+
if (!this.isActivePlaybackSession(sessionId) || this.player === void 0) {
|
|
9402
|
+
return;
|
|
9403
|
+
}
|
|
9404
|
+
if (!this.shouldHighlight()) {
|
|
9405
|
+
return;
|
|
9406
|
+
}
|
|
9407
|
+
const currentTime = this.player.currentTime;
|
|
9408
|
+
let wordBoundary;
|
|
9409
|
+
for (const e of this.wordBoundryList) {
|
|
9410
|
+
if (currentTime * 1e3 > e.audioOffset / 1e4) {
|
|
9411
|
+
wordBoundary = e;
|
|
9412
|
+
} else {
|
|
9413
|
+
break;
|
|
9414
|
+
}
|
|
9415
|
+
}
|
|
9416
|
+
if (wordBoundary === void 0) {
|
|
9417
|
+
this.resetCurrentHighlight();
|
|
9418
|
+
return;
|
|
9419
|
+
}
|
|
9420
|
+
if (this.playbackSegments.length > 0) {
|
|
9421
|
+
this.updateChainedHighlight(wordBoundary);
|
|
9422
|
+
return;
|
|
9423
|
+
}
|
|
9424
|
+
if (~PUNCTUATION_MARKS.indexOf(wordBoundary.text)) {
|
|
9425
|
+
wordBoundary = (_a = this.previousWordBoundary) != null ? _a : void 0;
|
|
9426
|
+
}
|
|
9427
|
+
if (wordBoundary === void 0 || this.prevTextOffset > wordBoundary.textOffset) {
|
|
9428
|
+
this.resetCurrentHighlight();
|
|
9429
|
+
return;
|
|
9430
|
+
}
|
|
9431
|
+
this.prevTextOffset = wordBoundary.textOffset;
|
|
9432
|
+
this.ensurePlaybackTextOffsetBase();
|
|
9433
|
+
const normalizedTextOffset = Math.max(
|
|
9434
|
+
0,
|
|
9435
|
+
wordBoundary.textOffset - ((_b = this.playbackTextOffsetBase) != null ? _b : 0)
|
|
9436
|
+
);
|
|
9437
|
+
this.highlightTextRange(
|
|
9438
|
+
this.highlightDiv,
|
|
9439
|
+
this.originalHighlightDivInnerHTML,
|
|
9440
|
+
normalizedTextOffset,
|
|
9441
|
+
wordBoundary.wordLength,
|
|
9442
|
+
this.textToRead
|
|
9443
|
+
);
|
|
9444
|
+
this.previousWordBoundary = wordBoundary;
|
|
9445
|
+
this.currentWord = wordBoundary.text;
|
|
9446
|
+
this.wordBoundaryOffset = wordBoundary.textOffset;
|
|
9447
|
+
}
|
|
9448
|
+
highlightTextRange(target, originalHtml, textOffset, wordLength, spokenText) {
|
|
9449
|
+
if (!target || wordLength <= 0 || textOffset < 0) {
|
|
9450
|
+
return;
|
|
9451
|
+
}
|
|
9452
|
+
target.innerHTML = originalHtml;
|
|
9453
|
+
const alignmentOffset = this.getSpokenTextDomOffset(target, spokenText);
|
|
9454
|
+
const mappedOffset = textOffset + alignmentOffset;
|
|
9455
|
+
const walker = document.createTreeWalker(
|
|
9456
|
+
target,
|
|
9457
|
+
NodeFilter.SHOW_TEXT
|
|
9458
|
+
);
|
|
9459
|
+
let currentOffset = 0;
|
|
9460
|
+
let startNode;
|
|
9461
|
+
let endNode;
|
|
9462
|
+
let startOffset = 0;
|
|
9463
|
+
let endOffset = 0;
|
|
9464
|
+
const requestedEnd = mappedOffset + wordLength;
|
|
9465
|
+
while (walker.nextNode()) {
|
|
9466
|
+
const node = walker.currentNode;
|
|
9467
|
+
const nodeLength = node.data.length;
|
|
9468
|
+
const nodeStart = currentOffset;
|
|
9469
|
+
const nodeEnd = currentOffset + nodeLength;
|
|
9470
|
+
if (!startNode && mappedOffset >= nodeStart && mappedOffset < nodeEnd) {
|
|
9471
|
+
startNode = node;
|
|
9472
|
+
startOffset = mappedOffset - nodeStart;
|
|
9473
|
+
}
|
|
9474
|
+
if (requestedEnd > nodeStart && requestedEnd <= nodeEnd) {
|
|
9475
|
+
endNode = node;
|
|
9476
|
+
endOffset = requestedEnd - nodeStart;
|
|
9477
|
+
break;
|
|
9447
9478
|
}
|
|
9448
|
-
|
|
9449
|
-
|
|
9450
|
-
|
|
9451
|
-
|
|
9452
|
-
|
|
9479
|
+
currentOffset = nodeEnd;
|
|
9480
|
+
}
|
|
9481
|
+
if (!startNode || !endNode) {
|
|
9482
|
+
return;
|
|
9483
|
+
}
|
|
9484
|
+
const range = document.createRange();
|
|
9485
|
+
try {
|
|
9486
|
+
range.setStart(startNode, startOffset);
|
|
9487
|
+
range.setEnd(endNode, endOffset);
|
|
9488
|
+
if (!this.isRangeVisuallyVisible(range, target)) {
|
|
9489
|
+
return;
|
|
9453
9490
|
}
|
|
9454
|
-
|
|
9455
|
-
|
|
9491
|
+
const mark = document.createElement("mark");
|
|
9492
|
+
mark.className = "co-tts-highlight";
|
|
9493
|
+
mark.appendChild(range.extractContents());
|
|
9494
|
+
range.insertNode(mark);
|
|
9495
|
+
} catch {
|
|
9496
|
+
target.innerHTML = originalHtml;
|
|
9497
|
+
}
|
|
9498
|
+
}
|
|
9499
|
+
getSpokenTextDomOffset(target, spokenText) {
|
|
9500
|
+
var _a;
|
|
9501
|
+
if (!spokenText) {
|
|
9502
|
+
return 0;
|
|
9503
|
+
}
|
|
9504
|
+
const targetText = (_a = target.textContent) != null ? _a : "";
|
|
9505
|
+
if (targetText === "" || targetText === spokenText) {
|
|
9506
|
+
return 0;
|
|
9507
|
+
}
|
|
9508
|
+
const index = targetText.indexOf(spokenText);
|
|
9509
|
+
return index >= 0 ? index : 0;
|
|
9510
|
+
}
|
|
9511
|
+
isRangeVisuallyVisible(range, target) {
|
|
9512
|
+
if (typeof range.getClientRects !== "function") {
|
|
9513
|
+
return true;
|
|
9514
|
+
}
|
|
9515
|
+
let rects;
|
|
9516
|
+
try {
|
|
9517
|
+
rects = Array.from(range.getClientRects()).filter(
|
|
9518
|
+
(rect) => rect.width > 0 && rect.height > 0
|
|
9519
|
+
);
|
|
9520
|
+
} catch {
|
|
9521
|
+
return true;
|
|
9522
|
+
}
|
|
9523
|
+
if (rects.length === 0) {
|
|
9524
|
+
return true;
|
|
9525
|
+
}
|
|
9526
|
+
const clipRect = this.getClippingRect(target);
|
|
9527
|
+
return rects.some((rect) => this.rectsOverlap(rect, clipRect));
|
|
9528
|
+
}
|
|
9529
|
+
getClippingRect(target) {
|
|
9530
|
+
let clipElement = target;
|
|
9531
|
+
while (clipElement) {
|
|
9532
|
+
if (this.isClippingElement(clipElement)) {
|
|
9533
|
+
return clipElement.getBoundingClientRect();
|
|
9456
9534
|
}
|
|
9535
|
+
clipElement = clipElement.parentElement;
|
|
9536
|
+
}
|
|
9537
|
+
return target.getBoundingClientRect();
|
|
9538
|
+
}
|
|
9539
|
+
isClippingElement(element) {
|
|
9540
|
+
var _a;
|
|
9541
|
+
const style = (_a = globalThis.getComputedStyle) == null ? void 0 : _a.call(globalThis, element);
|
|
9542
|
+
if (!style) {
|
|
9543
|
+
return false;
|
|
9544
|
+
}
|
|
9545
|
+
const overflowX = style.overflowX || style.overflow;
|
|
9546
|
+
const overflowY = style.overflowY || style.overflow;
|
|
9547
|
+
const clipsOverflow = this.isClippingOverflowValue(overflowX) || this.isClippingOverflowValue(overflowY);
|
|
9548
|
+
if (clipsOverflow) {
|
|
9549
|
+
return true;
|
|
9550
|
+
}
|
|
9551
|
+
const lineClamp = style.getPropertyValue("-webkit-line-clamp").trim();
|
|
9552
|
+
return lineClamp !== "" && lineClamp !== "none";
|
|
9553
|
+
}
|
|
9554
|
+
isClippingOverflowValue(value) {
|
|
9555
|
+
return value === "hidden" || value === "auto" || value === "scroll" || value === "clip";
|
|
9556
|
+
}
|
|
9557
|
+
rectsOverlap(a, b) {
|
|
9558
|
+
return a.right > b.left && a.left < b.right && a.bottom > b.top && a.top < b.bottom;
|
|
9559
|
+
}
|
|
9560
|
+
setHighlightTargetFromNode(node) {
|
|
9561
|
+
var _a;
|
|
9562
|
+
const highlightDiv = this.getHighlightDivForNode(node);
|
|
9563
|
+
this.highlightDiv = highlightDiv;
|
|
9564
|
+
this.originalHighlightDivInnerHTML = (_a = highlightDiv == null ? void 0 : highlightDiv.innerHTML) != null ? _a : "";
|
|
9565
|
+
}
|
|
9566
|
+
shouldHighlight() {
|
|
9567
|
+
if (this.highlightDiv) {
|
|
9568
|
+
return true;
|
|
9569
|
+
}
|
|
9570
|
+
return this.playbackSegments.some((segment) => Boolean(segment.highlightDiv));
|
|
9571
|
+
}
|
|
9572
|
+
resetHighlightState() {
|
|
9573
|
+
this.prevTextOffset = 0;
|
|
9574
|
+
this.previousWordBoundary = void 0;
|
|
9575
|
+
this.currentWord = "";
|
|
9576
|
+
this.currentOffset = 0;
|
|
9577
|
+
this.wordBoundaryOffset = 0;
|
|
9578
|
+
this.playbackTextOffsetBase = void 0;
|
|
9579
|
+
}
|
|
9580
|
+
ensurePlaybackTextOffsetBase() {
|
|
9581
|
+
if (this.playbackTextOffsetBase !== void 0 || this.wordBoundryList.length === 0) {
|
|
9582
|
+
return;
|
|
9583
|
+
}
|
|
9584
|
+
this.playbackTextOffsetBase = this.wordBoundryList.reduce(
|
|
9585
|
+
(minimum, boundary) => Math.min(minimum, boundary.textOffset),
|
|
9586
|
+
this.wordBoundryList[0].textOffset
|
|
9587
|
+
);
|
|
9588
|
+
}
|
|
9589
|
+
resetCurrentHighlight() {
|
|
9590
|
+
if (this.playbackSegments.length > 0) {
|
|
9591
|
+
this.resetPlaybackSegments();
|
|
9592
|
+
return;
|
|
9593
|
+
}
|
|
9594
|
+
if (this.highlightDiv) {
|
|
9595
|
+
this.highlightDiv.innerHTML = this.originalHighlightDivInnerHTML;
|
|
9457
9596
|
}
|
|
9458
|
-
return string.indexOf(subString);
|
|
9459
9597
|
}
|
|
9460
9598
|
buildSSML(text) {
|
|
9461
9599
|
let ssml = `<speak xmlns="http://www.w3.org/2001/10/synthesis"
|
|
@@ -9477,10 +9615,6 @@ class TextToSpeech {
|
|
|
9477
9615
|
p.textContent = input;
|
|
9478
9616
|
return p.innerHTML;
|
|
9479
9617
|
}
|
|
9480
|
-
closeSynthesizer() {
|
|
9481
|
-
this.closeResource(this.synthesizer);
|
|
9482
|
-
this.synthesizer = void 0;
|
|
9483
|
-
}
|
|
9484
9618
|
closeResource(resource) {
|
|
9485
9619
|
if (!resource || typeof resource.close !== "function") {
|
|
9486
9620
|
return;
|