admin管理员组

文章数量:1562608

前言
 最近项目是做机器人,机器人最大的(普通的)AI功能就是语音交流,所以AIUI就是一个很好的选择。AIUI是封装了,讯飞的语音合成、语音识别等功能,重点是它有个兜底功能选择,还有技能工作室的加持,虽然在开发应用中有很多的吐槽,但是本人对讯飞还是非常佩服的。而且在做项目时,发现目前unity跟aiui开发的技术文档少,所以还是想写个技术文档希望对看到的人有帮助。(第一次写技术博客,如果有不满,请多多包涵,多多指教,谢谢)

提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、AIUI平台配置
  • 二、开发步骤
    • 1.开发环境
    • 2.开发代码
  • 总结
  • 补充


前言

最近项目是做机器人,机器人最大的(普通的)AI功能就是语音交流,所以AIUI就是一个很好的选择。AIUI是封装了,讯飞的语音合成、语音识别等功能,重点是它有个兜底功能选择,还有技能工作室的加持,虽然在开发应用中有很多的吐槽,但是本人对讯飞还是非常佩服的。而且在做项目时,发现目前unity跟aiui开发的技术文档少,所以还是想写个技术文档希望对看到的人有帮助。(第一次写技术博客,如果有不满,请多多包涵,多多指教,谢谢)


一、AIUI平台配置

示例: 在讯飞申请的应用中—>其他—>AIUI平台,开通AIUI功能。上面有技术文档,是官方AIUI开发技术文档。(如下图)

AIUI平台的应用配置很重要,在里面可以配置你项目的基本技能跟信息。其中的语音技能是亮点一,在里面你能选择你需要的技能,无需你再次开发,是不是超棒的。其中有个自定义技能,这里你可以自己做你要的技能,解决了你项目的独立需求要求。关于自定义技能,可以再写一篇技术博客,因为我在上面遇到不少坑,我好想吐槽它,但是它也是真的很棒。语义技能中的设备人设也是很棒的,你可以把你的产品人设直接做到这个机器人设中,加入到基本技能里面,那么你的产品就有了它的自己人设,快捷方便。设备人设也是在技能工作室中设置。

AIUI的第二个亮点是它的兜底功能,里面有三个兜底选要项,多选,喜欢你就都选,不想要那么多就单选,不喜欢就多不选。如果要用到该AIUI功能,一定要发布。所以请提交审核上线。

到这里AIUI技能就已经网页配置好了,因为我的项目系统时安卓,而我开发环境是unity,所以我选择是AIUI SDK在Android Studio开发,然后导出jar到unity在调用接口。

二、开发步骤

1.开发环境

Unity,androidStudio

前面(示例):

2.开发代码

代码如下(示例):

下载AIUI SDK 后解压把相应的资源提出(如下图),配置到你的Androidstudio项目模块中。讯飞项目的资源文件是跟你申请的项目挂钩的,所以一定要用你申请的AIUI SDK
中的配置资源。如果你需要唤醒功能需要再下载唤醒SDK(看上图,里面有唤醒sdk),把里面的jet放到assets/ivw/中。

unity与androidStudio交互网上有很多技术文档,多可以看,这里我就不累赘了(跳过,因为我很懒)。
接下来就是写代码实现功能。这部分很重要(看图),appid是应用信息里面的APPID不能错了哦,错了就不能调用实现讯飞的功能。

然后是监听信息:

