Spaces:
Runtime error
Runtime error
| import { sleep } from './utils' | |
| const synth = window.speechSynthesis | |
| export class TTS { | |
| currentText = '' | |
| speakText = '' | |
| private controller = new AbortController() | |
| speaking = false | |
| get isSpeaking() { | |
| return this.speaking | |
| } | |
| finished = false | |
| constructor() {} | |
| abort = () => { | |
| this.controller.abort() | |
| } | |
| reset = () => { | |
| this.speaking = false | |
| this.finished = true | |
| this.currentText = '' | |
| this.speakText = '' | |
| this.abort() | |
| } | |
| speak = (text: string) => { | |
| if (!synth || text?.trim()?.length < 2) { | |
| return | |
| } | |
| this.currentText = text.replace(/[^\u4e00-\u9fa5_a-zA-Z0-9,。?,:;\.,:]+/g, '') | |
| this.finished = false | |
| this.loop() | |
| } | |
| private async doSpeek() { | |
| return new Promise((resolve) => { | |
| const endIndex = this.finished ? this.currentText.length : | |
| Math.max( | |
| this.currentText.lastIndexOf('。'), | |
| this.currentText.lastIndexOf(';'), | |
| this.currentText.lastIndexOf('、'), | |
| this.currentText.lastIndexOf('?'), | |
| this.currentText.lastIndexOf('\n') | |
| ) | |
| const startIndex = this.speakText.length ? Math.max(0, this.currentText.lastIndexOf(this.speakText) + this.speakText.length) : 0 | |
| if (startIndex >= endIndex) { | |
| return resolve(true) | |
| } | |
| const text = this.currentText.slice(startIndex, endIndex) | |
| this.speakText = text | |
| const utterThis = new SpeechSynthesisUtterance(text) | |
| this.controller.signal.onabort = () => { | |
| synth.cancel() | |
| this.finished = true | |
| resolve(false) | |
| } | |
| utterThis.onend = function (event) { | |
| resolve(true) | |
| } | |
| utterThis.onerror = function (event) { | |
| resolve(false) | |
| } | |
| const voice = synth.getVoices().find(v => v.name.includes('Microsoft Yunxi Online')) ?? null | |
| utterThis.voice = voice | |
| synth.speak(utterThis) | |
| }) | |
| } | |
| private async loop() { | |
| if (this.speaking) return | |
| this.speaking = true | |
| while(!this.finished) { | |
| await Promise.all([sleep(1000), this.doSpeek()]) | |
| } | |
| this.speaking = false | |
| } | |
| } | |