1
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
## 1.0.0(2025-09-21)
|
||||
支持Android,iOS,Harmony,web/h5文本语音播报功能。
|
||||
@@ -0,0 +1,97 @@
|
||||
{
|
||||
"id": "x-texttovoice-s",
|
||||
"displayName": "x-texttovoice-s",
|
||||
"version": "1.0.0",
|
||||
"description": "x-texttovoice-s",
|
||||
"keywords": [
|
||||
"x-texttovoice-s"
|
||||
],
|
||||
"repository": "",
|
||||
"engines": {
|
||||
"HBuilderX": "^3.6.8",
|
||||
"uni-app": "^3.1.0",
|
||||
"uni-app-x": "^3.1.0"
|
||||
},
|
||||
"dcloudext": {
|
||||
"type": "uts",
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "",
|
||||
"data": "",
|
||||
"permissions": ""
|
||||
},
|
||||
"npmurl": "",
|
||||
"darkmode": "-",
|
||||
"i18n": "-",
|
||||
"widescreen": "-"
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "-",
|
||||
"aliyun": "-",
|
||||
"alipay": "-"
|
||||
},
|
||||
"client": {
|
||||
"uni-app": {
|
||||
"vue": {
|
||||
"vue2": "-",
|
||||
"vue3": "-"
|
||||
},
|
||||
"web": {
|
||||
"safari": "-",
|
||||
"chrome": "-"
|
||||
},
|
||||
"app": {
|
||||
"vue": "-",
|
||||
"nvue": "-",
|
||||
"android": "-",
|
||||
"ios": "-",
|
||||
"harmony": "-"
|
||||
},
|
||||
"mp": {
|
||||
"weixin": "-",
|
||||
"alipay": "-",
|
||||
"toutiao": "-",
|
||||
"baidu": "-",
|
||||
"kuaishou": "-",
|
||||
"jd": "-",
|
||||
"harmony": "-",
|
||||
"qq": "-",
|
||||
"lark": "-"
|
||||
},
|
||||
"quickapp": {
|
||||
"huawei": "-",
|
||||
"union": "-"
|
||||
}
|
||||
},
|
||||
"uni-app-x": {
|
||||
"web": {
|
||||
"safari": "-",
|
||||
"chrome": "-"
|
||||
},
|
||||
"app": {
|
||||
"android": "-",
|
||||
"ios": "-",
|
||||
"harmony": "-"
|
||||
},
|
||||
"mp": {
|
||||
"weixin": "-"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
# x-texttovoice-s
|
||||
|
||||
### 开发文档
|
||||
|
||||
将文字转为语音播报。
|
||||
|
||||
### 兼容性
|
||||
|
||||
**注意:Harmony需要使用模拟器6.0+(api 20+)才支持在模拟器上使用本插件,低于此版本需要真机调试。**
|
||||
|
||||
| iOS | Harmony | 小程序 | Andriod | WEB |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 12+ | 12+ | x | 5.0+ | 全部 |
|
||||
|
||||
### 方法说明
|
||||
|
||||
使用
|
||||
```ts
|
||||
|
||||
import { XTtsSpeek, xTtsImplIns } from '@/uni_modules/x-texttovoice-s';
|
||||
const word = ref("我是待播放的语音文本。")
|
||||
const onclick = () => {
|
||||
XTtsSpeek({
|
||||
fail(res) {
|
||||
console.error(res)
|
||||
},
|
||||
onDone() {
|
||||
console.log('播放完毕')
|
||||
},
|
||||
onStop() {
|
||||
console.log('stop')
|
||||
},
|
||||
success(res) {
|
||||
// 初始成功后,调用播放功能。
|
||||
xTtsImplIns.play(word.value)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### XTtsSpeek(opts:XuiTextToVoiceOpts)
|
||||
引擎初始方法
|
||||
```ts
|
||||
|
||||
export type XuiTextToVoiceResult = {
|
||||
|
||||
}
|
||||
|
||||
export interface XuiTextToVoiceFail extends IUniError {
|
||||
errCode : number
|
||||
};
|
||||
|
||||
export type XuiTextToVoiceOpts = {
|
||||
onStart ?:() => void
|
||||
onStop ?:() => void
|
||||
onError ?:(res : XuiTextToVoiceFail) => void
|
||||
/** 播放完成 */
|
||||
onDone ?:() => void
|
||||
/** 函数执行初始化成功,然后就可以播放,停止等操作了。 */
|
||||
success ?: (res : XTTSImpl) => void
|
||||
/** 函数初始化失败 */
|
||||
fail ?: (res : XuiTextToVoiceFail) => void
|
||||
/** 不管初始失败还是成功都执行。 */
|
||||
complete ?: (res : XuiTextToVoiceResult|null) => void
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
||||
### xTtsImplIns
|
||||
语音播放静态类方法。
|
||||
|
||||
#### isPlaying(): boolean; 获取当前状态
|
||||
#### play(c:string); 播播文本语音
|
||||
#### stop(); 停止播报
|
||||
|
||||
```ts
|
||||
import { XTtsSpeek, xTtsImplIns } from '@/uni_modules/x-texttovoice-s';
|
||||
// xTtsImplIns就是实例 XTTSImpl,含有以下方法。
|
||||
export interface XTTSImpl {
|
||||
isPlaying(): boolean;
|
||||
play(c:string):void
|
||||
stop():void
|
||||
}
|
||||
|
||||
```
|
||||
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
|
||||
android:usesCleartextTraffic="true"
|
||||
package="io.dcloud.uni_modules.xTexttovoiceS">
|
||||
<meta-data android:name="ScopedStorage" android:value="true" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESSIBILITY_SERVICE" />
|
||||
<!-- 音频音量设置 -->
|
||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
||||
<queries>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.TTS_SERVICE" />
|
||||
</intent>
|
||||
</queries>
|
||||
</manifest>
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"minSdkVersion": "21"
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
import TextToSpeech from "android.speech.tts.TextToSpeech"
|
||||
import OnInitListener from "android.speech.tts.TextToSpeech.OnInitListener"
|
||||
import OnUtteranceCompletedListener from "android.speech.tts.TextToSpeech.OnUtteranceCompletedListener"
|
||||
import UtteranceProgressListener from "android.speech.tts.UtteranceProgressListener"
|
||||
import Locale from "java.util.Locale"
|
||||
import { XuiTextToVoiceOpts, XuiTextToVoiceResult, XTTSImpl } from "../interface.uts"
|
||||
import { XuiTextToVoiceFailImpl } from "../unierror.uts"
|
||||
import Bundle from "android.os.Bundle";
|
||||
let tts : TextToSpeech | null = null
|
||||
|
||||
/**
|
||||
* 检查权限
|
||||
*/
|
||||
export function checkPermissions(call : (isAuth : boolean) => void) {
|
||||
let cehckPerMissionsArs = [] as string[]
|
||||
if (UTSAndroid.checkSystemPermissionGranted(UTSAndroid.getUniActivity()!, cehckPerMissionsArs)) {
|
||||
call(true)
|
||||
} else {
|
||||
UTSAndroid.requestSystemPermission(UTSAndroid.getUniActivity()!, cehckPerMissionsArs, function (_A : boolean, _B : string[]) {
|
||||
call(true)
|
||||
}, function (_B : boolean, _A : string[]) {
|
||||
call(false)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
class TtsObj implements XTTSImpl {
|
||||
config : XuiTextToVoiceOpts;
|
||||
constructor(opts : XuiTextToVoiceOpts) {
|
||||
this.config = opts;
|
||||
}
|
||||
isPlaying() : boolean {
|
||||
let isplay = tts?.isSpeaking;
|
||||
return isplay == null ? false : isplay
|
||||
}
|
||||
play(c : string) {
|
||||
const params = Bundle()
|
||||
tts?.speak(c, TextToSpeech.QUEUE_FLUSH, params, Math.random().toString(16).substring(4))
|
||||
}
|
||||
stop() {
|
||||
tts?.stop()
|
||||
tts?.shutdown()
|
||||
this.config.onStop?.()
|
||||
}
|
||||
}
|
||||
let TtsObjIml:TtsObj|null = null;
|
||||
export class xTtsImplIns {
|
||||
public static play(c:string){
|
||||
TtsObjIml?.play(c)
|
||||
}
|
||||
public static isPlaying() : boolean {
|
||||
return TtsObjIml?.isPlaying()??false
|
||||
}
|
||||
public static stop(){
|
||||
TtsObjIml?.stop()
|
||||
}
|
||||
}
|
||||
|
||||
export function XTtsSpeek(opts : XuiTextToVoiceOpts) {
|
||||
function initTTS() {
|
||||
class Listen extends OnInitListener {
|
||||
override onInit(status : Int) {
|
||||
if (status != TextToSpeech.SUCCESS) {
|
||||
tts = null
|
||||
console.error("TMUI Error:", status)
|
||||
opts.fail?.(new XuiTextToVoiceFailImpl(1001));
|
||||
opts.complete?.(null)
|
||||
return;
|
||||
}
|
||||
let ttsObj = tts!;
|
||||
const result = ttsObj.setLanguage(Locale.CHINESE)
|
||||
if (result == TextToSpeech.LANG_MISSING_DATA) {
|
||||
tts = null
|
||||
opts.fail?.(new XuiTextToVoiceFailImpl(1002));
|
||||
opts.complete?.(null)
|
||||
return;
|
||||
}
|
||||
TtsObjIml = new TtsObj(opts)
|
||||
opts.success?.(TtsObjIml!)
|
||||
opts.complete?.(null)
|
||||
}
|
||||
}
|
||||
class ProgressListen extends UtteranceProgressListener {
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
public override onError(param : string | null) {
|
||||
opts.onError?.(new XuiTextToVoiceFailImpl(1004));
|
||||
}
|
||||
public override onStart(param : string | null) {
|
||||
opts.onStart?.();
|
||||
}
|
||||
|
||||
public override onDone(param : string | null) {
|
||||
opts.onDone?.();
|
||||
}
|
||||
}
|
||||
tts = new TextToSpeech(UTSAndroid.getAppContext()!, new Listen())
|
||||
tts!.setOnUtteranceProgressListener(new ProgressListen())
|
||||
}
|
||||
|
||||
if (tts != null) {
|
||||
tts?.stop()
|
||||
tts?.shutdown()
|
||||
tts = null;
|
||||
}
|
||||
initTTS()
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
import { XuiTextToVoiceOpts, XuiTextToVoiceResult, XTTSImpl } from "../interface.uts"
|
||||
import { XuiTextToVoiceFailImpl } from "../unierror.uts"
|
||||
import { textToSpeech } from '@kit.CoreSpeechKit';
|
||||
import { BusinessError } from '@kit.BasicServicesKit';
|
||||
let tts : textToSpeech.TextToSpeechEngine | null = null;
|
||||
class TtsObj implements XTTSImpl {
|
||||
config : XuiTextToVoiceOpts;
|
||||
constructor(opts : XuiTextToVoiceOpts) {
|
||||
this.config = opts;
|
||||
}
|
||||
isPlaying() : boolean {
|
||||
return tts?.isBusy() ?? false
|
||||
}
|
||||
play(c : string) {
|
||||
// 设置播报相关参数
|
||||
let extraParam:Record<string, Object> = {
|
||||
"queueMode": 0, "speed": 0.9, "pitch": 1, "languageContext": 'zh-CN',
|
||||
"audioType": "pcm", "soundChannel": 3, "playType": 1
|
||||
};
|
||||
let speakParams : textToSpeech.SpeakParams = {
|
||||
requestId: Math.random().toString(16).substring(4),
|
||||
extraParams: extraParam
|
||||
};
|
||||
tts?.speak(c, speakParams);
|
||||
}
|
||||
|
||||
stop() {
|
||||
tts?.stop()
|
||||
tts?.shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
let TtsObjIml : TtsObj | null = null;
|
||||
export class xTtsImplIns {
|
||||
public static play(c : string) {
|
||||
TtsObjIml?.play(c)
|
||||
}
|
||||
public static isPlaying() : boolean {
|
||||
return TtsObjIml?.isPlaying() ?? false
|
||||
}
|
||||
public static stop() {
|
||||
TtsObjIml?.stop()
|
||||
}
|
||||
}
|
||||
export function XTtsSpeek(opts : XuiTextToVoiceOpts) {
|
||||
tts = null;
|
||||
let extraParam:Record<string, Object> = { "style": 'interaction-broadcast', "locate": 'CN', "name": 'EngineName' };
|
||||
let initParamsInfo : textToSpeech.CreateEngineParams = {
|
||||
language: 'zh-CN',
|
||||
person: 0,
|
||||
online: 1,
|
||||
extraParams: extraParam
|
||||
};
|
||||
let speakListener : textToSpeech.SpeakListener = {
|
||||
// 开始播报回调
|
||||
onStart(requestId : string, response : textToSpeech.StartResponse) {
|
||||
opts.onStart?.()
|
||||
},
|
||||
// 合成完成及播报完成回调
|
||||
onComplete(requestId : string, response : textToSpeech.CompleteResponse) {
|
||||
opts.onDone?.()
|
||||
},
|
||||
// 停止播报回调
|
||||
onStop(requestId : string, response : textToSpeech.StopResponse) {
|
||||
opts.onStop?.()
|
||||
},
|
||||
// 返回音频流
|
||||
onData(requestId : string, audio : ArrayBuffer, response : textToSpeech.SynthesisResponse) {
|
||||
},
|
||||
// 错误回调
|
||||
onError(requestId : string, errorCode : number, errorMessage : string) {
|
||||
console.error(`onError, requestId: ${requestId} errorCode: ${errorCode} errorMessage: ${errorMessage}`);
|
||||
opts.onError?.(new XuiTextToVoiceFailImpl(1004))
|
||||
}
|
||||
};
|
||||
// 调用createEngine方法
|
||||
textToSpeech.createEngine(initParamsInfo, (err : BusinessError, textToSpeechEngine : textToSpeech.TextToSpeechEngine) => {
|
||||
if (!err) {
|
||||
console.info('Succeeded in creating engine');
|
||||
// 接收创建引擎的实例
|
||||
tts = textToSpeechEngine;
|
||||
tts!.setListener(speakListener);
|
||||
TtsObjIml = new TtsObj(opts)
|
||||
opts.success?.(TtsObjIml!)
|
||||
opts.complete?.(null)
|
||||
} else {
|
||||
console.error(`Failed to create engine. Code: ${err.code}, message: ${err.message}.`);
|
||||
opts.fail?.(new XuiTextToVoiceFailImpl(1001))
|
||||
opts.complete?.(null)
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"deploymentTarget": "12",
|
||||
"abis": ["armeabi-v7a", "arm64-v8a"]
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
import { XuiTextToVoiceOpts, XuiTextToVoiceResult, XTTSImpl } from "../interface.uts"
|
||||
import { XuiTextToVoiceFailImpl } from "../unierror.uts"
|
||||
import { AVSpeechBoundary, AVSpeechSynthesisVoice, AVSpeechSynthesizer, AVSpeechSynthesizerDelegate, AVSpeechUtterance } from "AVFAudio";
|
||||
import { NSObject } from "ObjectiveC";
|
||||
import {AVPlayerLayer} from "AVFoundation"
|
||||
let tts:AVSpeechSynthesizer|null = null
|
||||
|
||||
class TtsObj implements XTTSImpl {
|
||||
config : XuiTextToVoiceOpts;
|
||||
constructor(opts : XuiTextToVoiceOpts) {
|
||||
this.config = opts;
|
||||
}
|
||||
isPlaying() : boolean {
|
||||
return tts!.isSpeaking;
|
||||
}
|
||||
play(c : string) {
|
||||
|
||||
console.log(c)
|
||||
let utterance = new AVSpeechUtterance(string = c)
|
||||
utterance.voice = new AVSpeechSynthesisVoice(language = "zh-CN")
|
||||
tts!.speak(utterance)
|
||||
}
|
||||
|
||||
stop() {
|
||||
tts!.stopSpeaking(at = AVSpeechBoundary.immediate)
|
||||
this.config.onStop?.()
|
||||
}
|
||||
}
|
||||
// 委托类
|
||||
class TTSDelegate implements AVSpeechSynthesizerDelegate<AVSpeechSynthesizerDelegate>{
|
||||
private config: XuiTextToVoiceOpts
|
||||
constructor(opts : XuiTextToVoiceOpts) {
|
||||
this.config = opts
|
||||
}
|
||||
speechSynthesizer(synthesizer: AVSpeechSynthesizer, @argumentLabel("didStart") utterance: AVSpeechUtterance) {
|
||||
this.config.onStart?.()
|
||||
}
|
||||
|
||||
speechSynthesizer(synthesizer: AVSpeechSynthesizer, @argumentLabel("didFinish") utterance: AVSpeechUtterance) {
|
||||
this.config.onDone?.()
|
||||
}
|
||||
|
||||
speechSynthesizer(synthesizer: AVSpeechSynthesizer, @argumentLabel("didCancel") utterance: AVSpeechUtterance) {
|
||||
this.config.onDone?.()
|
||||
}
|
||||
|
||||
speechSynthesizer(synthesizer: AVSpeechSynthesizer, @argumentLabel("didPause") utterance: AVSpeechUtterance) {
|
||||
this.config.onStop?.()
|
||||
}
|
||||
|
||||
}
|
||||
let TtsObjIml:TtsObj|null = null;
|
||||
let ttsDelegate:TTSDelegate|null = null;
|
||||
export class xTtsImplIns {
|
||||
public static play(c:string){
|
||||
TtsObjIml?.play(c)
|
||||
}
|
||||
public static isPlaying() : boolean {
|
||||
return TtsObjIml?.isPlaying()??false
|
||||
}
|
||||
public static stop(){
|
||||
TtsObjIml?.stop()
|
||||
}
|
||||
}
|
||||
export function XTtsSpeek(opts : XuiTextToVoiceOpts) {
|
||||
tts = null;
|
||||
DispatchQueue.main.async(execute=():void => {
|
||||
try {
|
||||
tts = new AVSpeechSynthesizer()
|
||||
TtsObjIml = new TtsObj(opts)
|
||||
ttsDelegate = new TTSDelegate(opts)
|
||||
tts!.delegate = ttsDelegate!
|
||||
opts.success?.(TtsObjIml!)
|
||||
opts.complete?.(null)
|
||||
} catch (error) {
|
||||
console.error("TMUI Error:",error)
|
||||
opts.fail?.(new XuiTextToVoiceFailImpl(1001))
|
||||
opts.complete?.(null)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
|
||||
export type XuiTextToVoiceResult = {
|
||||
|
||||
}
|
||||
|
||||
export interface XuiTextToVoiceFail extends IUniError {
|
||||
errCode : number
|
||||
};
|
||||
|
||||
export type XuiTextToVoiceOpts = {
|
||||
onStart ?:() => void
|
||||
onStop ?:() => void
|
||||
onError ?:(res : XuiTextToVoiceFail) => void
|
||||
/** 播放完成 */
|
||||
onDone ?:() => void
|
||||
/** 函数执行初始化成功,然后就可以播放,停止等操作了。 */
|
||||
success ?: (res : XTTSImpl) => void
|
||||
/** 函数初始化失败 */
|
||||
fail ?: (res : XuiTextToVoiceFail) => void
|
||||
/** 不管初始失败还是成功都执行。 */
|
||||
complete ?: (res : XuiTextToVoiceResult|null) => void
|
||||
}
|
||||
|
||||
export interface XTTSImpl {
|
||||
isPlaying(): boolean;
|
||||
play(c:string):void
|
||||
stop():void
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/* 此规范为 uni 规范,可以按照自己的需要选择是否实现 */
|
||||
import { XuiTextToVoiceFail } from "./interface.uts"
|
||||
|
||||
export const XuiErrors : Map<number, string> = new Map([
|
||||
[1000, '没有相关权限'],
|
||||
[1001, '初始化失败'],
|
||||
[1002, '设置的语文包在系统中缺失'],
|
||||
[1003, '初始化中或者还在报播中'],
|
||||
[1004, '播放中出现错误'],
|
||||
]);
|
||||
|
||||
export class XuiTextToVoiceFailImpl extends UniError implements XuiTextToVoiceFail {
|
||||
/**
|
||||
* 错误对象构造函数
|
||||
*/
|
||||
constructor(errCode : number) {
|
||||
super();
|
||||
this.errSubject = "x-chinesetopinyin-s";
|
||||
this.errCode = errCode;
|
||||
this.errMsg = XuiErrors.get(errCode) ?? "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import { XuiTextToVoiceOpts, XuiTextToVoiceResult, XTTSImpl } from "../interface.uts"
|
||||
import { XuiTextToVoiceFailImpl } from "../unierror.uts"
|
||||
let tts:SpeechSynthesisUtterance|null = null;
|
||||
class TtsObj implements XTTSImpl {
|
||||
config : XuiTextToVoiceOpts;
|
||||
constructor(opts : XuiTextToVoiceOpts) {
|
||||
this.config = opts;
|
||||
}
|
||||
isPlaying() : boolean {
|
||||
return speechSynthesis.speaking;
|
||||
}
|
||||
play(c : string) {
|
||||
tts!.text = c;
|
||||
tts!.pitch = 0.5
|
||||
speechSynthesis.speak(tts!);
|
||||
}
|
||||
|
||||
stop() {
|
||||
speechSynthesis.pause()
|
||||
this.config.onStop?.()
|
||||
}
|
||||
}
|
||||
|
||||
let TtsObjIml:TtsObj|null = null;
|
||||
export class xTtsImplIns {
|
||||
public static play(c:string){
|
||||
TtsObjIml?.play(c)
|
||||
}
|
||||
public static isPlaying() : boolean {
|
||||
return TtsObjIml?.isPlaying()??false
|
||||
}
|
||||
public static stop(){
|
||||
TtsObjIml?.stop()
|
||||
}
|
||||
}
|
||||
export function XTtsSpeek(opts : XuiTextToVoiceOpts) {
|
||||
tts = null;
|
||||
// 检查浏览器支持
|
||||
if ('speechSynthesis' in window) {
|
||||
tts = new SpeechSynthesisUtterance();
|
||||
tts!.onend = ()=>{
|
||||
opts.onDone?.()
|
||||
}
|
||||
tts!.onerror = ()=>{
|
||||
opts.onError?.(new XuiTextToVoiceFailImpl(1004))
|
||||
}
|
||||
tts!.onstart = ()=>{
|
||||
opts.onStart?.()
|
||||
}
|
||||
TtsObjIml = new TtsObj(opts)
|
||||
opts.success?.(TtsObjIml!)
|
||||
opts.complete?.(null)
|
||||
} else {
|
||||
console.error("TMUI Error:",'浏览器不支持语音合成')
|
||||
opts.fail?.(new XuiTextToVoiceFailImpl(1001))
|
||||
opts.complete?.(null)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user