| @@ -1,6 +1,10 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <project version="4"> | |||
| <component name="ProjectRootManager" version="2" languageLevel="JDK_17_PREVIEW" project-jdk-name="jbr-17" project-jdk-type="JavaSDK"> | |||
| <component name="ASMIdeaPluginConfiguration"> | |||
| <asm skipDebug="true" skipFrames="true" skipCode="false" expandFrames="false" /> | |||
| <groovy codeStyle="LEGACY" /> | |||
| </component> | |||
| <component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK"> | |||
| <output url="file://$PROJECT_DIR$/build/classes" /> | |||
| </component> | |||
| <component name="ProjectType"> | |||
| @@ -23,6 +23,7 @@ android { | |||
| } | |||
| multiDexEnabled true | |||
| vectorDrawables.useSupportLibrary = true | |||
| flavorDimensions "channel" | |||
| } | |||
| signingConfigs { | |||
| @@ -54,6 +55,16 @@ android { | |||
| } | |||
| } | |||
| productFlavors { | |||
| ltLandscape { | |||
| } | |||
| portraitH5 { | |||
| } | |||
| } | |||
| externalNativeBuild { | |||
| ndkBuild { | |||
| path file('src/main/jni/Android.mk') | |||
| @@ -0,0 +1,37 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" | |||
| xmlns:tools="http://schemas.android.com/tools" | |||
| package="com.aispeech.nativedemo"> | |||
| <application | |||
| android:name=".DuiApplication" | |||
| android:allowBackup="true" | |||
| android:icon="@drawable/lt_logo" | |||
| android:label="@string/app_name" | |||
| android:supportsRtl="true" | |||
| android:theme="@style/AppTheme" | |||
| android:usesCleartextTraffic="true" | |||
| android:hardwareAccelerated="true" | |||
| android:requestLegacyExternalStorage="true" | |||
| > | |||
| <activity | |||
| android:name=".MainActivity" | |||
| android:launchMode="singleTask" | |||
| tools:node="replace" | |||
| android:screenOrientation="landscape"> | |||
| </activity> | |||
| <activity | |||
| android:name=".ui.LauncherActivity" | |||
| android:screenOrientation="landscape" | |||
| tools:node="replace" | |||
| android:exported="true"> | |||
| <intent-filter> | |||
| <action android:name="android.intent.action.MAIN" /> | |||
| <category android:name="android.intent.category.LAUNCHER" /> | |||
| </intent-filter> | |||
| </activity> | |||
| </application> | |||
| </manifest> | |||
| @@ -0,0 +1,167 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" | |||
| xmlns:app="http://schemas.android.com/apk/res-auto" | |||
| xmlns:tools="http://schemas.android.com/tools" | |||
| android:layout_width="match_parent" | |||
| android:layout_height="match_parent" | |||
| tools:context=".MainActivity" | |||
| android:id="@+id/rootView"> | |||
| <TextureView | |||
| app:layout_constraintLeft_toLeftOf="parent" | |||
| app:layout_constraintRight_toRightOf="parent" | |||
| app:layout_constraintTop_toTopOf="parent" | |||
| app:layout_constraintBottom_toBottomOf="parent" | |||
| android:rotation="90" | |||
| android:rotationY="180" | |||
| android:id="@+id/texture_hdmi" | |||
| android:layout_width="2000dp" | |||
| android:layout_height="1125dp" | |||
| android:visibility="gone"/> | |||
| <com.tencent.smtt.sdk.WebView | |||
| android:id="@+id/webview" | |||
| android:layout_width="match_parent" | |||
| android:layout_height="match_parent" | |||
| android:visibility="gone"/> | |||
| <com.tencent.smtt.sdk.WebView | |||
| android:id="@+id/webview2" | |||
| android:layout_width="match_parent" | |||
| android:layout_height="match_parent" | |||
| app:layout_constraintTop_toTopOf="parent" /> | |||
| <com.aispeech.nativedemo.widget.CameraTextureView | |||
| android:id="@+id/texture" | |||
| android:layout_width="2048px" | |||
| android:layout_height="1536px" | |||
| app:layout_constraintTop_toBottomOf="@id/webview2" | |||
| app:layout_constraintLeft_toLeftOf="parent" | |||
| app:layout_constraintRight_toRightOf="parent" /> | |||
| <com.aispeech.nativedemo.widget.CameraTextureView | |||
| android:id="@+id/web_camera" | |||
| android:layout_width="640px" | |||
| android:layout_height="480px" | |||
| app:layout_constraintTop_toTopOf="parent" | |||
| app:layout_constraintEnd_toEndOf="parent" /> | |||
| <TextView | |||
| android:id="@+id/face_focus_view" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:gravity="center" | |||
| android:textColor="@color/white" | |||
| android:textSize="40sp" | |||
| app:layout_constraintBottom_toBottomOf="@id/texture" | |||
| app:layout_constraintLeft_toLeftOf="@id/texture" | |||
| app:layout_constraintRight_toRightOf="@id/texture" | |||
| app:layout_constraintTop_toTopOf="@id/texture"/> | |||
| <TextView | |||
| android:id="@+id/my_view" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:gravity="center" | |||
| android:textColor="@color/white" | |||
| android:textSize="40sp" | |||
| app:layout_constraintBottom_toBottomOf="@id/texture" | |||
| app:layout_constraintLeft_toLeftOf="@id/texture" | |||
| app:layout_constraintRight_toRightOf="@id/texture" | |||
| app:layout_constraintTop_toTopOf="@id/texture" /> | |||
| <TextView | |||
| android:id="@+id/my_face_view" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:gravity="center" | |||
| android:textColor="@color/white" | |||
| android:textSize="40sp" | |||
| app:layout_constraintBottom_toBottomOf="@id/texture" | |||
| app:layout_constraintLeft_toLeftOf="@id/texture" | |||
| app:layout_constraintRight_toRightOf="@id/texture" | |||
| app:layout_constraintTop_toTopOf="@id/texture" /> | |||
| <Button | |||
| android:id="@+id/close_camera" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:text="关闭会话" | |||
| app:layout_constraintLeft_toLeftOf="parent" | |||
| app:layout_constraintRight_toRightOf="parent" | |||
| app:layout_constraintTop_toTopOf="parent" | |||
| android:visibility="gone"/> | |||
| <Button | |||
| android:id="@+id/open_camera" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:text="选择GLM-6B" | |||
| app:layout_constraintRight_toRightOf="parent" | |||
| app:layout_constraintTop_toTopOf="parent" | |||
| android:visibility="gone"/> | |||
| <Button | |||
| android:id="@+id/reboot" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:text="reload" | |||
| app:layout_constraintLeft_toLeftOf="parent" | |||
| app:layout_constraintTop_toTopOf="parent" | |||
| android:visibility="gone"/> | |||
| <Button | |||
| android:id="@+id/stop_web_socket" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:text="查询员工" | |||
| app:layout_constraintBottom_toBottomOf="parent" | |||
| app:layout_constraintLeft_toLeftOf="parent" | |||
| android:visibility="gone"/> | |||
| <Button | |||
| android:id="@+id/start_web_socket" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:text="start server" | |||
| app:layout_constraintBottom_toBottomOf="parent" | |||
| app:layout_constraintRight_toRightOf="parent" | |||
| android:layout_marginEnd="150px" | |||
| android:visibility="gone"/> | |||
| <Button | |||
| android:id="@+id/kill_app" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:text="kill app" | |||
| app:layout_constraintBottom_toBottomOf="parent" | |||
| app:layout_constraintLeft_toLeftOf="parent" | |||
| app:layout_constraintRight_toRightOf="parent" /> | |||
| <Button | |||
| android:id="@+id/stop_client" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:text="stop client" | |||
| app:layout_constraintBottom_toBottomOf="parent" | |||
| app:layout_constraintLeft_toLeftOf="@id/stop_web_socket" | |||
| app:layout_constraintRight_toRightOf="@id/kill_app" | |||
| android:visibility="gone"/> | |||
| <Button | |||
| android:id="@+id/start_client" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:text="start client" | |||
| app:layout_constraintBottom_toBottomOf="parent" | |||
| app:layout_constraintLeft_toLeftOf="@id/kill_app" | |||
| app:layout_constraintRight_toRightOf="@id/start_web_socket" | |||
| android:visibility="gone"/> | |||
| <com.aispeech.nativedemo.ui.InitView | |||
| android:id="@+id/initView" | |||
| android:layout_width="match_parent" | |||
| android:layout_height="match_parent" | |||
| /> | |||
| </androidx.constraintlayout.widget.ConstraintLayout> | |||
| @@ -1,5 +1,6 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" | |||
| xmlns:tools="http://schemas.android.com/tools" | |||
| package="com.aispeech.nativedemo"> | |||
| <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> | |||
| @@ -41,10 +42,14 @@ | |||
| <activity | |||
| android:name=".MainActivity" | |||
| android:launchMode="singleTask" | |||
| tools:node="replace" | |||
| android:screenOrientation="portrait"> | |||
| </activity> | |||
| <activity android:name=".ui.LauncherActivity" android:exported="true"> | |||
| <activity | |||
| android:name=".ui.LauncherActivity" | |||
| android:screenOrientation="portrait" | |||
| android:exported="true"> | |||
| <intent-filter> | |||
| <action android:name="android.intent.action.MAIN" /> | |||
| @@ -4,7 +4,6 @@ import android.annotation.TargetApi; | |||
| import android.app.Notification; | |||
| import android.app.PendingIntent; | |||
| import android.app.Service; | |||
| import android.content.ComponentName; | |||
| import android.content.Context; | |||
| import android.content.Intent; | |||
| import android.media.AudioFormat; | |||
| @@ -23,9 +22,11 @@ import com.aispeech.dui.dds.DDSAuthListener; | |||
| import com.aispeech.dui.dds.DDSConfig; | |||
| import com.aispeech.dui.dds.DDSInitListener; | |||
| import com.aispeech.dui.dds.agent.AsrListener; | |||
| import com.aispeech.dui.dds.agent.MessageObserver; | |||
| import com.aispeech.dui.dds.agent.tts.TTSEngine; | |||
| import com.aispeech.dui.dds.agent.wakeup.cb.BfListener; | |||
| import com.aispeech.dui.dds.exceptions.DDSNotInitCompleteException; | |||
| import com.aispeech.nativedemo.face.FaceManager; | |||
| import com.aispeech.nativedemo.asr.observer.DuiMessageObserver; | |||
| import com.aispeech.nativedemo.iat.IatManager; | |||
| import com.aispeech.nativedemo.log.Logger; | |||
| import com.aispeech.nativedemo.mqtt.MqttManager; | |||
| @@ -36,6 +37,8 @@ import com.aispeech.nativedemo.utils.StatusUtils; | |||
| import java.io.File; | |||
| import java.io.IOException; | |||
| import java.io.RandomAccessFile; | |||
| import java.util.LinkedList; | |||
| import java.util.List; | |||
| import java.util.Timer; | |||
| import java.util.TimerTask; | |||
| import java.util.UUID; | |||
| @@ -114,6 +117,7 @@ public class DDSService extends Service { | |||
| private void init() { | |||
| // DDS.getInstance().setDebugMode(2); //在调试时可以打开sdk调试日志,在发布版本时,请关闭 | |||
| DDS.getInstance().init(getApplicationContext(), createConfig(), mInitListener, mAuthListener); | |||
| } | |||
| // dds初始状态监听器,监听init是否成功 | |||
| @@ -133,7 +137,7 @@ public class DDSService extends Service { | |||
| // DDS.getInstance().getAgent().getTTSEngine().setMode(DDSMode.TTS_SILENCE); | |||
| // DDS.getInstance().getAgent().getTTSEngine().setSpeaker("zhilingfp"); | |||
| DDS.getInstance().getAgent().getTTSEngine().setSpeaker("hqqiaf","hqqiaf_lstm_210909.bin"); | |||
| // startAsrListening(); | |||
| startAsrListening(); | |||
| } catch (DDSNotInitCompleteException e) { | |||
| throw new RuntimeException(e); | |||
| } | |||
| @@ -141,7 +145,8 @@ public class DDSService extends Service { | |||
| } else{ | |||
| Logger.e("思必驰++++++++++++++++++++"); | |||
| } | |||
| if(!Config.CURRENT_URL.equals(Config.PROD_BASE_URL)){ | |||
| //if(!Config.CURRENT_URL.equals(Config.PROD_BASE_URL)) | |||
| { | |||
| DDS.getInstance().setAudioDebug(true); | |||
| } | |||
| } | |||
| @@ -188,6 +193,8 @@ public class DDSService extends Service { | |||
| super.onDestroy(); | |||
| // 在退出app时将dds组件注销 | |||
| isStarted = false; | |||
| // 注销 | |||
| DDS.getInstance().getAgent().unSubscribe(messageObserver); | |||
| DDS.getInstance().releaseSync(); | |||
| } | |||
| @@ -345,31 +352,120 @@ public class DDSService extends Service { | |||
| private long mAsrTime = 0; | |||
| private String mFileName = ""; | |||
| public void startKDXFListening() throws DDSNotInitCompleteException { | |||
| DDS.getInstance().getAgent().getWakeupEngine().setBfListener(new BfListener() { | |||
| @Override | |||
| public void onBeamforming(byte[] bytes) { | |||
| // Log.e(TAG, "onBeamforming" + bytes.length); | |||
| } | |||
| }); | |||
| } | |||
| private List<byte[]> byteList = new LinkedList<>(); | |||
| private long maxDuration = 0; | |||
| private static int WIRITE_DURATION = 300; | |||
| public void startAsrListening() { | |||
| DDS.getInstance().getAgent().getASREngine().setAsrListener(new AsrListener() { | |||
| @Override | |||
| public void onAsr(byte[] bytes) { | |||
| startTimer(); | |||
| if(mTime == 0){ | |||
| mTime = System.currentTimeMillis(); | |||
| mAsrTime = System.currentTimeMillis(); | |||
| } | |||
| if(System.currentTimeMillis() - mAsrTime > 1000){ | |||
| mTime = System.currentTimeMillis(); | |||
| //writeByFile(bytes); | |||
| writeNewMessage(bytes); | |||
| Log.e("testkd", "dds thread " + Thread.currentThread() + "bytes " + bytes.length); | |||
| } | |||
| }); | |||
| startListenMessage(); | |||
| } | |||
| private void startListenMessage() { | |||
| // 注册 | |||
| DDS.getInstance().getAgent().subscribe(new String[]{"local_vad.timeout", "sys.vad.begin", "sys.vad.end"}, messageObserver); | |||
| } | |||
| private MessageObserver messageObserver = new MessageObserver() { | |||
| @Override | |||
| public void onMessage(final String message, final String data) { | |||
| if (message.equals("sys.vad.begin")){ | |||
| mTime = System.currentTimeMillis(); | |||
| byteList = new LinkedList<>(); | |||
| Log.e("testkd", "mtime " + mTime + " vad.begin "); | |||
| }else if(message.equals("sys.vad.end")){ | |||
| if(!byteList.isEmpty()){ | |||
| IatManager.getInstance().executeStreamZXByte(DuiMessageObserver.mTime, byteList); | |||
| byteList = new LinkedList<>(); | |||
| } | |||
| mAsrTime = System.currentTimeMillis(); | |||
| File file = createFile("asr-" + mTime + ".pcm"); | |||
| RandomAccessFile raf = null; | |||
| try { | |||
| raf = new RandomAccessFile(file, "rw"); | |||
| raf.seek(file.length()); | |||
| raf.write(bytes); | |||
| raf.close(); | |||
| } catch (IOException e) { | |||
| throw new RuntimeException(e); | |||
| Log.e("testkd", "mtime " + mTime + " vad.end " + " thread " + Thread.currentThread()); | |||
| } else { | |||
| } | |||
| } | |||
| }; | |||
| private void writeNewMessage(byte[] bytes) { | |||
| byteList.add(bytes); | |||
| } | |||
| private void writeNew(byte[] bytes) { | |||
| startByteTimer(); | |||
| if(mTime == 0){ | |||
| mTime = System.currentTimeMillis(); | |||
| mAsrTime = System.currentTimeMillis(); | |||
| } | |||
| long duration = System.currentTimeMillis() - mAsrTime; | |||
| if(duration > WIRITE_DURATION){ | |||
| Log.e("testtest", "max duration " + maxDuration); | |||
| } else if (duration > maxDuration){ | |||
| maxDuration = duration; | |||
| } | |||
| if(System.currentTimeMillis() - mAsrTime > WIRITE_DURATION){ | |||
| mTime = System.currentTimeMillis(); | |||
| byteList = new LinkedList<>(); | |||
| Log.e("testtest", "mtime " + mTime); | |||
| } | |||
| byteList.add(bytes); | |||
| mAsrTime = System.currentTimeMillis(); | |||
| } | |||
| private void writeByFile(byte[] bytes) { | |||
| startTimer(); | |||
| if(mTime == 0){ | |||
| mTime = System.currentTimeMillis(); | |||
| mAsrTime = System.currentTimeMillis(); | |||
| } | |||
| if(System.currentTimeMillis() - mAsrTime > WIRITE_DURATION){ | |||
| mTime = System.currentTimeMillis(); | |||
| } | |||
| mAsrTime = System.currentTimeMillis(); | |||
| File file = createFile("asr-" + mTime + ".pcm"); | |||
| RandomAccessFile raf = null; | |||
| try { | |||
| raf = new RandomAccessFile(file, "rw"); | |||
| raf.seek(file.length()); | |||
| raf.write(bytes); | |||
| raf.close(); | |||
| } catch (IOException e) { | |||
| throw new RuntimeException(e); | |||
| } | |||
| } | |||
| private Timer mByteTimer; | |||
| public void startByteTimer(){ | |||
| if(mByteTimer != null){ | |||
| mByteTimer.cancel(); | |||
| mByteTimer = null; | |||
| } | |||
| mByteTimer = new Timer(); | |||
| mByteTimer.schedule(new TimerTask() { | |||
| @Override | |||
| public void run() { | |||
| if(!byteList.isEmpty()){ | |||
| IatManager.getInstance().executeStreamByte(String.valueOf(mTime), byteList); | |||
| byteList = new LinkedList<>(); | |||
| } | |||
| } | |||
| }); | |||
| }, WIRITE_DURATION); | |||
| } | |||
| private Timer mTimer; | |||
| @@ -392,7 +488,7 @@ public class DDSService extends Service { | |||
| mFileName = dirPath; | |||
| } | |||
| } | |||
| }, 1000); | |||
| }, WIRITE_DURATION); | |||
| } | |||
| public File createFile(String name) { | |||
| @@ -3,6 +3,7 @@ package com.aispeech.nativedemo; | |||
| import android.app.Application; | |||
| import android.content.Context; | |||
| import android.content.Intent; | |||
| import android.content.res.Configuration; | |||
| import android.util.Log; | |||
| import com.aispeech.dui.dds.DDS; | |||
| @@ -35,7 +36,7 @@ public class DuiApplication extends Application { | |||
| mContext = this; | |||
| Logger.init(); | |||
| CrashHandler.getInstance().init(this); | |||
| SpeechUtility.createUtility(this, "appid=e14469ab"); | |||
| SpeechUtility.createUtility(this, "appid=" + "948cf4b6");// "tmj appid=" + "e14469ab" "ljc appid=" + "6917197f" | |||
| Fresco.initialize(this); | |||
| // Thread.setDefaultUncaughtExceptionHandler(uncaughtExceptionHandler); | |||
| // FaceManager.getInstance(this).initEngine(); | |||
| @@ -425,7 +425,12 @@ public class MainActivity extends Activity implements DuiUpdateObserver.UpdateCa | |||
| public void onPageFinished(WebView var1, String var2) { | |||
| Log.e("testh", " onPageFinished " + Thread.currentThread()); | |||
| mInitView.updateDigital(InitView.INIT_SUCCESS); | |||
| mInitView.postDelayed(new Runnable() { | |||
| @Override | |||
| public void run() { | |||
| mInitView.updateDigital(InitView.INIT_SUCCESS); | |||
| } | |||
| }, 4000); | |||
| } | |||
| @Override | |||
| @@ -467,7 +472,9 @@ public class MainActivity extends Activity implements DuiUpdateObserver.UpdateCa | |||
| String method = "setVideoPlaybackRequiresUserGesture"; | |||
| Bundle bundle = new Bundle(); | |||
| bundle.putBoolean("require", true); | |||
| webView.getX5WebViewExtension().invokeMiscMethod(method, bundle); | |||
| if (webView.getX5WebViewExtension() != null) { | |||
| webView.getX5WebViewExtension().invokeMiscMethod(method, bundle); | |||
| } | |||
| } | |||
| private WebSocketManager mWSManager; | |||
| @@ -1,71 +0,0 @@ | |||
| package com.aispeech.nativedemo.asr.observer; | |||
| import android.content.Intent; | |||
| import android.net.Uri; | |||
| import android.util.Log; | |||
| import com.aispeech.dui.dds.DDS; | |||
| import com.aispeech.dui.dsk.duiwidget.CommandObserver; | |||
| import org.json.JSONObject; | |||
| /** | |||
| * 客户端CommandObserver, 用于处理客户端动作的执行以及快捷唤醒中的命令响应. | |||
| * 例如在平台配置客户端动作: command://call?phone=$phone$&name=#name#, | |||
| * 那么在CommandObserver的onCall方法中会回调topic为"call", data为 | |||
| */ | |||
| public class DuiCommandObserver implements CommandObserver { | |||
| private String TAG = "DuiCommandObserver"; | |||
| private static final String COMMAND_CALL = "sys.action.call"; | |||
| private static final String COMMAND_SELECT = "sys.action.call.select"; | |||
| private String mSelectedPhone = null; | |||
| public DuiCommandObserver() { | |||
| } | |||
| // 注册当前更新消息 | |||
| public void regist() { | |||
| DDS.getInstance().getAgent().subscribe(new String[]{COMMAND_CALL, COMMAND_SELECT}, | |||
| this); | |||
| } | |||
| // 注销当前更新消息 | |||
| public void unregist() { | |||
| DDS.getInstance().getAgent().unSubscribe(this); | |||
| } | |||
| @Override | |||
| public void onCall(String command, String data) { | |||
| Log.e(TAG, "command: " + command + " data: " + data); | |||
| try { | |||
| if (COMMAND_CALL.equals(command)) { | |||
| String number = new JSONObject(data).optString("phone"); | |||
| if (number == null) { | |||
| phoneDial(mSelectedPhone); | |||
| mSelectedPhone = null; | |||
| } else { | |||
| phoneDial(number); | |||
| } | |||
| } else if (COMMAND_SELECT.equals(command)) { | |||
| mSelectedPhone = new JSONObject(data).optString("phone"); | |||
| } | |||
| } catch (Exception e) { | |||
| e.printStackTrace(); | |||
| } | |||
| } | |||
| // 拨打电话 | |||
| private void phoneDial(String number) { | |||
| if (number == null) { | |||
| return; | |||
| } | |||
| Log.e(TAG, "phoneDial:" + number); | |||
| Intent intent = new Intent(); | |||
| intent.setAction(Intent.ACTION_CALL); | |||
| intent.addCategory(Intent.CATEGORY_DEFAULT); | |||
| intent.setData(Uri.parse("tel:" + number)); | |||
| intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); | |||
| // DuiApplication.getContext().startActivity(intent); | |||
| } | |||
| } | |||
| @@ -1,6 +1,7 @@ | |||
| package com.aispeech.nativedemo.asr.observer; | |||
| import android.os.Environment; | |||
| import android.text.TextUtils; | |||
| import android.util.Log; | |||
| import com.aispeech.dui.dds.DDS; | |||
| @@ -8,6 +9,7 @@ import com.aispeech.dui.dds.agent.DMTaskCallback; | |||
| import com.aispeech.dui.dds.agent.MessageObserver; | |||
| import com.aispeech.nativedemo.MainActivity; | |||
| import com.aispeech.nativedemo.config.Config; | |||
| import com.aispeech.nativedemo.iat.IatManager; | |||
| import com.aispeech.nativedemo.log.Logger; | |||
| import com.aispeech.nativedemo.mqtt.MqttManager; | |||
| import com.aispeech.nativedemo.network.ws.MessageUtils; | |||
| @@ -19,6 +21,7 @@ import org.json.JSONObject; | |||
| import java.io.File; | |||
| import java.io.IOException; | |||
| import java.util.concurrent.ConcurrentHashMap; | |||
| /** | |||
| * 客户端MessageObserver, 用于处理客户端动作的消息响应. | |||
| @@ -40,6 +43,9 @@ public class DuiMessageObserver implements MessageObserver { | |||
| private boolean mIsFirstVar = true; | |||
| private boolean mHasvar = false; | |||
| private Gson mGson; | |||
| public static volatile String mTime = ""; | |||
| private ConcurrentHashMap<String, String> mRecordIdMap = new ConcurrentHashMap<>(); | |||
| private String[] mSubscribeKeys = new String[]{ | |||
| "sys.dialog.state", | |||
| "context.output.text", | |||
| @@ -67,11 +73,22 @@ public class DuiMessageObserver implements MessageObserver { | |||
| public JSONObject onDMTaskResult(JSONObject jsonObject, Type type) { | |||
| if(type == Type.DM_OUTPUT){ | |||
| String sessionId = jsonObject.optString("sessionId"); | |||
| String recordId = jsonObject.optString("recordId"); | |||
| String display = jsonObject.optString("display"); | |||
| String nlg = jsonObject.optString("nlg"); | |||
| String dmInput = jsonObject.optString("dmInput"); | |||
| Log.e(Tag, display); | |||
| Log.e(Tag, nlg); | |||
| Log.e("testtest", "thread "+ Thread.currentThread()+" onDMTResult " + jsonObject.toString()); | |||
| Log.e("testtest", "thread "+ Thread.currentThread()+" md display " + display); | |||
| String time = ""; | |||
| if (!TextUtils.isEmpty(dmInput) && !TextUtils.isEmpty(recordId)) { | |||
| time = mRecordIdMap.get(recordId); | |||
| String asrStr = IatManager.getInstance().getASRStr(time); | |||
| Logger.e("djASR-思必驰: " + dmInput); | |||
| Log.e("testtest", "djASR-思必驰: " + dmInput + " asrStr " + asrStr + " time " + time); | |||
| } | |||
| String readText = nlg; | |||
| // if (JSONUtils.isJson(display)) { | |||
| // readText = MessageUtils.sendSkill(sessionId, display, dmInput); | |||
| @@ -91,17 +108,21 @@ public class DuiMessageObserver implements MessageObserver { | |||
| // e.printStackTrace(); | |||
| // } | |||
| // } | |||
| // onMessage 前置处理 nlp | |||
| if (JSONUtils.isJson(display)) { | |||
| MessageUtils.sendSkill(sessionId, display, dmInput); | |||
| MessageUtils.sendSkill(sessionId, display, dmInput, time); | |||
| } else { | |||
| MessageUtils.sendNlpResult(sessionId, display, dmInput); | |||
| MessageUtils.sendNlpResult(sessionId, display, dmInput, time); | |||
| } | |||
| try { | |||
| jsonObject.put("nlg", ""); | |||
| jsonObject.put("display", ""); | |||
| } catch (JSONException e) { | |||
| e.printStackTrace(); | |||
| } | |||
| } else if (type == DMTaskCallback.Type.CDM_ERROR) { | |||
| Log.e("testtest", "CDM_ERROR "); | |||
| } | |||
| return jsonObject; | |||
| } | |||
| @@ -116,19 +137,26 @@ public class DuiMessageObserver implements MessageObserver { | |||
| public static boolean mIsSleep = false; | |||
| public static boolean mSpeakFinishStatus = true; | |||
| public static long mSpeakFinishTime = 0; | |||
| public static String mAsrWord = ""; | |||
| @Override | |||
| public void onMessage(String message, String data) { | |||
| Log.d(Tag, "message : " + message + " data : " + data); | |||
| switch (message) { | |||
| case "context.output.text": | |||
| // nlp | |||
| MessageUtils.sendChatMessage(data); | |||
| Log.e("testtest", "message " + message + " data " + data.toString()); | |||
| break; | |||
| case "context.input.text": | |||
| // 识别的文本 | |||
| //判断有人的时候才发送给大模型 | |||
| // if(FaceManager.getInstance(DuiApplication.getContext()).hasPerson()){ | |||
| MessageUtils.sendAsr(data); | |||
| String recordId = MessageUtils.sendAsrAndGetRecordId(data, mTime); | |||
| if (!TextUtils.isEmpty(recordId)) { | |||
| mRecordIdMap.put(recordId, mTime); | |||
| } | |||
| Log.e("testtest", "message " + message + " data " + data.toString()); | |||
| //Logger.e("djASR-asrWord " + mAsrWord); | |||
| // } | |||
| break; | |||
| case "context.widget.content": | |||
| @@ -185,6 +213,7 @@ public class DuiMessageObserver implements MessageObserver { | |||
| break; | |||
| case "local_vad.pcm": | |||
| //Log.e("testtest", "data = " + data.getBytes().length); | |||
| // Log.e(Tag, "data = " + data); | |||
| // byte[] buffer = data.getBytes(); | |||
| // File file = createFile(mTime + ".pcm"); | |||
| @@ -200,9 +229,12 @@ public class DuiMessageObserver implements MessageObserver { | |||
| // } | |||
| break; | |||
| case "sys.vad.begin": | |||
| // mTime = SystemClock.uptimeMillis(); | |||
| Log.e("testtest", "mtime message " +" vad.begin "); | |||
| mTime = String.valueOf(System.currentTimeMillis()); | |||
| break; | |||
| case "sys.vad.end": | |||
| Log.e("testtest", "message " +" vad.end " + Thread.currentThread()); | |||
| break; | |||
| default: | |||
| } | |||
| @@ -228,6 +260,5 @@ public class DuiMessageObserver implements MessageObserver { | |||
| return objFile; | |||
| } | |||
| private long mTime = 0; | |||
| } | |||
| @@ -1,96 +0,0 @@ | |||
| package com.aispeech.nativedemo.asr.observer; | |||
| import android.util.Log; | |||
| import com.aispeech.dui.dds.DDS; | |||
| import com.aispeech.dui.dds.agent.MessageObserver; | |||
| import com.aispeech.dui.dds.exceptions.DDSNotInitCompleteException; | |||
| import com.aispeech.dui.dds.update.DDSUpdateListener; | |||
| /** | |||
| * 更新Observer,用于更新当前dds组件 | |||
| */ | |||
| public class DuiUpdateObserver implements MessageObserver { | |||
| private static final String Tag = "DuiUpdateObserver"; | |||
| public static final int START = 0; // 开始更新 | |||
| public static final int UPDATEING = 1; // 正在更新 | |||
| public static final int FINISH = 2;// 更新完成 | |||
| public static final int ERROR = 3;// 更新失败 | |||
| private UpdateCallback mUpdateCallback; | |||
| // 更新回调 | |||
| public interface UpdateCallback { | |||
| void onUpdate(int type, String result); | |||
| } | |||
| // 注册当前更新消息 | |||
| public void regist(UpdateCallback updateCallback) { | |||
| mUpdateCallback = updateCallback; | |||
| DDS.getInstance().getAgent().subscribe("sys.resource.updated", this); | |||
| initUpdate(); | |||
| } | |||
| // 注销当前更新消息 | |||
| public void unregist() { | |||
| DDS.getInstance().getAgent().unSubscribe(this); | |||
| } | |||
| // 初始化更新 | |||
| private void initUpdate() { | |||
| try { | |||
| DDS.getInstance().getUpdater().update(ddsUpdateListener); | |||
| } catch (DDSNotInitCompleteException e) { | |||
| e.printStackTrace(); | |||
| } | |||
| } | |||
| @Override | |||
| public void onMessage(String s, String s1) { | |||
| initUpdate(); | |||
| } | |||
| private DDSUpdateListener ddsUpdateListener = new DDSUpdateListener() { | |||
| @Override | |||
| public void onUpdateFound(String detail) { | |||
| try { | |||
| if (mUpdateCallback != null) { | |||
| mUpdateCallback.onUpdate(START, "发现新版本"); | |||
| } | |||
| DDS.getInstance().getAgent().getTTSEngine().speak("发现新版本,正在为您更新", 1); | |||
| } catch (DDSNotInitCompleteException e) { | |||
| e.printStackTrace(); | |||
| } | |||
| } | |||
| @Override | |||
| public void onUpdateFinish() { | |||
| if (mUpdateCallback != null) { | |||
| mUpdateCallback.onUpdate(FINISH, "更新成功"); | |||
| } | |||
| // 更新成功后不要立即调用speak提示用户更新成功, 这个时间DDS正在初始化 | |||
| } | |||
| @Override | |||
| public void onDownloadProgress(float progress) { | |||
| if (mUpdateCallback != null) { | |||
| mUpdateCallback.onUpdate(UPDATEING, "正在更新 -> " + progress + " / 100"); | |||
| } | |||
| } | |||
| @Override | |||
| public void onError(int what, String error) { | |||
| if (mUpdateCallback != null) { | |||
| mUpdateCallback.onUpdate(ERROR, "更新失败,详情看Log"); | |||
| } | |||
| Log.e(Tag, "what = " + what + ", error = " + error); | |||
| } | |||
| @Override | |||
| public void onUpgrade(String version) { | |||
| if (mUpdateCallback != null) { | |||
| mUpdateCallback.onUpdate(ERROR, "更新失败 -> 当前sdk版本过低,和dui平台上的dui内核不匹配,请更新sdk"); | |||
| } | |||
| } | |||
| }; | |||
| } | |||
| @@ -267,6 +267,7 @@ public class ConfigManager { | |||
| // MessageUtils.sendMessage("技能:" + success); | |||
| if (obj.has("data")) { | |||
| JSONArray array = obj.getJSONArray("data"); | |||
| MessageUtils.sendSkills(array.toString()); | |||
| for (int i = 0; i < array.length(); i++) { | |||
| JSONObject jsonObject = array.getJSONObject(i); | |||
| Skill skill = new Skill(); | |||
| @@ -1,5 +1,11 @@ | |||
| package com.aispeech.nativedemo.entity; | |||
| import static com.aispeech.nativedemo.entity.PositionEnum.POSITION_LEFT_TO_LEFT; | |||
| import static com.aispeech.nativedemo.entity.PositionEnum.POSITION_LEFT_TO_RIGHT; | |||
| import static com.aispeech.nativedemo.entity.PositionEnum.POSITION_ONCE; | |||
| import static com.aispeech.nativedemo.entity.PositionEnum.POSITION_RIGHT_TO_LEFT; | |||
| import static com.aispeech.nativedemo.entity.PositionEnum.POSITION_RIGHT_TO_RIGHT; | |||
| import android.graphics.Bitmap; | |||
| import android.util.Log; | |||
| @@ -10,6 +16,10 @@ import org.json.JSONObject; | |||
| public class PersonInfo { | |||
| private static final long LEAVE_DURATION = 60 * 1000; | |||
| private static final int DELTA_X = 20; | |||
| private static final int RIGHT_MAX = 1700; | |||
| private static final int MAX_LENGTH = 800; | |||
| private static final int MAX_GREETING_DURATION = 1000; | |||
| public String id; // eid | |||
| public String name; //昵称 | |||
| public String tag; //人物类别 | |||
| @@ -41,7 +51,7 @@ public class PersonInfo { | |||
| * 初始值 0:从未出现 | |||
| * 方向 1:从左到屏幕中静止 左右 (包含折现) 2:从右走到屏幕中(包含折现) 右左 3:左右左 4. 右左右 | |||
| * 最终判定值配合是否在屏幕中 1.1:从左到右走出屏幕 2.1:从右到左走出屏幕 3.1:左右左后走出屏幕4.1右左右走出屏幕 | |||
| * 5.只出现一次 6.出现并静止 | |||
| * 5.只出现一次 6.出现并静止 最左 142 517 最右1959 1887 | |||
| */ | |||
| public PositionEnum position_type; // directionType | |||
| public int face_x1; | |||
| @@ -82,7 +92,7 @@ public class PersonInfo { | |||
| this.last_in_time = startTime; | |||
| this.is_in_view = 1; | |||
| this.hasBeenGreeted = false; | |||
| this.position_type = PositionEnum.POSITION_ONCE; | |||
| this.position_type = POSITION_ONCE; | |||
| if (att.hd_bd > 0) { | |||
| this.startBodyPositionX = (result.bd_x1 + result.bd_x2) / 2; | |||
| } | |||
| @@ -126,8 +136,17 @@ public class PersonInfo { | |||
| obj.put("bd_x", (result.bd_x1 + result.bd_x2) / 2); | |||
| long time = System.currentTimeMillis(); | |||
| obj.put("ts", time / 1000 + "." + time % 1000); | |||
| obj.put("lastInTime", last_in_time); | |||
| obj.put("lastInTime", last_in_time); // 进入屏幕的时间 | |||
| obj.put("lastGreetTime", last_greet_time); // 打招呼的时间 | |||
| obj.put("directionType", position_type.ordinal()); // 运动的方向 | |||
| obj.put("currentBodyPositionX", currentBodyPositionX); // 运动的方向 | |||
| obj.put("previewBodyPositionX", previewBodyPositionX); // 运动的方向 | |||
| obj.put("startBodyPositionX", startBodyPositionX); // 运动的方向 | |||
| obj.put("startFacePositionX", startFacePositionX); // 运动的方向 | |||
| obj.put("previewFacePositionX", previewFacePositionX); // 运动的方向 | |||
| obj.put("currentFacePositionX", currentFacePositionX); // 运动的方向 | |||
| obj.put("hasBeenGreeted", hasBeenGreeted); | |||
| obj.put("isInView", is_in_view); | |||
| return obj.toString(); | |||
| } catch (JSONException e) { | |||
| e.printStackTrace(); | |||
| @@ -147,6 +166,7 @@ public class PersonInfo { | |||
| this.startBodyPositionX = -1; | |||
| this.previewBodyPositionX = -1; | |||
| this.currentBodyPositionX = -1; | |||
| this.position_type = POSITION_ONCE; | |||
| this.hasBeenGreeted = false; | |||
| } else { | |||
| this.previewFacePositionX = this.currentFacePositionX; | |||
| @@ -185,10 +205,14 @@ public class PersonInfo { | |||
| } | |||
| if (att.hd_fa > 0) { | |||
| this.currentFacePositionX = (face_x1 + face_x2) / 2; | |||
| if (!hasDirection && startFacePositionX >= 0 && previewFacePositionX >= 0) { | |||
| calculateDirection(startFacePositionX, previewFacePositionX, currentFacePositionX); | |||
| } else if (!hasDirection && startBodyPositionX >= 0) { | |||
| calculateDirection(currentFacePositionX, previewBodyPositionX, currentFacePositionX); | |||
| if (startFacePositionX >= 0 && previewFacePositionX >= 0) { | |||
| if (!hasDirection) { | |||
| calculateDirection(startFacePositionX, previewFacePositionX, currentFacePositionX); | |||
| } | |||
| } else if (startFacePositionX >= 0) { | |||
| if (!hasDirection) { | |||
| calculateDirection(startFacePositionX, currentFacePositionX); | |||
| } | |||
| } else { | |||
| startFacePositionX = currentFacePositionX; | |||
| } | |||
| @@ -199,32 +223,36 @@ public class PersonInfo { | |||
| private void calculateDirection(int startBodyPositionX, int currentBodyPositionX) { | |||
| int deltaX = currentBodyPositionX - startBodyPositionX; | |||
| if (deltaX > 0) { | |||
| if (deltaX >= DELTA_X) { | |||
| position_type = PositionEnum.POSITION_LEFT_TO_RIGHT; | |||
| } else if (deltaX == 0) { | |||
| position_type = PositionEnum.POSITION_WAITING; | |||
| } else { | |||
| } else if (deltaX <= -DELTA_X) { | |||
| position_type = PositionEnum.POSITION_RIGHT_TO_LEFT; | |||
| } else { | |||
| position_type = PositionEnum.POSITION_WAITING; | |||
| } | |||
| } | |||
| private void calculateDirection(int startBodyPositionX, int previewBodyPositionX, int currentBodyPositionX) { | |||
| int deltaCurrentX = currentBodyPositionX - previewBodyPositionX; | |||
| int deltaX = previewBodyPositionX - startBodyPositionX; | |||
| if (deltaCurrentX > 0) { | |||
| if (deltaX > 0) { | |||
| if (deltaCurrentX >= DELTA_X) { | |||
| if (deltaX >= DELTA_X) { | |||
| position_type = PositionEnum.POSITION_LEFT_TO_RIGHT; | |||
| } else { | |||
| } else if (deltaX <= -DELTA_X) { | |||
| position_type = PositionEnum.POSITION_RIGHT_TO_RIGHT; | |||
| } else { | |||
| position_type = PositionEnum.POSITION_LEFT_TO_RIGHT; | |||
| } | |||
| } else if (deltaX < 0) { | |||
| if (deltaX < 0) { | |||
| } else if (deltaX <= -DELTA_X) { | |||
| if (deltaX <= -DELTA_X) { | |||
| position_type = PositionEnum.POSITION_RIGHT_TO_LEFT; | |||
| } else { | |||
| } else if (deltaX >= DELTA_X){ | |||
| position_type = PositionEnum.POSITION_LEFT_TO_LEFT; | |||
| } else { | |||
| position_type = PositionEnum.POSITION_RIGHT_TO_LEFT; | |||
| } | |||
| } else { | |||
| position_type = PositionEnum.POSITION_WAITING; | |||
| calculateDirection(startBodyPositionX, previewBodyPositionX); | |||
| } | |||
| } | |||
| @@ -243,4 +271,28 @@ public class PersonInfo { | |||
| return 1; | |||
| } | |||
| public boolean hasDirection() { | |||
| long time = System.currentTimeMillis(); | |||
| if (position_type == POSITION_LEFT_TO_LEFT || position_type == POSITION_RIGHT_TO_LEFT) { | |||
| return true; | |||
| } | |||
| if (position_type == POSITION_LEFT_TO_RIGHT || position_type == POSITION_RIGHT_TO_RIGHT) { | |||
| if (startBodyPositionX > MAX_LENGTH && ((currentBodyPositionX - startBodyPositionX) > MAX_LENGTH)) { | |||
| return true; | |||
| } else if (startFacePositionX > MAX_LENGTH && ((currentFacePositionX - startFacePositionX) > MAX_LENGTH)) { | |||
| return true; | |||
| } else { | |||
| return false; | |||
| } | |||
| } | |||
| if (startBodyPositionX != -1 && currentBodyPositionX != -1 | |||
| && startBodyPositionX != currentBodyPositionX && ((time - last_in_time) > MAX_GREETING_DURATION)) { | |||
| return true; | |||
| } | |||
| if (startFacePositionX != -1 && currentFacePositionX != -1 | |||
| && startFacePositionX != currentFacePositionX && ((time - last_in_time) > MAX_GREETING_DURATION)) { | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| } | |||
| @@ -458,8 +458,7 @@ public class FaceChatGreetingQueueMode implements ChatMode { | |||
| // Log.e("testtest", "sendMsgAndLog mSelectedPerson is null "); | |||
| return; | |||
| } | |||
| // Log.e("testtest", "slected trackid " +mSelectPerson.trackId + "selectperson bd.x1 " + mSelectPerson.curResult.bd_x1+ " x2 " + mSelectPerson.curResult.bd_x2 | |||
| // + " facex1 " + mSelectPerson.curResult.fa_x1 + " facex2 " + mSelectPerson.curResult.fa_x2 + " bitmap " + mSelectPerson.bitmap); | |||
| Log.e("testtrack", " select people " + mSelectPerson); | |||
| FaceManager.getInstance().updateRect(mSelectPerson.curResult); | |||
| sendMsg(mSelectPerson); | |||
| @@ -568,7 +567,7 @@ public class FaceChatGreetingQueueMode implements ChatMode { | |||
| for (PersonInfo value : mPersonsInScreen) { | |||
| // 是否有脸 | |||
| long time = System.currentTimeMillis(); | |||
| if (!value.hasBeenGreeted) { | |||
| if (!value.hasBeenGreeted && value.hasDirection()) { | |||
| value.last_greet_time = time; | |||
| value.hasBeenGreeted = true; | |||
| Log.e("testtrack", "greeting emp eid " + value); | |||
| @@ -579,17 +578,18 @@ public class FaceChatGreetingQueueMode implements ChatMode { | |||
| for (PersonInfo value : mUndeterminedPersonsInScreen) { | |||
| // 是否有脸 | |||
| long time = System.currentTimeMillis(); | |||
| if (!value.hasBeenGreeted && (time - value.last_in_time) >= UNKNOWN_GREETING_DURATION) { | |||
| if (!value.hasBeenGreeted && ((time - value.last_in_time) >= UNKNOWN_GREETING_DURATION) | |||
| && value.hasDirection()) { | |||
| value.last_greet_time = time; | |||
| value.hasBeenGreeted = true; | |||
| Log.e("testtrack", "greeting trackId " + value.trackId + " old trackid" + value.oldTrackId + " fa.qulity " + value.face_quality + " greeting duration " + (System.currentTimeMillis() - value.last_greet_time)); | |||
| Log.e("testtrack", "greeting trackId " + value.trackId + " old value " + value + " greeting duration " + (System.currentTimeMillis() - value.last_in_time)); | |||
| sendGreeting(value); | |||
| break; | |||
| } else { | |||
| // 陌生人打招呼超过两秒才打 | |||
| if (System.currentTimeMillis() - value.last_greet_time < 2500) { | |||
| //sendGreeting(value); | |||
| Log.e("testtrack", "< 2500 " + value.trackId + " old trackid" + value.oldTrackId + " fa.qulity " + value.face_quality + " greeting duration " + (System.currentTimeMillis() - value.last_greet_time)); | |||
| //Log.e("testtrack", "< 2500 " + value.trackId + " old trackid" + value.oldTrackId + " fa.qulity " + value.face_quality + " greeting duration " + (System.currentTimeMillis() - value.last_greet_time)); | |||
| return; | |||
| } | |||
| @@ -614,7 +614,6 @@ public class FaceChatGreetingQueueMode implements ChatMode { | |||
| person.put("lastGreetTime", message.last_greet_time); // 打招呼的时间 | |||
| person.put("directionType", message.position_type.ordinal()); // 运动的方向 | |||
| Skill skill = SkillDbHelper.getInstance().getByType(message.tag); | |||
| Log.e("testtrack", "skill " + skill); | |||
| if (skill != null) { | |||
| person.put("skillStatus", skill.status); | |||
| person.put("resp", skill.resp); | |||
| @@ -206,7 +206,7 @@ public class FaceManager implements ChatMode { | |||
| long postDelay = 0; | |||
| if (IdentityMode.MODE_CURRENT.equals(IdentityMode.MODE_CHAT)) { | |||
| // 工作模式和闲聊模式 | |||
| postDelay = mFaceChatMode.detectFace(textureBitmap); | |||
| postDelay = mFaceChatReasonMode.detectFace(textureBitmap); | |||
| } else { | |||
| // 接待模式 | |||
| postDelay = mFaceReceptionMode.detectFace(textureBitmap); | |||
| @@ -0,0 +1,11 @@ | |||
| package com.aispeech.nativedemo.iat; | |||
| public class AsrMessageItem { | |||
| public String sessionId; | |||
| public String display; | |||
| public AsrMessageItem(String sessionId, String display) { | |||
| this.sessionId = sessionId; | |||
| this.display = display; | |||
| } | |||
| } | |||
| @@ -0,0 +1,7 @@ | |||
| package com.aispeech.nativedemo.iat; | |||
| public class AsrResult { | |||
| public String asr = ""; | |||
| public AsrMessageItem asrMessageItem; | |||
| } | |||
| @@ -8,6 +8,8 @@ import android.util.Log; | |||
| import com.aispeech.nativedemo.DuiApplication; | |||
| import com.aispeech.nativedemo.log.Logger; | |||
| import com.aispeech.nativedemo.network.ws.MessageUtils; | |||
| import com.aispeech.nativedemo.rtasr.RTASRZXManager; | |||
| import com.aispeech.nativedemo.rtasr.RecognizerExtendResult; | |||
| import com.iflytek.cloud.ErrorCode; | |||
| import com.iflytek.cloud.InitListener; | |||
| import com.iflytek.cloud.RecognizerListener; | |||
| @@ -21,34 +23,34 @@ import org.json.JSONObject; | |||
| import java.io.FileInputStream; | |||
| import java.io.IOException; | |||
| import java.io.InputStream; | |||
| import java.net.URL; | |||
| import java.nio.charset.Charset; | |||
| import java.text.SimpleDateFormat; | |||
| import java.util.Base64; | |||
| import java.util.Date; | |||
| import java.util.HashMap; | |||
| import java.util.LinkedHashMap; | |||
| import java.util.Locale; | |||
| import java.util.TimeZone; | |||
| import javax.crypto.Mac; | |||
| import javax.crypto.spec.SecretKeySpec; | |||
| import okhttp3.HttpUrl; | |||
| import okhttp3.OkHttpClient; | |||
| import okhttp3.Request; | |||
| import java.util.List; | |||
| import java.util.concurrent.ConcurrentHashMap; | |||
| import java.util.concurrent.ConcurrentLinkedQueue; | |||
| public class IatManager { | |||
| public static final String ASR_ERROR = "ASR_ERROR"; | |||
| private static String TAG = IatManager.class.getSimpleName(); | |||
| private static IatManager mInstance; | |||
| private Context mContext; | |||
| private SpeechRecognizer mIat; | |||
| private StringBuffer buffer = new StringBuffer(); | |||
| private HashMap<String, String> mIatResults = new LinkedHashMap<>(); | |||
| private HashMap<String, String> mIatZXResults = new LinkedHashMap<>(); | |||
| private String resultType = "json"; | |||
| private String language = "zh_cn"; | |||
| private String mEngineType = SpeechConstant.TYPE_CLOUD; | |||
| int ret = 0; // 函数调用返回值 | |||
| String mTime; | |||
| private ConcurrentHashMap<String, AsrResult> mTimeStrMap = new ConcurrentHashMap<>(); | |||
| private ConcurrentLinkedQueue<String> mTimeList = new ConcurrentLinkedQueue(); | |||
| private volatile boolean mIsAsring; | |||
| private volatile RTASRZXManager mRTASR; | |||
| private IatManager(Context context) { | |||
| mContext = context; | |||
| initIat(); | |||
| @@ -67,6 +69,8 @@ public class IatManager { | |||
| private void initIat(){ | |||
| mIat = SpeechRecognizer.createRecognizer(mContext, mInitListener); | |||
| mRTASR = new RTASRZXManager(); | |||
| mRTASR.startBackgroundThread(); | |||
| } | |||
| private InitListener mInitListener = new InitListener() { | |||
| @@ -123,11 +127,10 @@ public class IatManager { | |||
| // mIat.setParameter(SpeechConstant.ASR_AUDIO_PATH, mContext.getExternalFilesDir("msc").getAbsolutePath() + "/iat.wav"); | |||
| } | |||
| int ret = 0; // 函数调用返回值 | |||
| public void executeStream(String filePath) { | |||
| buffer.setLength(0); | |||
| mIatResults.clear(); | |||
| mIatZXResults.clear(); | |||
| // 设置参数 | |||
| setParam(); | |||
| // 设置音频来源为外部文件 | |||
| @@ -141,7 +144,7 @@ public class IatManager { | |||
| return; | |||
| } | |||
| try (FileInputStream open = new FileInputStream(filePath)) { | |||
| // InputStream open = getAssets().open("iattest.wav"); | |||
| //InputStream open = getAssets().open("iattest.wav"); | |||
| byte[] buff = new byte[1280]; | |||
| while (open.available() > 0) { | |||
| int read = open.read(buff); | |||
| @@ -154,12 +157,67 @@ public class IatManager { | |||
| } | |||
| } | |||
| public boolean executeStreamByte(String time, List<byte[]> buffs) { | |||
| if (mIsAsring) { | |||
| Log.e("testkd", "asr vad listsize " + mTimeList.size() + " isArs " + mIsAsring); | |||
| return false; | |||
| } | |||
| // 也可以像以下这样直接设置音频文件路径识别(要求设置文件在sdcard上的全路径): | |||
| // mIat.setParameter(SpeechConstant.AUDIO_SOURCE, "-2"); | |||
| // mIat.setParameter(SpeechConstant.ASR_SOURCE_PATH, "sdcard/XXX/XXX.pcm"); | |||
| //if (!mIat.isListening()) | |||
| { | |||
| Log.e("testkd", "start listen " + Thread.currentThread() + " buffer size " + buffs.size()); | |||
| buffer.setLength(0); | |||
| mIatResults.clear(); | |||
| mIatZXResults.clear(); | |||
| // 设置参数 | |||
| setParam(); | |||
| // 设置音频来源为外部文件 | |||
| mIat.setParameter(SpeechConstant.AUDIO_SOURCE, "-1"); | |||
| ret = mIat.startListening(mRecognizerListener); | |||
| if (ret != ErrorCode.SUCCESS) { | |||
| Logger.e("djTtsTime-" + "识别失败,错误码:" + ret + ",请点击网址https://www.xfyun.cn/document/error-code查询解决方案"); | |||
| return true; | |||
| } | |||
| } | |||
| for (byte[] buff: buffs) { | |||
| mIat.writeAudio(buff, 0, buff.length); | |||
| } | |||
| mIat.stopListening(); | |||
| mTime = time; | |||
| mTimeList.offer(time); | |||
| mTimeStrMap.put(String.valueOf(mTime), new AsrResult()); | |||
| if (mTimeList.size() > 1) { | |||
| Log.e("testtest", "asr vad listsize " + mTimeList.size()); | |||
| } | |||
| return true; | |||
| } | |||
| public boolean executeStreamZXByte(String time, List<byte[]> buffs) { | |||
| if (mIsAsring) { | |||
| Log.e("testkd", "asr vad listsize " + mTimeList.size() + " isArs " + mIsAsring); | |||
| return false; | |||
| } | |||
| mIatZXResults.clear(); | |||
| mRTASR.startAsr(buffs, mRecognizerZXListener); | |||
| mTime = time; | |||
| mTimeList.offer(time); | |||
| mTimeStrMap.put(String.valueOf(mTime), new AsrResult()); | |||
| if (mTimeList.size() > 1) { | |||
| Log.e("testtest", "asr vad listsize " + mTimeList.size()); | |||
| } | |||
| return true; | |||
| } | |||
| private RecognizerListener mRecognizerListener = new RecognizerListener() { | |||
| @Override | |||
| public void onBeginOfSpeech() { | |||
| // 此回调表示:sdk内部录音机已经准备好了,用户可以开始语音输入 | |||
| Logger.e("djTtsTime-" + "开始说话"); | |||
| Log.e("testkd", "listen begin "); | |||
| } | |||
| @Override | |||
| @@ -167,27 +225,41 @@ public class IatManager { | |||
| // Tips: | |||
| // 错误码:10118(您没有说话),可能是录音机权限被禁,需要提示用户打开应用的录音权限。 | |||
| Logger.e("djTtsTime-" + "onError " + error.getPlainDescription(true)); | |||
| mIsAsring = false; | |||
| onAsrResult(ASR_ERROR); | |||
| Log.e("testkd", "listen error " + error.getErrorCode()); | |||
| } | |||
| @Override | |||
| public void onEndOfSpeech() { | |||
| // 此回调表示:检测到了语音的尾端点,已经进入识别过程,不再接受语音输入 | |||
| Logger.e("djTtsTime-" + "结束说话"); | |||
| mIsAsring = true; | |||
| Log.e("testkd", "listen end"); | |||
| } | |||
| @Override | |||
| public void onResult(RecognizerResult results, boolean isLast) { | |||
| mIsAsring = false; | |||
| Logger.e(results.getResultString()); | |||
| if (resultType.equals("json")) { | |||
| printResult(results); | |||
| if (isLast) { | |||
| Logger.e("djTtsTime-" + "onResult 结束"); | |||
| StringBuffer resultBuffer = new StringBuffer(); | |||
| //resultBuffer.append("科大讯飞转写结果: "); | |||
| for (String key : mIatResults.keySet()) { | |||
| resultBuffer.append(mIatResults.get(key)); | |||
| } | |||
| String res = resultBuffer.toString(); | |||
| MessageUtils.sendAsrText(res); | |||
| //MessageUtils.sendAsrText(res); | |||
| if (TextUtils.isEmpty(res)) { | |||
| res = ASR_ERROR; | |||
| } | |||
| onAsrResult(res); | |||
| Log.e("testkd", "讯飞实时听写 last result " + res + " time " + mTime); | |||
| Logger.e( "djASR-讯飞实时听写: "+ res); | |||
| Logger.e("djTtsTime-" + "ASR转写结果: " + res); | |||
| } | |||
| return; | |||
| @@ -195,6 +267,7 @@ public class IatManager { | |||
| if (resultType.equals("plain")) { | |||
| buffer.append(results.getResultString()); | |||
| Logger.e("djTtsTime-" + "听写结果: " + buffer.toString()); | |||
| Log.e("testkd", "plain " + buffer.toString()); | |||
| } | |||
| } | |||
| @@ -214,6 +287,96 @@ public class IatManager { | |||
| } | |||
| }; | |||
| private RecognizerListener mRecognizerZXListener = new RecognizerListener() { | |||
| @Override | |||
| public void onBeginOfSpeech() { | |||
| // 此回调表示:sdk内部录音机已经准备好了,用户可以开始语音输入 | |||
| Logger.e("djTtsTime-" + "开始说话"); | |||
| Log.e("testkd", "listen begin "); | |||
| } | |||
| @Override | |||
| public void onError(SpeechError error) { | |||
| // Tips: | |||
| // 错误码:10118(您没有说话),可能是录音机权限被禁,需要提示用户打开应用的录音权限。 | |||
| Logger.e("djTtsTime-" + "onError " + error.getPlainDescription(true)); | |||
| mIsAsring = false; | |||
| onAsrResult(ASR_ERROR); | |||
| Log.e("testkd", "listen error " + error.getErrorCode()); | |||
| } | |||
| @Override | |||
| public void onEndOfSpeech() { | |||
| // 此回调表示:检测到了语音的尾端点,已经进入识别过程,不再接受语音输入 | |||
| Logger.e("djTtsTime-" + "结束说话"); | |||
| mIsAsring = true; | |||
| Log.e("testkd", "listen end"); | |||
| } | |||
| @Override | |||
| public void onResult(RecognizerResult results, boolean isLast) { | |||
| mIsAsring = false; | |||
| Logger.e(results.getResultString()); | |||
| printResultZX(results); | |||
| if (isLast) { | |||
| Logger.e("djTtsTime-" + "onResult 结束"); | |||
| StringBuffer resultBuffer = new StringBuffer(); | |||
| //resultBuffer.append("科大讯飞转写结果: "); | |||
| for (String key : mIatZXResults.keySet()) { | |||
| resultBuffer.append(mIatZXResults.get(key)); | |||
| } | |||
| String res = resultBuffer.toString(); | |||
| //MessageUtils.sendAsrText(res); | |||
| if (TextUtils.isEmpty(res)) { | |||
| res = ASR_ERROR; | |||
| } | |||
| onAsrResult(res); | |||
| Log.e("testkd", "讯飞实时语音转写 last result " + res + " time " + mTime); | |||
| Logger.e("djASR-讯飞实时语音转写: " + res); | |||
| } | |||
| } | |||
| @Override | |||
| public void onVolumeChanged(int volume, byte[] data) { | |||
| // Logger.e("djTtsTime-" + "当前正在说话,音量大小 = " + volume + " 返回音频数据 = " + data.length); | |||
| } | |||
| @Override | |||
| public void onEvent(int eventType, int arg1, int arg2, Bundle obj) { | |||
| // 以下代码用于获取与云端的会话id,当业务出错时将会话id提供给技术支持人员,可用于查询会话日志,定位出错原因 | |||
| // 若使用本地能力,会话id为null | |||
| // if (SpeechEvent.EVENT_SESSION_ID == eventType) { | |||
| // String sid = obj.getString(SpeechEvent.KEY_EVENT_SESSION_ID); | |||
| // Log.d(TAG, "session id =" + sid); | |||
| // } | |||
| } | |||
| }; | |||
| private void onAsrResult(String res) { | |||
| String time = mTimeList.poll(); | |||
| if (time != null && time.equals(mTime)) { | |||
| Log.e("testtest", "time is equal "); | |||
| } | |||
| if (mTime == null) { | |||
| Log.e("testkd", "res " + res + " mtime is null"); | |||
| } | |||
| AsrResult result = mTimeStrMap.get(mTime); | |||
| if (result != null) { | |||
| result.asr = res; | |||
| if (result.asrMessageItem != null) { | |||
| MessageUtils.sendXFASRResult(result.asrMessageItem.sessionId, res, mTime); | |||
| } else if (!TextUtils.isEmpty(res)){ | |||
| MessageUtils.sendXFASRResult("", res, mTime); | |||
| } | |||
| removeAsr(mTime); | |||
| } else { | |||
| Log.e("testtest", "result is null "); | |||
| } | |||
| } | |||
| private void printResult(RecognizerResult results) { | |||
| // String text = JsonParser.parseIatResult(results.getResultString()); | |||
| // String sn = null; | |||
| @@ -238,9 +401,11 @@ public class IatManager { | |||
| sn = resultJson.optString("sn"); | |||
| pgs = resultJson.optString("pgs"); | |||
| rg = resultJson.optString("rg"); | |||
| Log.e("testkd", resultJson.toString()); | |||
| } catch (JSONException e) { | |||
| e.printStackTrace(); | |||
| } | |||
| //如果pgs是rpl就在已有的结果中删除掉要覆盖的sn部分 | |||
| if (pgs.equals("rpl")) { | |||
| String[] strings = rg.replace("[", "").replace("]", "").split(","); | |||
| @@ -253,4 +418,35 @@ public class IatManager { | |||
| mIatResults.put(sn, text); | |||
| } | |||
| private void printResultZX(RecognizerResult results) { | |||
| if (results instanceof RecognizerExtendResult) { | |||
| String text = results.getResultString(); | |||
| int segId = ((RecognizerExtendResult) results).seg_id; | |||
| mIatZXResults.put(String.valueOf(segId), text); | |||
| } | |||
| } | |||
| public String getASRStr(String mTime) { | |||
| AsrResult result = mTimeStrMap.get(mTime); | |||
| if (result == null) { | |||
| return null; | |||
| } | |||
| return result.asr; | |||
| } | |||
| public AsrResult getASRResult(String mTime) { | |||
| AsrResult result = mTimeStrMap.get(mTime); | |||
| return result; | |||
| } | |||
| public String pollASRStr() { | |||
| String time = mTimeList.poll(); | |||
| return getASRStr(String.valueOf(time)); | |||
| } | |||
| public void removeAsr(String key) { | |||
| mTimeStrMap.remove(key); | |||
| } | |||
| } | |||
| @@ -40,6 +40,11 @@ public class LogFile { | |||
| return saveFile(logMessage,"tts-total-time-"); | |||
| } | |||
| public static String saveDjTtsASRFile(String logMessage) { | |||
| logMessage = logMessage.replace("djASR-", ""); | |||
| return saveFile(logMessage,"asr-"); | |||
| } | |||
| /** | |||
| * 保存日志文件 | |||
| * @param logMessage 日志消息 | |||
| @@ -381,6 +381,8 @@ final class LoggerPrinter implements Printer { | |||
| LogFile.saveDjLogMessageFile(chunk); | |||
| } else if(chunk.contains("djTtsTime")){ | |||
| LogFile.saveDjTtsTimeFile(chunk); | |||
| } else if (chunk.contains("djASR")) { | |||
| LogFile.saveDjTtsASRFile(chunk); | |||
| } else{ | |||
| LogFile.saveLogMessageFile(chunk); | |||
| } | |||
| @@ -25,9 +25,10 @@ import androidx.recyclerview.widget.LinearLayoutManager; | |||
| import androidx.recyclerview.widget.RecyclerView; | |||
| import com.aispeech.nativedemo.R; | |||
| import com.bumptech.glide.Glide; | |||
| import com.bumptech.glide.request.target.Target; | |||
| import com.google.android.material.navigation.NavigationView; | |||
| import com.google.gson.Gson; | |||
| import com.squareup.picasso.Picasso; | |||
| import org.json.JSONObject; | |||
| @@ -126,9 +127,9 @@ public class PlayerActivity extends AppCompatActivity { | |||
| Observable.just("") | |||
| .observeOn(Schedulers.io()) | |||
| .subscribe(o -> { | |||
| Bitmap bitmap = Picasso.get().load(player.getCurrentMusic().getImageUrl()).get(); | |||
| runOnUiThread(() -> playerAlbum.setImageBitmap(bitmap)); | |||
| }, throwable -> Log.e(TAG, throwable.getLocalizedMessage(), throwable)); | |||
| Glide.with(PlayerActivity.this).load(player.getCurrentMusic().getImageUrl()).into(playerAlbum); | |||
| } else { | |||
| playerAlbum.setImageResource(R.drawable.default_record_album); | |||
| } | |||
| @@ -1,5 +1,7 @@ | |||
| package com.aispeech.nativedemo.network.ws; | |||
| import static com.aispeech.nativedemo.iat.IatManager.ASR_ERROR; | |||
| import android.text.TextUtils; | |||
| import android.util.Log; | |||
| @@ -15,8 +17,10 @@ import com.aispeech.nativedemo.entity.Model; | |||
| import com.aispeech.nativedemo.entity.Skill; | |||
| import com.aispeech.nativedemo.face.FaceChatMode; | |||
| import com.aispeech.nativedemo.face.IdentityMode; | |||
| import com.aispeech.nativedemo.iat.AsrMessageItem; | |||
| import com.aispeech.nativedemo.iat.AsrResult; | |||
| import com.aispeech.nativedemo.iat.IatManager; | |||
| import com.aispeech.nativedemo.log.Logger; | |||
| import com.aispeech.nativedemo.shape.ShapeManager; | |||
| import com.aispeech.nativedemo.utils.StatusUtils; | |||
| import com.aispeech.nativedemo.utils.Utils; | |||
| @@ -140,7 +144,7 @@ public class MessageUtils { | |||
| } | |||
| } | |||
| public static String sendSkill(String sessionId, String display, String dmInput){ | |||
| public static String sendSkill(String sessionId, String display, String dmInput, String time){ | |||
| String readText = ""; | |||
| try { | |||
| JSONObject jo = new JSONObject(); | |||
| @@ -191,6 +195,7 @@ public class MessageUtils { | |||
| } | |||
| skillObj.put("sessionId", sessionId); | |||
| skillObj.put("dmInput", dmInput); | |||
| skillObj.put("time", time); | |||
| jo.put("data", skillObj.toString()); | |||
| sendMessage(jo.toString()); | |||
| } catch (JSONException e){ | |||
| @@ -201,7 +206,7 @@ public class MessageUtils { | |||
| return readText; | |||
| } | |||
| public static void sendNlpResult(String sessionId, String display, String dmInput){ | |||
| public static void sendNlpResult(String sessionId, String display, String dmInput, String time){ | |||
| if(TextUtils.isEmpty(dmInput)){ | |||
| return; | |||
| } | |||
| @@ -212,6 +217,25 @@ public class MessageUtils { | |||
| data.put("sessionId", sessionId); | |||
| data.put("display", display); | |||
| data.put("dmInput", dmInput); | |||
| data.put("time", time); | |||
| jo.put("data", data.toString()); | |||
| sendMessage(jo.toString()); | |||
| } catch (JSONException e){ | |||
| e.printStackTrace(); | |||
| } | |||
| } | |||
| public static void sendXFASRResult(String sessionId, String dmInput, String time){ | |||
| if(TextUtils.isEmpty(dmInput)){ | |||
| return; | |||
| } | |||
| try { | |||
| JSONObject jo = new JSONObject(); | |||
| jo.put("type", "xfASRResult"); | |||
| JSONObject data = new JSONObject(); | |||
| data.put("sessionId", sessionId); | |||
| data.put("dmInput", dmInput); | |||
| data.put("time", time); | |||
| jo.put("data", data.toString()); | |||
| sendMessage(jo.toString()); | |||
| } catch (JSONException e){ | |||
| @@ -219,21 +243,50 @@ public class MessageUtils { | |||
| } | |||
| } | |||
| public static void sendAsr(String data){ | |||
| public static String sendAsrAndGetRecordId(String data, String time){ | |||
| String recordId = ""; | |||
| try { | |||
| JSONObject jo = new JSONObject(data); | |||
| String txt = jo.optString("text", ""); | |||
| if(!TextUtils.isEmpty(txt)){ | |||
| DuiMessageObserver.mAsrWord = txt; | |||
| if (!TextUtils.isEmpty(txt)) { | |||
| JSONObject person = new JSONObject(); | |||
| JSONObject asrData = new JSONObject(); | |||
| person.put("type", "asr"); | |||
| person.put("data", txt); | |||
| asrData.put("text", txt); | |||
| asrData.put("time", time); | |||
| person.put("data", asrData.toString()); | |||
| sendMessage(person.toString()); | |||
| // ShapeManager.getInstance().nlpFromNewDevGpt(txt); | |||
| } | |||
| recordId = jo.optString("recordId", ""); | |||
| //sendXfMsg(jo, time); | |||
| } catch (JSONException e) { | |||
| e.printStackTrace(); | |||
| } | |||
| return recordId; | |||
| } | |||
| public static void sendXfMsg(JSONObject jo, String time) { | |||
| String sessionId = jo.optString("sessionId", ""); | |||
| if (TextUtils.isEmpty(sessionId)) { | |||
| return ; | |||
| } | |||
| AsrResult result = IatManager.getInstance().getASRResult(time); | |||
| String asrStr = ""; | |||
| if (result != null) { | |||
| asrStr = result.asr; | |||
| } | |||
| if (!TextUtils.isEmpty(asrStr)) { | |||
| if (ASR_ERROR.equals(asrStr)) { | |||
| return; | |||
| } | |||
| // 直接发送自己的后台获取nlp结果 增加mtime | |||
| MessageUtils.sendXFASRResult(sessionId, asrStr, time); | |||
| IatManager.getInstance().removeAsr(time); | |||
| } else if (result != null) { | |||
| String display = jo.optString("display", ""); | |||
| result.asrMessageItem = new AsrMessageItem(sessionId, display); | |||
| } | |||
| } | |||
| public static void sendAsrText(String data){ | |||
| @@ -394,4 +447,15 @@ public class MessageUtils { | |||
| throw new RuntimeException(e); | |||
| } | |||
| } | |||
| public static void sendSkills(String message){ | |||
| try { | |||
| JSONObject jo = new JSONObject(); | |||
| jo.put("type", "skillList"); | |||
| jo.put("data", message); | |||
| sendMessage(jo.toString()); | |||
| } catch (JSONException e) { | |||
| throw new RuntimeException(e); | |||
| } | |||
| } | |||
| } | |||
| @@ -1,388 +0,0 @@ | |||
| package com.aispeech.nativedemo.observer; | |||
| import android.content.Intent; | |||
| import android.util.Log; | |||
| import com.aispeech.dui.dds.DDS; | |||
| import com.aispeech.dui.dds.agent.DMTaskCallback; | |||
| import com.aispeech.dui.dds.agent.MessageObserver; | |||
| import com.aispeech.nativedemo.DuiApplication; | |||
| import com.aispeech.nativedemo.bean.MessageBean; | |||
| import com.aispeech.nativedemo.bean.WeatherBean; | |||
| import com.aispeech.nativedemo.music.PlayerActivity; | |||
| import com.aispeech.nativedemo.widget.pageview.utils.HttpUtil; | |||
| import com.google.gson.Gson; | |||
| import org.json.JSONArray; | |||
| import org.json.JSONException; | |||
| import org.json.JSONObject; | |||
| import java.text.SimpleDateFormat; | |||
| import java.util.Date; | |||
| import java.util.HashMap; | |||
| import java.util.LinkedList; | |||
| import okhttp3.MediaType; | |||
| import okhttp3.RequestBody; | |||
| /** | |||
| * 客户端MessageObserver, 用于处理客户端动作的消息响应. | |||
| */ | |||
| public class DuiMessageObserver implements MessageObserver { | |||
| private final String Tag = "DuiMessageObserver"; | |||
| public DuiMessageObserver() { | |||
| mGson = new Gson(); | |||
| } | |||
| public interface MessageCallback { | |||
| void onMessage(); | |||
| void onState(String state); | |||
| } | |||
| private MessageCallback mMessageCallback; | |||
| private LinkedList<MessageBean> mMessageList; | |||
| private boolean mIsFirstVar = true; | |||
| private boolean mHasvar = false; | |||
| private Gson mGson; | |||
| private String[] mSubscribeKeys = new String[]{ | |||
| "sys.dialog.state", | |||
| "context.output.text", | |||
| "context.input.text", | |||
| "context.widget.content", | |||
| "context.widget.list", | |||
| "context.widget.web", | |||
| "context.widget.media", | |||
| "context.widget.custom" | |||
| }; | |||
| // 注册当前更新消息 | |||
| public void regist(MessageCallback messageCallback, LinkedList<MessageBean> msgList) { | |||
| mMessageCallback = messageCallback; | |||
| mMessageList = msgList; | |||
| DDS.getInstance().getAgent().subscribe(mSubscribeKeys, this); | |||
| DDS.getInstance().getAgent().setDMTaskCallback(new DMTaskCallback() { | |||
| @Override | |||
| public JSONObject onDMTaskResult(JSONObject jsonObject, Type type) { | |||
| if(type == Type.DM_OUTPUT){ | |||
| String display = jsonObject.optString("display"); | |||
| String nlg = jsonObject.optString("nlg"); | |||
| Log.e(Tag, display); | |||
| Log.e(Tag, nlg); | |||
| // if(JSONUtils.isJson(display)){ | |||
| // try { | |||
| // JSONObject object = new JSONObject(display); | |||
| // String url = object.optString("url"); | |||
| // String newStr = getMsg(url); | |||
| // jsonObject.put("display", newStr); | |||
| // jsonObject.put("nlg", newStr); | |||
| // | |||
| // } catch (JSONException e) { | |||
| // throw new RuntimeException(e); | |||
| // } | |||
| // } | |||
| } | |||
| return jsonObject; | |||
| } | |||
| }); | |||
| } | |||
| private String getMsg(String url){ | |||
| HashMap<String, String> postParam = new HashMap<>(); | |||
| postParam.put("knowledge", "卫生间"); | |||
| RequestBody requestBody = RequestBody.create(MediaType.parse("application/json"), new JSONObject(postParam).toString()); | |||
| String result = HttpUtil.post(url, requestBody, null, null); | |||
| try { | |||
| JSONObject obj = new JSONObject(result); | |||
| String extra = obj.optString("extra"); | |||
| JSONObject resultObj = new JSONObject(extra); | |||
| String resultStr = resultObj.optString("result"); | |||
| // DDS.getInstance().getAgent().getTTSEngine().speak(result, 1); | |||
| // DDS.getInstance().getAgent().getTTSEngine().speak(resultStr, 1, "10001", AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK); | |||
| return resultStr; | |||
| // clearVar(); | |||
| // MessageBean bean = new MessageBean(); | |||
| // bean.setText(resultStr); | |||
| // bean.setType(MessageBean.TYPE_OUTPUT); | |||
| // mMessageList.add(bean); | |||
| // if (mMessageCallback != null) { | |||
| // mMessageCallback.onMessage(); | |||
| // } | |||
| // JSONTokener t = new JSONTokener(success); | |||
| // JSONObject obj = (JSONObject) t.nextValue(); | |||
| // int status = -1001001; | |||
| // String errMsg = ""; | |||
| // | |||
| // if (obj.has("status")) { | |||
| // status = obj.getInt("status"); | |||
| // } | |||
| // if (obj.has("errMsg")) { | |||
| // errMsg = obj.getString("errMsg"); | |||
| // } | |||
| // | |||
| // if (status == 0) { | |||
| // | |||
| // } else { | |||
| // onFail(errMsg); | |||
| // } | |||
| } catch (Exception e) { | |||
| e.printStackTrace(); | |||
| } | |||
| // HttpUtil.post(url, requestBody, null, new HttpUtil.HttpCallback<String>() { | |||
| // @Override | |||
| // public void onSuccess(String success) { | |||
| // try { | |||
| // JSONObject obj = new JSONObject(success); | |||
| // String extra = obj.optString("extra"); | |||
| // JSONObject resultObj = new JSONObject(extra); | |||
| // String result = resultObj.optString("result"); | |||
| //// DDS.getInstance().getAgent().getTTSEngine().speak(result, 1); | |||
| // DDS.getInstance().getAgent().getTTSEngine().speak(result, 1, "10001", AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK); | |||
| // | |||
| // clearVar(); | |||
| // MessageBean bean = new MessageBean(); | |||
| // bean.setText(result); | |||
| // bean.setType(MessageBean.TYPE_OUTPUT); | |||
| // mMessageList.add(bean); | |||
| // if (mMessageCallback != null) { | |||
| // mMessageCallback.onMessage(); | |||
| // } | |||
| //// JSONTokener t = new JSONTokener(success); | |||
| //// JSONObject obj = (JSONObject) t.nextValue(); | |||
| //// int status = -1001001; | |||
| //// String errMsg = ""; | |||
| //// | |||
| //// if (obj.has("status")) { | |||
| //// status = obj.getInt("status"); | |||
| //// } | |||
| //// if (obj.has("errMsg")) { | |||
| //// errMsg = obj.getString("errMsg"); | |||
| //// } | |||
| //// | |||
| //// if (status == 0) { | |||
| //// | |||
| //// } else { | |||
| //// onFail(errMsg); | |||
| //// } | |||
| // } catch (Exception e) { | |||
| // e.printStackTrace(); | |||
| // onFail(e.getMessage()); | |||
| // } | |||
| // } | |||
| // | |||
| // @Override | |||
| // public void onFail(String error) { | |||
| // | |||
| // } | |||
| // }); | |||
| return ""; | |||
| } | |||
| // 注销当前更新消息 | |||
| public void unregist() { | |||
| DDS.getInstance().getAgent().unSubscribe(this); | |||
| } | |||
| private void clearVar() { | |||
| if (mHasvar) { | |||
| mMessageList.pollLast(); | |||
| } | |||
| } | |||
| @Override | |||
| public void onMessage(String message, String data) { | |||
| Log.d(Tag, "message : " + message + " data : " + data); | |||
| MessageBean bean = null; | |||
| switch (message) { | |||
| case "context.output.text": | |||
| // clearVar(); | |||
| // bean = new MessageBean(); | |||
| // String txt = ""; | |||
| // try { | |||
| // JSONObject jo = new JSONObject(data); | |||
| // txt = jo.optString("text", ""); | |||
| // } catch (JSONException e) { | |||
| // e.printStackTrace(); | |||
| // } | |||
| // bean.setText(txt); | |||
| // bean.setType(MessageBean.TYPE_OUTPUT); | |||
| // mMessageList.add(bean); | |||
| // if (mMessageCallback != null) { | |||
| // mMessageCallback.onMessage(); | |||
| // } | |||
| String txt = ""; | |||
| try { | |||
| JSONObject jo = new JSONObject(data); | |||
| txt = jo.optString("text", ""); | |||
| JSONObject person = new JSONObject(); | |||
| person.put("type", "chatMessage"); | |||
| person.put("content", txt); | |||
| String person_msg = person.toString(); | |||
| long time = System.currentTimeMillis(); | |||
| Date date = new Date(time); | |||
| SimpleDateFormat spTime = new SimpleDateFormat("yyyy-MM-dd/HH:mm:ss");//yyyy-MM-dd/HH:mm:ss HH24 hh12 | |||
| String sT = spTime.format(date); | |||
| Log.e(Tag, "sendMsg: " + person_msg + "/" + sT); | |||
| // WebSocketManager.getInstance(DuiApplication.getContext()).sendMsg(person_msg); | |||
| } catch (JSONException e) { | |||
| e.printStackTrace(); | |||
| } | |||
| break; | |||
| case "context.input.text": | |||
| bean = new MessageBean(); | |||
| try { | |||
| JSONObject jo = new JSONObject(data); | |||
| if (jo.has("var")) { | |||
| String var = jo.optString("var", ""); | |||
| if (mIsFirstVar) { | |||
| mIsFirstVar = false; | |||
| mHasvar = true; | |||
| bean.setText(var); | |||
| bean.setType(MessageBean.TYPE_INPUT); | |||
| mMessageList.add(bean); | |||
| if (mMessageCallback != null) { | |||
| mMessageCallback.onMessage(); | |||
| } | |||
| } else { | |||
| mMessageList.pollLast(); | |||
| bean.setText(var); | |||
| bean.setType(MessageBean.TYPE_INPUT); | |||
| mMessageList.add(bean); | |||
| if (mMessageCallback != null) { | |||
| mMessageCallback.onMessage(); | |||
| } | |||
| } | |||
| } | |||
| if (jo.has("text")) { | |||
| if (mHasvar) { | |||
| mMessageList.pollLast(); | |||
| mHasvar = false; | |||
| mIsFirstVar = true; | |||
| } | |||
| String text = jo.optString("text", ""); | |||
| bean.setText(text); | |||
| bean.setType(MessageBean.TYPE_INPUT); | |||
| mMessageList.add(bean); | |||
| if (mMessageCallback != null) { | |||
| mMessageCallback.onMessage(); | |||
| } | |||
| } | |||
| } catch (JSONException e) { | |||
| e.printStackTrace(); | |||
| } | |||
| break; | |||
| case "context.widget.content": | |||
| bean = new MessageBean(); | |||
| try { | |||
| JSONObject jo = new JSONObject(data); | |||
| String title = jo.optString("title", ""); | |||
| String subTitle = jo.optString("subTitle", ""); | |||
| String imgUrl = jo.optString("imageUrl", ""); | |||
| bean.setTitle(title); | |||
| bean.setSubTitle(subTitle); | |||
| bean.setImgUrl(imgUrl); | |||
| bean.setType(MessageBean.TYPE_WIDGET_CONTENT); | |||
| mMessageList.add(bean); | |||
| if (mMessageCallback != null) { | |||
| mMessageCallback.onMessage(); | |||
| } | |||
| } catch (JSONException e) { | |||
| e.printStackTrace(); | |||
| } | |||
| break; | |||
| case "context.widget.list": | |||
| bean = new MessageBean(); | |||
| try { | |||
| JSONObject jo = new JSONObject(data); | |||
| JSONArray array = jo.optJSONArray("content"); | |||
| if (array == null || array.length() == 0) { | |||
| return; | |||
| } | |||
| for (int i = 0; i < array.length(); i++) { | |||
| JSONObject object = array.optJSONObject(i); | |||
| String title = object.optString("title", ""); | |||
| String subTitle = object.optString("subTitle", ""); | |||
| MessageBean b = new MessageBean(); | |||
| b.setTitle(title); | |||
| b.setSubTitle(subTitle); | |||
| bean.addMessageBean(b); | |||
| } | |||
| int currentPage = jo.optInt("currentPage"); | |||
| bean.setCurrentPage(currentPage); | |||
| bean.setType(MessageBean.TYPE_WIDGET_LIST); | |||
| int itemsPerPage = jo.optInt("itemsPerPage"); | |||
| bean.setItemsPerPage(itemsPerPage); | |||
| mMessageList.add(bean); | |||
| if (mMessageCallback != null) { | |||
| mMessageCallback.onMessage(); | |||
| } | |||
| } catch (JSONException e) { | |||
| e.printStackTrace(); | |||
| } | |||
| break; | |||
| case "context.widget.web": | |||
| bean = new MessageBean(); | |||
| try { | |||
| JSONObject jo = new JSONObject(data); | |||
| String url = jo.optString("url"); | |||
| bean.setUrl(url); | |||
| bean.setType(MessageBean.TYPE_WIDGET_WEB); | |||
| mMessageList.add(bean); | |||
| if (mMessageCallback != null) { | |||
| mMessageCallback.onMessage(); | |||
| } | |||
| } catch (JSONException e) { | |||
| e.printStackTrace(); | |||
| } | |||
| break; | |||
| case "context.widget.custom": | |||
| bean = new MessageBean(); | |||
| try { | |||
| JSONObject jo = new JSONObject(data); | |||
| String name = jo.optString("name"); | |||
| if (name.equals("weather")) { | |||
| bean.setWeatherBean(mGson.fromJson(data, WeatherBean.class)); | |||
| bean.setType(MessageBean.TYPE_WIDGET_WEATHER); | |||
| mMessageList.add(bean); | |||
| if (mMessageCallback != null) { | |||
| mMessageCallback.onMessage(); | |||
| } | |||
| } | |||
| } catch (JSONException e) { | |||
| e.printStackTrace(); | |||
| } | |||
| break; | |||
| case "context.widget.media": | |||
| JSONObject jsonObject; | |||
| int count = 0; | |||
| String name = ""; | |||
| try { | |||
| jsonObject = new JSONObject(data); | |||
| count = jsonObject.optInt("count"); | |||
| name = jsonObject.optString("widgetName"); | |||
| } catch (JSONException e) { | |||
| e.printStackTrace(); | |||
| } | |||
| if (count > 0) { | |||
| Intent intent = new Intent(DuiApplication.getContext(), PlayerActivity.class); | |||
| intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | |||
| intent.putExtra("data", data); | |||
| DuiApplication.getContext().startActivity(intent); | |||
| } | |||
| break; | |||
| case "sys.dialog.state": | |||
| if (mMessageCallback != null) { | |||
| mMessageCallback.onState(data); | |||
| } | |||
| break; | |||
| default: | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,31 @@ | |||
| package com.aispeech.nativedemo.rtasr; | |||
| import org.java_websocket.drafts.Draft; | |||
| import org.java_websocket.drafts.Draft_6455; | |||
| import org.java_websocket.handshake.ClientHandshakeBuilder; | |||
| import com.sun.istack.internal.NotNull; | |||
| @SuppressWarnings("deprecation") | |||
| public class DraftWithOrigin extends Draft_6455 { | |||
| private String originUrl; | |||
| public DraftWithOrigin(String originUrl) { | |||
| this.originUrl = originUrl; | |||
| } | |||
| @Override | |||
| public Draft copyInstance() { | |||
| System.out.println(originUrl); | |||
| return new DraftWithOrigin(originUrl); | |||
| } | |||
| @NotNull | |||
| @Override | |||
| public ClientHandshakeBuilder postProcessHandshakeRequestAsClient(@NotNull ClientHandshakeBuilder request) { | |||
| super.postProcessHandshakeRequestAsClient(request); | |||
| request.put("Origin", originUrl); | |||
| return request; | |||
| } | |||
| } | |||
| @@ -0,0 +1,340 @@ | |||
| package com.aispeech.nativedemo.rtasr; | |||
| import android.os.Handler; | |||
| import android.os.HandlerThread; | |||
| import android.util.Log; | |||
| import com.aispeech.nativedemo.rtasr.util.EncryptUtil; | |||
| import com.iflytek.cloud.RecognizerListener; | |||
| import com.iflytek.cloud.SpeechError; | |||
| import org.java_websocket.client.WebSocketClient; | |||
| import org.java_websocket.drafts.Draft; | |||
| import org.java_websocket.enums.ReadyState; | |||
| import org.java_websocket.handshake.ServerHandshake; | |||
| import org.json.JSONArray; | |||
| import org.json.JSONObject; | |||
| import org.json.JSONTokener; | |||
| import java.io.UnsupportedEncodingException; | |||
| import java.net.URI; | |||
| import java.net.URLEncoder; | |||
| import java.nio.ByteBuffer; | |||
| import java.security.cert.CertificateException; | |||
| import java.security.cert.X509Certificate; | |||
| import java.text.SimpleDateFormat; | |||
| import java.util.Date; | |||
| import java.util.List; | |||
| import java.util.Objects; | |||
| import java.util.concurrent.CountDownLatch; | |||
| import javax.net.ssl.SSLContext; | |||
| import javax.net.ssl.TrustManager; | |||
| import javax.net.ssl.X509TrustManager; | |||
| /** | |||
| * 实时转写调用demo | |||
| * 此demo只是一个简单的调用示例,不适合用到实际生产环境中 | |||
| * | |||
| * @author white | |||
| * | |||
| */ | |||
| public class RTASRTestJSON { | |||
| // appid | |||
| private static final String APPID = "948cf4b6"; | |||
| // appid对应的secret_key | |||
| private static final String SECRET_KEY = "fe5032b61525baa92d4d0baf1bb18713"; | |||
| // 请求地址 | |||
| private static final String HOST = "rtasr.xfyun.cn/v1/ws"; | |||
| private static final String BASE_URL = "wss://" + HOST; | |||
| private static final String ORIGIN = "https://" + HOST; | |||
| private static final String BASE_HTTP_URL = "ws://" + HOST; | |||
| private static final String ORIGIN_HTTP = "http://" + HOST; | |||
| // 音频文件路径 | |||
| private static final String AUDIO_PATH = "./resource/test_1.pcm"; | |||
| // 每次发送的数据大小 1280 字节 | |||
| private static final int CHUNCKED_SIZE = 1280; | |||
| private static final SimpleDateFormat sdf = new SimpleDateFormat("yyy-MM-dd HH:mm:ss.SSS"); | |||
| private static final String HANDLE_THREAD_NAME = "RTASR"; | |||
| private static RecognizerListener mRecognizerListener; | |||
| private static List<byte[]> buffs; | |||
| private HandlerThread backgroundThread; | |||
| private Handler backgroundHandler; | |||
| private final Runnable asrRunnable = new Runnable() { | |||
| @Override | |||
| public void run() { | |||
| try { | |||
| testAsr(); | |||
| } catch (Exception e) { | |||
| throw new RuntimeException(e); | |||
| } | |||
| } | |||
| }; | |||
| public void startBackgroundThread() { | |||
| backgroundThread = new HandlerThread(HANDLE_THREAD_NAME); | |||
| backgroundThread.start(); | |||
| backgroundHandler = new Handler(backgroundThread.getLooper()); | |||
| } | |||
| public void startAsr(List<byte[]> buffs, RecognizerListener recognizerListener) { | |||
| mRecognizerListener = recognizerListener; | |||
| this.buffs = buffs; | |||
| backgroundHandler.post(asrRunnable); | |||
| } | |||
| public static void testAsr() throws Exception { | |||
| while (true) { | |||
| URI url = new URI(BASE_URL + getHandShakeParams(APPID, SECRET_KEY)); | |||
| DraftWithOrigin draft = new DraftWithOrigin(ORIGIN); | |||
| CountDownLatch handshakeSuccess = new CountDownLatch(1); | |||
| CountDownLatch connectClose = new CountDownLatch(1); | |||
| MyWebSocketClient client = new MyWebSocketClient(url, draft, handshakeSuccess, connectClose); | |||
| client.connect(); | |||
| while (!client.getReadyState().equals(ReadyState.OPEN)) { | |||
| System.out.println(getCurrentTimeStr() + "\t连接中"); | |||
| Thread.sleep(1000); | |||
| } | |||
| // 等待握手成功 | |||
| handshakeSuccess.await(); | |||
| System.out.println(sdf.format(new Date()) + " 开始发送音频数据"); | |||
| // 发送音频 | |||
| //byte[] bytes = new byte[CHUNCKED_SIZE]; | |||
| { | |||
| int len = -1; | |||
| long lastTs = 0; | |||
| for ( byte[] buff: buffs) { | |||
| if (len < CHUNCKED_SIZE) { | |||
| //bytes = Arrays.copyOfRange(bytes, 0, len) | |||
| //send(client, ); | |||
| //break; | |||
| } | |||
| long curTs = System.currentTimeMillis(); | |||
| if (lastTs == 0) { | |||
| lastTs = System.currentTimeMillis(); | |||
| } else { | |||
| long s = curTs - lastTs; | |||
| if (s < 40) { | |||
| System.out.println("error time interval: " + s + " ms"); | |||
| } | |||
| } | |||
| send(client, buff); | |||
| // 每隔40毫秒发送一次数据 | |||
| Thread.sleep(40); | |||
| } | |||
| // 发送结束标识 | |||
| send(client,"{\"end\": true}".getBytes()); | |||
| System.out.println(getCurrentTimeStr() + "\t发送结束标识完成"); | |||
| } | |||
| // 等待连接关闭 | |||
| connectClose.await(); | |||
| break; | |||
| } | |||
| } | |||
| // 生成握手参数 | |||
| public static String getHandShakeParams(String appId, String secretKey) { | |||
| String ts = System.currentTimeMillis()/1000 + ""; | |||
| String signa = ""; | |||
| try { | |||
| signa = EncryptUtil.HmacSHA1Encrypt(EncryptUtil.MD5(appId + ts), secretKey); | |||
| return "?appid=" + appId + "&ts=" + ts + "&signa=" + URLEncoder.encode(signa, "UTF-8"); | |||
| } catch (Exception e) { | |||
| e.printStackTrace(); | |||
| } | |||
| return ""; | |||
| } | |||
| public static void send(WebSocketClient client, byte[] bytes) { | |||
| if (client.isClosed()) { | |||
| throw new RuntimeException("client connect closed!"); | |||
| } | |||
| client.send(bytes); | |||
| } | |||
| public static String getCurrentTimeStr() { | |||
| return sdf.format(new Date()); | |||
| } | |||
| public static class MyWebSocketClient extends WebSocketClient { | |||
| private CountDownLatch handshakeSuccess; | |||
| private CountDownLatch connectClose; | |||
| public MyWebSocketClient(URI serverUri, Draft protocolDraft, CountDownLatch handshakeSuccess, CountDownLatch connectClose) { | |||
| super(serverUri, protocolDraft); | |||
| this.handshakeSuccess = handshakeSuccess; | |||
| this.connectClose = connectClose; | |||
| if(serverUri.toString().contains("wss")){ | |||
| trustAllHosts(this); | |||
| } | |||
| } | |||
| @Override | |||
| public void onOpen(ServerHandshake handshake) { | |||
| System.out.println(getCurrentTimeStr() + "\t连接建立成功!"); | |||
| } | |||
| @Override | |||
| public void onMessage(String msg) { | |||
| try { | |||
| JSONTokener tokener = new JSONTokener(msg); | |||
| JSONObject msgObj = new JSONObject(tokener); | |||
| String action = msgObj.getString("action"); | |||
| if (Objects.equals("started", action)) { | |||
| // 握手成功 | |||
| System.out.println(getCurrentTimeStr() + "\t握手成功!sid: " + msgObj.getString("sid")); | |||
| handshakeSuccess.countDown(); | |||
| } else if (Objects.equals("result", action)) { | |||
| // 转写结果 | |||
| String result = getContent(msgObj.getString("data")); | |||
| Log.e("testkd", "zx json" + msgObj); | |||
| Log.e("testkd", "zx " + result); | |||
| System.out.println(getCurrentTimeStr() + "\tresult: " + result); | |||
| if (mRecognizerListener != null) { | |||
| mRecognizerListener.onError(new SpeechError(100, msg)); | |||
| } | |||
| } else if (Objects.equals("error", action)) { | |||
| // 连接发生错误 | |||
| System.out.println("Error: " + msg); | |||
| //System.exit(0); | |||
| if (mRecognizerListener != null) { | |||
| mRecognizerListener.onError(new SpeechError(100, msg)); | |||
| } | |||
| } | |||
| } catch (Exception e) { | |||
| } | |||
| } | |||
| @Override | |||
| public void onError(Exception e) { | |||
| System.out.println(getCurrentTimeStr() + "\t连接发生错误:" + e.getMessage() + ", " + new Date()); | |||
| e.printStackTrace(); | |||
| System.exit(0); | |||
| } | |||
| @Override | |||
| public void onClose(int arg0, String arg1, boolean arg2) { | |||
| System.out.println(getCurrentTimeStr() + "\t链接关闭"); | |||
| connectClose.countDown(); | |||
| } | |||
| @Override | |||
| public void onMessage(ByteBuffer bytes) { | |||
| try { | |||
| System.out.println(getCurrentTimeStr() + "\t服务端返回:" + new String(bytes.array(), "UTF-8")); | |||
| } catch (UnsupportedEncodingException e) { | |||
| e.printStackTrace(); | |||
| } | |||
| } | |||
| public void trustAllHosts(MyWebSocketClient appClient) { | |||
| System.out.println("wss"); | |||
| TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() { | |||
| @Override | |||
| public X509Certificate[] getAcceptedIssuers() { | |||
| return new X509Certificate[]{}; | |||
| } | |||
| @Override | |||
| public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { | |||
| // TODO Auto-generated method stub | |||
| } | |||
| @Override | |||
| public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { | |||
| // TODO Auto-generated method stub | |||
| } | |||
| }}; | |||
| try { | |||
| SSLContext sc = SSLContext.getInstance("TLS"); | |||
| sc.init(null, trustAllCerts, new java.security.SecureRandom()); | |||
| appClient.setSocket(sc.getSocketFactory().createSocket()); | |||
| } catch (Exception e) { | |||
| e.printStackTrace(); | |||
| } | |||
| } | |||
| } | |||
| public static String getContent(String message) { | |||
| StringBuffer resultBuilder = new StringBuffer(); | |||
| try { | |||
| JSONTokener tokener = new JSONTokener(message); | |||
| JSONObject messageObj = new JSONObject(tokener); | |||
| JSONObject cn = messageObj.getJSONObject("cn"); | |||
| JSONObject st = cn.getJSONObject("st"); | |||
| JSONArray rtArr = st.getJSONArray("rt"); | |||
| for (int i = 0; i < rtArr.length(); i++) { | |||
| JSONObject rtArrObj = rtArr.getJSONObject(i); | |||
| JSONArray wsArr = rtArrObj.getJSONArray("ws"); | |||
| for (int j = 0; j < wsArr.length(); j++) { | |||
| JSONObject wsArrObj = wsArr.getJSONObject(j); | |||
| JSONArray cwArr = wsArrObj.getJSONArray("cw"); | |||
| for (int k = 0; k < cwArr.length(); k++) { | |||
| JSONObject cwArrObj = cwArr.getJSONObject(k); | |||
| String wStr = cwArrObj.getString("w"); | |||
| resultBuilder.append(wStr); | |||
| } | |||
| } | |||
| } | |||
| } catch (Exception e) { | |||
| return message; | |||
| } | |||
| return resultBuilder.toString(); | |||
| } | |||
| // 把转写结果解析为句子 | |||
| // public static String getContent(String message) { | |||
| // StringBuffer resultBuilder = new StringBuffer(); | |||
| // try { | |||
| // JSONObject messageObj = JSON.parseObject(message); | |||
| // JSONObject cn = messageObj.getJSONObject("cn"); | |||
| // JSONObject st = cn.getJSONObject("st"); | |||
| // JSONArray rtArr = st.getJSONArray("rt"); | |||
| // for (int i = 0; i < rtArr.size(); i++) { | |||
| // JSONObject rtArrObj = rtArr.getJSONObject(i); | |||
| // JSONArray wsArr = rtArrObj.getJSONArray("ws"); | |||
| // for (int j = 0; j < wsArr.size(); j++) { | |||
| // JSONObject wsArrObj = wsArr.getJSONObject(j); | |||
| // JSONArray cwArr = wsArrObj.getJSONArray("cw"); | |||
| // for (int k = 0; k < cwArr.size(); k++) { | |||
| // JSONObject cwArrObj = cwArr.getJSONObject(k); | |||
| // String wStr = cwArrObj.getString("w"); | |||
| // resultBuilder.append(wStr); | |||
| // } | |||
| // } | |||
| // } | |||
| // } catch (Exception e) { | |||
| // return message; | |||
| // } | |||
| // | |||
| // return resultBuilder.toString(); | |||
| // } | |||
| } | |||
| @@ -0,0 +1,344 @@ | |||
| package com.aispeech.nativedemo.rtasr; | |||
| import android.os.Handler; | |||
| import android.os.HandlerThread; | |||
| import android.util.Log; | |||
| import java.io.UnsupportedEncodingException; | |||
| import java.net.URI; | |||
| import java.net.URLEncoder; | |||
| import java.nio.ByteBuffer; | |||
| import java.security.cert.CertificateException; | |||
| import java.security.cert.X509Certificate; | |||
| import java.text.SimpleDateFormat; | |||
| import java.util.Date; | |||
| import java.util.List; | |||
| import java.util.Objects; | |||
| import java.util.concurrent.ConcurrentLinkedQueue; | |||
| import java.util.concurrent.CountDownLatch; | |||
| import org.java_websocket.client.WebSocketClient; | |||
| import org.java_websocket.drafts.Draft; | |||
| import org.java_websocket.enums.ReadyState; | |||
| import org.java_websocket.handshake.ServerHandshake; | |||
| import com.aispeech.nativedemo.rtasr.util.EncryptUtil; | |||
| import com.alibaba.fastjson.JSON; | |||
| import com.alibaba.fastjson.JSONArray; | |||
| import com.alibaba.fastjson.JSONObject; | |||
| import com.iflytek.cloud.RecognizerListener; | |||
| import com.iflytek.cloud.SpeechError; | |||
| import javax.net.ssl.SSLContext; | |||
| import javax.net.ssl.TrustManager; | |||
| import javax.net.ssl.X509TrustManager; | |||
| /** | |||
| * 实时转写调用demo | |||
| * 此demo只是一个简单的调用示例,不适合用到实际生产环境中 | |||
| * | |||
| * @author white | |||
| */ | |||
| public class RTASRZXManager { | |||
| // appid | |||
| private static final String APPID = "948cf4b6"; | |||
| // appid对应的secret_key | |||
| private static final String SECRET_KEY = "fe5032b61525baa92d4d0baf1bb18713"; | |||
| // 请求地址 | |||
| private static final String HOST = "rtasr.xfyun.cn/v1/ws"; | |||
| private static final String BASE_URL = "wss://" + HOST; | |||
| private static final String ORIGIN = "https://" + HOST; | |||
| private static final String BASE_HTTP_URL = "ws://" + HOST; | |||
| private static final String ORIGIN_HTTP = "http://" + HOST; | |||
| // 音频文件路径 | |||
| private static final String AUDIO_PATH = "./resource/test_1.pcm"; | |||
| // 每次发送的数据大小 1280 字节 | |||
| private static final int CHUNCKED_SIZE = 1280; | |||
| private static final SimpleDateFormat sdf = new SimpleDateFormat("yyy-MM-dd HH:mm:ss.SSS"); | |||
| private static final String HANDLE_THREAD_NAME = "RTASR"; | |||
| private static MyWebSocketClient mWebSocketClient; | |||
| private static URI url; | |||
| private static DraftWithOrigin draft; | |||
| private ConcurrentLinkedQueue<RecognizerListener> mRecognizerListenerList = new ConcurrentLinkedQueue<>(); | |||
| private ConcurrentLinkedQueue<List<byte[]>> buffsList = new ConcurrentLinkedQueue<>(); | |||
| private HandlerThread backgroundThread; | |||
| private Handler backgroundHandler; | |||
| private final Runnable asrRunnable = new Runnable() { | |||
| @Override | |||
| public void run() { | |||
| List<byte[]> bytesList = buffsList.poll(); | |||
| RecognizerListener recognizerListener = mRecognizerListenerList.poll(); | |||
| try { | |||
| if (bytesList != null && recognizerListener != null) { | |||
| testAsr(bytesList, recognizerListener); | |||
| } | |||
| } catch (Exception e) { | |||
| Log.e("testkd", "zx " + e.getMessage()); | |||
| if (recognizerListener != null) { | |||
| recognizerListener.onError(new SpeechError(400, "socket is closed")); | |||
| } | |||
| } | |||
| } | |||
| }; | |||
| public void startBackgroundThread() { | |||
| backgroundThread = new HandlerThread(HANDLE_THREAD_NAME); | |||
| backgroundThread.start(); | |||
| backgroundHandler = new Handler(backgroundThread.getLooper()); | |||
| } | |||
| public void startAsr(List<byte[]> buffs, RecognizerListener recognizerListener) { | |||
| mRecognizerListenerList.offer(recognizerListener); | |||
| buffsList.offer(buffs); | |||
| backgroundHandler.post(asrRunnable); | |||
| } | |||
| public static void testAsr(List<byte[]> buffs, RecognizerListener recognizerListener) throws Exception { | |||
| while (true) { | |||
| //if (mWebSocketClient == null || mWebSocketClient.isClosed() || mWebSocketClient.isClosing()) | |||
| url = new URI(BASE_URL + getHandShakeParams(APPID, SECRET_KEY)); | |||
| draft = new DraftWithOrigin(ORIGIN); | |||
| CountDownLatch handshakeSuccess = new CountDownLatch(1); | |||
| CountDownLatch connectClose = new CountDownLatch(1); | |||
| mWebSocketClient = new MyWebSocketClient(url, draft, handshakeSuccess, connectClose, recognizerListener); | |||
| mWebSocketClient.connect(); | |||
| int count = 0; | |||
| while (!mWebSocketClient.getReadyState().equals(ReadyState.OPEN)) { | |||
| System.out.println(getCurrentTimeStr() + "\t连接中"); | |||
| Log.e("testkd", "zx " + "连接中" + Thread.currentThread()); | |||
| Thread.sleep(1000); | |||
| if (count > 20) { | |||
| throw new RuntimeException("client connect error"); | |||
| } | |||
| count++; | |||
| } | |||
| // 等待握手成功 | |||
| handshakeSuccess.await(); | |||
| System.out.println(sdf.format(new Date()) + " 开始发送音频数据"); | |||
| // 发送音频 | |||
| //byte[] bytes = new byte[CHUNCKED_SIZE]; | |||
| { | |||
| int len = -1; | |||
| long lastTs = 0; | |||
| for (byte[] buff : buffs) { | |||
| if (len < CHUNCKED_SIZE) { | |||
| //bytes = Arrays.copyOfRange(bytes, 0, len) | |||
| //send(client, ); | |||
| //break; | |||
| } | |||
| long curTs = System.currentTimeMillis(); | |||
| if (lastTs == 0) { | |||
| lastTs = System.currentTimeMillis(); | |||
| } else { | |||
| long s = curTs - lastTs; | |||
| if (s < 40) { | |||
| System.out.println("error time interval: " + s + " ms"); | |||
| } | |||
| } | |||
| send(mWebSocketClient, buff); | |||
| // 每隔40毫秒发送一次数据' | |||
| Thread.sleep(40); | |||
| } | |||
| // 发送结束标识 | |||
| send(mWebSocketClient, "{\"end\": true}".getBytes()); | |||
| System.out.println(getCurrentTimeStr() + "\t发送结束标识完成"); | |||
| } | |||
| // 等待连接关闭 | |||
| connectClose.await(); | |||
| break; | |||
| } | |||
| } | |||
| // 生成握手参数 | |||
| public static String getHandShakeParams(String appId, String secretKey) { | |||
| String ts = System.currentTimeMillis() / 1000 + ""; | |||
| String signa = ""; | |||
| try { | |||
| signa = EncryptUtil.HmacSHA1Encrypt(EncryptUtil.MD5(appId + ts), secretKey); | |||
| return "?appid=" + appId + "&ts=" + ts + "&signa=" + URLEncoder.encode(signa, "UTF-8"); | |||
| } catch (Exception e) { | |||
| e.printStackTrace(); | |||
| } | |||
| return ""; | |||
| } | |||
| public static void send(WebSocketClient client, byte[] bytes) { | |||
| if (client.isClosed()) { | |||
| throw new RuntimeException("client connect closed!"); | |||
| } | |||
| client.send(bytes); | |||
| } | |||
| public static String getCurrentTimeStr() { | |||
| return sdf.format(new Date()); | |||
| } | |||
| public static class MyWebSocketClient extends WebSocketClient { | |||
| private CountDownLatch handshakeSuccess; | |||
| private CountDownLatch connectClose; | |||
| private RecognizerListener recognizerListener; | |||
| public MyWebSocketClient(URI serverUri, Draft protocolDraft, CountDownLatch handshakeSuccess, CountDownLatch connectClose, RecognizerListener recognizerListener) { | |||
| super(serverUri, protocolDraft); | |||
| this.handshakeSuccess = handshakeSuccess; | |||
| this.connectClose = connectClose; | |||
| this.recognizerListener = recognizerListener; | |||
| if (serverUri.toString().contains("wss")) { | |||
| trustAllHosts(this); | |||
| } | |||
| } | |||
| @Override | |||
| public void onOpen(ServerHandshake handshake) { | |||
| Log.e("testkd", "zx " + "连接建立成功!"); | |||
| } | |||
| @Override | |||
| public void onMessage(String msg) { | |||
| JSONObject msgObj = JSON.parseObject(msg); | |||
| String action = msgObj.getString("action"); | |||
| if (Objects.equals("started", action)) { | |||
| // 握手成功 | |||
| System.out.println(getCurrentTimeStr() + "\t握手成功!sid: " + msgObj.getString("sid")); | |||
| handshakeSuccess.countDown(); | |||
| } else if (Objects.equals("result", action)) { | |||
| // 转写结果 | |||
| String result = getContent(msgObj.getString("data"), recognizerListener); | |||
| Log.e("testkd", "zx json" + msgObj); | |||
| Log.e("testkd", "zx " + result); | |||
| System.out.println(getCurrentTimeStr() + "\tresult: " + result); | |||
| } else if (Objects.equals("error", action)) { | |||
| // 连接发生错误 | |||
| System.out.println("Error: " + msg); | |||
| //System.exit(0); | |||
| if (recognizerListener != null) { | |||
| recognizerListener.onError(new SpeechError(100, msg)); | |||
| } | |||
| } | |||
| } | |||
| @Override | |||
| public void onError(Exception e) { | |||
| System.out.println(getCurrentTimeStr() + "\t连接发生错误:" + e.getMessage() + ", " + new Date()); | |||
| Log.e("testkd", "zx " + "连接发生错误:" + e.getMessage() + ", " + new Date()); | |||
| e.printStackTrace(); | |||
| if (recognizerListener != null) { | |||
| recognizerListener.onError(new SpeechError(100, e.getMessage())); | |||
| } | |||
| } | |||
| @Override | |||
| public void onClose(int arg0, String arg1, boolean arg2) { | |||
| System.out.println(getCurrentTimeStr() + "\t链接关闭"); | |||
| Log.e("testkd", "zx " + "链接关闭"); | |||
| connectClose.countDown(); | |||
| } | |||
| @Override | |||
| public void onMessage(ByteBuffer bytes) { | |||
| try { | |||
| System.out.println(getCurrentTimeStr() + "\t服务端返回:" + new String(bytes.array(), "UTF-8")); | |||
| } catch (UnsupportedEncodingException e) { | |||
| e.printStackTrace(); | |||
| } | |||
| } | |||
| public void trustAllHosts(MyWebSocketClient appClient) { | |||
| System.out.println("wss"); | |||
| TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() { | |||
| @Override | |||
| public X509Certificate[] getAcceptedIssuers() { | |||
| return new X509Certificate[]{}; | |||
| } | |||
| @Override | |||
| public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { | |||
| // TODO Auto-generated method stub | |||
| } | |||
| @Override | |||
| public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { | |||
| // TODO Auto-generated method stub | |||
| } | |||
| }}; | |||
| try { | |||
| SSLContext sc = SSLContext.getInstance("TLS"); | |||
| sc.init(null, trustAllCerts, new java.security.SecureRandom()); | |||
| appClient.setSocket(sc.getSocketFactory().createSocket()); | |||
| } catch (Exception e) { | |||
| e.printStackTrace(); | |||
| } | |||
| } | |||
| } | |||
| // 把转写结果解析为句子 | |||
| public static String getContent(String message, RecognizerListener recognizerListener) { | |||
| StringBuffer resultBuilder = new StringBuffer(); | |||
| boolean isLast = false; | |||
| int segId = 0; | |||
| String type = ""; | |||
| try { | |||
| JSONObject messageObj = JSON.parseObject(message); | |||
| isLast = messageObj.getBoolean("ls"); | |||
| segId = messageObj.getIntValue("seg_id"); | |||
| JSONObject cn = messageObj.getJSONObject("cn"); | |||
| JSONObject st = cn.getJSONObject("st"); | |||
| JSONArray rtArr = st.getJSONArray("rt"); | |||
| type = st.getString("type"); | |||
| for (int i = 0; i < rtArr.size(); i++) { | |||
| JSONObject rtArrObj = rtArr.getJSONObject(i); | |||
| JSONArray wsArr = rtArrObj.getJSONArray("ws"); | |||
| for (int j = 0; j < wsArr.size(); j++) { | |||
| JSONObject wsArrObj = wsArr.getJSONObject(j); | |||
| JSONArray cwArr = wsArrObj.getJSONArray("cw"); | |||
| for (int k = 0; k < cwArr.size(); k++) { | |||
| JSONObject cwArrObj = cwArr.getJSONObject(k); | |||
| String wStr = cwArrObj.getString("w"); | |||
| resultBuilder.append(wStr); | |||
| } | |||
| } | |||
| } | |||
| } catch (Exception e) { | |||
| if (recognizerListener != null) { | |||
| recognizerListener.onError(new SpeechError(300, e.getMessage())); | |||
| } | |||
| return message; | |||
| } | |||
| String result = resultBuilder.toString(); | |||
| if (recognizerListener != null && "0".equals(type)) { | |||
| recognizerListener.onResult(new RecognizerExtendResult(result, segId), isLast); | |||
| } | |||
| return result; | |||
| } | |||
| } | |||
| @@ -0,0 +1,21 @@ | |||
| package com.aispeech.nativedemo.rtasr; | |||
| import android.os.Parcel; | |||
| import com.iflytek.cloud.RecognizerResult; | |||
| public class RecognizerExtendResult extends RecognizerResult { | |||
| public int seg_id; | |||
| public RecognizerExtendResult(Parcel parcel) { | |||
| super(parcel); | |||
| } | |||
| public RecognizerExtendResult(String str) { | |||
| super(str); | |||
| } | |||
| public RecognizerExtendResult(String str, int segId) { | |||
| super(str); | |||
| seg_id = segId; | |||
| } | |||
| } | |||
| @@ -0,0 +1,66 @@ | |||
| package com.aispeech.nativedemo.rtasr.util; | |||
| import java.io.UnsupportedEncodingException; | |||
| import java.security.InvalidKeyException; | |||
| import java.security.MessageDigest; | |||
| import java.security.NoSuchAlgorithmException; | |||
| import java.security.SignatureException; | |||
| import javax.crypto.Mac; | |||
| import javax.crypto.spec.SecretKeySpec; | |||
| import cz.msebera.android.httpclient.extras.Base64; | |||
| public class EncryptUtil { | |||
| /** | |||
| * 加密数字签名(基于HMACSHA1算法) | |||
| * | |||
| * @param encryptText | |||
| * @param encryptKey | |||
| * @return | |||
| * @throws SignatureException | |||
| */ | |||
| public static String HmacSHA1Encrypt(String encryptText, String encryptKey) throws SignatureException { | |||
| byte[] rawHmac = null; | |||
| try { | |||
| byte[] data = encryptKey.getBytes("UTF-8"); | |||
| SecretKeySpec secretKey = new SecretKeySpec(data, "HmacSHA1"); | |||
| Mac mac = Mac.getInstance("HmacSHA1"); | |||
| mac.init(secretKey); | |||
| byte[] text = encryptText.getBytes("UTF-8"); | |||
| rawHmac = mac.doFinal(text); | |||
| } catch (InvalidKeyException e) { | |||
| throw new SignatureException("InvalidKeyException:" + e.getMessage()); | |||
| } catch (NoSuchAlgorithmException e) { | |||
| throw new SignatureException("NoSuchAlgorithmException:" + e.getMessage()); | |||
| } catch (UnsupportedEncodingException e) { | |||
| throw new SignatureException("UnsupportedEncodingException:" + e.getMessage()); | |||
| } | |||
| String oauth = new String(Base64.encodeToString(rawHmac, Base64.DEFAULT)); | |||
| return oauth; | |||
| } | |||
| public final static String MD5(String pstr) { | |||
| char md5String[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; | |||
| try { | |||
| byte[] btInput = pstr.getBytes(); | |||
| MessageDigest mdInst = MessageDigest.getInstance("MD5"); | |||
| mdInst.update(btInput); | |||
| byte[] md = mdInst.digest(); | |||
| int j = md.length; | |||
| char str[] = new char[j * 2]; | |||
| int k = 0; | |||
| for (int i = 0; i < j; i++) { // i = 0 | |||
| byte byte0 = md[i]; // 95 | |||
| str[k++] = md5String[byte0 >>> 4 & 0xf]; // 5 | |||
| str[k++] = md5String[byte0 & 0xf]; // F | |||
| } | |||
| return new String(str); | |||
| } catch (Exception e) { | |||
| return null; | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,37 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> | |||
| <!--背景--> | |||
| <item> | |||
| <bitmap | |||
| android:gravity="top" | |||
| android:tileMode="clamp" | |||
| android:src="@drawable/pic_bg_sp" /> | |||
| </item> | |||
| <!--文字--> | |||
| <!-- <item android:bottom="120dp">--> | |||
| <!-- <bitmap--> | |||
| <!-- android:gravity="center"--> | |||
| <!-- android:src="@drawable/pic_text_sp" />--> | |||
| <!-- </item>--> | |||
| <!--logo--> | |||
| <item | |||
| android:bottom="0dp"> | |||
| <bitmap | |||
| android:gravity="center" | |||
| android:src="@drawable/lt_logo" /> | |||
| </item> | |||
| <!-- <item--> | |||
| <!-- android:bottom="88dp"--> | |||
| <!-- android:gravity="bottom|center">--> | |||
| <!-- <bitmap--> | |||
| <!-- android:gravity="bottom|center"--> | |||
| <!-- android:src="@drawable/lt_logo" />--> | |||
| <!-- </item>--> | |||
| </layer-list> | |||
| @@ -7,5 +7,6 @@ | |||
| android:id="@+id/initView" | |||
| android:layout_width="match_parent" | |||
| android:layout_height="match_parent" | |||
| android:visibility="gone" | |||
| /> | |||
| </RelativeLayout> | |||
| @@ -7,12 +7,11 @@ | |||
| <ImageView | |||
| android:layout_width="match_parent" | |||
| android:layout_height="match_parent" | |||
| android:background="@drawable/bg_guide" /> | |||
| android:background="@drawable/pic_bg_sp" /> | |||
| <ImageView | |||
| android:layout_width="0dp" | |||
| android:layout_height="0dp" | |||
| app:layout_constraintWidth_percent="0.25" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:src="@drawable/lt_logo" | |||
| app:layout_constraintDimensionRatio="467:349" | |||
| app:layout_constraintBottom_toBottomOf="parent" | |||
| @@ -4,7 +4,7 @@ | |||
| <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> | |||
| <!-- Customize your theme here. --> | |||
| <item name="colorPrimary">@color/speed_normal_text_color</item> | |||
| <item name="android:windowBackground">@drawable/bg_guide</item> | |||
| <item name="android:windowBackground">@drawable/launcher_drawable</item> | |||
| </style> | |||
| <style name="Dialog" parent="android:style/Theme.Dialog"> | |||