From 3bdbd3d015145a1adc455caf3b8c0452eea90da2 Mon Sep 17 00:00:00 2001 From: Huang Xin Date: Tue, 7 Jan 2025 21:09:04 +0100 Subject: [PATCH] Init view TTS with text segmentation granularity in order to support more TTS backends --- text-walker.js | 2 +- tts.js | 10 +++++----- view.js | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/text-walker.js b/text-walker.js index 3e2c44e..62518aa 100644 --- a/text-walker.js +++ b/text-walker.js @@ -32,7 +32,7 @@ export const textWalker = function* (x, func) { const walker = document.createTreeWalker(root, filter, { acceptNode }) const walk = x.commonAncestorContainer ? walkRange : walkDocument const nodes = walk(x, walker) - const strs = nodes.map(node => node.nodeValue) + const strs = nodes.map(node => node.nodeValue ?? '') const makeRange = (startIndex, startOffset, endIndex, endOffset) => { const range = document.createRange() range.setStart(nodes[startIndex], startOffset) diff --git a/tts.js b/tts.js index 0089ed3..9a695af 100644 --- a/tts.js +++ b/tts.js @@ -25,7 +25,7 @@ const getSegmenter = (lang = 'en', granularity = 'word') => { const segmenter = new Intl.Segmenter(lang, { granularity }) const granularityIsWord = granularity === 'word' return function* (strs, makeRange) { - const str = strs.join('') + const str = strs.join('').replace(/\r\n/g, ' ').replace(/\r/g, ' ').replace(/\n/g, ' ') let name = 0 let strIndex = -1 let sum = 0 @@ -34,10 +34,10 @@ const getSegmenter = (lang = 'en', granularity = 'word') => { while (sum <= index) sum += strs[++strIndex].length const startIndex = strIndex const startOffset = index - (sum - strs[strIndex].length) - const end = index + segment.length + const end = index + segment.length - 1 if (end < str.length) while (sum <= end) sum += strs[++strIndex].length const endIndex = strIndex - const endOffset = end - (sum - strs[strIndex].length) + const endOffset = end - (sum - strs[strIndex].length) + 1 yield [(name++).toString(), makeRange(startIndex, startOffset, endIndex, endOffset)] } @@ -207,11 +207,11 @@ export class TTS { #ranges #lastMark #serializer = new XMLSerializer() - constructor(doc, textWalker, highlight) { + constructor(doc, textWalker, highlight, granularity) { this.doc = doc this.highlight = highlight this.#list = new ListIterator(getBlocks(doc), range => { - const { entries, ssml } = getFragmentWithMarks(range, textWalker) + const { entries, ssml } = getFragmentWithMarks(range, textWalker, granularity) this.#ranges = new Map(entries) return [ssml, range] }) diff --git a/view.js b/view.js index 17566ac..55c818f 100644 --- a/view.js +++ b/view.js @@ -577,12 +577,12 @@ export class View extends HTMLElement { for (const item of list) this.deleteAnnotation(item) this.#searchResults.clear() } - async initTTS() { + async initTTS(granularity = 'word') { const doc = this.renderer.getContents()[0].doc if (this.tts && this.tts.doc === doc) return const { TTS } = await import('./tts.js') this.tts = new TTS(doc, textWalker, range => - this.renderer.scrollToAnchor(range, true)) + this.renderer.scrollToAnchor(range, true), granularity) } startMediaOverlay() { const { index } = this.renderer.getContents()[0]