AIUIListener mAIUILis=new AIUIListener() {
        @Override
        public void onEvent(AIUIEvent aiuiEvent) {
            switch (aiuiEvent.eventType) {
                //唤醒事件
                case AIUIConstant.EVENT_CONNECTED_TO_SERVER:
                    tips = "已连接服务器";
                    break;

                case AIUIConstant.EVENT_SERVER_DISCONNECTED:
                    tips = "与服务器断连" ;
                    break;
                case AIUIConstant.EVENT_WAKEUP:
                    tips = "进入识别状态" ;
                    break;
                //结果事件(包含听写,语义,离线语法结果)
                case AIUIConstant.EVENT_RESULT:
                    if (aiuiEvent.info != null) {
                        int node = 0;//结果解析事件
                        // s1
                        try {
                            JSONObject bizParamJson = new JSONObject(aiuiEvent.info);
                            JSONObject data = bizParamJson.getJSONArray("data").getJSONObject(0);
                            JSONObject params = data.getJSONObject("params");
                            node = 2;
                            JSONObject content = data.getJSONArray("content").getJSONObject(0);

                            long rspTime = aiuiEvent.data.getLong("eos_rslt", -1);  //响应时间

                            //  听写结果(iat)  语义结果(nlp) 后处理服务结果(tpp) 云端tts结果(tts)  翻译结果(itrans)
                            String sub = params.optString("sub");
                            if (content.has("cnt_id") && !"tts".equals(sub)) //查询对象是否包含该key,返回boolean,与Map.containsKey(key)用法一致
                            {
                                String cnt_id = content.getString("cnt_id");
                                node = 0;
                                JSONObject cntJson = new JSONObject(new String(aiuiEvent.data.getByteArray(cnt_id), "utf-8"));
                                // 语义结果
                                if ("nlp".equals(sub)) {
                                    NLPResult(cntJson);
                                }

                                // 听写结果
                                else if ("iat".equals(sub)) {
                                    IATResult(cntJson);
                                }
                            } else if ("tts".equals(sub)) {
                                TTSResult(aiuiEvent, content);
                                //endregion
                            } else {
                                tips = "cnt_id is null" ;
                            }

                        } catch (Throwable e) {

                        }

                    } else {
                        tips = "info is null";
                    }
                    break;
                case AIUIConstant.EVENT_TTS:
                    switch (aiuiEvent.arg1) {
                        case AIUIConstant.TTS_SPEAK_BEGIN:
                            tips = ("开始播放");
                       
                            if (isRecording) {
                                String params = "data_type=audio,sample_rate=16000";
                                //停止写入
                                AIUIMessage msg = new AIUIMessage(AIUIConstant.CMD_STOP_RECORD, 0, 0, params, null);
                                mAIUI.sendMessage(msg);
                            }
                            break;

                        case AIUIConstant.TTS_SPEAK_COMPLETED:
                            tips = ("播放完成");
                            }
                            break;
                    }

                    break;
                //region START
                //休眠事件
                case AIUIConstant.EVENT_SLEEP:
                    tips = "休眠";
                    break;
                // 状态事件
                case AIUIConstant.EVENT_STATE:
                    mAIUIState = aiuiEvent.arg1;
                    switch (aiuiEvent.arg1) {
                        // 闲置状态,AIUI未开启
                        case AIUIConstant.STATE_IDLE:
                            tips = "0:未开启";
                            break;
                        // AIUI已就绪,等待唤醒
                        case AIUIConstant.STATE_READY:
                            tips = "1:等待唤醒";
                            break;
                        // AIUI工作中,可进行交互
                        case AIUIConstant.STATE_WORKING:
                            // tips="2:可进行交互";
                            break;
                    }
                    break;
                //错误事件
                case AIUIConstant.EVENT_ERROR:

                    tips = "错误: " + aiuiEvent.arg1 + "\n" + aiuiEvent.info;
                    //aiuiEvent.arg1 =20006 没有开启录音
                    break;
 

                case AIUIConstant.EVENT_START_RECORD: {
                    //开始录音事件
                    // tips="开始录音 ";
                }
                break;

                case AIUIConstant.EVENT_STOP_RECORD: {
                    //停止录音事件
                    tips = "停止录音 ";
                }
                break;
            }
      
        }
    };

听写解析代码:

 void IATResult(JSONObject iatjson)
    {
        JSONObject text=iatjson.optJSONObject("text");
        //
        StringBuffer iatText=new StringBuffer();
        try
        {
            JSONArray words=text.getJSONArray("ws");
            boolean iatResult=text.optBoolean("ls");
            for(int node=0;node<words.length();node++)
            {
                JSONArray cwWord=words.optJSONObject(node).optJSONArray("cw");
                for(int snode=0;snode<cwWord.length();snode++)
                {
                    iatText.append(cwWord.optJSONObject(snode).opt("w"));
                }
            }
            if(!TextUtils.isEmpty(iatText))
            {
              //  tips=iatText+"\n iat="+text.toString();
                messageAS=new MessageAS("iat",iatText.toString());
                tips=messageAS.ToString();
                return;
            }
            }catch(Exception e)
            {
            }
            }

播报解析代码:


