diff --git a/app/build.gradle b/app/build.gradle index 101b57e..e55946d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -94,7 +94,6 @@ dependencies { implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation files('libs\\ymeeting.jar')//亿晟 implementation project(path: ':playerview') - implementation files('libs\\Msc.jar') implementation files('libs\\zckjAPI-2.2.jar')//卓策 implementation files('libs\\jna-min.jar') implementation files('libs\\toolbox_kit.jar')//灰度主板 罗湖寻车机 diff --git a/app/libs/SparkChain.aar b/app/libs/SparkChain.aar new file mode 100644 index 0000000..51937e1 Binary files /dev/null and b/app/libs/SparkChain.aar differ diff --git a/app/src/main/java/qianmu/container/activity/H5/WebViewActivity.java b/app/src/main/java/qianmu/container/activity/H5/WebViewActivity.java index 87b9cff..28a8205 100644 --- a/app/src/main/java/qianmu/container/activity/H5/WebViewActivity.java +++ b/app/src/main/java/qianmu/container/activity/H5/WebViewActivity.java @@ -38,6 +38,8 @@ import com.aispeech.export.listeners.AIASRListener; import com.alibaba.android.arouter.facade.annotation.Route; import com.alibaba.android.arouter.launcher.ARouter; +import com.iflytek.sparkchain.core.asr.ASR; +import com.iflytek.sparkchain.core.asr.AsrCallbacks; import com.shockman.sm.vendor.IHttp; import com.shockman.sm.vendor.SmUtils; import com.shockman.sm.vendor.TargetVo; @@ -75,6 +77,7 @@ import qianmu.container.util.AppUtil; import qianmu.container.util.CalendarUtils; import qianmu.container.util.DeviceUtil; import qianmu.container.util.FileUtil; +import qianmu.container.util.KDXFUtil; import qianmu.container.util.KeyboardUtil; import qianmu.container.util.LoggerUtil; import qianmu.container.util.SignWayUtil; @@ -103,6 +106,13 @@ public class WebViewActivity extends BaseActivity { private TTSUtil ttsUtil; private String oldString=""; + // 语音听写对象 + private ASR mAsr; + private boolean isrun = false; // 是否正在听写 + private KDXFUtil kxdfttsUtil; + + + Handler handler = new Handler() { @SuppressLint("HandlerLeak") @Override @@ -169,6 +179,10 @@ public class WebViewActivity extends BaseActivity { initASR(); ttsUtil = new TTSUtil(); ttsUtil.initTts(); + }else if("kdxf".equals(Constant.TTSHome)){ + initKXDFASR(); + kxdfttsUtil = new KDXFUtil(); + kxdfttsUtil.initTts(); } } @@ -347,7 +361,7 @@ public class WebViewActivity extends BaseActivity { binding.web.addJavascriptInterface(new AndroidtoJs(), "android"); } - + int count = 0; public class AndroidtoJs extends Object { // 回到屏保页面 @@ -387,6 +401,9 @@ public class WebViewActivity extends BaseActivity { // h5调用android:开始识别语音 @JavascriptInterface public void startRecord() { + if(isrun){ + return; + } LoggerUtil.e(TAG,"JS调用了Android的方法:startRecord()"); if("sbc".equals(Constant.TTSHome)){ ttsUtil.stopTTs(); @@ -396,16 +413,21 @@ public class WebViewActivity extends BaseActivity { initASR(); } mEngine.start(aiCloudASRIntent); + }else if("kdxf".equals(Constant.TTSHome)){ + if(mAsr == null){ + LoggerUtil.d(TAG, "未初始化"); + initASR(); + } + count++; } + isrun = true; } // h5调用android:结束识别语音 @JavascriptInterface public void stopRecord() { LoggerUtil.e(TAG,"JS调用了Android的方法:stopVoice()"); - if("sbc".equals(Constant.TTSHome)){ - stopAsr(); - } + stopAsr(); } // h5调用android:传入要播报语音 @@ -414,6 +436,8 @@ public class WebViewActivity extends BaseActivity { LoggerUtil.e(TAG,"JS调用了Android的方法:startTTS()"); if("sbc".equals(Constant.TTSHome)){ ttsUtil.startTTs(content.toLowerCase(Locale.ENGLISH)); + }else if("kdxf".equals(Constant.TTSHome)){ + kxdfttsUtil.startTTs(content); } } // h5调用android:停止语音播报播报语音 @@ -422,6 +446,8 @@ public class WebViewActivity extends BaseActivity { LoggerUtil.e(TAG,"JS调用了Android的方法:stopTTS()"); if("sbc".equals(Constant.TTSHome)){ ttsUtil.stopTTs(); + }else if("kdxf".equals(Constant.TTSHome)){ + kxdfttsUtil.stopTTs(); } } @@ -714,5 +740,64 @@ public class WebViewActivity extends BaseActivity { mEngine.cancel(); mEngine.stop(); } + if(isrun){ + if(mAsr!=null){ + mAsr.stopListener(false); + } + isrun = false; + } + } + + /////////////// + private void initKXDFASR(){ + if(mAsr == null){ + mAsr = new ASR(); + mAsr.registerCallbacks(mAsrCallbacks); + mAsr.language("zh_cn");//语种,zh_cn:中文,en_us:英文。其他语种参见集成文档 + mAsr.domain("iat");//应用领域,iat:日常用语。其他领域参见集成文档 + mAsr.accent("mandarin");//方言,mandarin:普通话。方言仅当language为中文时才会生效。其他方言参见集成文档。 + mAsr.vinfo(true);//返回子句结果对应的起始和结束的端点帧偏移值。 + mAsr.dwa("wpgs");//动态修正 + } } + AsrCallbacks mAsrCallbacks = new AsrCallbacks() { + @Override + public void onResult(ASR.ASRResult asrResult, Object o) { + //以下信息需要开发者根据自身需求,如无必要,可不需要解析执行。 + int status = asrResult.getStatus(); //结果数据状态,0:识别的第一块结果,1:识别中间结果,2:识别最后一块结果 + String result = asrResult.getBestMatchText(); //识别结果 + if(status == 0){ + voiceContent(result); + }else if(status == 2){ + voiceContent(result); + stopAsr(); + }else{ + voiceContent(result); + } + } + @Override + public void onError(ASR.ASRError asrError, Object o) { + // 在非主线程中需要调用 WebView 方法时 + runOnUiThread(new Runnable() { + @Override + public void run() { + binding.web.evaluateJavascript("javascript: window.audioStop();", null); + } + }); + stopAsr(); + } + @Override + public void onBeginOfSpeech() { + } + @Override + public void onEndOfSpeech() { + Log.d(TAG, "结束说话"); + runOnUiThread(new Runnable() { + @Override + public void run() { + binding.web.evaluateJavascript("javascript: window.audioStop();", null); + } + }); + } + }; } diff --git a/app/src/main/java/qianmu/container/app/MyApplication.java b/app/src/main/java/qianmu/container/app/MyApplication.java index 307cc31..c4f1330 100644 --- a/app/src/main/java/qianmu/container/app/MyApplication.java +++ b/app/src/main/java/qianmu/container/app/MyApplication.java @@ -12,6 +12,8 @@ import android.os.PowerManager; import com.alibaba.android.arouter.BuildConfig; import com.alibaba.android.arouter.launcher.ARouter; +import com.iflytek.sparkchain.core.SparkChain; +import com.iflytek.sparkchain.core.SparkChainConfig; import com.jakewharton.processphoenix.ProcessPhoenix; import com.lztek.toolkit.Lztek; import com.lzy.okgo.OkGo; @@ -122,6 +124,20 @@ public class MyApplication extends Application { }); if("sbc".equals(Constant.TTSHome)){ // 思必驰语音 initSbcSDK(); + }else if("kdxf".equals(Constant.TTSHome)){ + // 初始化SDK,Appid等信息在清单中配置 + SparkChainConfig sparkChainConfig = SparkChainConfig.builder(); + sparkChainConfig.appID("5c2055f8") + .apiKey("2d1e9e5604d66089bda42ff4797201c1") + .apiSecret("2bc7168506a38cf1a7a52fb3ba63d873")//应用申请的appid三元组 + .logLevel(666); + int ret = SparkChain.getInst().init(getApplicationContext(),sparkChainConfig); + String result; + if(ret == 0){ + result = "SDK初始化成功,请选择相应的功能点击体验。"; + }else{ + result = "SDK初始化失败,错误码:" + ret; + } } } // 思必驰授权 diff --git a/app/src/main/java/qianmu/container/util/KDXFUtil.java b/app/src/main/java/qianmu/container/util/KDXFUtil.java new file mode 100644 index 0000000..b988b56 --- /dev/null +++ b/app/src/main/java/qianmu/container/util/KDXFUtil.java @@ -0,0 +1,183 @@ +package qianmu.container.util; + +import android.media.AudioFormat; +import android.media.AudioManager; +import android.media.AudioTrack; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.util.Log; + +import androidx.annotation.NonNull; + +import com.iflytek.sparkchain.core.tts.OnlineTTS; +import com.iflytek.sparkchain.core.tts.TTS; +import com.iflytek.sparkchain.core.tts.TTSCallbacks; + + +import org.greenrobot.eventbus.EventBus; + +import qianmu.container.app.Constant; +import qianmu.container.entity.MessageEvent; + +/** + * 科大讯飞语音 + */ +public class KDXFUtil { + private static String TAG = "TTS: "; + // 语音合成对象 + private OnlineTTS mOnlineTTS; + private int sampleRate = 16000; + + TTSCallbacks mTTSCallback = new TTSCallbacks() { + + @Override + public void onResult(TTS.TTSResult result, Object o) { + //解析获取的交互结果,示例展示所有结果获取,开发者可根据自身需要,选择获取。 + byte[] audio = result.getData();//音频数据 + int status = result.getStatus();//数据状态 + + Bundle bundle = new Bundle(); + bundle.putByteArray("audio", audio); + Message msg = mAudioPlayHandler.obtainMessage(); + msg.what = AUDIOPLAYER_WRITE; + msg.obj = bundle; + mAudioPlayHandler.sendMessage(msg); + if(status == 2){ + //音频合成回调结束状态,注意,此状态不是播报完成状态 + mAudioPlayHandler.sendEmptyMessage(AUDIOPLAYER_END); + } + } + + @Override + public void onError(TTS.TTSError ttsError, Object o) { + int errCode = ttsError.getCode();//错误码 + String errMsg = ttsError.getErrMsg();//错误信息 + LoggerUtil.d(TAG, "onError:errCode:" + errCode+ ",errMsg:" + errMsg); + //如果此时已经播报,则停止播报 + EventBus.getDefault().post(new MessageEvent(Constant.VOID_STOP)); + stopTTs(); + } + }; + + public void initTts(){ + mAudioPlayThread.start(); + // 初始化合成对象 发音人 + mOnlineTTS = new OnlineTTS("x4_xiaoyan"); //xiaoyan + } + + //开始听写 + public void startTTs(String texts){ + if (null == mOnlineTTS) { + LoggerUtil.d(TAG, "未初始化"); + initTts(); + } + if(audioTrack == null){ + mAudioPlayHandler.sendEmptyMessage(AUDIOPLAYER_INIT); + }else{ + mAudioPlayHandler.sendEmptyMessage(AUDIOPLAYER_START); + } + setParam(); + // 合成并播放 + int ret = mOnlineTTS.aRun(texts); + if(ret!=0){ + LoggerUtil.d(TAG, "语音合成失败" ); + } + } + + //停止听写 + public void stopTTs(){ + LoggerUtil.d(TAG, "手动暂停播放"); + if (null == mOnlineTTS) { + LoggerUtil.d(TAG, "未初始化"); + return; + } + mAudioPlayHandler.removeCallbacksAndMessages(null); + mAudioPlayHandler.sendEmptyMessage(AUDIOPLAYER_END); + mOnlineTTS.stop(); + } + + /** + * 参数设置 + * + * @return + */ + private void setParam() { + mOnlineTTS.aue("raw"); + mOnlineTTS.auf("audio/L16;rate="+sampleRate); // 8K 或 16K + mOnlineTTS.speed(60);//语速:0对应默认语速的1/2,100对应默认语速的2倍。最⼩值:0, 最⼤值:100 + mOnlineTTS.pitch(50);//语调:0对应默认语速的1/2,100对应默认语速的2倍。最⼩值:0, 最⼤值:100 + mOnlineTTS.volume(80);//音量:0是静音,1对应默认音量1/2,100对应默认音量的2倍。最⼩值:0, 最⼤值:100 + mOnlineTTS.bgs(0); //合成音频的背景音 0:无背景音(默认值) 1:有背景音 + mOnlineTTS.tte("UTF8"); + mOnlineTTS.registerCallbacks(mTTSCallback); + } + + /** + * 播放器,用于播报合成的音频。 + * 注意:当前Demo中的播放器仅实现了播放PCM格式的音频,如果客户合成的是其他格式的音频,需自行实现播放功能。 + */ + private static final int AUDIOPLAYER_INIT = 0x0000; + private static final int AUDIOPLAYER_START = 0x0001; + private static final int AUDIOPLAYER_WRITE = 0x0002; + private static final int AUDIOPLAYER_END = 0x0003; + private static final int CHANNEL_CONFIG = AudioFormat.CHANNEL_OUT_MONO; // 单声道输出 + private static final int AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT; // PCM 16位编码 + private AudioTrack audioTrack; + private Handler mAudioPlayHandler; + private boolean isPlaying = false; + int count = 0; + private Thread mAudioPlayThread = new Thread(new Runnable() { + @Override + public void run() { + Looper.prepare(); + mAudioPlayHandler = new Handler(Looper.myLooper()){ + @Override + public void handleMessage(@NonNull Message msg) { + super.handleMessage(msg); + switch(msg.what){ + case AUDIOPLAYER_INIT: + Log.d(TAG,"audioInit"); + int minBufferSize = AudioTrack.getMinBufferSize(sampleRate, CHANNEL_CONFIG, AUDIO_FORMAT); + audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, CHANNEL_CONFIG, AUDIO_FORMAT, minBufferSize, AudioTrack.MODE_STREAM); + mAudioPlayHandler.sendEmptyMessage(AUDIOPLAYER_START); + break; + case AUDIOPLAYER_START: + Log.d(TAG,"audioStart"); + if(audioTrack!=null) { + isPlaying = true; + audioTrack.play(); + } + break; + case AUDIOPLAYER_WRITE: + count ++; + if(count%5 == 0){ + Log.d(TAG,"audioWrite"); + count = 0; + } + Bundle bundle = (Bundle) msg.obj; + byte[] audioData = bundle.getByteArray("audio"); + if(audioTrack!=null&&audioData.length>0){ + audioTrack.write(audioData,0,audioData.length); + } + break; + case AUDIOPLAYER_END: + Log.d(TAG,"audioEnd"); + EventBus.getDefault().post(new MessageEvent(Constant.VOID_STOP)); + if(audioTrack!=null) { + audioTrack.stop(); + audioTrack.release(); + audioTrack = null; + isPlaying = false; + } + break; + + } + } + }; + Looper.loop(); + } + }); + +}