forked from quisquous/cactbot
-
Notifications
You must be signed in to change notification settings - Fork 44
/
browser_tts_engine.ts
92 lines (80 loc) · 2.48 KB
/
browser_tts_engine.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import { Lang } from '../../resources/languages';
class TTSItem {
readonly text: string;
readonly item: SpeechSynthesisUtterance;
constructor(text: string, lang?: string, voice?: SpeechSynthesisVoice) {
this.text = text;
this.item = new SpeechSynthesisUtterance(text);
if (lang !== undefined)
this.item.lang = lang;
if (voice)
this.item.voice = voice;
}
play() {
window.speechSynthesis.speak(this.item);
}
}
type TTSItemDictionary = {
[key: string]: TTSItem;
};
export default class BrowserTTSEngine {
readonly ttsItems: TTSItemDictionary = {};
private speechLang?: string;
private speechVoice?: SpeechSynthesisVoice;
private initializeAttempts = 0;
constructor(private cactbotLang: Lang) {
if (window.speechSynthesis !== undefined) {
// https://bugs.chromium.org/p/chromium/issues/detail?id=334847
window.speechSynthesis.getVoices();
window.speechSynthesis.onvoiceschanged = () => this.initializeVoice();
} else
console.error('BrowserTTS error: no browser support for window.speechSynthesis');
}
initializeVoice(): boolean {
if (window.speechSynthesis === undefined)
return false;
if (this.speechVoice !== undefined)
return true;
if (this.initializeAttempts > 5)
return false;
this.initializeAttempts++;
const cactbotLangToSpeechLang = {
en: 'en-US',
de: 'de-DE',
fr: 'fr-FR',
ja: 'ja-JP',
// TODO: maybe need to provide an option of zh-CN, zh-HK, zh-TW?
cn: 'zh-CN',
ko: 'ko-KR',
};
// figure out what TTS engine type we need
const speechLang = cactbotLangToSpeechLang[this.cactbotLang];
const voice = window.speechSynthesis.getVoices().find((voice) =>
voice.lang.replaceAll('_', '-') === speechLang
);
if (voice) {
this.speechLang = speechLang;
this.speechVoice = voice;
window.speechSynthesis.onvoiceschanged = null;
return true;
}
console.error('BrowserTTS error: could not find voice');
return false;
}
play(text: string): void {
// TODO: try to address a report of the constructor not finding voices
// by lazily looking later.
if (!this.initializeVoice())
return;
try {
let ttsItem = this.ttsItems[text];
if (!ttsItem) {
ttsItem = new TTSItem(text, this.speechLang, this.speechVoice);
this.ttsItems[text] = ttsItem;
}
ttsItem.play();
} catch (e) {
console.error('Exception performing TTS', e);
}
}
}