```java
 void TTSResult(AIUIEvent event,JSONObject content)
    {

        if(content.has("cnt_id"))
        {
            String sid = event.data.getString("sid");
            try
            {
                String cnt_id = content.getString("cnt_id");
                byte[] audio = event.data.getByteArray(cnt_id); //合成音频数据
              
                int id=content.getInt("frame_id");

                seg =content.getString("text_seg");
                if(seg.length()==0||seg==null||seg.equals(",")||seg.equals(" ,"))
                {
                    return;
                }
               else if(textMap.containsKey(id))
                {
                    textMap.replace(id,seg);
                }
                else if(!textMap.containsValue(seg))
                {
                    textMap.put(id,seg);
                }
              
                tips=textMap.values().ToString();
            }
            catch (Exception e)
            {
               
            }
        }
    }
  void NLPResult(JSONObject cntJson)
    {
        JSONObject result = cntJson.optJSONObject("intent");//?
        if(result.length() != 0 && result!=null)
            return;
           tips=result.toString();
    }

前面说了讯飞的资源是根据你申请的AIUI配置,是一对一的,再调用讯飞接口时,你会明显感觉它的配置让你想:就不能简单一点。自己配置可以实现多元化的功能需求,但是也是操作麻烦(AIUI的工程师大神,这真的有些烦人)。资源里有配置文件aiui_phone.cfg,各种配置多是跟它有密切关系,请一定要把它放好到assets/scf中,然后解析读取,配置到讯飞的创建中。

private String getAIUIParams() {
        String params = "";
        AssetManager assetManager = getResources().getAssets();
        try {
            InputStream ins = assetManager.open( "cfg/aiui_phone.cfg" );
            byte[] buffer = new byte[ins.available()];

            ins.read(buffer);
            ins.close();

            params = new String(buffer);

        } catch (IOException e) {
            tips="cfg:"+e.toString();
            OnTips(tips);
        }
        return params;
    }

快结束了,申请收音代码:

    public void startVoice()
    {
      
        AIUIMessage setMsg0=new AIUIMessage(AIUIConstant.CMD_TTS, AIUIConstant.PAUSE, 0, null, null);

        String params = "sample_rate=16000,data_type=audio";
        params+=",vad={\"vad_enable\":\"1\",\"engine_type\":\"meta\", \"res_type\":\"assets\",\"res_path\":\"vad/meta_vad_16k.jet\"}";
        AIUIMessage writeMsg = new AIUIMessage( AIUIConstant.CMD_START_RECORD, 0, 0, params, null );
        String setParams = "{\"global\":{\"scene\":\"main_box\"}}";
        AIUIMessage setMsg = new AIUIMessage(AIUIConstant.CMD_SET_PARAMS, 0 , 0, setParams, null);
      
    }

    //取消录音
    public void stopVoice()
    {
        String params = "data_type=audio,sample_rate=16000";
        //停止写入
        AIUIMessage msg = new AIUIMessage(AIUIConstant.CMD_STOP_RECORD, 0, 0, params, null);
        mAIUI.sendMessage(msg);
      
    }

Unity调用讯飞接口跟AIUI SDK交接,你就可以聊天了。

 public AndroidJavaObject andObj;
 void Start()
    {
        
            AndroidJavaClass jc = new  AndroidJavaClass("com.unity3d.player.UnityPlayer");
            andObj = jc.GetStatic<AndroidJavaObject>("currentActivity");
      
    }
    //调用接口跟讯飞语音交流
    void OnVoiceNlp()
    {
        // print("fff");
        try
        {
            andObj.Call("startVoice";
        }
        catch (Exception e)
        {
            tips = e.ToString();
        }
      }
      //获取讯飞语音内容
public void UITips(string str)
    {

        textjson.text =str;
        
    }

把解析信息反馈到Unity。


总结

到这里Unity与讯飞的AIUISDK的交互就是完成了,你已经能够再快速的完成一款AI聊天设备。哦,一定要再Android Studio的AndroidManifest.xml里面添加需要的权限,权限代码在讯飞的开发文档里就可以找到。

补充

UnityPlayer 函数调用,在AS 的对应Module的lib加入Classes.jar

UnityPlayer.UnitySendMessage("ObjName","FunctionName","传递信息");

本文标签: UnityAIUI