1 | import { SyntheticPlatformEmitter, CodedError } from '@unimodules/core';
|
2 |
|
3 | import { SpeechOptions, WebVoice, VoiceQuality } from './Speech.types';
|
4 |
|
5 |
|
6 | const MAX_SPEECH_INPUT_LENGTH = 32767;
|
7 |
|
8 | export default {
|
9 | get name(): string {
|
10 | return 'ExponentSpeech';
|
11 | },
|
12 | async speak(id: string, text: string, options: SpeechOptions): Promise<SpeechSynthesisUtterance> {
|
13 | if (text.length > MAX_SPEECH_INPUT_LENGTH) {
|
14 | throw new CodedError(
|
15 | 'ERR_SPEECH_INPUT_LENGTH',
|
16 | 'Speech input text is too long! Limit of input length is: ' + MAX_SPEECH_INPUT_LENGTH
|
17 | );
|
18 | }
|
19 |
|
20 | const message = new SpeechSynthesisUtterance();
|
21 |
|
22 | if (typeof options.rate === 'number') {
|
23 | message.rate = options.rate;
|
24 | }
|
25 | if (typeof options.pitch === 'number') {
|
26 | message.pitch = options.pitch;
|
27 | }
|
28 | if (typeof options.language === 'string') {
|
29 | message.lang = options.language;
|
30 | }
|
31 | if (typeof options.volume === 'number') {
|
32 | message.volume = options.volume;
|
33 | }
|
34 | if ('_voiceIndex' in options && options._voiceIndex != null) {
|
35 | const voices = window.speechSynthesis.getVoices();
|
36 | message.voice = voices[Math.min(voices.length - 1, Math.max(0, options._voiceIndex))];
|
37 | }
|
38 | if (typeof options.onResume === 'function') {
|
39 | message.onresume = options.onResume;
|
40 | }
|
41 | if (typeof options.onMark === 'function') {
|
42 | message.onmark = options.onMark;
|
43 | }
|
44 | if (typeof options.onBoundary === 'function') {
|
45 | message.onboundary = options.onBoundary;
|
46 | }
|
47 |
|
48 | message.onstart = (nativeEvent: SpeechSynthesisEvent) => {
|
49 | SyntheticPlatformEmitter.emit('Exponent.speakingStarted', { id, nativeEvent });
|
50 | };
|
51 | message.onend = (nativeEvent: SpeechSynthesisEvent) => {
|
52 | SyntheticPlatformEmitter.emit('Exponent.speakingDone', { id, nativeEvent });
|
53 | };
|
54 | message.onpause = (nativeEvent: SpeechSynthesisEvent) => {
|
55 | SyntheticPlatformEmitter.emit('Exponent.speakingStopped', { id, nativeEvent });
|
56 | };
|
57 | message.onerror = (nativeEvent: SpeechSynthesisErrorEvent) => {
|
58 | SyntheticPlatformEmitter.emit('Exponent.speakingError', { id, nativeEvent });
|
59 | };
|
60 |
|
61 | message.text = text;
|
62 |
|
63 | window.speechSynthesis.speak(message);
|
64 |
|
65 | return message;
|
66 | },
|
67 | getVoices(): WebVoice[] {
|
68 | const voices = window.speechSynthesis.getVoices();
|
69 | return voices.map(voice => ({
|
70 | identifier: voice.voiceURI,
|
71 | quality: VoiceQuality.Default,
|
72 | isDefault: voice.default,
|
73 | language: voice.lang,
|
74 | localService: voice.localService,
|
75 | name: voice.name,
|
76 | voiceURI: voice.voiceURI,
|
77 | }));
|
78 | },
|
79 | async isSpeaking(): Promise<boolean> {
|
80 | return window.speechSynthesis.speaking;
|
81 | },
|
82 | async stop(): Promise<void> {
|
83 | return window.speechSynthesis.cancel();
|
84 | },
|
85 | async pause(): Promise<void> {
|
86 | return window.speechSynthesis.pause();
|
87 | },
|
88 | async resume(): Promise<void> {
|
89 | return window.speechSynthesis.resume();
|
90 | },
|
91 | maxSpeechInputLength: MAX_SPEECH_INPUT_LENGTH,
|
92 | };
|