first commit
This commit is contained in:
commit
aea14e50f0
|
@ -0,0 +1,83 @@
|
||||||
|
# Created by .ignore support plugin (hsz.mobi)
|
||||||
|
### Cordova template
|
||||||
|
# gitignore template for the Cordova framework
|
||||||
|
# website: https://cordova.apache.org/
|
||||||
|
#
|
||||||
|
# Recommended template: Node.gitignore
|
||||||
|
|
||||||
|
# App platform binaries and built files
|
||||||
|
/platforms
|
||||||
|
|
||||||
|
# Optional to ignore plugin Git clones
|
||||||
|
#/plugins
|
||||||
|
|
||||||
|
### JetBrains template
|
||||||
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
|
||||||
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
|
||||||
|
# User-specific stuff
|
||||||
|
.idea/**/workspace.xml
|
||||||
|
.idea/**/tasks.xml
|
||||||
|
.idea/**/usage.statistics.xml
|
||||||
|
.idea/**/dictionaries
|
||||||
|
.idea/**/shelf
|
||||||
|
|
||||||
|
# Generated files
|
||||||
|
.idea/**/contentModel.xml
|
||||||
|
|
||||||
|
# Sensitive or high-churn files
|
||||||
|
.idea/**/dataSources/
|
||||||
|
.idea/**/dataSources.ids
|
||||||
|
.idea/**/dataSources.local.xml
|
||||||
|
.idea/**/sqlDataSources.xml
|
||||||
|
.idea/**/dynamic.xml
|
||||||
|
.idea/**/uiDesigner.xml
|
||||||
|
.idea/**/dbnavigator.xml
|
||||||
|
|
||||||
|
# Gradle
|
||||||
|
.idea/**/gradle.xml
|
||||||
|
.idea/**/libraries
|
||||||
|
|
||||||
|
# Gradle and Maven with auto-import
|
||||||
|
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||||
|
# since they will be recreated, and may cause churn. Uncomment if using
|
||||||
|
# auto-import.
|
||||||
|
# .idea/modules.xml
|
||||||
|
# .idea/*.iml
|
||||||
|
# .idea/modules
|
||||||
|
# *.iml
|
||||||
|
# *.ipr
|
||||||
|
|
||||||
|
# CMake
|
||||||
|
cmake-build-*/
|
||||||
|
|
||||||
|
# Mongo Explorer plugin
|
||||||
|
.idea/**/mongoSettings.xml
|
||||||
|
|
||||||
|
# File-based project format
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
out/
|
||||||
|
|
||||||
|
# mpeltonen/sbt-idea plugin
|
||||||
|
.idea_modules/
|
||||||
|
|
||||||
|
# JIRA plugin
|
||||||
|
atlassian-ide-plugin.xml
|
||||||
|
|
||||||
|
# Cursive Clojure plugin
|
||||||
|
.idea/replstate.xml
|
||||||
|
|
||||||
|
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||||
|
com_crashlytics_export_strings.xml
|
||||||
|
crashlytics.properties
|
||||||
|
crashlytics-build.properties
|
||||||
|
fabric.properties
|
||||||
|
|
||||||
|
# Editor-based Rest Client
|
||||||
|
.idea/httpRequests
|
||||||
|
|
||||||
|
# Android studio 3.1+ serialized cache file
|
||||||
|
.idea/caches/build_file_checksums.ser
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="JAVA_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$" />
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="JavaScriptSettings">
|
||||||
|
<option name="languageLevel" value="ES6" />
|
||||||
|
</component>
|
||||||
|
</project>
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/chineseTTS.iml" filepath="$PROJECT_DIR$/.idea/chineseTTS.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"name": "chinesetts",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"author": "KeiferJu <jkf19980216@163.com> (myllcn.com)",
|
||||||
|
"license": "MIT"
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<plugin id="cordova-plugin-chinese-tts" version="0.0.1" xmlns="http://apache.org/cordova/ns/plugins/1.0"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<name>chineseTTS</name>
|
||||||
|
<js-module name="ChineseTTS" src="www/chineseTTS.js">
|
||||||
|
<clobbers target="cordova.plugins.chineseTTS"/>
|
||||||
|
</js-module>
|
||||||
|
<platform name="android">
|
||||||
|
<config-file parent="/*" target="res/xml/config.xml">
|
||||||
|
<feature name="ChineseTTS">
|
||||||
|
<param name="android-package" value="com.smartmapx.tts.ChineseTTS"/>
|
||||||
|
</feature>
|
||||||
|
</config-file>
|
||||||
|
<config-file parent="/*" target="AndroidManifest.xml"></config-file>
|
||||||
|
<source-file src="src/android/ChineseTTS.java" target-dir="src/com/smartmapx/tts"/>
|
||||||
|
<source-file src="src/android/FileUtils.java" target-dir="src/com/smartmapx/tts"/>
|
||||||
|
<source-file src="src/android/OfflineResource.java" target-dir="src/com/smartmapx/tts"/>
|
||||||
|
<source-file src="src/android/SpeechUtilOffline.java" target-dir="src/com/smartmapx/tts"/>
|
||||||
|
<source-file src="src/android/libs/usc.jar" target-dir="libs"/>
|
||||||
|
<source-file src="src/android/libs/armeabi/libyzstts.so" target-dir="src/main/jniLibs/armeabi"/>
|
||||||
|
<source-file src="src/android/libs/armeabi/libuscasr.so" target-dir="src/main/jniLibs/armeabi"/>
|
||||||
|
<source-file src="src/android/assets/backend_lzl" target-dir="src/main/assets"/>
|
||||||
|
<source-file src="src/android/assets/frontend_model" target-dir="src/main/assets"/>
|
||||||
|
<config-file target="AndroidManifest.xml" parent="/manifest">
|
||||||
|
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
|
||||||
|
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
|
||||||
|
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
|
||||||
|
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
|
||||||
|
<uses-permission android:name="android.permission.READ_CONTACTS"/>
|
||||||
|
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||||
|
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||||
|
</config-file>
|
||||||
|
<framework src="com.hjq:xxpermissions:6.0"/>
|
||||||
|
</platform>
|
||||||
|
</plugin>
|
|
@ -0,0 +1,111 @@
|
||||||
|
package com.smartmapx.tts;
|
||||||
|
|
||||||
|
import org.apache.cordova.CordovaPlugin;
|
||||||
|
import org.apache.cordova.CallbackContext;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.Manifest;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.hjq.permissions.OnPermission;
|
||||||
|
import com.hjq.permissions.Permission;
|
||||||
|
import com.hjq.permissions.XXPermissions;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class echoes a string called from JavaScript.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class ChineseTTS extends CordovaPlugin {
|
||||||
|
|
||||||
|
private Context context;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||||
|
|
||||||
|
if (action.equals("init")) {
|
||||||
|
this.init();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (action.equals("speak")) {
|
||||||
|
String message = args.getString(0);
|
||||||
|
this.speak(message, callbackContext);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化引擎
|
||||||
|
*
|
||||||
|
* @param
|
||||||
|
*/
|
||||||
|
private void init() {
|
||||||
|
context = this.cordova.getActivity();
|
||||||
|
permissionRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 讲话
|
||||||
|
*
|
||||||
|
* @param message
|
||||||
|
* @param callbackContext
|
||||||
|
*/
|
||||||
|
private void speak(String message, CallbackContext callbackContext) {
|
||||||
|
if (message != null && message.length() > 0) {
|
||||||
|
SpeechUtilOffline.getInstance(context).play(message, SpeechUtilOffline.PLAY_MODE.QUEUED);
|
||||||
|
} else {
|
||||||
|
callbackContext.error("信息为空.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 运行时权限--
|
||||||
|
*/
|
||||||
|
public void permissionRequest() {
|
||||||
|
// Manifest.permission.WRITE_EXTERNAL_STORAGE,
|
||||||
|
// Manifest.permission.ACCESS_FINE_LOCATION,
|
||||||
|
// Manifest.permission.READ_PHONE_STATE,
|
||||||
|
// Manifest.permission.RECEIVE_BOOT_COMPLETED
|
||||||
|
|
||||||
|
if (XXPermissions.isHasPermission(context, Permission.WRITE_EXTERNAL_STORAGE) && XXPermissions.isHasPermission(context, Permission.ACCESS_FINE_LOCATION) && XXPermissions.isHasPermission(context, Permission.READ_PHONE_STATE)) {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
XXPermissions.with(this.cordova.getActivity())
|
||||||
|
// 可设置被拒绝后继续申请,直到用户授权或者永久拒绝
|
||||||
|
.constantRequest()
|
||||||
|
// 支持请求6.0悬浮窗权限8.0请求安装权限
|
||||||
|
.permission(Permission.WRITE_EXTERNAL_STORAGE, Permission.ACCESS_FINE_LOCATION, Permission.READ_PHONE_STATE)
|
||||||
|
// 不指定权限则自动获取清单中的危险权限
|
||||||
|
// .permission(Permission.Group.STORAGE, Permission.Group.CALENDAR)
|
||||||
|
.request(new OnPermission() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void hasPermission(List<String> granted, boolean isAll) {
|
||||||
|
if (isAll) {
|
||||||
|
Toast.makeText(context, "权限获取成功,正在初始化语音包", Toast.LENGTH_SHORT).show();
|
||||||
|
SpeechUtilOffline.getInstance(context).play("语音包初始化完成", SpeechUtilOffline.PLAY_MODE.QUEUED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void noPermission(List<String> denied, boolean quick) {
|
||||||
|
Toast.makeText(context, "权限获取失败,语音包初始化失败", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,152 @@
|
||||||
|
package com.smartmapx.tts;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.AssetManager;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件操作类
|
||||||
|
*
|
||||||
|
* @author KeiferJu
|
||||||
|
* @date 2019/8/
|
||||||
|
*/
|
||||||
|
public final class FileUtils {
|
||||||
|
private static final String LOGTAG ="/ing/tts";
|
||||||
|
private static FileUtils fileUtils = new FileUtils();
|
||||||
|
|
||||||
|
private FileUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FileUtils getInstance() {
|
||||||
|
return fileUtils;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取SDCARD根路径
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private static StringBuffer getRootDir() throws Exception {
|
||||||
|
return new StringBuffer().append(Environment
|
||||||
|
.getExternalStorageDirectory());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取存在SDCARD上文件的绝对路径
|
||||||
|
*
|
||||||
|
* @param mContext
|
||||||
|
* @param folderName
|
||||||
|
*/
|
||||||
|
public static StringBuffer getExternalFileAbsoluteDir(Context mContext,
|
||||||
|
String folderName, String fileName) throws Exception {
|
||||||
|
StringBuffer stringBuffer = getRootDir().append(File.separator);
|
||||||
|
stringBuffer.append(getExternalFilesDir(mContext, folderName));
|
||||||
|
if (fileName != null) {
|
||||||
|
if (0 == fileName.indexOf(File.separator)) {
|
||||||
|
stringBuffer.append(fileName);
|
||||||
|
} else {
|
||||||
|
stringBuffer.append(File.separator);
|
||||||
|
stringBuffer.append(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return stringBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取SDCARD上应用存储路径
|
||||||
|
*
|
||||||
|
* @param mContext
|
||||||
|
* @param folderName
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private static StringBuffer getExternalFilesDir(Context mContext,
|
||||||
|
String folderName) throws Exception {
|
||||||
|
String packageName = mContext.getPackageName();
|
||||||
|
StringBuffer stringBuffer = new StringBuffer();
|
||||||
|
stringBuffer.append("Android").append(File.separator).append("data")
|
||||||
|
.append(File.separator).append(packageName);
|
||||||
|
if (folderName != null) {
|
||||||
|
if (0 == folderName.indexOf(File.separator)) {
|
||||||
|
stringBuffer.append(folderName);
|
||||||
|
} else {
|
||||||
|
stringBuffer.append(File.separator);
|
||||||
|
stringBuffer.append(folderName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Log.d(LOGTAG, "FileUtils getExternalFilesDir "
|
||||||
|
+ stringBuffer.toString());
|
||||||
|
return stringBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建一个临时目录,用于复制临时文件,如assets目录下的离线资源文件
|
||||||
|
* @param context
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
|
||||||
|
public static String createTmpDir(Context context) {
|
||||||
|
String sampleDir = "/ing/tts";
|
||||||
|
String tmpDir = Environment.getExternalStorageDirectory().toString() + sampleDir;
|
||||||
|
if (!FileUtils.makeDir(tmpDir)) {
|
||||||
|
tmpDir = context.getExternalFilesDir(sampleDir).getAbsolutePath();
|
||||||
|
if (!FileUtils.makeDir(sampleDir)) {
|
||||||
|
throw new RuntimeException("create model resources dir failed :" + tmpDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tmpDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean makeDir(String dirPath) {
|
||||||
|
File file = new File(dirPath);
|
||||||
|
if (!file.exists()) {
|
||||||
|
return file.mkdirs();
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* assets文件2 sdcard
|
||||||
|
* @param assets
|
||||||
|
* @param source
|
||||||
|
* @param dest
|
||||||
|
* @param isCover
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public static void copyFromAssets(AssetManager assets, String source, String dest, boolean isCover) throws IOException {
|
||||||
|
File file = new File(dest);
|
||||||
|
if (isCover || (!isCover && !file.exists())) {
|
||||||
|
InputStream is = null;
|
||||||
|
FileOutputStream fos = null;
|
||||||
|
try {
|
||||||
|
is = assets.open(source);
|
||||||
|
String path = dest;
|
||||||
|
fos = new FileOutputStream(path);
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
int size = 0;
|
||||||
|
while ((size = is.read(buffer, 0, 1024)) >= 0) {
|
||||||
|
fos.write(buffer, 0, size);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (fos != null) {
|
||||||
|
try {
|
||||||
|
fos.close();
|
||||||
|
} finally {
|
||||||
|
if (is != null) {
|
||||||
|
is.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
package com.smartmapx.tts;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.AssetManager;
|
||||||
|
import android.util.Log;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static android.content.ContentValues.TAG;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 离线文件
|
||||||
|
*
|
||||||
|
* @author ing
|
||||||
|
* @date 2018/3/27
|
||||||
|
*/
|
||||||
|
public class OfflineResource {
|
||||||
|
|
||||||
|
|
||||||
|
private AssetManager assets;
|
||||||
|
private String destPath;
|
||||||
|
|
||||||
|
private String backFilename;
|
||||||
|
private String modelFilename;
|
||||||
|
|
||||||
|
public OfflineResource(Context context) throws IOException {
|
||||||
|
this.assets = context.getAssets();
|
||||||
|
this.destPath = FileUtils.createTmpDir(context);
|
||||||
|
setOfflineVoiceType();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getModelFilename() {
|
||||||
|
return modelFilename;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBackFilename() {
|
||||||
|
return backFilename;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOfflineVoiceType() throws IOException {
|
||||||
|
String back = "backend_lzl";
|
||||||
|
String model = "frontend_model";
|
||||||
|
backFilename = copyAssetsFile(back);
|
||||||
|
modelFilename = copyAssetsFile(model);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String copyAssetsFile(String sourceFilename) throws IOException {
|
||||||
|
String destFilename = destPath + "/" + sourceFilename;
|
||||||
|
FileUtils.copyFromAssets(assets, sourceFilename, destFilename, false);
|
||||||
|
Log.i(TAG, "Assets to sdcard successed:" + destFilename);
|
||||||
|
return destFilename;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,207 @@
|
||||||
|
package com.smartmapx.tts;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.media.AudioManager;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.unisound.client.SpeechConstants;
|
||||||
|
import com.unisound.client.SpeechSynthesizer;
|
||||||
|
import com.unisound.client.SpeechSynthesizerListener;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 离线语音
|
||||||
|
*
|
||||||
|
* @author KeiferJu
|
||||||
|
* @date 2019/8/15
|
||||||
|
*/
|
||||||
|
public class SpeechUtilOffline {
|
||||||
|
public static final String appKey = "_appKey_";
|
||||||
|
public static final String secret = "_secret_";
|
||||||
|
private static SpeechUtilOffline instance;
|
||||||
|
private SpeechSynthesizer mTTSPlayer;
|
||||||
|
private boolean isSpeaking = false;
|
||||||
|
private List<SpeechItem> speechList = new ArrayList<>();
|
||||||
|
private boolean released = false;
|
||||||
|
protected OfflineResource offlineResource;
|
||||||
|
|
||||||
|
private SpeechUtilOffline(Context context) {
|
||||||
|
init(context);
|
||||||
|
released = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SpeechUtilOffline getInstance(Context context) {
|
||||||
|
if (instance == null) {
|
||||||
|
instance = new SpeechUtilOffline(context);
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化引擎
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private void init(final Context context) {
|
||||||
|
try {
|
||||||
|
offlineResource = new OfflineResource(context);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e("ing","offlineResouce failed , error msg : "+e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
// 初始化语音合成对象
|
||||||
|
mTTSPlayer = new SpeechSynthesizer(context, appKey, secret);
|
||||||
|
// 设置本地合成
|
||||||
|
mTTSPlayer.setOption(SpeechConstants.TTS_SERVICE_MODE, SpeechConstants.TTS_SERVICE_MODE_LOCAL);
|
||||||
|
mTTSPlayer.setOption(SpeechConstants.TTS_KEY_VOICE_PITCH, 50);//音调
|
||||||
|
mTTSPlayer.setOption(SpeechConstants.TTS_KEY_VOICE_SPEED, 52);//语速
|
||||||
|
mTTSPlayer.setOption(SpeechConstants.TTS_KEY_VOICE_VOLUME, 100);//音量
|
||||||
|
mTTSPlayer.setOption(SpeechConstants.TTS_KEY_STREAM_TYPE, AudioManager.STREAM_NOTIFICATION);
|
||||||
|
mTTSPlayer.setOption(SpeechConstants.TTS_KEY_FRONTEND_MODEL_PATH, offlineResource.getModelFilename());
|
||||||
|
// 设置后端模型
|
||||||
|
mTTSPlayer.setOption(SpeechConstants.TTS_KEY_BACKEND_MODEL_PATH, offlineResource.getBackFilename());
|
||||||
|
// 设置回调监听
|
||||||
|
mTTSPlayer.setTTSListener(new SpeechSynthesizerListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEvent(int type) {
|
||||||
|
switch (type) {
|
||||||
|
case SpeechConstants.TTS_EVENT_INIT:
|
||||||
|
// 初始化成功回调
|
||||||
|
break;
|
||||||
|
case SpeechConstants.TTS_EVENT_SYNTHESIZER_START:
|
||||||
|
// 开始合成回调
|
||||||
|
break;
|
||||||
|
case SpeechConstants.TTS_EVENT_SYNTHESIZER_END:
|
||||||
|
// 合成结束回调
|
||||||
|
break;
|
||||||
|
case SpeechConstants.TTS_EVENT_BUFFER_BEGIN:
|
||||||
|
// 开始缓存回调
|
||||||
|
break;
|
||||||
|
case SpeechConstants.TTS_EVENT_BUFFER_READY:
|
||||||
|
// 缓存完毕回调
|
||||||
|
break;
|
||||||
|
case SpeechConstants.TTS_EVENT_PLAYING_START:
|
||||||
|
// 开始播放回调
|
||||||
|
break;
|
||||||
|
case SpeechConstants.TTS_EVENT_PLAYING_END:
|
||||||
|
// 播放完成回调
|
||||||
|
break;
|
||||||
|
case SpeechConstants.TTS_EVENT_PAUSE:
|
||||||
|
// 暂停回调
|
||||||
|
break;
|
||||||
|
case SpeechConstants.TTS_EVENT_RESUME:
|
||||||
|
// 恢复回调
|
||||||
|
break;
|
||||||
|
case SpeechConstants.TTS_EVENT_STOP:
|
||||||
|
// 停止回调
|
||||||
|
break;
|
||||||
|
case SpeechConstants.TTS_EVENT_RELEASE:
|
||||||
|
// 释放资源回调
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(int type, String errorMSG) {
|
||||||
|
// 语音合成错误回调
|
||||||
|
Log.e("ing","TTS onError __ type : "+ type +" errorMsg : " +errorMSG );
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 初始化合成引擎
|
||||||
|
mTTSPlayer.init("");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 停止播放
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void stop() {
|
||||||
|
mTTSPlayer.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 播放
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void play(String content) {
|
||||||
|
playImmediately(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void play(String content, PLAY_MODE playMode) {
|
||||||
|
switch (playMode) {
|
||||||
|
case QUEUED: {
|
||||||
|
playQueued(content);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case IMMEDIATELY: {
|
||||||
|
playImmediately(content);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateSpeech() {
|
||||||
|
if (!isSpeaking) {
|
||||||
|
if (speechList.size() > 0) {
|
||||||
|
speak(speechList.remove(speechList.size() - 1).content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void speak(String content) {
|
||||||
|
mTTSPlayer.playText(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void playQueued(String content) {
|
||||||
|
speechList.add(new SpeechItem(content, PLAY_MODE.QUEUED));
|
||||||
|
updateSpeech();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void playImmediately(String content) {
|
||||||
|
speak(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 释放资源
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void release() {
|
||||||
|
// 主动释放离线引擎
|
||||||
|
if (released) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mTTSPlayer != null) {
|
||||||
|
mTTSPlayer.stop();
|
||||||
|
mTTSPlayer.release(SpeechConstants.TTS_RELEASE_ENGINE, null);
|
||||||
|
}
|
||||||
|
instance = null;
|
||||||
|
released = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public enum PLAY_MODE {
|
||||||
|
QUEUED,
|
||||||
|
IMMEDIATELY
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SpeechItem {
|
||||||
|
public String content;
|
||||||
|
public PLAY_MODE playMode;
|
||||||
|
|
||||||
|
public SpeechItem(String content, PLAY_MODE mode) {
|
||||||
|
this.content = content;
|
||||||
|
this.playMode = mode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,9 @@
|
||||||
|
var exec = require('cordova/exec');
|
||||||
|
|
||||||
|
exports.init = function (arg0, success, error) {
|
||||||
|
exec(success, error, 'ChineseTTS', 'init', [arg0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.speak = function (arg0, success, error) {
|
||||||
|
exec(success, error, 'ChineseTTS', 'speak', [arg0]);
|
||||||
|
}
|
Loading…
Reference in New Issue