001package com.aispeech.dui.dds.agent;
002
003import android.text.TextUtils;
004
005import com.aispeech.dui.BaseNode;
006import com.aispeech.dui.BusClient;
007import com.aispeech.dui.BusClient.RPCResult;
008import com.aispeech.dui.dds.DDS;
009import com.aispeech.dui.dds.agent.tts.TTSEngine;
010import com.aispeech.dui.dds.exceptions.DDSNotInitCompleteException;
011import com.aispeech.dui.manager.AIJavaException;
012import com.aispeech.dui.manager.AILog;
013import com.aispeech.libbase.bussiness.JSONObjectUtil;
014import com.aispeech.libcomm.business.LocalKeys;
015import com.aispeech.libcomm.business.LocalKeysUtil;
016import com.aispeech.libcomm.business.call.AsrppCallUtil;
017import com.aispeech.libcomm.business.call.DmsCallUtil;
018import com.aispeech.libcomm.business.call.VadCallUtils;
019import com.aispeech.libcomm.business.config.PlayerConfig;
020import com.aispeech.libcomm.business.topic.DmsTopicUtil;
021import com.aispeech.libcomm.business.topic.RecorderTopicUtil;
022import com.aispeech.libcomm.business.topic.Topic;
023import com.aispeech.libcomm.business.topic.VadTopicUtil;
024
025import org.json.JSONArray;
026import org.json.JSONException;
027import org.json.JSONObject;
028
029/**
030 * Created by Jinrui on 2017/11/8.
031 */
032
033public class ASREngine {
034    public static final String TAG = "ASREngine";
035
036    private final String[] mTopics = new String[]{
037            Topic.Processor.ASR_SPEECH_TEXT,
038            Topic.Processor.ASR_SPEECH_RESULT,
039            Topic.Pcm.StreamData.TOPIC_NAME,
040            Topic.Dms.AsrVolume.TOPIC_NAME,
041            Topic.Vad.LocalVadTimeout.TOPIC_NAME,
042            Topic.Sys.SysDialogError.TOPIC_NAME,
043            Topic.Sys.SysVadBegin.TOPIC_NAME,
044            Topic.Sys.SysVadEnd.TOPIC_NAME
045    };
046    private boolean mIsUserSubscribe = false;// 用户注册消息判断位
047
048    public enum AsrppType {
049        GENDER, AGE, EMOTION;
050    }
051
052    private volatile static ASREngine mInstance;
053    private Callback mCallbackOnce;
054    private ASREngine.DDSAsrCallback mDDSAsrListener;
055    private final InnerAsr mInnerAsr = new InnerAsr();
056
057    private class InnerAsr {
058        String mAppendVar = "";
059
060        private void reset() {
061            mAppendVar = "";
062        }
063
064        protected String mockVarInputText(JSONObject asrObj) {
065            if(asrObj == null) return "";
066            String from = asrObj.optString("from");
067            if(!TextUtils.equals(from,"liteAsr")) {
068                AILog.d(TAG,"not from liteAsr");
069                return "";
070            }
071            String showText = "";
072            String var = asrObj.optString("var", "");
073            String text = asrObj.optString("text", "");
074            int eof = asrObj.optInt("eof");
075
076            if(eof == 0) {
077                if (!TextUtils.isEmpty(var)) {
078                    showText = mAppendVar + var;
079                } else if (!TextUtils.isEmpty(text)) {
080                    mAppendVar += text;
081                    showText = mAppendVar;
082                } else {
083                    showText = mAppendVar;
084                }
085            }
086
087            asrObj.remove("text");
088            try {
089                asrObj.put("var", showText);
090            } catch (JSONException exception) {
091                exception.printStackTrace();
092            }
093            AILog.d(TAG, "mockVarInputText after data =", asrObj.toString());
094            return asrObj.toString();
095        }
096    }
097
098    private final BaseNode mNode = new BaseNode() {
099        @Override
100        public String getName() {
101            return "ASREngine";
102        }
103
104        @Override
105        public void onMessage(String topic, Object... parts) {
106            if (Topic.Processor.ASR_SPEECH_TEXT.equals(topic)) {
107                onPartialResults(mInnerAsr.mockVarInputText((JSONObject) parts[0]));
108            } else if (Topic.Processor.ASR_SPEECH_RESULT.equals(topic)) {
109                JSONObject dataObj = (JSONObject) parts[0];
110                if(dataObj != null) {
111                    mInnerAsr.reset();
112                    String from = dataObj.optString("from");
113                    if(!TextUtils.equals(from,"liteAsr")) {
114                        AILog.d(TAG,"not from liteAsr");
115                        return;
116                    }
117                    onFinalResults(dataObj.toString());
118                }
119            } else if (Topic.Dms.AsrVolume.TOPIC_NAME.equals(topic)) {
120                JSONObject dataObj = (JSONObject) parts[0];
121                onRmsChanged((float) dataObj.optInt(Topic.Dms.AsrVolume.VOLUME));
122            } else if (Topic.Sys.SysVadBegin.TOPIC_NAME.equals(topic)) {
123                onBeginningOfSpeech();
124                if(mDDSAsrListener != null) {
125                    mDDSAsrListener.onVadBegin();
126                }
127            } else if (Topic.Sys.SysVadEnd.TOPIC_NAME.equals(topic)) {
128                onEndOfSpeech();
129                if(mDDSAsrListener != null) {
130                    mDDSAsrListener.onVadEnd();
131                }
132            } else if (Topic.Pcm.StreamData.TOPIC_NAME.equals(topic)) {
133                byte[] data = (byte[]) parts[0];
134                if (data.length != 0) {
135                    onBufferReceived(data);
136                }
137            } else if (Topic.Sys.SysDialogError.TOPIC_NAME.equals(topic)) {
138                JSONObject dataObj = (JSONObject) parts[0];
139                AILog.e(TAG, dataObj.toString());
140                JSONObject error = dataObj.optJSONObject("error");
141                String errorStr = error.toString();
142                AILog.e(TAG, "error:",error.toString());
143                onError(errorStr);
144            } else if(Topic.Pcm.LocalVadPcm.TOPIC_NAME.equals(topic)) {
145                if(mDDSAsrListener != null) {
146                    mDDSAsrListener.receivedAsrData((byte[]) parts[0]);
147                }
148            } else if(Topic.Pcm.LocalRecorderPcm.TOPIC_NAME.equals(topic)) {
149                if(mDDSAsrListener != null) {
150                    mDDSAsrListener.receivedAsrData((byte[]) parts[0]);
151                }
152            } else if(Topic.Dms.DmsSpeechCtrl.TOPIC_NAME.equals(topic)) {
153                JSONObject data = (JSONObject) parts[0];
154                String recordId = data.optString("recordId");
155                if(mDDSAsrListener != null) {
156                    mDDSAsrListener.onStreamStart(recordId);
157                }
158            }
159        }
160
161        @Override
162        public RPCResult onCall(String url, byte[]... args) throws Exception {
163            return null;
164        }
165
166        @Override
167        public void onExit() {
168            asrUnsubscribe();
169        }
170    };
171
172    private void asrSubscribe() {
173        if (mNode.getBusClient() != null && mIsUserSubscribe) {
174            mNode.getBusClient().subscribe(mTopics);
175        }
176    }
177
178    private void asrUnsubscribe() {
179        if (mNode.getBusClient() != null && mIsUserSubscribe) {
180            mNode.getBusClient().unsubscribe(mTopics);
181            mIsUserSubscribe = false;
182        }
183    }
184
185    protected ASREngine() {
186        mNode.start();
187    }
188
189    public static ASREngine getInstance() {
190        ASREngine localResource = mInstance;
191        if (localResource == null) {
192            synchronized (ASREngine.class) {
193                localResource = mInstance;
194                if (localResource == null) {
195                    mInstance = localResource = new ASREngine();
196                }
197            }
198        }
199        return localResource;
200    }
201
202    /**
203     * 获取 ASREngine 实例快照
204     *
205     * @return ASREngine
206     */
207    public static ASREngine getInstanceSnapshot() {
208        return mInstance;
209    }
210
211    private void checkInitComplete() throws DDSNotInitCompleteException {
212        if (DDS.getInstance().getInitStatus() != DDS.INIT_COMPLETE_FULL) {
213            throw new DDSNotInitCompleteException();
214        }
215    }
216
217    private void onPartialResults(String data) {
218        AILog.d(TAG,"onPartialResults data =",data,",has mCallbackOnce = ",mCallbackOnce != null);
219        if (null != mCallbackOnce) {
220            mCallbackOnce.partialResults(data);
221        }
222    }
223
224    private void onFinalResults(String data) {
225        AILog.d(TAG,"onFinalResults data =",data,",has mCallbackOnce = ",mCallbackOnce != null);
226        if (null != mCallbackOnce) {
227            asrUnsubscribe();
228            mCallbackOnce.finalResults(data);
229            mCallbackOnce = null;
230        }
231    }
232
233    private void onBeginningOfSpeech() {
234        if (null != mCallbackOnce) {
235            mCallbackOnce.beginningOfSpeech();
236        }
237    }
238
239    private void onBufferReceived(byte[] data) {
240        if (null != mCallbackOnce) {
241            mCallbackOnce.bufferReceived(data);
242        }
243    }
244
245    private void onRmsChanged(float rmsdB) {
246        if (null != mCallbackOnce) {
247            mCallbackOnce.rmsChanged(rmsdB);
248        }
249    }
250
251    private void onEndOfSpeech() {
252        if (null != mCallbackOnce) {
253            mCallbackOnce.endOfSpeech();
254        }
255    }
256
257    private void onError(String error) {
258        if (null != mCallbackOnce) {
259            mCallbackOnce.error(error);
260            mCallbackOnce = null;
261        }
262    }
263
264    /**
265     * 主动开始识别
266     * <p>
267     * 调用后直接进入识别,识别结果通过{@link Callback}返回。
268     * 若当前正在对话中,会先结束当前对话,再开启识别。
269     * 若开启VAD,用户结束说话之后会自动结束识别,无须调用{@link ASREngine#stopListening()}
270     *
271     * @param callback 识别结果通的回调接口。识别结束或取消后,将被清除。
272     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
273     */
274    public void startListening(Callback callback) throws DDSNotInitCompleteException {
275        startListening(new ListeningParams(), callback);
276    }
277
278    /**
279     * 主动开始识别
280     * <p>
281     * 调用后直接进入识别,识别结果通过{@link Callback}返回。
282     * 若开启VAD,用户结束说话之后会自动结束识别,无须调用{@link ASREngine#stopListening()}
283     *
284     * @param callback 识别结果通的回调接口。识别结束或取消后,将被清除。
285     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
286     */
287    public void startListening(ListeningParams params, Callback callback) throws DDSNotInitCompleteException {
288        checkInitComplete();
289        AILog.userI(TAG, "startListening input =", (params != null ? params.getJsonObject() : "null"));
290        mCallbackOnce = callback;
291        mIsUserSubscribe = true;
292        asrSubscribe();
293        mInnerAsr.reset();
294        BusClient bc = DDS.getInstance().getAgent().getBusClient();
295        if (bc != null) {
296            RecorderTopicUtil.publishRecorderCtrl(bc, "start", "asr", false);
297            JSONObject asrObj = params != null ? params.getJsonObject() : new ListeningParams().getJsonObject();
298            try {
299                asrObj.put(Topic.Dms.AsrCtrl.CTRL, "start");
300            } catch (JSONException e) {
301                e.printStackTrace();
302            }
303            DmsTopicUtil.publishAsrCtrl(bc, asrObj);
304        } else {
305            AILog.e(TAG, "startListening failed due to null busclient");
306        }
307    }
308
309    /**
310     * 设置VAD前端超时时间的接口
311     * <p>
312     * 若VAD启动,一直未检测到用户说话,超过一定时间,发出sys.vad.timeout消息,结束录音。
313     * 设置成功后,vad再次启动生效
314     *
315     * @param mills 前端超时时间,单位为毫秒。默认值为8000毫秒。
316     * @return true-设置成功;false-设置失败
317     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
318     */
319    public boolean setVadTimeout(long mills) throws DDSNotInitCompleteException {
320        checkInitComplete();
321        AILog.userI(TAG, "setVadTimeout input =", mills);
322        BusClient bc = DDS.getInstance().getAgent().getBusClient();
323        if (bc != null) {
324            VadTopicUtil.publishVadSetTimeOut(bc, mills);
325            return true;
326        } else {
327            AILog.e(TAG, "setVadTimeout failed due to null busclient");
328            return false;
329        }
330    }
331
332    /**
333     * 在全双工模式下,跳过Vad的超时检测
334     * <p>
335     * 全双工模式下, VAD启动后调用此接口会清除VAD的超时机制, 人声检测超时机制交由服务端来判断
336     * 此接口只在全双工模式下生效, 并在当前对话时生效, 开启新的对话后此状态会恢复为默认的vad超时检测
337     *
338     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
339     */
340    public void killVadTimeoutInFullDuplex() throws DDSNotInitCompleteException {
341        checkInitComplete();
342        AILog.userI(TAG, "killVadTimeoutInFullDuplex");
343        BusClient bc = DDS.getInstance().getAgent().getBusClient();
344        if (bc != null) {
345            VadTopicUtil.publishKillVadTimeout(bc, true);
346        } else {
347            AILog.e(TAG, "killVadTimeoutInFullDuplex failed due to null busclient");
348        }
349    }
350
351    /**
352     * 在全双工模式下,实时开启Vad的超时检测
353     * 在调用killVadTimeoutInFullDuplex之后,如果想在本轮对话中再次开启Vad的超时检测机制,则调用此接口
354     * <p>
355     *
356     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
357     */
358    public void startVadTimeoutInFullDuplex() throws DDSNotInitCompleteException {
359        checkInitComplete();
360        AILog.userI(TAG, "startVadTimeoutInFullDuplex");
361        BusClient bc = DDS.getInstance().getAgent().getBusClient();
362        if (bc != null) {
363            VadTopicUtil.publishKillVadTimeout(bc, false);
364        } else {
365            AILog.e(TAG, "killVadTimeoutInFullDuplex failed due to null busclient");
366        }
367    }
368
369    /**
370     * 获取VAD前端超时时间的接口
371     *
372     * @return millis 前端超时时间,单位为毫秒。
373     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
374     */
375    public long getVadTimeout() throws DDSNotInitCompleteException {
376        checkInitComplete();
377        long pauseTime = 8000;
378        BusClient bc = DDS.getInstance().getAgent().getBusClient();
379        if (bc != null) {
380            pauseTime = VadCallUtils.callVadGetTimeout(bc);
381        } else {
382            AILog.e(TAG, "getVadTimeout failed due to null busclient");
383        }
384        AILog.userI(TAG, "getVadTimeout result =", pauseTime);
385        return pauseTime;
386    }
387
388    /**
389     * 设置VAD后端停顿时间的接口
390     * <p>
391     * 若VAD在用户说话时停顿超过一定的时间,则认为用户已经说完,发出sys.vad.end消息,结束录音。
392     *
393     * @param millis 后端停顿时间,单位为毫秒。默认值为500毫秒。
394     * @return true-设置成功;false-设置失败
395     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
396     */
397    public boolean setVadPauseTime(long millis) throws DDSNotInitCompleteException {
398        checkInitComplete();
399        AILog.userI(TAG, "setVadPauseTime input =", millis);
400        BusClient bc = DDS.getInstance().getAgent().getBusClient();
401        if (bc != null) {
402            VadTopicUtil.publishVadSetPauseTime(bc, millis);
403            return true;
404        } else {
405            AILog.e(TAG, "setVadPauseTime failed due to null busclient");
406            return false;
407        }
408    }
409
410    /**
411     * 获取VAD后端停顿时间的接口
412     *
413     * @return millis 后端停顿时间,单位为毫秒。
414     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
415     */
416    public long getVadPauseTime() throws DDSNotInitCompleteException {
417        checkInitComplete();
418        long pauseTime = 500;
419        BusClient bc = DDS.getInstance().getAgent().getBusClient();
420        if (bc != null) {
421            pauseTime = VadCallUtils.callVadGetPauseTime(bc);
422        } else {
423            AILog.e(TAG, "getVadPauseTime failed due to null busclient");
424        }
425        AILog.userI(TAG, "getVadPauseTime result =", pauseTime);
426        return pauseTime;
427    }
428
429
430    /**
431     * 获取vad版本:lua版本 - c版本 接口
432     *
433     * @return vadVersion vad版本信息
434     * @throws DDSNotInitCompleteException
435     */
436    public String getVadVersion() throws DDSNotInitCompleteException {
437        checkInitComplete();
438        String vadVersion = "";
439        BusClient bc = DDS.getInstance().getAgent().getBusClient();
440        if (bc != null) {
441            vadVersion = VadCallUtils.callVadGetVersion(bc);
442        } else {
443            AILog.e(TAG, "getVadVersion failed due to null busclient");
444        }
445        AILog.userI(TAG, "getVadVersion result =", vadVersion);
446        return vadVersion;
447    }
448
449
450    /**
451     * 获取vad dump info
452     *
453     * @return vadDump vad dmp info
454     * @throws DDSNotInitCompleteException
455     */
456    public String getVadDump() throws DDSNotInitCompleteException {
457        checkInitComplete();
458        String vadDump = "";
459        BusClient bc = DDS.getInstance().getAgent().getBusClient();
460        if (bc != null) {
461            vadDump = VadCallUtils.callVadDump(bc);
462        } else {
463            AILog.e(TAG, "getVadDump failed due to null busclient");
464        }
465        AILog.userI(TAG, "getVadDump result =", vadDump);
466        return vadDump;
467    }
468
469
470    /**
471     * 设置使用VAD检测接口
472     *
473     * @return true-设置成功;false-设置失败
474     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
475     */
476    public boolean enableVad() throws DDSNotInitCompleteException {
477        checkInitComplete();
478        AILog.userI(TAG, "enableVad");
479        BusClient bc = DDS.getInstance().getAgent().getBusClient();
480        if (bc != null) {
481            VadTopicUtil.publishVadEnable(bc);
482            return true;
483        } else {
484            AILog.e(TAG, "setVadEnable failed due to null busclient");
485            return false;
486        }
487    }
488
489    /**
490     * 设置取消VAD检测接口
491     *
492     * @return true-设置成功;false-设置失败
493     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
494     */
495    public boolean disableVad() throws DDSNotInitCompleteException {
496        checkInitComplete();
497        AILog.userI(TAG, "disableVad");
498        BusClient bc = DDS.getInstance().getAgent().getBusClient();
499        if (bc != null) {
500            VadTopicUtil.publishVadDisable(bc);
501            return true;
502        } else {
503            AILog.e(TAG, "setVadDisable failed due to null busclient");
504            return false;
505        }
506    }
507
508
509    /**
510     * 主动结束识别
511     * <p>
512     * 调用后,最终识别结果会通过{@link Callback#finalResults(String)}回调,然后{@link Callback}将会被清除。
513     *
514     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
515     */
516    public void stopListening() throws DDSNotInitCompleteException {
517        checkInitComplete();
518        AILog.userI(TAG, "stopListening");
519        mInnerAsr.reset();
520        BusClient bc = DDS.getInstance().getAgent().getBusClient();
521        if (bc != null) {
522            RecorderTopicUtil.publishRecorderCtrl(bc, "stop", "asr", false);
523            DmsTopicUtil.publishAsrCtrl(bc, JSONObjectUtil.create()
524                    .put(Topic.Dms.AsrCtrl.CTRL, "stop")
525                    .build());
526        } else {
527            AILog.e(TAG, "stopListening failed due to null busclient");
528        }
529    }
530
531    /**
532     * 取消此次识别
533     * <p>
534     * 调用后,{@link Callback}将会被清除,并不再触发任何回调。
535     *
536     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
537     */
538    public void cancel() throws DDSNotInitCompleteException {
539        checkInitComplete();
540        AILog.userI(TAG, "cancel");
541        mInnerAsr.reset();
542        mCallbackOnce = null;
543        asrUnsubscribe();
544        mInnerAsr.reset();
545        BusClient bc = DDS.getInstance().getAgent().getBusClient();
546        if (bc != null) {
547            RecorderTopicUtil.publishRecorderCtrl(bc, "stop", "asr", false);
548            DmsTopicUtil.publishAsrCtrl(bc, JSONObjectUtil.create()
549                    .put(Topic.Dms.AsrCtrl.CTRL, "cancel")
550                    .build());
551        } else {
552            AILog.e(TAG, "cancel failed due to null busclient");
553        }
554    }
555
556    private void setPunctuation(boolean enable) throws DDSNotInitCompleteException {
557        checkInitComplete();
558        AILog.userI(TAG, "setPunctuation input =", enable);
559        BusClient bc = DDS.getInstance().getAgent().getBusClient();
560        if (bc != null) {
561            DmsTopicUtil.publishUpdateAsrEnablePunctuation(bc, enable);
562        } else {
563            AILog.e(TAG, "setPunctuation failed due to null busclient");
564        }
565    }
566
567    private void setCensorEnable(boolean enable) throws DDSNotInitCompleteException {
568        checkInitComplete();
569        AILog.userI(TAG, "setCensorEnable input =", enable);
570        BusClient bc = DDS.getInstance().getAgent().getBusClient();
571        if (bc != null) {
572            DmsTopicUtil.publishUpdateAsrEnableCensor(bc, enable);
573        } else {
574            AILog.e(TAG, "setCensorEnable failed due to null busclient");
575        }
576    }
577
578
579    /**
580     * 打开识别支持标点符号的特性
581     *
582     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
583     */
584    public void enablePunctuation() throws DDSNotInitCompleteException {
585        setPunctuation(true);
586    }
587
588
589    /**
590     * 关闭识别支持标点符号的特性
591     *
592     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
593     */
594    public void disablePunctuation() throws DDSNotInitCompleteException {
595        setPunctuation(false);
596    }
597
598
599    /**
600     * 打开识别敏感词
601     *
602     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
603     */
604    public void enableCensor() throws DDSNotInitCompleteException {
605        setCensorEnable(true);
606    }
607
608    /**
609     * 打开识别敏感词
610     *
611     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
612     */
613    public void disableCensor() throws DDSNotInitCompleteException {
614        setCensorEnable(false);
615    }
616
617
618    /**
619     * 更新云端识别的模型名字,
620     * 在调用完该接口后,下一次对话开始时生效,并一直用该模型,除非客户端再调用该接口设置为其他的模型
621     *
622     * @param asrModel 云端识别的模型名字,有aihome, airobot等,
623     *                 默认为dui控制台配置的模型资源,
624     *                 如果填null,则表示清除之前本地配置的模型名,之后会使用dui控制台配置的模型资源
625     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
626     */
627    public void updateAsrModel(String asrModel) throws DDSNotInitCompleteException {
628        checkInitComplete();
629        AILog.userI(TAG, "updateAsrModel input =", asrModel);
630        BusClient bc = DDS.getInstance().getAgent().getBusClient();
631        if (bc != null) {
632            DmsTopicUtil.publishUpdateAsrModel(bc, asrModel);
633        } else {
634            AILog.e(TAG, "updateAsrRes failed due to null busclient");
635        }
636    }
637
638    /**
639     * 获取当前使用的云端识别的模型
640     *
641     * @return asrModel 当前使用的云端识别的模型, 如果失败会返回null,或者抛出DDSNotInitCompleteException
642     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
643     */
644    public String getAsrModel() throws DDSNotInitCompleteException {
645        checkInitComplete();
646        AILog.d(TAG, "getAsrModel : ");
647        String asrModel = "";
648        BusClient bc = DDS.getInstance().getAgent().getBusClient();
649        if (bc != null) {
650            asrModel = DmsCallUtil.callGetAsrModel(bc);
651        } else {
652            AILog.e(TAG, "updateAsrRes failed due to null busclient");
653        }
654        AILog.userI(TAG, "getAsrModel result =", asrModel);
655        return asrModel;
656    }
657
658    /**
659     * 获取音频对应的性别/年龄/情绪
660     *
661     * @param pcm 一次性输入识别的单路音频(建议输入1.2~1.5秒的有效音频,有效音频不得少于0.6秒)
662     * @return 返回音频识别结果: 性别/年龄/情绪字段
663     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
664     */
665    @Deprecated
666    public String getGenderWithPcm(byte[] pcm) throws DDSNotInitCompleteException {
667        return getAsrppWithPcm(pcm, AsrppType.GENDER);
668    }
669
670    /**
671     * 获取音频对应的性别/年龄/情绪
672     *
673     * @param pcm 一次性输入识别的单路音频(建议输入1.2~1.5秒的有效音频,有效音频不得少于0.6秒)
674     * @return 返回音频识别结果: 性别/年龄/情绪字段
675     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
676     */
677    public String getAsrppWithPcm(byte[] pcm, AsrppType... types) throws DDSNotInitCompleteException {
678        checkInitComplete();
679        int pcmLength = pcm.length;
680        if (pcmLength < (6 * 3200)) {// 判断小于0.6秒的音频就返回null
681            AILog.e(TAG, "getAsrppWithPcm failed : pcm is too small");
682            return null;
683        }
684        AILog.d(TAG, "getAsrppWithPcm length = " + pcmLength);
685        String genderStr = null;
686        long start = System.currentTimeMillis();
687        BusClient bc = DDS.getInstance().getAgent().getBusClient();
688
689        if (bc != null) {
690            String params = "";
691            if (types != null) {
692                for (AsrppType asrppType : types) {
693                    String typeStr = asrppType.name().toLowerCase();
694                    params += "," + typeStr;
695                }
696            }
697            params = params.replaceFirst(",", "");
698            // lua 的 topic: "/gender/getasrpp_result";
699            genderStr = AsrppCallUtil.callAsrppResult(bc, pcm, params);
700        } else {
701            AILog.e(TAG, "getAsrppWithPcm failed due to null busclient");
702        }
703        long disTime = System.currentTimeMillis() - start;
704        AILog.d(TAG, "getAsrppWithPcm time = " + disTime);
705        AILog.userI(TAG, "getAsrppWithPcm result =", genderStr);
706        return genderStr;
707    }
708
709    /**
710     * 设置时时回传音量大小, 默认为true
711     *
712     * @param enable true/false  true:支持时时回传音量 false:关闭时时回传音量
713     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
714     */
715    public void enableVolume(boolean enable) throws DDSNotInitCompleteException {
716        checkInitComplete();
717        AILog.userI(TAG, "enableVolume input =", enable);
718        BusClient bc = DDS.getInstance().getAgent().getBusClient();
719        if (bc != null) {
720            RecorderTopicUtil.publishEnableVolume(bc, enable, 1);
721        } else {
722            AILog.e(TAG, "setEnableVolume failed due to null busclient");
723        }
724    }
725
726    /**
727     * 设置时时回传音量大小, 默认为true
728     *
729     * @param enable    true/false  true:支持时时回传音量 false:关闭时时回传音量
730     * @param frequency 每几次计算一次音量, 默认 1
731     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
732     */
733    public void enableVolume(boolean enable, int frequency) throws DDSNotInitCompleteException {
734        checkInitComplete();
735        AILog.userI(TAG, "enableVolume enable =", enable, ", frequency =", frequency);
736        BusClient bc = DDS.getInstance().getAgent().getBusClient();
737        if (bc != null) {
738            RecorderTopicUtil.publishEnableVolume(bc, enable, frequency > 1 ? frequency : 1);
739        } else {
740            AILog.e(TAG, "setEnableVolume failed due to null busclient");
741        }
742    }
743
744    /**
745     * 动态设置录音通道是否反转
746     *
747     * @param isReversedChannel true or false
748     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
749     */
750    public void setReversedChannel(boolean isReversedChannel) throws DDSNotInitCompleteException {
751        checkInitComplete();
752        AILog.userI(TAG, "setReversedChannel input =", isReversedChannel);
753        BusClient bc = DDS.getInstance().getAgent().getBusClient();
754        if (bc != null) {
755            RecorderTopicUtil.publishRecorderCtrl(bc, "reversedChannel", "Agent", isReversedChannel);
756        } else {
757            AILog.e(TAG, "setReversedChannel failed due to null busclient");
758        }
759    }
760
761    /**
762     * 设置三路模型标识
763     *
764     * @param cLmId 模型id new JSONArray().put("123").put("234");
765     */
766    public void setCLmId(JSONArray cLmId) throws DDSNotInitCompleteException {
767        checkInitComplete();
768        AILog.userI(TAG, "setCLmId input =", cLmId);
769        BusClient bc = DDS.getInstance().getAgent().getBusClient();
770        if (bc != null) {
771            JSONObject jsonObject = new JSONObject();
772            try {
773                jsonObject.put(Topic.Dms.UpdateAsrParams.CLMID, cLmId);
774                DmsTopicUtil.publishUpdateAsrParams(bc, jsonObject);
775            } catch (Exception e) {
776                e.printStackTrace();
777            }
778        } else {
779            AILog.e(TAG, "setCLmId failed due to null busclient");
780        }
781    }
782
783    /**
784     * 设置识别声纹参数
785     *
786     * @param asrPlus
787     */
788    public void setAsrPlus(AsrPlusRequestBean asrPlus) throws DDSNotInitCompleteException {
789        checkInitComplete();
790        String asrPlusStr = "";
791        if (asrPlus != null) {
792            JSONObject asrPlusObj = asrPlus.getAsrPlusObj();
793            asrPlusStr = asrPlusObj.toString();
794        }
795        AILog.d(TAG, "asrPlusObj : " + asrPlusStr);
796        LocalKeysUtil.getInstance().put(LocalKeys.Asr.LOCAL_KEY_ASR_ASRPLUS, asrPlusStr);
797    }
798
799    /**
800     * 设置二路模型标识
801     *
802     * @param abLmId 模型id new JSONArray().put("123").put("234");
803     */
804    public void setAbLmId(JSONArray abLmId) throws DDSNotInitCompleteException {
805        checkInitComplete();
806        AILog.userI(TAG, "setAbLmId input =", abLmId);
807        BusClient bc = DDS.getInstance().getAgent().getBusClient();
808        if (bc != null) {
809            JSONObject jsonObject = new JSONObject();
810            try {
811                jsonObject.put(Topic.Dms.UpdateAsrParams.ABLMID, abLmId);
812                DmsTopicUtil.publishUpdateAsrParams(bc, jsonObject);
813            } catch (Exception e) {
814                e.printStackTrace();
815            }
816        } else {
817            AILog.e(TAG, "setAbLmId failed due to null busclient");
818        }
819    }
820
821    /**
822     * 是否开启nlu_rec
823     *
824     * @param enableNluRec 取值 true or false
825     */
826    public void setEnableNluRec(boolean enableNluRec) throws DDSNotInitCompleteException {
827        checkInitComplete();
828        AILog.userI(TAG, "setEnableNluRec input =", enableNluRec);
829        BusClient bc = DDS.getInstance().getAgent().getBusClient();
830        if (bc != null) {
831            JSONObject jsonObject = new JSONObject();
832            try {
833                jsonObject.put(Topic.Dms.UpdateAsrParams.ENABLENLUREC, enableNluRec);
834                DmsTopicUtil.publishUpdateAsrParams(bc, jsonObject);
835            } catch (Exception e) {
836                e.printStackTrace();
837            }
838        } else {
839            AILog.e(TAG, "setEnableNluRec failed due to null busclient");
840        }
841    }
842
843    /**
844     * 是否开启enableAlignment
845     *
846     * @param enableAlignment 取值 true or false
847     */
848    public void setEnableAlignment(boolean enableAlignment) throws DDSNotInitCompleteException {
849        checkInitComplete();
850        AILog.userI(TAG, "setEnableAlignment input =", enableAlignment);
851        BusClient bc = DDS.getInstance().getAgent().getBusClient();
852        if (bc != null) {
853            JSONObject jsonObject = new JSONObject();
854            try {
855                jsonObject.put(Topic.Dms.UpdateAsrParams.ENABLEALIGNMENT, enableAlignment);
856                DmsTopicUtil.publishUpdateAsrParams(bc, jsonObject);
857            } catch (Exception e) {
858                e.printStackTrace();
859            }
860        } else {
861            AILog.e(TAG, "setEnableAlignment failed due to null busclient");
862        }
863    }
864
865    /**
866     * 动态设置Asrpp是否打开,下一轮对话生效
867     * @param enable 默认是false,如果用户配置,取用户配置的值
868     * @throws DDSNotInitCompleteException
869     */
870    public void setEnableCloudAsrPP(boolean enable) throws DDSNotInitCompleteException {
871        checkInitComplete();
872        AILog.userI(TAG, "setEnableCloudAsrPP enable =", enable);
873
874        BusClient bc = DDS.getInstance().getAgent().getBusClient();
875        try {
876            DmsTopicUtil.publishCloudAsrPPEnable(bc, enable);
877        } catch (Exception e) {
878            e.printStackTrace();
879        }
880    }
881
882    public interface Callback {
883
884        /**
885         * 当用户开始说话时的回调, 产品开启VAD时有效
886         */
887        void beginningOfSpeech();
888
889        /**
890         * 检测到用户结束说话的回调, 产品开启VAD时有效
891         */
892        void endOfSpeech();
893
894        /**
895         * 采集到音频数据的回调
896         */
897        void bufferReceived(byte[] buffer);
898
899        /**
900         * 实时识别结果的回调
901         */
902        void partialResults(String results);
903
904        /**
905         * 最终识别结果的回调
906         */
907        void finalResults(String results);
908
909        /**
910         * 识别过程发生错误的回调
911         */
912        void error(String error);
913
914        /**
915         * 音量变化的回调
916         */
917        void rmsChanged(float rmsdB);
918    }
919
920    public static class ListeningParams {
921        private boolean vadEnable = true;
922        private boolean volumeEnable = true;
923        private int noSpeechTimeOut = 8000;
924        private long vadPauseTime = 300;
925        private boolean enablePunctuation = true;
926        private boolean enableCensor= true;
927        private boolean enableNumberConvert = false;
928        private String resource;
929        private int maxSpeechTimeS = -1;
930
931        public long getVadPauseTime() {
932            return vadPauseTime;
933        }
934
935        public void setVadPauseTime(int vadPauseTime) {
936            this.vadPauseTime = vadPauseTime;
937        }
938
939        public int getMaxSpeechTimeS() {
940            return maxSpeechTimeS;
941        }
942
943        public void setMaxSpeechTimeS(int maxSpeechTimeS) {
944            this.maxSpeechTimeS = maxSpeechTimeS;
945        }
946
947        public void setVadEnable(boolean vadEnable) {
948            this.vadEnable = vadEnable;
949        }
950
951        public void setVolumeEnable(boolean volumeEnable) {
952            this.volumeEnable = volumeEnable;
953        }
954
955        public void setNoSpeechTimeOut(int noSpeechTimeOut) {
956            this.noSpeechTimeOut = noSpeechTimeOut;
957        }
958
959        public void setEnablePunctuation(boolean enablePunctuation) {
960            this.enablePunctuation = enablePunctuation;
961        }
962
963        public void setEnableCensor(boolean enableCensor) {
964            this.enableCensor = enableCensor;
965        }
966
967        public void setEnableNumberConvert(boolean enableNumberConvert) {
968            this.enableNumberConvert = enableNumberConvert;
969        }
970
971        public void setResource(String resource) {
972            this.resource = resource;
973        }
974
975        private JSONObject getJsonObject() {
976            return JSONObjectUtil.create()
977                    .put(Topic.Dms.AsrCtrl.ENABLE_VAD, vadEnable)
978                    .put(Topic.Dms.AsrCtrl.TIME_OUT, noSpeechTimeOut)
979                    .put(Topic.Dms.AsrCtrl.ENABLE_VOLUME, volumeEnable)
980                    .put(Topic.Dms.AsrCtrl.ENABLE_PUNCTUATION, enablePunctuation)
981                    .put(Topic.Dms.AsrCtrl.ENABLE_CENSOR, enableCensor)
982                    .put(Topic.Dms.AsrCtrl.ENABLE_NUMBER_CONVERT, enableNumberConvert)
983                    .put(Topic.Dms.AsrCtrl.RESOURCE, resource)
984                    .put(Topic.Dms.AsrCtrl.VAD_PAUSE_TIME, vadPauseTime)
985                    .put(Topic.Dms.AsrCtrl.MAX_SPEECH_TIME_S, maxSpeechTimeS)
986                    .build();
987        }
988    }
989
990    protected void destroy() {
991        if (mInstance != null) {
992            if (mInstance.mNode != null) {
993                mInstance.mNode.stop();
994            }
995            mInstance = null;
996        }
997    }
998
999    private void subscribeDDSAsr() {
1000
1001    }
1002
1003    /**
1004     * 设置全链路相关事件的监听
1005     *
1006     * @param listener 回调各事件
1007     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
1008     */
1009    public void setDDSAsrListener(ASREngine.DDSAsrCallback listener) throws DDSNotInitCompleteException {
1010        AILog.userI(TAG, "setCallbackListener input =", (listener != null));
1011        checkInitComplete();
1012        mDDSAsrListener = listener;
1013
1014        boolean vadEnable = LocalKeysUtil.getInstance().getBoolean(LocalKeys.Asr.LOCAL_KEY_VAD_ENABLE,true);
1015
1016        String pcmNameTopic = "";
1017        if (vadEnable) {
1018            pcmNameTopic = Topic.Pcm.LocalVadPcm.TOPIC_NAME;
1019        } else {
1020            pcmNameTopic = Topic.Pcm.LocalRecorderPcm.TOPIC_NAME;
1021        }
1022
1023        String[] topics = new String[]{
1024                pcmNameTopic,
1025                Topic.Sys.SysVadBegin.TOPIC_NAME,
1026                Topic.Sys.SysVadEnd.TOPIC_NAME,
1027                Topic.Dms.DmsSpeechCtrl.TOPIC_NAME,
1028        };
1029        BusClient bc = mNode.getBusClient();
1030        if (bc != null && mDDSAsrListener != null) {
1031            bc.subscribe(topics);
1032        } else {
1033            if (bc == null) {
1034                AILog.e(TAG, "subscribe error -> bc is null");
1035            }
1036            if (mDDSAsrListener == null) {
1037                AILog.w(TAG, "subscribe error -> Listener is null");
1038            }
1039        }
1040    }
1041
1042    public interface DDSAsrCallback {
1043        /**
1044         * 请求云端
1045         *
1046         * @param recordId 当前对话的recordId
1047         */
1048        void onStreamStart(String recordId);
1049
1050        void onVadBegin();
1051
1052        void onVadEnd();
1053
1054        /**
1055         * 识别的音频数据的回调,可能会返回多次
1056         *
1057         * @param data 音频数据
1058         */
1059        void receivedAsrData(byte[] data);
1060
1061    }
1062
1063    // 识别声纹请求业务bean
1064    public static class AsrPlusRequestBean {
1065        private boolean enableAsrPlus;
1066        private boolean enableVprPassThrough;
1067        private String groupId;
1068        private String organization;
1069        private JSONArray users;
1070
1071        public AsrPlusRequestBean setEnableAsrPlus(boolean enableAsrPlus) {
1072            this.enableAsrPlus = enableAsrPlus;
1073            return this;
1074        }
1075
1076        public AsrPlusRequestBean setEnableVprPassThrough(boolean enableVprPassThrough) {
1077            this.enableVprPassThrough = enableVprPassThrough;
1078            return this;
1079        }
1080
1081        public AsrPlusRequestBean setGroupId(String groupId) {
1082            this.groupId = groupId;
1083            return this;
1084        }
1085
1086        public AsrPlusRequestBean setOrganization(String organization) {
1087            this.organization = organization;
1088            return this;
1089        }
1090
1091        public AsrPlusRequestBean addUserId(String userId) {
1092            if (users == null) {
1093                users = new JSONArray();
1094            }
1095            users.put(userId);
1096            return this;
1097        }
1098
1099        public JSONObject getAsrPlusObj() {
1100            return JSONObjectUtil.create()
1101                    .put("enableAsrPlus", enableAsrPlus)
1102                    .put("enableVprPassThrough", enableVprPassThrough)
1103                    .put("groupId", groupId)
1104                    .put("organization", organization)
1105                    .put("users", users)
1106                    .build();
1107        }
1108
1109    }
1110}