提交 779f384e 作者: 王苏进

feat: 添加安卓类到 plugin

上级 1f741427
...@@ -72,3 +72,7 @@ android { ...@@ -72,3 +72,7 @@ android {
} }
} }
} }
dependencies {
implementation 'androidx.lifecycle:lifecycle-process:2.8.5'
}
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.aivoice_plugin"> package="com.example.aivoice_plugin">
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
</manifest> </manifest>
package com.example.aivoice_plugin package com.example.aivoice_plugin
import android.content.Context
import androidx.annotation.NonNull import androidx.annotation.NonNull
import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result import io.flutter.plugin.common.MethodChannel.Result
/** AivoicePlugin */ /** AivoicePlugin */
class AivoicePlugin: FlutterPlugin, MethodCallHandler { class AivoicePlugin: FlutterPlugin, MethodCallHandler {
/// The MethodChannel that will the communication between Flutter and native Android /// The MethodChannel that will the communication between Flutter and native Android
/// ///
/// This local reference serves to register the plugin with the Flutter Engine and unregister it /// This local reference serves to register the plugin with the Flutter Engine and unregister it
/// when the Flutter Engine is detached from the Activity /// when the Flutter Engine is detached from the Activity
private lateinit var channel : MethodChannel private lateinit var channel : MethodChannel
private var context: Context? = null
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
context = flutterPluginBinding.getApplicationContext();
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "aivoice_plugin") channel = MethodChannel(flutterPluginBinding.binaryMessenger, "aivoice_plugin")
channel.setMethodCallHandler(this) channel.setMethodCallHandler(this)
} }
...@@ -39,6 +44,7 @@ class AivoicePlugin: FlutterPlugin, MethodCallHandler { ...@@ -39,6 +44,7 @@ class AivoicePlugin: FlutterPlugin, MethodCallHandler {
// 现有的实现 // 现有的实现
} }
"ttsStartEngineBtnClick" -> { "ttsStartEngineBtnClick" -> {
println("来自安卓")
// 空实现 // 空实现
result.success(null) result.success(null)
} }
...@@ -47,6 +53,7 @@ class AivoicePlugin: FlutterPlugin, MethodCallHandler { ...@@ -47,6 +53,7 @@ class AivoicePlugin: FlutterPlugin, MethodCallHandler {
result.success(null) result.success(null)
} }
"ttsStopEngineBtnClicked" -> { "ttsStopEngineBtnClicked" -> {
// 空实现 // 空实现
result.success(null) result.success(null)
} }
......
// Copyright 2020 Bytedance Inc. All Rights Reserved.
// Author: fengkai.0518@bytedance.com (fengkai.0518)
package com.example.aivoice_plugin;
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.Button;
import android.widget.TextView;
import android.content.ContextWrapper;
import androidx.lifecycle.LifecycleObserver;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.ProcessLifecycleOwner;
import java.io.File;
import com.bytedance.speech.speechengine.SpeechEngine;
import com.bytedance.speech.speechengine.SpeechEngineDefines;
import com.bytedance.speech.speechengine.SpeechEngineGenerator;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.File;
import java.util.Collections;
import java.util.List;
public class AsrActivity implements SpeechEngine.SpeechListener, LifecycleObserver {
private Context _context;
// Record
private Handler recordHandler = null;
private Runnable recordRunnable = null;
private boolean recordIsRunning = false;
// Settings
protected Settings mSettings;
// Paths
private String mDebugPath = "";
// Engine
private SpeechEngine mSpeechEngine = null;
private boolean mEngineStarted = false;
// Permissions
private static final List<String> ASR_PERMISSIONS = Collections.singletonList(
Manifest.permission.RECORD_AUDIO
);
// StreamRecorder
private SpeechStreamRecorder mStreamRecorder;
// Statistics
private long mFinishTalkingTimestamp = -1;
@SuppressLint({"ClickableViewAccessibility", "InflateParams", "HardwareIds", "UseCompatLoadingForDrawables"})
public AsrActivity(Context context) {
_context = context;
Log.i(SpeechDemoDefines.TAG, "Asr onCreate");
final String viewId = SpeechDemoDefines.ASR_VIEW;
mSettings = SettingsActivity.getSettings(viewId);
mStreamRecorder = new SpeechStreamRecorder();
if (mDebugPath.isEmpty()) {
mDebugPath = getDebugPath();
}
Log.i(SpeechDemoDefines.TAG, "当前调试路径:" + mDebugPath);
}
/**
* get default debug path
* @return string: debugPath
*/
public String getDebugPath() {
if (mDebugPath != null) {
return mDebugPath;
}
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
Log.d(SpeechDemoDefines.TAG, "External storage can be read and write.");
} else {
Log.e(SpeechDemoDefines.TAG, "External storage can't write.");
return "";
}
File debugDir = _context.getExternalFilesDir(null);
if (debugDir == null) {
return "";
}
if (!debugDir.exists()) {
if (debugDir.mkdirs()) {
Log.d(SpeechDemoDefines.TAG, "Create debug path successfully.");
} else {
Log.e(SpeechDemoDefines.TAG, "Failed to create debug path.");
return "";
}
}
mDebugPath = debugDir.getAbsolutePath();
return mDebugPath;
}
public void onDestroy() {
Log.i(SpeechDemoDefines.TAG, "Asr onDestroy");
uninitEngine();
}
private void configInitParams() {
Log.i("111", "bbbb");
//【必需配置】Engine Name
mSpeechEngine.setOptionString(SpeechEngineDefines.PARAMS_KEY_ENGINE_NAME_STRING, SpeechEngineDefines.ASR_ENGINE);
//【可选配置】Debug & Log
mSpeechEngine.setOptionString(SpeechEngineDefines.PARAMS_KEY_DEBUG_PATH_STRING, mDebugPath);
mSpeechEngine.setOptionString(SpeechEngineDefines.PARAMS_KEY_LOG_LEVEL_STRING, SpeechEngineDefines.LOG_LEVEL_DEBUG);
//【可选配置】User ID(用以辅助定位线上用户问题)
mSpeechEngine.setOptionString(SpeechEngineDefines.PARAMS_KEY_UID_STRING, SensitiveDefines.UID);
//【必需配置】配置音频来源
mSpeechEngine.setOptionString(SpeechEngineDefines.PARAMS_KEY_RECORDER_TYPE_STRING, "Recorder");
if (mSettings.getBoolean(R.string.config_asr_rec_save)) {
//【可选配置】录音文件保存路径,如配置,SDK会将录音保存到该路径下,文件格式为 .wav
mSpeechEngine.setOptionString(SpeechEngineDefines.PARAMS_KEY_ASR_REC_PATH_STRING, mDebugPath);
}
//【可选配置】音频采样率,默认16000
mSpeechEngine.setOptionInt(SpeechEngineDefines.PARAMS_KEY_SAMPLE_RATE_INT, mSettings.getInt(R.string.config_sample_rate));
//【可选配置】音频通道数,默认1,可选1或2
mSpeechEngine.setOptionInt(SpeechEngineDefines.PARAMS_KEY_CHANNEL_NUM_INT, mSettings.getInt(R.string.config_channel));
//【可选配置】上传给服务的音频通道数,默认1,可选1或2,一般与PARAMS_KEY_CHANNEL_NUM_INT保持一致即可
mSpeechEngine.setOptionInt(SpeechEngineDefines.PARAMS_KEY_UP_CHANNEL_NUM_INT, mSettings.getInt(R.string.config_channel));
// 当音频来源为 RECORDER_TYPE_STREAM 时,如输入音频采样率不等于 16K,需添加如下配置
// if (mSettings.getOptionsValue(R.string.config_recorder_type, this).equals(SpeechEngineDefines.RECORDER_TYPE_STREAM)) {
// if (mStreamRecorder.GetStreamSampleRate() != 16000 || mStreamRecorder.GetStreamChannel() != 1) {
// // 当音频来源为 RECORDER_TYPE_STREAM 时【必需配置】,否则【无需配置】
// // 启用 SDK 内部的重采样
// mSpeechEngine.setOptionBoolean(SpeechEngineDefines.PARAMS_KEY_ENABLE_RESAMPLER_BOOL, true);
// // 将重采样所需的输入采样率设置为 APP 层输入的音频的实际采样率
// mSpeechEngine.setOptionInt(SpeechEngineDefines.PARAMS_KEY_CUSTOM_SAMPLE_RATE_INT, mStreamRecorder.GetStreamSampleRate());
// mSpeechEngine.setOptionInt(SpeechEngineDefines.PARAMS_KEY_CUSTOM_CHANNEL_INT, mStreamRecorder.GetStreamChannel());
// }
// }
String address = mSettings.getString(R.string.config_address);
if (address.isEmpty()) {
address = SensitiveDefines.DEFAULT_ADDRESS;
}
Log.i(SpeechDemoDefines.TAG, "Current address: " + address);
//【必需配置】识别服务域名
mSpeechEngine.setOptionString(SpeechEngineDefines.PARAMS_KEY_ASR_ADDRESS_STRING, address);
String uri = mSettings.getString(R.string.config_uri);
if (uri.isEmpty()) {
uri = SensitiveDefines.ASR_DEFAULT_URI;
}
Log.i(SpeechDemoDefines.TAG, "Current uri: " + uri);
//【必需配置】识别服务Uri
mSpeechEngine.setOptionString(SpeechEngineDefines.PARAMS_KEY_ASR_URI_STRING, uri);
String appid = mSettings.getString(R.string.config_app_id);
if (appid.isEmpty()) {
appid = SensitiveDefines.APPID;
}
//【必需配置】鉴权相关:Appid
mSpeechEngine.setOptionString(SpeechEngineDefines.PARAMS_KEY_APP_ID_STRING, appid);
String token = mSettings.getString(R.string.config_token);
if (token.isEmpty()) {
token = SensitiveDefines.TOKEN;
}
//【必需配置】鉴权相关:Token
mSpeechEngine.setOptionString(SpeechEngineDefines.PARAMS_KEY_APP_TOKEN_STRING, token);
String cluster = mSettings.getString(R.string.config_cluster);
if (cluster.isEmpty()) {
cluster = SensitiveDefines.ASR_DEFAULT_CLUSTER;
}
Log.i(SpeechDemoDefines.TAG, "Current cluster: " + cluster);
//【必需配置】识别服务所用集群
mSpeechEngine.setOptionString(SpeechEngineDefines.PARAMS_KEY_ASR_CLUSTER_STRING, cluster);
//【可选配置】在线请求的建连与接收超时,一般不需配置使用默认值即可
mSpeechEngine.setOptionInt(SpeechEngineDefines.PARAMS_KEY_ASR_CONN_TIMEOUT_INT, 3000);
mSpeechEngine.setOptionInt(SpeechEngineDefines.PARAMS_KEY_ASR_RECV_TIMEOUT_INT, 5000);
//【可选配置】在线请求断连后,重连次数,默认值为0,如果需要开启需要设置大于0的次数
mSpeechEngine.setOptionInt(SpeechEngineDefines.PARAMS_KEY_ASR_MAX_RETRY_TIMES_INT, mSettings.getInt(R.string.config_asr_max_retry_times));
}
private void configStartAsrParams() {
//【可选配置】是否开启顺滑(DDC)
mSpeechEngine.setOptionBoolean(SpeechEngineDefines.PARAMS_KEY_ASR_ENABLE_DDC_BOOL, mSettings.getBoolean(R.string.config_asr_enable_ddc));
//【可选配置】是否开启文字转数字(ITN)
mSpeechEngine.setOptionBoolean(SpeechEngineDefines.PARAMS_KEY_ASR_ENABLE_ITN_BOOL, mSettings.getBoolean(R.string.config_asr_enable_itn));
//【可选配置】是否开启标点
mSpeechEngine.setOptionBoolean(SpeechEngineDefines.PARAMS_KEY_ASR_SHOW_NLU_PUNC_BOOL, mSettings.getBoolean(R.string.config_asr_enable_nlu_punctuation));
//【可选配置】设置识别语种
mSpeechEngine.setOptionString(SpeechEngineDefines.PARAMS_KEY_ASR_LANGUAGE_STRING, mSettings.getString(R.string.config_asr_language));
//【可选配置】是否隐藏句尾标点
mSpeechEngine.setOptionBoolean(SpeechEngineDefines.PARAMS_KEY_ASR_DISABLE_END_PUNC_BOOL, mSettings.getBoolean(R.string.config_asr_disable_end_punctuation));
// 【可选配置】直接传递自定义的ASR请求JSON,若使用此参数需自行确保JSON格式正确
mSpeechEngine.setOptionString(SpeechEngineDefines.PARAMS_KEY_ASR_REQ_PARAMS_STRING, mSettings.getString(R.string.config_asr_req_params));
//【可选配置】控制识别结果返回的形式,全量返回或增量返回,默认为全量
// mSpeechEngine.setOptionString(SpeechEngineDefines.PARAMS_KEY_ASR_RESULT_TYPE_STRING, mSettings.getOptionsValue(R.string.config_asr_result_type, this));
//【可选配置】设置VAD头部静音时长,用户多久没说话视为空音频,即静音检测时长
mSpeechEngine.setOptionInt(SpeechEngineDefines.PARAMS_KEY_ASR_VAD_START_SILENCE_TIME_INT, mSettings.getInt(R.string.config_asr_vad_start_silence_time));
//【可选配置】设置VAD尾部静音时长,用户说话后停顿多久视为说话结束,即自动判停时长
mSpeechEngine.setOptionInt(SpeechEngineDefines.PARAMS_KEY_ASR_VAD_END_SILENCE_TIME_INT, mSettings.getInt(R.string.config_asr_vad_end_silence_time));
//【可选配置】设置VAD模式,用于定制VAD场景,默认为空
mSpeechEngine.setOptionString(SpeechEngineDefines.PARAMS_KEY_ASR_VAD_MODE_STRING, mSettings.getString(R.string.config_asr_vad_mode));
//【可选配置】用户音频输入最大时长,仅一句话识别场景生效,单位毫秒,默认为 60000ms.
mSpeechEngine.setOptionInt(SpeechEngineDefines.PARAMS_KEY_VAD_MAX_SPEECH_DURATION_INT, mSettings.getInt(R.string.config_vad_max_speech_duration));
//【可选配置】控制是否返回录音音量,在 APP 需要显示音频波形时可以启用
mSpeechEngine.setOptionBoolean(SpeechEngineDefines.PARAMS_KEY_ENABLE_GET_VOLUME_BOOL, mSettings.getBoolean(R.string.config_get_volume));
//【可选配置】设置纠错词表,识别结果会根据设置的纠错词纠正结果,例如:"{\"古爱玲\":\"谷爱凌\"}",当识别结果中出现"古爱玲"时会替换为"谷爱凌"
mSpeechEngine.setOptionString(SpeechEngineDefines.PARAMS_KEY_ASR_CORRECT_WORDS_STRING, mSettings.getString(R.string.config_asr_correct_words));
//【可选配置】更新 ASR 热词
if (!mSettings.getString(R.string.config_asr_hotwords).isEmpty()) {
Log.d(SpeechDemoDefines.TAG, "Set hotwords.");
setHotWords(mSettings.getString(R.string.config_asr_hotwords));
}
// if (mSettings.getOptionsValue(R.string.config_recorder_type, this).equals(SpeechEngineDefines.RECORDER_TYPE_STREAM)) {
// if (!mStreamRecorder.Start()) {
// requestPermission(ASR_PERMISSIONS);
// }
// } else if (mSettings.getOptionsValue(R.string.config_recorder_type, this).equals(SpeechEngineDefines.RECORDER_TYPE_FILE)) {
// // 使用音频文件识别时,需要设置文件的绝对路径
// String test_file_path = mDebugPath + "/asr_rec_file.pcm";
// Log.d(SpeechDemoDefines.TAG, "输入的音频文件路径: " + test_file_path);
// // 使用音频文件识别时【必须配置】,否则【无需配置】
// mSpeechEngine.setOptionString(SpeechEngineDefines.PARAMS_KEY_RECORDER_FILE_STRING, test_file_path);
// }
}
private void setHotWords(String hotWords) {
if (mSpeechEngine != null) {
// 更新 ASR 热词,例如:"{\"hotwords\":[{\"word\":\"过秦论\",\"scale\":2.0}]}"
// scale为float类型参数,其中叠词的范围为[1.0,2.0],非叠词的范围为[1.0,50.0],scale值越大,结果中出现热词的概率越大
mSpeechEngine.sendDirective(SpeechEngineDefines.DIRECTIVE_UPDATE_ASR_HOTWORDS, hotWords);
}
}
private void initEngine() {
if (mSpeechEngine == null) {
Log.i(SpeechDemoDefines.TAG, "创建引擎.");
mSpeechEngine = SpeechEngineGenerator.getInstance();
mSpeechEngine.createEngine();
mSpeechEngine.setContext(_context.getApplicationContext());
}
Log.d(SpeechDemoDefines.TAG, "SDK 版本号: " + mSpeechEngine.getVersion());
Log.i(SpeechDemoDefines.TAG, "配置初始化参数.");
configInitParams();
Log.i(SpeechDemoDefines.TAG, "引擎初始化.");
int ret = mSpeechEngine.initEngine();
if (ret != SpeechEngineDefines.ERR_NO_ERROR) {
String errMessage = "初始化失败,返回值: " + ret;
Log.e(SpeechDemoDefines.TAG, errMessage);
speechEngineInitFailed(errMessage);
return;
}
Log.i(SpeechDemoDefines.TAG, "设置消息监听");
mSpeechEngine.setListener(this);
speechEnginInitucceeded();
}
private void uninitEngine() {
if (mSpeechEngine != null) {
Log.i(SpeechDemoDefines.TAG, "引擎析构.");
mSpeechEngine.destroyEngine();
mSpeechEngine = null;
Log.i(SpeechDemoDefines.TAG, "引擎析构完成!");
}
}
private void initEngineBtnClicked() {
if (mEngineStarted) {
return;
}
initEngine();
}
private void uninitEngineBtnClicked() {
if (mEngineStarted) {
return;
}
uninitEngine();
}
private void startEngineBtnClicked() {
Log.i(SpeechDemoDefines.TAG, "配置启动参数.");
configStartAsrParams();
//【可选配置】是否启用云端自动判停,仅一句话识别场景生效
Log.i(SpeechDemoDefines.TAG, "开启 ASR 云端自动判停");
mSpeechEngine.setOptionBoolean(SpeechEngineDefines.PARAMS_KEY_ASR_AUTO_STOP_BOOL, true);
// Directive:启动引擎前调用SYNC_STOP指令,保证前一次请求结束。
Log.i(SpeechDemoDefines.TAG, "关闭引擎(同步)");
Log.i(SpeechDemoDefines.TAG, "Directive: DIRECTIVE_SYNC_STOP_ENGINE");
int ret = mSpeechEngine.sendDirective(SpeechEngineDefines.DIRECTIVE_SYNC_STOP_ENGINE, "");
if (ret != SpeechEngineDefines.ERR_NO_ERROR) {
Log.e(SpeechDemoDefines.TAG, "send directive syncstop failed, " + ret);
} else {
Log.i(SpeechDemoDefines.TAG, "启动引擎");
Log.i(SpeechDemoDefines.TAG, "Directive: DIRECTIVE_START_ENGINE");
ret = mSpeechEngine.sendDirective(SpeechEngineDefines.DIRECTIVE_START_ENGINE, "");
if (ret == SpeechEngineDefines.ERR_REC_CHECK_ENVIRONMENT_FAILED) {
// mEngineStatusTv.setText(R.string.check_rec_permission);
// requestPermission(ASR_PERMISSIONS);
} else if (ret != SpeechEngineDefines.ERR_NO_ERROR) {
Log.e(SpeechDemoDefines.TAG, "send directive start failed, " + ret);
}
}
clearResultText();
}
private void stopEngineBtnClicked() {
Log.i(SpeechDemoDefines.TAG, "关闭引擎(异步)");
Log.i(SpeechDemoDefines.TAG, "Directive: DIRECTIVE_STOP_ENGINE");
mSpeechEngine.sendDirective(SpeechEngineDefines.DIRECTIVE_STOP_ENGINE, "");
}
private void recordBtnTouchDown() {
recordIsRunning = false;
recordHandler = new Handler();
recordRunnable = () -> {
recordIsRunning = true;
Log.i(SpeechDemoDefines.TAG, "配置启动参数.");
configStartAsrParams();
//【可选配置】该按钮为长按模式,预期是按下开始录音,抬手结束录音,需要关闭云端自动判停功能。
mSpeechEngine.setOptionBoolean(SpeechEngineDefines.PARAMS_KEY_ASR_AUTO_STOP_BOOL, false);
// Directive:启动引擎前调用SYNC_STOP指令,保证前一次请求结束。
Log.i(SpeechDemoDefines.TAG, "关闭引擎(同步)");
Log.i(SpeechDemoDefines.TAG, "Directive: DIRECTIVE_SYNC_STOP_ENGINE");
int ret = mSpeechEngine.sendDirective(SpeechEngineDefines.DIRECTIVE_SYNC_STOP_ENGINE, "");
if (ret != SpeechEngineDefines.ERR_NO_ERROR) {
Log.e(SpeechDemoDefines.TAG, "send directive syncstop failed, " + ret);
} else {
Log.i(SpeechDemoDefines.TAG, "启动引擎");
Log.i(SpeechDemoDefines.TAG, "Directive: DIRECTIVE_START_ENGINE");
ret = mSpeechEngine.sendDirective(SpeechEngineDefines.DIRECTIVE_START_ENGINE, "");
if (ret == SpeechEngineDefines.ERR_REC_CHECK_ENVIRONMENT_FAILED) {
// mEngineStatusTv.setText(R.string.check_rec_permission);
// requestPermission(ASR_PERMISSIONS);
} else if (ret != SpeechEngineDefines.ERR_NO_ERROR) {
Log.e(SpeechDemoDefines.TAG, "send directive start failed, " + ret);
}
}
clearResultText();
};
recordHandler.postDelayed(recordRunnable, 500);
}
private void recordBtnTouchUp() {
if (recordIsRunning) {
recordIsRunning = false;
Log.i(SpeechDemoDefines.TAG, "AsrTouch: Finish");
mFinishTalkingTimestamp = System.currentTimeMillis();
// Directive:结束用户音频输入。
Log.i(SpeechDemoDefines.TAG, "Directive: DIRECTIVE_FINISH_TALKING");
mSpeechEngine.sendDirective(SpeechEngineDefines.DIRECTIVE_FINISH_TALKING, "");
mStreamRecorder.Stop();
} else if (recordRunnable != null) {
Log.i(SpeechDemoDefines.TAG, "AsrTouch: Cancel");
recordHandler.removeCallbacks(recordRunnable);
recordRunnable = null;
}
}
@Override
public void onSpeechMessage(int type, byte[] data, int len) {
String stdData = new String(data);
switch (type) {
case SpeechEngineDefines.MESSAGE_TYPE_ENGINE_START:
// Callback: 引擎启动成功回调
Log.i(SpeechDemoDefines.TAG, "Callback: 引擎启动成功: data: " + stdData);
speechStart();
break;
case SpeechEngineDefines.MESSAGE_TYPE_ENGINE_STOP:
// Callback: 引擎关闭回调
Log.i(SpeechDemoDefines.TAG, "Callback: 引擎关闭: data: " + stdData);
speechStop();
break;
case SpeechEngineDefines.MESSAGE_TYPE_ENGINE_ERROR:
// Callback: 错误信息回调
Log.e(SpeechDemoDefines.TAG, "Callback: 错误信息: " + stdData);
speechError(stdData);
break;
case SpeechEngineDefines.MESSAGE_TYPE_CONNECTION_CONNECTED:
Log.i(SpeechDemoDefines.TAG, "Callback: 建连成功: data: " + stdData);
break;
case SpeechEngineDefines.MESSAGE_TYPE_PARTIAL_RESULT:
// Callback: ASR 当前请求的部分结果回调
Log.d(SpeechDemoDefines.TAG, "Callback: ASR 当前请求的部分结果");
speechAsrResult(stdData, false);
break;
case SpeechEngineDefines.MESSAGE_TYPE_FINAL_RESULT:
// Callback: ASR 当前请求最终结果回调
Log.i(SpeechDemoDefines.TAG, "Callback: ASR 当前请求最终结果");
speechAsrResult(stdData, true);
break;
case SpeechEngineDefines.MESSAGE_TYPE_VOLUME_LEVEL:
// Callback: 录音音量回调
Log.d(SpeechDemoDefines.TAG, "Callback: 录音音量");
break;
default:
break;
}
}
public void speechEnginInitucceeded() {
Log.i(SpeechDemoDefines.TAG, "引擎初始化成功!");
mStreamRecorder.SetSpeechEngine(SpeechDemoDefines.ASR_VIEW, mSpeechEngine);
}
public void speechEngineInitFailed(String tipText) {
Log.e(SpeechDemoDefines.TAG, "引擎初始化失败: " + tipText);
// this.runOnUiThread(() -> {
// mEngineStatusTv.setText(R.string.hint_setup_failure);
// setButton(mInitEngineBtn, true);
// setResultText(tipText);
// });
}
public void speechStart() {
mEngineStarted = true;
// this.runOnUiThread(() -> {
// mEngineStatusTv.setText(R.string.hint_start_cb);
// setButton(mStartEngineBtn, false);
// setButton(mStopEngineBtn, true);
// });
}
public void speechStop() {
mEngineStarted = false;
// this.runOnUiThread(() -> {
// mStreamRecorder.Stop();
// mEngineStatusTv.setText(R.string.hint_stop_cb);
// setButton(mStartEngineBtn, true);
// setButton(mStopEngineBtn, false);
// });
}
public void speechAsrResult(final String data, boolean isFinal) {
// 计算由录音结束到 ASR 最终结果之间的延迟
long delay = 0;
if (isFinal && mFinishTalkingTimestamp > 0) {
delay = System.currentTimeMillis() - mFinishTalkingTimestamp;
mFinishTalkingTimestamp = 0;
}
final long response_delay = delay;
try {
// 从回调的 json 数据中解析 ASR 结果
JSONObject reader = new JSONObject(data);
if (!reader.has("result")) {
return;
}
String text = reader.getJSONArray("result").getJSONObject(0).getString("text");
if (text.isEmpty()) {
return;
}
text = "result: " + text;
if (isFinal) {
text += "\nreqid: " + reader.getString("reqid");
text += "\nresponse_delay: " + response_delay;
}
setResultText(text);
} catch (JSONException e) {
e.printStackTrace();
}
}
public void speechError(final String data) {
try {
// 从回调的 json 数据中解析错误码和错误详细信息
JSONObject reader = new JSONObject(data);
if (!reader.has("err_code") || !reader.has("err_msg")) {
return;
}
setResultText(data);
} catch (JSONException e) {
e.printStackTrace();
}
}
public void setResultText(final String text) {
// mResultTv.setText("");
// mResultTv.append("\n" + text);
}
public void clearResultText() {
}
}
package com.example.aivoice_plugin;
/**
* SensitiveDefines
* Defines in this class should be different for different business,
* please contact with @Bytedance AILab about what value should be set before use it.
*/
public class SensitiveDefines {
// User Info
public static final String UID = "YOUR USER ID";
// Device Info
public static final String DID = "YOUR DEVICE ID";
// Online & Resource Authorization
public static final String APPID = "2301072440";
public static final String TOKEN = "Bearer;75UvJCpxRjTCppqQUkQ-o-4UfjnCgVmp";
public static final String APP_VERSION = "1.0.0";
// Offline Authorization
public static final String AUTHENTICATE_ADDRESS = "AUTHENTICAT ADDRESS";
public static final String AUTHENTICATE_URI = "AUTHENTICATE URI";
public static final String LICENSE_NAME = "YOUR LICENSE NAME";
public static final String LICENSE_BUSI_ID = "YOUR LICENSE BUSI_ID";
public static final String SECRET = "YOUR SECRET";
public static final String BUSINESS_KEY = "YOUR BUSINESS KEY";
// Address
public static final String DEFAULT_ADDRESS = "wss://openspeech.bytedance.com";
public static final String DEFAULT_HTTP_ADDRESS = "https://openspeech.bytedance.com";
// ASR
public static final String ASR_DEFAULT_URI = "/api/v2/asr";
public static final String ASR_DEFAULT_CLUSTER = "volcengine_streaming_common";
public static final String ASR_DEFAULT_MODEL_NAME = "YOUR ASR MODEL NAME";
// AU
public static final String AU_DEFAULT_URI = "/api/v1/sauc";
public static final String AU_DEFAULT_CLUSTER = "YOUR AU CLUSTER";
// TTS
public static final String TTS_DEFAULT_URI = "/api/v1/tts/ws_binary";
public static final String TTS_DEFAULT_CLUSTER = "volcano_tts";
public static final String TTS_DEFAULT_BACKEND_CLUSTER = "YOUR TTS BACKEND CLUSTER";
public static final String TTS_DEFAULT_ONLINE_VOICE = "灿灿";
public static final String TTS_DEFAULT_ONLINE_VOICE_TYPE = "BV002_streaming";
public static final String TTS_DEFAULT_OFFLINE_VOICE = "TTS OFFLINE VOICE";
public static final String TTS_DEFAULT_OFFLINE_VOICE_TYPE = "TTS OFFLINE VOICE TYPE";
public static final String TTS_DEFAULT_ONLINE_LANGUAGE = "TTS ONLINE LANGUAGE";
public static final String TTS_DEFAULT_OFFLINE_LANGUAGE = "TTS OFFLINE LANGUAGE";
public static final String[] TTS_DEFAULT_DOWNLOAD_OFFLINE_VOICES = new String[]{};
// VoiceClone
public static final String VOICECLONE_DEFAULT_UIDS = "uid_1;uid_2";
public static final int VOICECLONE_DEFAULT_TASK_ID = -1;
// VoiceConv
public static final String VOICECONV_DEFAULT_URI = "/api/v1/voice_conv/ws";
public static final String VOICECONV_DEFAULT_CLUSTER = "YOUR VOICECONV CLUSTER";
public static final String VOICECONV_DEFAULT_VOICE = "VOICECONV VOICE";
public static final String VOICECONV_DEFAULT_VOICE_TYPE = "VOICECONV VOICE TYPE";
// Fulllink
public static final String FULLLINK_DEFAULT_URI = "FULLLINK URI";
// Dialog
public static final String DIALOG_DEFAULT_URI = "DIALOG URI";
public static final String DIALOG_DEFAULT_APP_ID = "DIALOG APP ID";
public static final String DIALOG_DEFAULT_ID = "DIALOG ID";
public static final String DIALOG_DEFAULT_ROLE = "DIALOG ROLE";
public static final String DIALOG_DEFAULT_CLOTHES_TYPE = "DIALOG CLOTHES TYPE";
public static final String DIALOG_DEFAULT_TTA_VOICE_TYPE = "DIALOG TTA_VOICE_TYPE";
// CAPT
public static final String CAPT_DEFAULT_MDD_URI = "CAPT MDD URI";
public static final String CAPT_DEFAULT_CLUSTER = "YOUR CAPT CLUSTER";
}
// Copyright 2021 Bytedance Inc. All Rights Reserved.
// Author: chengzihao.ds@bytedance.com (chengzihao.ds)
package com.example.aivoice_plugin;
import java.util.List;
public class SettingItem {
public static final int TYPE_TOTAL_NUM = 5;
public static class Type {
public static final int GROUP = 0; // group: means followed-items construct a group
public static final int BOOL = 1;
public static final int NUMBER = 2;
public static final int STRING = 3;
public static final int OPTIONS = 4;
}
public static class Options {
public int arrayId;
public List<String> arrayObj;
public int chooseIdx;
public Options(int id, int idx) {
this.arrayId = id;
this.arrayObj = null;
this.chooseIdx = idx;
}
public Options(List<String> obj, int idx) {
this.arrayId = 0;
this.arrayObj = obj;
this.chooseIdx = idx;
}
}
// id must be resource id: R.string.config_xxx, will be used for TextView.setText()
public int id;
// type must be one of SettingItem.Type
public int type;
// value must correspond to type
public Object value;
// hint must be resource id: can be R.string.nohint, will be used for TextView.setHint()
public int hint;
public SettingItem(int t, int i, Object v, int h) {
type = t;
id = i;
value = v;
hint = h;
}
}
// Copyright 2021 Bytedance Inc. All Rights Reserved.
// Author: chengzihao.ds@bytedance.com (chengzihao.ds)
package com.example.aivoice_plugin;
import android.content.Context;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.Switch;
import android.widget.TextView;
public class SettingListAdapter extends BaseAdapter {
private Context context;
private Settings settings;
public SettingListAdapter(Context context, Settings settings) {
this.context = context;
this.settings = settings;
}
@Override
public int getCount() {
return settings.configs.size();
}
@Override
public Object getItem(int i) {
return null;
}
@Override
public long getItemId(int i) {
return 0;
}
@Override
public int getItemViewType(int position) {
return settings.configs.get(position).type;
}
@Override
public int getViewTypeCount() {
return SettingItem.TYPE_TOTAL_NUM;
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
// SettingItem item = settings.configs.get(i);
// final int pos = i;
// switch (item.type) {
// case SettingItem.Type.GROUP:
// GroupViewHolder groupViewHolder;
// if (view == null) {
// view = LayoutInflater.from(context).inflate(R.layout.settings_item_group, null);
// groupViewHolder = new GroupViewHolder();
// groupViewHolder.itemTv = view.findViewById(R.id.item_text_view);
// view.setTag(groupViewHolder);
// } else {
// groupViewHolder = (GroupViewHolder) view.getTag();
// }
// groupViewHolder.itemTv.setText(item.id);
// break;
// case SettingItem.Type.BOOL:
// BoolViewHolder boolViewHolder;
// if (view == null) {
// view = LayoutInflater.from(context).inflate(R.layout.settings_item_bool, null);
// boolViewHolder = new BoolViewHolder();
// boolViewHolder.itemSwt = view.findViewById(R.id.item_switch);
// view.setTag(boolViewHolder);
// } else {
// boolViewHolder = (BoolViewHolder) view.getTag();
// }
// boolViewHolder.itemSwt.setText(item.id);
// boolViewHolder.itemSwt.setChecked((Boolean) item.value);
// boolViewHolder.itemSwt.setOnClickListener((switchBtn) -> {
// settings.set(settings.configs.get(pos).id, ((Switch) switchBtn).isChecked());
// });
// break;
// case SettingItem.Type.NUMBER:
// NumberViewHolder numberViewHolder;
// if (view == null) {
// view = LayoutInflater.from(context).inflate(R.layout.settings_item_number, null);
// numberViewHolder = new NumberViewHolder();
// numberViewHolder.itemIdTv = view.findViewById(R.id.item_key_text_view);
// numberViewHolder.itemValEt = view.findViewById(R.id.item_value_edit_text);
// numberViewHolder.itemValEt.setTag(pos);
// view.setTag(numberViewHolder);
// numberViewHolder.itemValEt.addTextChangedListener(new AttachPosTextWatch(numberViewHolder.itemValEt) {
// @Override
// public void onAttachPosTextChanged(String text, int pos) {
// Number num = null;
// try {
// num = Integer.parseInt(text);
// } catch (Exception parseIntException) {
// try {
// num = Double.parseDouble(text);
// } catch (Exception parseDoubleException) {
// }
// }
// settings.set(settings.configs.get(pos).id, num);
// }
// });
// } else {
// numberViewHolder = (NumberViewHolder) view.getTag();
// numberViewHolder.itemValEt.setTag(pos);
// }
// numberViewHolder.itemIdTv.setText(item.id);
// if (item.value != null) {
// numberViewHolder.itemValEt.setText(item.value.toString());
// }
// numberViewHolder.itemValEt.setHint(item.hint);
// break;
// case SettingItem.Type.STRING:
// StringViewHolder stringViewHolder;
// if (view == null) {
// view = LayoutInflater.from(context).inflate(R.layout.settings_item_string, null);
// stringViewHolder = new StringViewHolder();
// stringViewHolder.itemIdTv = view.findViewById(R.id.item_key_text_view);
// stringViewHolder.itemValEt = view.findViewById(R.id.item_value_edit_text);
// stringViewHolder.itemValEt.setTag(pos);
// view.setTag(stringViewHolder);
// stringViewHolder.itemValEt.addTextChangedListener(new AttachPosTextWatch(stringViewHolder.itemValEt) {
// @Override
// public void onAttachPosTextChanged(String text, int pos) {
// settings.set(settings.configs.get(pos).id, text);
// }
// });
// } else {
// stringViewHolder = (StringViewHolder) view.getTag();
// stringViewHolder.itemValEt.setTag(pos);
// }
// stringViewHolder.itemIdTv.setText(item.id);
// stringViewHolder.itemValEt.setText((String) item.value);
// stringViewHolder.itemValEt.setHint(item.hint);
// break;
// case SettingItem.Type.OPTIONS:
// OptionsViewHolder optionsViewHolder;
// if (view == null) {
// view = LayoutInflater.from(context).inflate(R.layout.settings_item_options, null);
// optionsViewHolder = new OptionsViewHolder();
// optionsViewHolder.itemIdTv = view.findViewById(R.id.item_key_text_view);
// optionsViewHolder.itemOptSp = view.findViewById(R.id.item_options_spinner);
// view.setTag(optionsViewHolder);
// } else {
// optionsViewHolder = (OptionsViewHolder) view.getTag();
// }
// SettingItem.Options itemOpt = (SettingItem.Options) item.value;
// optionsViewHolder.itemIdTv.setText(item.id);
// if (itemOpt.arrayObj != null) {
// optionsViewHolder.itemOptSp.setAdapter(new ArrayAdapter<>(context, android.R.layout.simple_list_item_activated_1,
// itemOpt.arrayObj));
// } else {
// optionsViewHolder.itemOptSp.setAdapter(new ArrayAdapter<>(context, android.R.layout.simple_list_item_activated_1,
// context.getResources().getStringArray(itemOpt.arrayId)));
// }
// optionsViewHolder.itemOptSp.setSelection(itemOpt.chooseIdx);
// optionsViewHolder.itemOptSp.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
// @Override
// public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
// SettingItem.Options itemOpt = (SettingItem.Options) settings.configs.get(pos).value;
// itemOpt.chooseIdx = i;
// settings.set(settings.configs.get(pos).id, itemOpt);
// }
//
// @Override
// public void onNothingSelected(AdapterView<?> adapterView) {
//
// }
// });
// break;
// }
// if (view != null) {
// view.setId(item.id);
// }
return view;
}
static class GroupViewHolder {
TextView itemTv;
}
static class BoolViewHolder {
Switch itemSwt;
}
static class NumberViewHolder {
TextView itemIdTv;
EditText itemValEt;
}
static class StringViewHolder {
TextView itemIdTv;
EditText itemValEt;
}
static class OptionsViewHolder {
TextView itemIdTv;
Spinner itemOptSp;
}
/**
* When ListView reuse EditText, the real position might be changed.
* Use AttachPosTextWatch to avoid this problem. view.getTag() will record pos
*/
static abstract class AttachPosTextWatch implements TextWatcher {
private View attachPosView;
public AttachPosTextWatch(View apv) {
attachPosView = apv;
}
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
int pos = (Integer) attachPosView.getTag();
onAttachPosTextChanged(charSequence.toString(), pos);
}
@Override
public void afterTextChanged(Editable editable) {
}
public abstract void onAttachPosTextChanged(String text, int pos);
}
}
// Copyright 2021 Bytedance Inc. All Rights Reserved.
// Author: chengzihao.ds@bytedance.com (chengzihao.ds)
package com.example.aivoice_plugin;
import android.content.Context;
import android.content.res.Resources;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class Settings {
// TODO: can be optimized here, use hashmap + arraylist to accelerate
public List<SettingItem> configs;
public Settings() {
configs = new ArrayList<>();
}
public void register(SettingItem item) {
for (int i = 0; i < configs.size(); ++i) {
if (configs.get(i).id == item.id) {
configs.set(i, item);
return;
}
}
configs.add(item);
}
public void register(List<SettingItem> items) {
for (SettingItem item : items) {
register(item);
}
}
public void set(String key, boolean val, Context context) {
set(key, val, SettingItem.Type.BOOL, context);
}
public void set(int id, boolean val) {
set(id, val, SettingItem.Type.BOOL);
}
public void set(String key, Number val, Context context) {
set(key, val, SettingItem.Type.NUMBER, context);
}
public void set(int id, Number val) {
set(id, val, SettingItem.Type.NUMBER);
}
public void set(String key, String val, Context context) {
set(key, val, SettingItem.Type.STRING, context);
}
public void set(int id, String val) {
set(id, val, SettingItem.Type.STRING);
}
public void set(String key, SettingItem.Options val, Context context) {
set(key, val, SettingItem.Type.OPTIONS, context);
}
public void set(int id, SettingItem.Options val) {
set(id, val, SettingItem.Type.OPTIONS);
}
private void set(String key, Object val, int type, Context context) {
for (int i = 0; i < configs.size(); ++i) {
SettingItem item = configs.get(i);
if (Objects.equals(context.getString(item.id), key)) {
item.type = type;
item.value = val;
configs.set(i, item);
return;
}
}
}
private void set(int id, Object val, int type) {
for (int i = 0; i < configs.size(); ++i) {
SettingItem item = configs.get(i);
if (item.id == id) {
if (item.type == type) {
item.value = val;
configs.set(i, item);
}
return;
}
}
// not found
register(new SettingItem(type, id, val, 0));
}
public boolean getBoolean(int id) {
return getBoolean(id, false);
}
public boolean getBoolean(int id, boolean def) {
SettingItem item = get(id, SettingItem.Type.BOOL);
if (item != null) {
return (Boolean) item.value;
}
return def;
}
public int getInt(int id) {
return getInt(id, 0);
}
public int getInt(int id, int def) {
SettingItem item = get(id, SettingItem.Type.NUMBER);
if (item != null && item.value != null) {
return ((Number) item.value).intValue();
}
return def;
}
public Double getDouble(int id) {
return getDouble(id, 0.);
}
public Double getDouble(int id, Double def) {
SettingItem item = get(id, SettingItem.Type.NUMBER);
if (item != null && item.value != null) {
return ((Number) item.value).doubleValue();
}
return def;
}
public String getString(int id) {
return getString(id, "");
}
public String getString(int id, String def) {
SettingItem item = get(id, SettingItem.Type.STRING);
if (item != null) {
return (String) item.value;
}
return def;
}
public SettingItem.Options getOptions(int id) {
return getOptions(id, new SettingItem.Options(0, 0));
}
public SettingItem.Options getOptions(int id, SettingItem.Options def) {
SettingItem item = get(id, SettingItem.Type.OPTIONS);
if (item != null) {
return (SettingItem.Options) item.value;
}
return def;
}
public String getOptionsValue(int id, Context context) {
SettingItem.Options options = getOptions(id);
try {
String[] array = context.getResources().getStringArray(options.arrayId);
if (array == null) {
return "";
}
if (array.length <= options.chooseIdx) {
return "";
}
return array[options.chooseIdx];
} catch (Resources.NotFoundException e) {
return "";
}
}
public String getOptionsValue(int id) {
SettingItem.Options options = getOptions(id);
List<String> array = options.arrayObj;
if (array == null) {
return "";
}
if (array.size() <= options.chooseIdx) {
return "";
}
return array.get(options.chooseIdx);
}
private SettingItem get(int id, int type) {
for (SettingItem item : configs) {
if (item.id == id) {
if (item.type == type) {
return item;
}
return null;
}
}
return null;
}
}
// Copyright 2021 Bytedance Inc. All Rights Reserved.
// Author: chengzihao.ds@bytedance.com (chengzihao.ds)
package com.example.aivoice_plugin;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.widget.ListView;
import java.util.Arrays;
public class SettingsActivity {
public static final String BUNDLE_KEY_VIEW_ID = "VIEW_ID";
private static final Settings asrSettings;
private static final Settings asrOfflineSettings;
private static final Settings auSettings;
private static final Settings captSettings;
private static final Settings fulllinkSettings;
private static final Settings ttsSettings;
private static final Settings voiceCloneSettings;
private static final Settings voiceConvSettings;
private static final Settings dialogSettings;
static {
// Asr settings
asrSettings = new Settings();
asrSettings.register(Arrays.asList(
// common
new SettingItem(SettingItem.Type.GROUP, R.string.common_setting_title, null, R.string.no_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_app_id, SensitiveDefines.APPID, R.string.app_id_setting_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_token, SensitiveDefines.TOKEN, R.string.token_setting_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_cluster, SensitiveDefines.ASR_DEFAULT_CLUSTER, R.string.cluster_setting_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_address, SensitiveDefines.DEFAULT_ADDRESS, R.string.address_setting_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_uri, SensitiveDefines.ASR_DEFAULT_URI, R.string.uri_setting_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_get_volume, false, R.string.no_hint),
new SettingItem(SettingItem.Type.OPTIONS, R.string.config_recorder_type, new SettingItem.Options(R.array.recorder_type, 0), R.string.no_hint),
new SettingItem(SettingItem.Type.NUMBER, R.string.config_recorder_preset, 1, R.string.no_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_disable_recorder_reuse, false, R.string.no_hint),
new SettingItem(SettingItem.Type.NUMBER, R.string.config_sample_rate, 16000, R.string.no_hint),
new SettingItem(SettingItem.Type.NUMBER, R.string.config_channel, 1, R.string.no_hint),
new SettingItem(SettingItem.Type.NUMBER, R.string.config_vad_max_speech_duration, 60000, R.string.no_hint),
new SettingItem(SettingItem.Type.OPTIONS, R.string.config_resource_location, new SettingItem.Options(R.array.resource_location, 0), R.string.no_hint),
// asr
new SettingItem(SettingItem.Type.GROUP, R.string.asr_setting_title, null, R.string.no_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_asr_rec_save, false, R.string.no_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_asr_enable_ddc, false, R.string.no_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_asr_enable_itn, false, R.string.no_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_asr_enable_nlu_punctuation, true, R.string.no_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_asr_disable_end_punctuation, false, R.string.no_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_asr_keep_recording, false, R.string.no_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_asr_language, "en-US", R.string.asr_language_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_asr_hotwords, "{\"hotwords\":[{\"word\":\"过秦论\",\"scale\":2.0}]}", R.string.asr_hotwords_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_asr_correct_words, "{\"古爱玲\":\"谷爱凌\",\"古埃宁\":\"谷爱凌\",\"谷爱玲\":\"谷爱凌\",\"谷埃宁\":\"谷爱凌\"}", R.string.asr_correct_words_hint),
new SettingItem(SettingItem.Type.OPTIONS, R.string.config_asr_result_type, new SettingItem.Options(R.array.asr_result_type, 0), R.string.no_hint),
new SettingItem(SettingItem.Type.NUMBER, R.string.config_asr_max_retry_times, 0, R.string.no_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_asr_req_params, "{\"reqid\":\"20a3d61f-1a5d-4874-bf46-4bb65216fa46\",\"sequence\":\"10\",\"params\":\"abc\"}", R.string.asr_req_params_hint)
));
// Asr offline settins
asrOfflineSettings = new Settings();
asrOfflineSettings.register(Arrays.asList(
// common
new SettingItem(SettingItem.Type.GROUP, R.string.common_setting_title, null, R.string.no_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_app_id, SensitiveDefines.APPID, R.string.app_id_setting_hint),
new SettingItem(SettingItem.Type.OPTIONS, R.string.config_recorder_type, new SettingItem.Options(R.array.recorder_type, 0), R.string.no_hint),
new SettingItem(SettingItem.Type.NUMBER, R.string.config_vad_max_speech_duration, 5000, R.string.no_hint),
new SettingItem(SettingItem.Type.OPTIONS, R.string.config_authenticate_type, new SettingItem.Options(R.array.authentication_type, 1), R.string.no_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_license_name, SensitiveDefines.LICENSE_NAME, R.string.license_name_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_license_busi_id, SensitiveDefines.LICENSE_BUSI_ID, R.string.license_busi_id_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_business_key, SensitiveDefines.BUSINESS_KEY, R.string.business_key_setting_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_authenticate_secret, SensitiveDefines.SECRET, R.string.authenticate_secret_setting_hint),
// asr
new SettingItem(SettingItem.Type.GROUP, R.string.asr_setting_title, null, R.string.no_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_asr_model_name, SensitiveDefines.ASR_DEFAULT_MODEL_NAME, R.string.no_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_asr_rec_save, false, R.string.no_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_asr_enable_itn, false, R.string.no_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_asr_show_lang, false, R.string.no_hint)
));
// Au settings
auSettings = new Settings();
auSettings.register(Arrays.asList(
// common
new SettingItem(SettingItem.Type.GROUP, R.string.common_setting_title, null, R.string.no_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_app_id, SensitiveDefines.APPID, R.string.app_id_setting_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_token, SensitiveDefines.TOKEN, R.string.token_setting_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_cluster, SensitiveDefines.AU_DEFAULT_CLUSTER, R.string.cluster_setting_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_address, SensitiveDefines.DEFAULT_ADDRESS, R.string.address_setting_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_uri, SensitiveDefines.AU_DEFAULT_URI, R.string.uri_setting_hint),
new SettingItem(SettingItem.Type.OPTIONS, R.string.config_recorder_type, new SettingItem.Options(R.array.recorder_type, 0), R.string.no_hint),
new SettingItem(SettingItem.Type.NUMBER, R.string.config_recorder_preset, 1, R.string.no_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_disable_recorder_reuse, false, R.string.no_hint),
// au
new SettingItem(SettingItem.Type.GROUP, R.string.au_setting_title, null, R.string.no_hint),
new SettingItem(SettingItem.Type.OPTIONS, R.string.config_au_ability, new SettingItem.Options(R.array.au_ability, 2), R.string.no_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_au_rec_save, false, R.string.no_hint),
new SettingItem(SettingItem.Type.NUMBER, R.string.config_au_process_timeout, 3000, R.string.no_hint),
new SettingItem(SettingItem.Type.NUMBER, R.string.config_au_audio_packet_duration, 80, R.string.no_hint),
new SettingItem(SettingItem.Type.NUMBER, R.string.config_au_empty_packet_interval, 500, R.string.no_hint),
new SettingItem(SettingItem.Type.NUMBER, R.string.config_vad_max_speech_duration, 60000, R.string.no_hint),
new SettingItem(SettingItem.Type.NUMBER, R.string.config_vad_max_music_duration, 12000, R.string.no_hint),
// asr
new SettingItem(SettingItem.Type.GROUP, R.string.asr_setting_title, null, R.string.no_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_asr_enable_ddc, false, R.string.no_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_asr_enable_itn, false, R.string.no_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_asr_enable_nlu_punctuation, true, R.string.no_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_asr_show_lang, false, R.string.no_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_asr_language, "en-US", R.string.asr_language_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_asr_hotwords, "{\"hotwords\":[{\"word\":\"过秦论\",\"scale\":\"2.0\"}]}", R.string.asr_hotwords_hint),
new SettingItem(SettingItem.Type.NUMBER, R.string.config_asr_vad_start_silence_time, 0, R.string.no_hint),
new SettingItem(SettingItem.Type.NUMBER, R.string.config_asr_vad_end_silence_time, 0, R.string.no_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_asr_vad_mode, "", R.string.no_hint),
new SettingItem(SettingItem.Type.OPTIONS, R.string.config_asr_result_type, new SettingItem.Options(R.array.asr_result_type, 0), R.string.no_hint)
));
// Capt settings
captSettings = new Settings();
captSettings.register(Arrays.asList(
// common
new SettingItem(SettingItem.Type.GROUP, R.string.common_setting_title, null, R.string.no_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_cluster, "", R.string.cluster_setting_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_address, "", R.string.address_setting_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_uri, "", R.string.uri_setting_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_get_volume, false, R.string.no_hint),
new SettingItem(SettingItem.Type.OPTIONS, R.string.config_recorder_type, new SettingItem.Options(R.array.recorder_type, 0), R.string.no_hint),
new SettingItem(SettingItem.Type.NUMBER, R.string.config_recorder_preset, 1, R.string.no_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_disable_recorder_reuse, false, R.string.no_hint),
new SettingItem(SettingItem.Type.NUMBER, R.string.config_vad_max_speech_duration, 15000, R.string.no_hint),
// capt
new SettingItem(SettingItem.Type.GROUP, R.string.capt_setting_title, null, R.string.no_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_capt_rec_save, false, R.string.no_hint),
new SettingItem(SettingItem.Type.OPTIONS, R.string.config_capt_core_type, new SettingItem.Options(R.array.capt_core_type, 0), R.string.no_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_capt_offline, false, R.string.no_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_capt_streaming, false, R.string.no_hint),
new SettingItem(SettingItem.Type.NUMBER, R.string.config_capt_difficulty, 2, R.string.capt_input_difficulty_hint)
));
// Fulllink settings
fulllinkSettings = new Settings();
fulllinkSettings.register(Arrays.asList(
// common
new SettingItem(SettingItem.Type.GROUP, R.string.common_setting_title, null, R.string.no_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_cluster, "", R.string.cluster_setting_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_address, "", R.string.address_setting_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_uri, "", R.string.uri_setting_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_enable_ppe_env, false, R.string.no_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_get_volume, false, R.string.no_hint),
new SettingItem(SettingItem.Type.NUMBER, R.string.config_sample_rate, 16000, R.string.no_hint),
new SettingItem(SettingItem.Type.OPTIONS, R.string.config_recorder_type, new SettingItem.Options(R.array.recorder_type, 0), R.string.no_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_file_or_directory_name, "", R.string.no_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_disable_check_record_permission, false, R.string.no_hint),
new SettingItem(SettingItem.Type.NUMBER, R.string.config_compression_rate, 10, R.string.no_hint),
new SettingItem(SettingItem.Type.NUMBER, R.string.config_complexity, 8, R.string.no_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_no_variable_bitrate, false, R.string.no_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_disable_upload_client_vad, false, R.string.no_hint),
// fulllink
new SettingItem(SettingItem.Type.GROUP, R.string.fulllink_setting_title, null, R.string.no_hint),
new SettingItem(SettingItem.Type.OPTIONS, R.string.config_fulllink_engine_type, new SettingItem.Options(R.array.fulllink_engine_type, 0), R.string.no_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_traffic_for_test, true, R.string.no_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_rec_dump, false, R.string.no_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_kws_dump, false, R.string.no_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_scene_id, "general", R.string.no_hint),
new SettingItem(SettingItem.Type.OPTIONS, R.string.config_wakeup_mode, new SettingItem.Options(R.array.wakeup_mode, 0), R.string.no_hint),
new SettingItem(SettingItem.Type.OPTIONS, R.string.config_kws_work_mode, new SettingItem.Options(R.array.kws_work_mode, 0), R.string.no_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_only_asr, false, R.string.no_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_asr_auto_stop, true, R.string.no_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_disable_semantic_vad, false, R.string.no_hint),
new SettingItem(SettingItem.Type.NUMBER, R.string.config_vad_head_wait_time, 6000, R.string.no_hint),
new SettingItem(SettingItem.Type.NUMBER, R.string.config_max_valid_speech_duration, 10000, R.string.no_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_disable_tts, false, R.string.no_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_disable_auto_play, false, R.string.no_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_disable_signal, false, R.string.no_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_disable_send_asr_info_to_signal, false, R.string.no_hint),
new SettingItem(SettingItem.Type.NUMBER, R.string.config_kws_worker_num, 0, R.string.no_hint),
new SettingItem(SettingItem.Type.OPTIONS, R.string.config_device_type, new SettingItem.Options(R.array.device_type, 0), R.string.no_hint),
new SettingItem(SettingItem.Type.NUMBER, R.string.signal_thread_priority_title, -10, R.string.no_hint)
));
// Tts settings
ttsSettings = new Settings();
ttsSettings.register(Arrays.asList(
// common
new SettingItem(SettingItem.Type.GROUP, R.string.common_setting_title, null, R.string.no_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_app_id, SensitiveDefines.APPID, R.string.app_id_setting_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_token, SensitiveDefines.TOKEN, R.string.token_setting_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_cluster, SensitiveDefines.TTS_DEFAULT_CLUSTER, R.string.cluster_setting_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_address, SensitiveDefines.DEFAULT_ADDRESS, R.string.address_setting_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_uri, SensitiveDefines.TTS_DEFAULT_URI, R.string.uri_setting_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_voice_online, SensitiveDefines.TTS_DEFAULT_ONLINE_VOICE, R.string.voice_online_setting_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_voice_type_online, SensitiveDefines.TTS_DEFAULT_ONLINE_VOICE_TYPE, R.string.voice_type_online_setting_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_voice_offline, SensitiveDefines.TTS_DEFAULT_OFFLINE_VOICE, R.string.voice_online_setting_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_voice_type_offline, SensitiveDefines.TTS_DEFAULT_OFFLINE_VOICE_TYPE, R.string.voice_type_online_setting_hint),
new SettingItem(SettingItem.Type.OPTIONS, R.string.config_authenticate_type, new SettingItem.Options(R.array.authentication_type, 0), R.string.no_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_license_name, SensitiveDefines.LICENSE_NAME, R.string.license_name_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_license_busi_id, SensitiveDefines.LICENSE_BUSI_ID, R.string.license_busi_id_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_business_key, SensitiveDefines.BUSINESS_KEY, R.string.business_key_setting_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_authenticate_secret, SensitiveDefines.SECRET, R.string.authenticate_secret_setting_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_disable_player_reuse, false, R.string.no_hint),
new SettingItem(SettingItem.Type.NUMBER, R.string.config_audio_fadeout_duration, 0, R.string.audio_fadeout_duration_hint),
new SettingItem(SettingItem.Type.NUMBER, R.string.config_player_stream_type, 3, R.string.player_stream_type_hint),
// tts
new SettingItem(SettingItem.Type.GROUP, R.string.tts_setting_title, null, R.string.no_hint),
new SettingItem(SettingItem.Type.OPTIONS, R.string.tts_work_mode_title, new SettingItem.Options(R.array.tts_work_mode, 0), R.string.no_hint),
new SettingItem(SettingItem.Type.OPTIONS, R.string.tts_text_type_title, new SettingItem.Options(R.array.tts_text_type, 0), R.string.no_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_tts_enable_resume_from_breakpoint, true, R.string.no_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_sdk_player, true, R.string.no_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_demo_player, false, R.string.no_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_tts_dump, false, R.string.no_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_tts_data_callback, false, R.string.no_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_tts_enable_word_level_progress_update, true, R.string.no_hint),
new SettingItem(SettingItem.Type.NUMBER, R.string.config_tts_silence_duration, 0, R.string.tts_silence_duration_hint),
new SettingItem(SettingItem.Type.NUMBER, R.string.config_tts_speak_speed, 1.0, R.string.tts_speak_speed_hint),
new SettingItem(SettingItem.Type.NUMBER, R.string.config_tts_audio_volume, 1.0, R.string.tts_audio_volume_hint),
new SettingItem(SettingItem.Type.NUMBER, R.string.config_tts_audio_pitch, 1.0, R.string.tts_audio_pitch_hint),
new SettingItem(SettingItem.Type.NUMBER, R.string.config_tts_sample_rate, 24000, R.string.tts_sample_rate_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.enable_cache, false, R.string.no_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_tts_with_intent, false, R.string.no_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_tts_language_online, SensitiveDefines.TTS_DEFAULT_ONLINE_LANGUAGE, R.string.tts_language_online_setting_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_tts_emotion, "", R.string.tts_emotion_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_tts_use_voiceclone, false, R.string.no_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_backend_cluster, SensitiveDefines.TTS_DEFAULT_BACKEND_CLUSTER, R.string.backend_cluster_setting_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_tts_request_id, "", R.string.no_hint),
new SettingItem(SettingItem.Type.OPTIONS, R.string.tts_offline_resource_format_title, new SettingItem.Options(R.array.tts_offline_resource_formats, 0), R.string.no_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_tts_language_offline, SensitiveDefines.TTS_DEFAULT_OFFLINE_LANGUAGE, R.string.tts_language_offline_setting_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_tts_model_name, "aispeech_tts", R.string.tts_model_name_setting_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.tts_limit_cpu_usage, false, R.string.no_hint)
));
// VoiceClone settings
voiceCloneSettings = new Settings();
voiceCloneSettings.register(Arrays.asList(
// common
new SettingItem(SettingItem.Type.GROUP, R.string.common_setting_title, null, R.string.no_hint),
new SettingItem(SettingItem.Type.NUMBER, R.string.config_sample_rate, 44100, R.string.no_hint),
new SettingItem(SettingItem.Type.NUMBER, R.string.config_recorder_preset, 1, R.string.no_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_app_id, SensitiveDefines.APPID, R.string.no_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_token, SensitiveDefines.TOKEN, R.string.no_hint),
new SettingItem(SettingItem.Type.OPTIONS, R.string.config_recorder_type, new SettingItem.Options(R.array.recorder_type, 0), R.string.no_hint),
// voiceclone
new SettingItem(SettingItem.Type.GROUP, R.string.voiceclone_setting_title, null, R.string.no_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_voiceclone_address, SensitiveDefines.DEFAULT_HTTP_ADDRESS, R.string.no_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_voiceclone_stream_address, SensitiveDefines.DEFAULT_ADDRESS, R.string.no_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_voiceclone_uid, SensitiveDefines.UID, R.string.voiceclone_uid_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_voiceclone_query_uids, SensitiveDefines.VOICECLONE_DEFAULT_UIDS, R.string.voiceclone_query_uids_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_voiceclone_voice_type, "", R.string.voiceclone_voice_type_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_voiceclone_gender, false, R.string.no_hint),
new SettingItem(SettingItem.Type.NUMBER, R.string.config_voiceclone_task_id, SensitiveDefines.VOICECLONE_DEFAULT_TASK_ID, R.string.no_hint)
));
// VoiceConv settings
voiceConvSettings = new Settings();
voiceConvSettings.register(Arrays.asList(
// common
new SettingItem(SettingItem.Type.GROUP, R.string.common_setting_title, null, R.string.no_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_voice, SensitiveDefines.VOICECONV_DEFAULT_VOICE, R.string.voice_online_setting_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_voice_type, SensitiveDefines.VOICECONV_DEFAULT_VOICE_TYPE, R.string.voice_type_online_setting_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_get_volume, false, R.string.no_hint),
new SettingItem(SettingItem.Type.OPTIONS, R.string.config_recorder_type, new SettingItem.Options(R.array.recorder_type, 0), R.string.no_hint),
new SettingItem(SettingItem.Type.NUMBER, R.string.config_recorder_preset, 1, R.string.no_hint),
// voiceconv
new SettingItem(SettingItem.Type.GROUP, R.string.voiceconv_setting_title, null, R.string.no_hint),
new SettingItem(SettingItem.Type.NUMBER, R.string.config_voiceconv_result_sample_rate, 24000, R.string.no_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_voiceconv_enable_record_dump, true, R.string.no_hint),
new SettingItem(SettingItem.Type.BOOL, R.string.config_voiceconv_enable_result_dump, true, R.string.no_hint),
new SettingItem(SettingItem.Type.NUMBER, R.string.config_voiceconv_request_interval, 200, R.string.no_hint)
));
// Dialog settings
dialogSettings = new Settings();
dialogSettings.register(Arrays.asList(
// common
new SettingItem(SettingItem.Type.GROUP, R.string.common_setting_title, null, R.string.no_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_app_id, SensitiveDefines.APPID, R.string.app_id_setting_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_token, SensitiveDefines.TOKEN, R.string.token_setting_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_address, SensitiveDefines.DEFAULT_ADDRESS, R.string.address_setting_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_uri, SensitiveDefines.DIALOG_DEFAULT_URI, R.string.uri_setting_hint),
// dialog
new SettingItem(SettingItem.Type.GROUP, R.string.dialog_setting_title, null, R.string.no_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_dialog_id, SensitiveDefines.DIALOG_DEFAULT_ID, R.string.dialog_id_setting_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_dialog_role, SensitiveDefines.DIALOG_DEFAULT_ROLE, R.string.dialog_role_setting_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_dialog_clothes_type, SensitiveDefines.DIALOG_DEFAULT_CLOTHES_TYPE, R.string.dialog_clothes_type_setting_hint),
new SettingItem(SettingItem.Type.STRING, R.string.config_dialog_tta_voice_type, SensitiveDefines.DIALOG_DEFAULT_TTA_VOICE_TYPE, R.string.dialog_tta_voice_type_setting_hint)
));
}
public static Settings getSettings(String viewId) {
switch (viewId) {
case SpeechDemoDefines.ASR_VIEW:
return asrSettings;
case SpeechDemoDefines.ASR_OFFLINE_VIEW:
return asrOfflineSettings;
case SpeechDemoDefines.CAPT_VIEW:
return captSettings;
case SpeechDemoDefines.FULLLINK_VIEW:
return fulllinkSettings;
case SpeechDemoDefines.TTS_VIEW:
return ttsSettings;
case SpeechDemoDefines.VOICECLONE_VIEW:
return voiceCloneSettings;
case SpeechDemoDefines.VOICECONV_VIEW:
return voiceConvSettings;
case SpeechDemoDefines.DIALOG_VIEW:
return dialogSettings;
case SpeechDemoDefines.AU_VIEW:
return auSettings;
default:
return new Settings();
}
}
@SuppressLint("ResourceType")
public SettingsActivity() {
Log.i(SpeechDemoDefines.TAG, "Settings onCreate");
}
}
package com.example.aivoice_plugin;
public class SpeechDemoDefines {
public static final String TAG = "SpeechDemo";
public static final String ASR_VIEW = "ASR";
public static final String ASR_OFFLINE_VIEW = "ASR_OFFLINE";
public static final String CAPT_VIEW = "CAPT";
public static final String FULLLINK_VIEW = "FULLLINK";
public static final String TTS_VIEW = "TTS";
public static final String VOICECLONE_VIEW = "VOICECLONE";
public static final String VOICECONV_VIEW = "VOICECONV";
public static final String DIALOG_VIEW = "DIALOG";
public static final String VAD_VIEW = "VAD";
public static final String AFP_VIEW = "AFP";
public static final String AU_VIEW = "AU";
}
package com.example.aivoice_plugin;
import android.util.Log;
import com.bytedance.speech.speechengine.SpeechEngine;
import java.io.InputStream;
public class SpeechFileRecorder {
private static final int SAMPLE_RATE = 8000;
private static final int BYTES_PER_SAMPLE = 2;
private static final float BUFFER_SIZE_IN_SECONDS = 0.08f;
private Thread mWorker = null;
private SpeechEngine mSpeechEngine = null;
private long mSpeechEngineHandler = -1;
private String mPath;
private String mFilename;
public int GetStreamSampleRate() {
return SAMPLE_RATE;
}
public void SetSpeechEngine(SpeechEngine speechEngine, long speechEngineHandler) {
mSpeechEngine = speechEngine;
mSpeechEngineHandler = speechEngineHandler;
}
public boolean Start(String path, String filename) {
mPath = path;
mFilename = filename;
if (null != mWorker) {
if (mWorker.isAlive()) {
Log.w(SpeechDemoDefines.TAG, "Already start!");
return true;
}
mWorker = null;
}
mWorker = new RecorderThread();
mWorker.start();
Log.i(SpeechDemoDefines.TAG, "Stream Recorder Started.");
return true;
}
public void Stop() {
if (null == mWorker) {
Log.w(SpeechDemoDefines.TAG, "Not start yet!");
return;
}
mWorker.interrupt();
try {
mWorker.join();
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
}
mWorker = null;
Log.i(SpeechDemoDefines.TAG, "Stream Recorder Stopped.");
}
private final class RecorderThread extends Thread {
@Override
public void run() {
InputStream in = SpeechFileUtils.OpenInputFile(mPath, mFilename);
int bufferSize = Math.round(SAMPLE_RATE * BUFFER_SIZE_IN_SECONDS * BYTES_PER_SAMPLE);
byte[] buffer = new byte[bufferSize];
int nread;
int total = 0;
while (!interrupted()) {
nread = SpeechFileUtils.ReadData(in, buffer, bufferSize);
if (nread > 0) {
total += nread;
int ret = mSpeechEngine.feedAudio(mSpeechEngineHandler, buffer, nread);
if (ret != 0) {
Log.e(SpeechDemoDefines.TAG, "Feed audio failed.");
}
} else if (nread < 0) {
Log.e(SpeechDemoDefines.TAG, "Recorder error.");
break;
}
}
Log.i(SpeechDemoDefines.TAG, "total: " + total);
SpeechFileUtils.CloseInputFile(in);
}
}
}
package com.example.aivoice_plugin;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class SpeechFileUtils {
public static OutputStream OpenOutputFile(String path, String filename) {
try {
File outFile = new File(path, filename);
return new FileOutputStream(outFile);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
public static boolean WriteData(OutputStream out, byte[] data, int len) {
if (out == null || len == 0) {
return false;
}
try {
out.write(data, 0, len);
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
public static void CloseOutputFile(OutputStream out) {
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
static InputStream OpenInputFile(String path, String filename) {
try {
File inFile = new File(path, filename);
return new FileInputStream(inFile);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
static int ReadData(InputStream in, byte[] data, int len) {
if (in == null) {
return 0;
}
int ret;
try {
ret = in.read(data, 0, len);
} catch (IOException e) {
e.printStackTrace();
ret = -1;
}
return ret;
}
static void CloseInputFile(InputStream in) {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package com.example.aivoice_plugin;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.util.Log;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SpeechStreamPlayer {
private static int mSampleRate = 24000;
private BlockingQueue mAudioBuffer = new LinkedBlockingQueue<byte[]>();
private AudioTrack mPlayer = null;
private Thread mWorker = null;
// Player Status
private boolean mIsPlaying = false;
private boolean mIsPaused = false;
private Lock mLock = new ReentrantLock();
private Condition mWaitStop = mLock.newCondition();
private Condition mWaitResume = mLock.newCondition();
private AtomicBoolean mAudioEnd = new AtomicBoolean(true);
private boolean InitStreamPlayer() {
final int minBufferSize = AudioTrack.getMinBufferSize(mSampleRate,
AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT);
mPlayer = new AudioTrack(AudioManager.STREAM_MUSIC,
mSampleRate,
AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT,
minBufferSize,
AudioTrack.MODE_STREAM);
if (mPlayer.getState() != AudioTrack.STATE_INITIALIZED) {
Log.e(SpeechDemoDefines.TAG, "Failed to initialize stream player.");
mPlayer.release();
mPlayer = null;
return false;
}
return true;
}
public void SetPlayerSampleRate(int sampleRate) {
mSampleRate = sampleRate;
}
public void WaitPlayerStop() {
mLock.lock();
try {
Log.d(SpeechDemoDefines.TAG, "Demo player is_playing: " + mIsPlaying);
while (mIsPlaying) {
mWaitStop.await();
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
mLock.unlock();
}
}
public boolean Start() {
if (!InitStreamPlayer()) {
return false;
}
mIsPaused = false;
if (null != mWorker) {
if (mWorker.isAlive()) {
Log.w(SpeechDemoDefines.TAG, "Already start!");
return true;
}
mWorker = null;
}
mWorker = new PlayerThread();
mAudioEnd.set(false);
mLock.lock();
try {
mIsPlaying = true;
} finally {
mLock.unlock();
}
mWorker.start();
Log.i(SpeechDemoDefines.TAG, "Stream Player Started.");
return true;
}
public void Stop() {
if (null == mWorker) {
Log.w(SpeechDemoDefines.TAG, "Not start yet!");
return;
}
mLock.lock();
try {
mIsPaused = false;
mWaitResume.signal();
mIsPlaying = false;
mWaitStop.signal();
} finally {
mLock.unlock();
}
mWorker.interrupt();
try {
mWorker.join();
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
}
mAudioBuffer.clear();
mWorker = null;
Log.i(SpeechDemoDefines.TAG, "Stream Player Stopped.");
}
public void Feed(byte[] audio, boolean isFinal) {
if (mPlayer == null || mAudioBuffer == null) {
return;
}
try {
final int singleBufferMaxSize = mSampleRate / 1000 * 2 * 40; // 40ms
int start = 0;
while (audio.length > start) {
int end = Math.min(start + singleBufferMaxSize, audio.length);
mAudioBuffer.put(Arrays.copyOfRange(audio, start, end));
start += (end - start);
}
} catch (InterruptedException e) {
Log.e(SpeechDemoDefines.TAG, "Put audio to block queue failed.");
e.printStackTrace();
}
mAudioEnd.set(isFinal);
}
public void Pause() {
if (mPlayer == null) {
return;
}
Log.i(SpeechDemoDefines.TAG, "Pause Stream Player.");
mLock.lock();
try {
if (!mIsPaused) {
mPlayer.pause();
mIsPaused = true;
}
} finally {
mLock.unlock();
}
}
public void Resume() {
if (mPlayer == null) {
return;
}
mLock.lock();
try {
if (mIsPaused) {
mIsPaused = false;
mPlayer.play();
mWaitResume.signal();
}
} finally {
mLock.unlock();
}
}
private final class PlayerThread extends Thread {
@Override
public void run() {
if (mPlayer == null) {
return;
}
mPlayer.play();
while (!interrupted()) {
try {
if (mAudioEnd.get()) {
LinkedList<byte[]> audioBlocks = new LinkedList<>();
mAudioBuffer.drainTo(audioBlocks);
for (int i = 0; i < audioBlocks.size(); ++i) {
byte[] audio = audioBlocks.get(i);
writeAudio(audio, i < audioBlocks.size() - 1);
if (interrupted()) {
break;
}
}
break;
} else {
writeAudio((byte[]) (mAudioBuffer.take()), false);
}
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
}
mPlayer.stop();
mLock.lock();
try {
mIsPlaying = false;
mWaitStop.signalAll();
} finally {
mLock.unlock();
}
}
}
private void writeAudio(byte[] audio, boolean isFinal) {
if (audio.length <= 0) {
Log.w(SpeechDemoDefines.TAG, "Audio block length is invalid.");
}
int playedBytes = mPlayer.write(audio, 0, audio.length);
Log.d(SpeechDemoDefines.TAG, "Audio block size: " + audio.length + ", played size: " + playedBytes);
if (playedBytes < audio.length) {
mLock.lock();
try {
while (mIsPaused) {
mWaitResume.await();
}
mPlayer.write(audio, playedBytes, audio.length - playedBytes);
} catch (InterruptedException e) {
e.printStackTrace();
return;
} finally {
mLock.unlock();
}
}
}
}
package com.example.aivoice_plugin;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.util.Log;
import com.bytedance.speech.speechengine.SpeechEngine;
import java.io.ByteArrayOutputStream;
public class SpeechStreamRecorder {
private static final int SAMPLE_RATE = 44100;
private static final int CHANNEL_NUM = 2;
private static final int BYTES_PER_SAMPLE = 2;
private static final float BUFFER_SIZE_IN_SECONDS = 0.08f;
private static final int DEFAULT_PACKAGE_DURATION = 100;
private AudioRecord mRecorder;
private Thread mWorker = null;
private int mBufferSize = 0;
private int mPackageDuration = DEFAULT_PACKAGE_DURATION;
private String mViewId = "";
private SpeechEngine mSpeechEngine = null;
public int GetStreamSampleRate() {
return SAMPLE_RATE;
}
public int GetStreamChannel() {
return CHANNEL_NUM;
}
public void SetSpeechEngine(String viewId, SpeechEngine speechEngine) {
mViewId = viewId;
mSpeechEngine = speechEngine;
}
public boolean Start() {
if (!InitStreamRecorder()) {
return false;
}
if (null != mWorker) {
if (mWorker.isAlive()) {
Log.w(SpeechDemoDefines.TAG, "Already start!");
return true;
}
mWorker = null;
}
mPackageDuration = SettingsActivity.getSettings(mViewId).getInt(R.string.config_stream_package_duration, DEFAULT_PACKAGE_DURATION);
mWorker = new RecorderThread();
mWorker.start();
Log.i(SpeechDemoDefines.TAG, "Stream Recorder Started.");
return true;
}
public void Stop() {
if (null == mWorker) {
Log.w(SpeechDemoDefines.TAG, "Not start yet!");
return;
}
mWorker.interrupt();
try {
mWorker.join();
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
}
mWorker = null;
Log.i(SpeechDemoDefines.TAG, "Stream Recorder Stopped.");
}
private final class RecorderThread extends Thread {
@Override
public void run() {
if (mRecorder == null) {
return;
}
mRecorder.startRecording();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int nread = 0;
long totalPackageSize = (long)SAMPLE_RATE * CHANNEL_NUM * BYTES_PER_SAMPLE * mPackageDuration / 1000;
while (!isInterrupted() && nread >= 0) {
byte[] buffer = new byte[mBufferSize];
bos.reset();
long curPackageSize = 0;
while (!isInterrupted() && nread >= 0 && curPackageSize < totalPackageSize) {
nread = mRecorder.read(buffer, 0, mBufferSize);
if (nread > 0) {
Log.i(SpeechDemoDefines.TAG, "Current package size: " + curPackageSize + ", total package size: " + totalPackageSize);
curPackageSize += nread;
bos.write(buffer, 0, nread);
} else if (nread < 0) {
Log.e(SpeechDemoDefines.TAG, "Recorder error.");
}
}
if (!isInterrupted()) {
buffer = bos.toByteArray();
int ret = mSpeechEngine.feedAudio(buffer, buffer.length);
if (ret != 0) {
Log.e(SpeechDemoDefines.TAG, "Feed audio failed.");
break;
}
}
}
mRecorder.stop();
}
}
private boolean InitStreamRecorder() {
if (mRecorder != null) {
return true;
}
mBufferSize = Math.round(SAMPLE_RATE * BUFFER_SIZE_IN_SECONDS * BYTES_PER_SAMPLE * CHANNEL_NUM);
int minBufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE,
CHANNEL_NUM == 1 ? AudioFormat.CHANNEL_IN_MONO : AudioFormat.CHANNEL_IN_STEREO,
AudioFormat.ENCODING_PCM_16BIT);
minBufferSize = Math.max(minBufferSize, mBufferSize);
mRecorder = new AudioRecord(
MediaRecorder.AudioSource.MIC, SAMPLE_RATE,
CHANNEL_NUM == 1 ? AudioFormat.CHANNEL_IN_MONO : AudioFormat.CHANNEL_IN_STEREO,
AudioFormat.ENCODING_PCM_16BIT, minBufferSize * 10);
if (mRecorder.getState() == AudioRecord.STATE_UNINITIALIZED) {
Log.e(SpeechDemoDefines.TAG, "Failed to initialize stream recorder.");
mRecorder.release();
mRecorder = null;
return false;
}
return true;
}
}
...@@ -46,7 +46,7 @@ android { ...@@ -46,7 +46,7 @@ android {
// You can update the following values to match your application needs. // You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdkVersion flutter.minSdkVersion minSdkVersion flutter.minSdkVersion
targetSdkVersion flutter.targetSdkVersion targetSdkVersion 29
versionCode flutterVersionCode.toInteger() versionCode flutterVersionCode.toInteger()
versionName flutterVersionName versionName flutterVersionName
multiDexEnabled true multiDexEnabled true
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论