|
@@ -1,188 +1,165 @@
|
|
|
import { mlog } from "./mjapi";
|
|
|
|
|
|
-export interface recType {
|
|
|
- timeOut: number;
|
|
|
- asrLanguage?: string;
|
|
|
- listener?: (result: string) => void;
|
|
|
- onEnd?: () => void;
|
|
|
- onStart?: () => void;
|
|
|
+export interface recType{
|
|
|
+ timeOut:number
|
|
|
+ asrLanguage?:string
|
|
|
+ listener?: (result: string) => void
|
|
|
+ onEnd?: () => void
|
|
|
+ onStart?: () => void
|
|
|
}
|
|
|
export class Recognition {
|
|
|
- private recognition: any;
|
|
|
- private listener?: (result: string) => void;
|
|
|
- private isStop = false;
|
|
|
- private recOpt: recType = { timeOut: 2000 };
|
|
|
- private handleTime: any;
|
|
|
- private hTime: Date | undefined;
|
|
|
- private asrLanguage = "cmn-Hans-CN";
|
|
|
- private onEnd?: () => void;
|
|
|
- private onStart?: () => void;
|
|
|
-
|
|
|
- public setListener(fn: (result: string) => void) {
|
|
|
- this.listener = fn;
|
|
|
- return this;
|
|
|
- }
|
|
|
-
|
|
|
- public setOnEnd(fn: () => void) {
|
|
|
- this.onEnd = fn;
|
|
|
- return this;
|
|
|
- }
|
|
|
-
|
|
|
- public setOpt(opt: recType) {
|
|
|
- this.recOpt = opt;
|
|
|
- if (opt.listener) this.setListener(opt.listener);
|
|
|
- if (opt.onEnd) this.setOnEnd(opt.onEnd);
|
|
|
- if (opt.asrLanguage) this.setLang(opt.asrLanguage);
|
|
|
- if (opt.onStart) this.onStart = opt.onStart;
|
|
|
- return this;
|
|
|
- }
|
|
|
-
|
|
|
- public setLang(lang: string) {
|
|
|
- this.asrLanguage = lang;
|
|
|
- return this;
|
|
|
- }
|
|
|
-
|
|
|
- public start() {
|
|
|
- this.isStop = false;
|
|
|
- if (
|
|
|
- typeof window === "undefined" ||
|
|
|
- (!window.SpeechRecognition && !window.webkitSpeechRecognition)
|
|
|
- ) {
|
|
|
- console.warn("当前浏览器不支持 SpeechRecognition,请使用 Chrome 或 Edge");
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- if (!this.recognition) {
|
|
|
- const recognition = new (window.SpeechRecognition ||
|
|
|
- window.webkitSpeechRecognition)();
|
|
|
- this.recognition = recognition;
|
|
|
- }
|
|
|
- const recognition = this.recognition;
|
|
|
-
|
|
|
- recognition.interimResults = true;
|
|
|
- recognition.lang = this.asrLanguage;
|
|
|
- recognition.continuous = true;
|
|
|
-
|
|
|
- this.hTime = new Date();
|
|
|
- this.handleTime = setInterval(() => this.check(this), this.recOpt.timeOut);
|
|
|
-
|
|
|
- recognition.addEventListener("result", (event: any) => {
|
|
|
- let transcript = "";
|
|
|
- for (let index = 0; index < event.results.length; index++) {
|
|
|
- const item = event.results[index];
|
|
|
- if (transcript && this.asrLanguage.includes("Han")) transcript += ",";
|
|
|
- transcript += (item as unknown as SpeechRecognitionAlternative[])[0]
|
|
|
- ?.transcript;
|
|
|
- }
|
|
|
- if (!transcript) return;
|
|
|
- this.hTime = new Date();
|
|
|
- this.listener?.(transcript);
|
|
|
- });
|
|
|
-
|
|
|
- recognition.addEventListener("end", () => {
|
|
|
- if (this.isStop) {
|
|
|
- clearInterval(this.handleTime);
|
|
|
- return;
|
|
|
- }
|
|
|
- setTimeout(() => recognition.start(), 1000);
|
|
|
- });
|
|
|
-
|
|
|
- recognition.start();
|
|
|
- this.onStart?.();
|
|
|
- return this;
|
|
|
- }
|
|
|
-
|
|
|
- private check(that: Recognition) {
|
|
|
- if (!that.hTime) return;
|
|
|
- const dt = new Date().getTime() - that.hTime.getTime();
|
|
|
- if (dt > that.recOpt.timeOut) {
|
|
|
- that.stop(); // 只在 stop 中触发 onEnd
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- public stop() {
|
|
|
- if (this.isStop) return this; // 如果已经停止,直接返回
|
|
|
- this.isStop = true;
|
|
|
- this.recognition?.stop();
|
|
|
- clearInterval(this.handleTime);
|
|
|
- this.onEnd?.(); // 只在这里触发一次 onEnd
|
|
|
- return this;
|
|
|
- }
|
|
|
-
|
|
|
- // public stop() {
|
|
|
- // this.isStop = true;
|
|
|
- // this.recognition?.stop();
|
|
|
- // return this;
|
|
|
- // }
|
|
|
-
|
|
|
- // private check(that: Recognition) {
|
|
|
- // if (!that.hTime) return;
|
|
|
- // const dt = new Date().getTime() - that.hTime.getTime();
|
|
|
- // if (dt > that.recOpt.timeOut) that.stop();
|
|
|
- // }
|
|
|
+ private recognition: any;
|
|
|
+ private listener?: (result: string) => void;
|
|
|
+ private isStop = false;
|
|
|
+ private recOpt: recType = { timeOut: 2000 };
|
|
|
+ private handleTime: any;
|
|
|
+ private hTime: Date | undefined;
|
|
|
+ private asrLanguage = "cmn-Hans-CN";
|
|
|
+ private onEnd?: () => void;
|
|
|
+ private onStart?: () => void;
|
|
|
+
|
|
|
+ public setListener(fn: (result: string) => void) {
|
|
|
+ this.listener = fn;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ public setOnEnd(fn: () => void) {
|
|
|
+ this.onEnd = fn;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ public setOpt(opt: recType) {
|
|
|
+ this.recOpt = opt;
|
|
|
+ if (opt.listener) this.setListener(opt.listener);
|
|
|
+ if (opt.onEnd) this.setOnEnd(opt.onEnd);
|
|
|
+ if (opt.asrLanguage) this.setLang(opt.asrLanguage);
|
|
|
+ if (opt.onStart) this.onStart = opt.onStart;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ public setLang(lang: string) {
|
|
|
+ this.asrLanguage = lang;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ public start() {
|
|
|
+ this.isStop = false;
|
|
|
+ if (typeof window === "undefined" || (!window.SpeechRecognition && !window.webkitSpeechRecognition)) {
|
|
|
+ console.warn("当前浏览器不支持 SpeechRecognition,请使用 Chrome 或 Edge");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!this.recognition) {
|
|
|
+ const recognition = new (window.SpeechRecognition || window.webkitSpeechRecognition)();
|
|
|
+ this.recognition = recognition;
|
|
|
+ }
|
|
|
+ const recognition = this.recognition;
|
|
|
+
|
|
|
+ recognition.interimResults = true;
|
|
|
+ recognition.lang = this.asrLanguage;
|
|
|
+ recognition.continuous = true;
|
|
|
+
|
|
|
+ this.hTime = new Date();
|
|
|
+ this.handleTime = setInterval(() => this.check(this), this.recOpt.timeOut);
|
|
|
+
|
|
|
+ recognition.addEventListener("result", (event: any) => {
|
|
|
+ let transcript = "";
|
|
|
+ for (let index = 0; index < event.results.length; index++) {
|
|
|
+ const item = event.results[index];
|
|
|
+ if (transcript && this.asrLanguage.includes("Han")) transcript += ",";
|
|
|
+ transcript += (item as unknown as SpeechRecognitionAlternative[])[0]?.transcript;
|
|
|
+ }
|
|
|
+ if (!transcript) return;
|
|
|
+ this.hTime = new Date();
|
|
|
+ this.listener?.(transcript);
|
|
|
+ });
|
|
|
+
|
|
|
+ recognition.addEventListener("end", () => {
|
|
|
+ if (this.isStop) {
|
|
|
+ this.onEnd?.();
|
|
|
+ clearInterval(this.handleTime);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ setTimeout(() => recognition.start(), 1000); // 避免 Mac 触发安全限制
|
|
|
+ });
|
|
|
+
|
|
|
+ recognition.start();
|
|
|
+ this.onStart?.();
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ public stop() {
|
|
|
+ this.isStop = true;
|
|
|
+ this.recognition?.stop();
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ private check(that: Recognition) {
|
|
|
+ if (!that.hTime) return;
|
|
|
+ const dt = new Date().getTime() - that.hTime.getTime();
|
|
|
+ if (dt > that.recOpt.timeOut) that.stop();
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
+
|
|
|
export const supportLanguages: Record<string, string> = {
|
|
|
- "cmn-Hans-CN": "普通话 (中国大陆)",
|
|
|
- "cmn-Hans-HK": "普通话 (香港)",
|
|
|
- "yue-Hant-HK": "粵語 (香港)",
|
|
|
- "en-US": "English(United States)",
|
|
|
- "en-GB": "English(United Kingdom)",
|
|
|
- "en-IN": "English(India)",
|
|
|
- "es-ES": "Español",
|
|
|
- "fr-FR": "Français",
|
|
|
- "de-DE": "Deutsch",
|
|
|
- "it-IT": "Italiano",
|
|
|
- "ja-JP": "日本語",
|
|
|
- "ko-KR": "한국어",
|
|
|
- "ar-SA": "العربية",
|
|
|
- "pt-BR": "Português",
|
|
|
- "ru-RU": "Русский",
|
|
|
- "nl-NL": "Nederlands",
|
|
|
- "tr-TR": "Türkçe",
|
|
|
- "sv-SE": "Svenska",
|
|
|
- "hi-IN": "हिन्दी",
|
|
|
- "el-GR": "Ελληνικά",
|
|
|
- "he-IL": "עברית",
|
|
|
- "id-ID": "Bahasa Indonesia",
|
|
|
- "pl-PL": "Polski",
|
|
|
- "th-TH": "ไทย",
|
|
|
- "cs-CZ": "Čeština",
|
|
|
- "hu-HU": "Magyar",
|
|
|
- "da-DK": "Dansk",
|
|
|
- "fi-FI": "Suomi",
|
|
|
- "no-NO": "Norsk",
|
|
|
- "sk-SK": "Slovenčina",
|
|
|
- "uk-UA": "Українська",
|
|
|
- "vi-VN": "Tiếng Việt",
|
|
|
+ 'cmn-Hans-CN': '普通话 (中国大陆)',
|
|
|
+ 'cmn-Hans-HK': '普通话 (香港)',
|
|
|
+ 'yue-Hant-HK': '粵語 (香港)',
|
|
|
+ 'en-US': 'English(United States)',
|
|
|
+ 'en-GB': 'English(United Kingdom)',
|
|
|
+ 'en-IN': 'English(India)',
|
|
|
+ 'es-ES': 'Español',
|
|
|
+ 'fr-FR': 'Français',
|
|
|
+ 'de-DE': 'Deutsch',
|
|
|
+ 'it-IT': 'Italiano',
|
|
|
+ 'ja-JP': '日本語',
|
|
|
+ 'ko-KR': '한국어',
|
|
|
+ 'ar-SA': 'العربية',
|
|
|
+ 'pt-BR': 'Português',
|
|
|
+ 'ru-RU': 'Русский',
|
|
|
+ 'nl-NL': 'Nederlands',
|
|
|
+ 'tr-TR': 'Türkçe',
|
|
|
+ 'sv-SE': 'Svenska',
|
|
|
+ 'hi-IN': 'हिन्दी',
|
|
|
+ 'el-GR': 'Ελληνικά',
|
|
|
+ 'he-IL': 'עברית',
|
|
|
+ 'id-ID': 'Bahasa Indonesia',
|
|
|
+ 'pl-PL': 'Polski',
|
|
|
+ 'th-TH': 'ไทย',
|
|
|
+ 'cs-CZ': 'Čeština',
|
|
|
+ 'hu-HU': 'Magyar',
|
|
|
+ 'da-DK': 'Dansk',
|
|
|
+ 'fi-FI': 'Suomi',
|
|
|
+ 'no-NO': 'Norsk',
|
|
|
+ 'sk-SK': 'Slovenčina',
|
|
|
+ 'uk-UA': 'Українська',
|
|
|
+ 'vi-VN': 'Tiếng Việt',
|
|
|
};
|
|
|
|
|
|
function sleep(time: number) {
|
|
|
- return new Promise((resolve) => setTimeout(resolve, time));
|
|
|
+ return new Promise((resolve) => setTimeout(resolve, time));
|
|
|
}
|
|
|
|
|
|
//浏览器文字播放
|
|
|
-export async function speakText(
|
|
|
- content: string,
|
|
|
- callback: (playing: boolean) => void
|
|
|
-) {
|
|
|
- if (!window.speechSynthesis) return;
|
|
|
- if (speechSynthesis.speaking) {
|
|
|
- speechSynthesis.cancel();
|
|
|
- callback(false);
|
|
|
- }
|
|
|
-
|
|
|
- await sleep(300);
|
|
|
-
|
|
|
- const msg = new SpeechSynthesisUtterance(content);
|
|
|
- msg.lang = "zh";
|
|
|
- msg.rate = 1;
|
|
|
- msg.addEventListener("end", () => {
|
|
|
- callback(false);
|
|
|
- });
|
|
|
- msg.addEventListener("error", () => {
|
|
|
- callback(false);
|
|
|
- });
|
|
|
- callback(true);
|
|
|
- speechSynthesis.speak(msg);
|
|
|
+export async function speakText(content: string, callback: (playing: boolean) => void) {
|
|
|
+ if (!window.speechSynthesis) return;
|
|
|
+ if (speechSynthesis.speaking) {
|
|
|
+ speechSynthesis.cancel();
|
|
|
+ callback(false);
|
|
|
+ }
|
|
|
+
|
|
|
+ await sleep(300);
|
|
|
+
|
|
|
+ const msg = new SpeechSynthesisUtterance(content);
|
|
|
+ msg.lang = 'zh';
|
|
|
+ msg.rate = 1;
|
|
|
+ msg.addEventListener('end', () => {
|
|
|
+ callback(false);
|
|
|
+ });
|
|
|
+ msg.addEventListener('error', () => {
|
|
|
+ callback(false);
|
|
|
+ });
|
|
|
+ callback(true);
|
|
|
+ speechSynthesis.speak(msg);
|
|
|
}
|