用Unity制作一个Siri问答软件

大家好,我是云图。 之前项目有个需求要做一个简单的siri类似的问答系统,经过一番周折,终于还是实现出来了,软件不单可以在pc上面运行,也可以打包到安卓,ios平台,真正实现一键跨平台打包。

现在我们先说一些实现的思路,所谓问答系统,第一步就是用户首先说一句话,然后我们的软件把听到的话转换成文字。那现在问题有了,第二步我们就需要一个地方,把我们的问题文字转化成我们软件回答的答案内容。第三步就是把要回答的文字读出来。

思路有了,开始动手,首先,我们把用户输入的语音,要转成文字,经过我几个挑选,我发现百度语音和讯飞语音都不错,只不过讯飞语音的接入麻烦了点,所以就采用了百度的语音识别
 

  1. private string token;                           //access_token
  2. private string cuid = "随便写的d";        //用户标识
  3. private string format = "pcm";                  //语音格式
  4. private int rate = 8000;                        //采样率
  5. private int channel = 1;                        //声道数
  6. private string speech;                          //语音数据,进行base64编码
  7. private int len;                                //原始语音长度
  8. private string lan = "zh";                      //语种
  9. private string grant_Type = "client_credentials";
  10. private string client_ID = "9152186";                       //百度appkey
  11. private string client_Secret = "14c703ce0f900eae40e95b2cdd564472";                   //百度Secret Key
  12. private string baiduAPI = "http://vop.baidu.com/server_api";
  13. private string getTokenAPIPath =
  14.     "https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=ekGb1G5XHY4BIVSA8nLzX5cA&client_secret=14c703ce0f900eae40e95b2cdd564472";
  15. private Byte[] clipByte;
  16. ///
  17. /// 转换出来的TEXT
  18. ///
  19. public static string audioToString;
  20. private AudioSource aud;
  21. private int audioLength;//录音的长度
  22. public void StartRecord()
  23. {
  24.     Debug.Log("开始说话");
  25.     if (Microphone.devices.Length == 0) return;
  26.     Microphone.End(null);
  27.     aud.clip = Microphone.Start(null, false, 10, rate);
  28. }
  29. public void EndRecord()
  30. {
  31.     Debug.Log("结束说话");
  32.     int lastPos = Microphone.GetPosition(null);
  33.     if (Microphone.IsRecording(null))
  34.         audioLength = lastPos / rate;//录音时长  
  35.     else
  36.         audioLength = 10;
  37.     Microphone.End(null);
  38.     clipByte = GetClipData();
  39.     len = clipByte.Length;
  40.     speech = Convert.ToBase64String(clipByte);
  41.     StartCoroutine(GetToken(getTokenAPIPath));
  42.     StartCoroutine(GetAudioString(baiduAPI));
  43. }
  44. ///
  45. /// 把录音转换为Byte[]
  46. ///
  47. ///
  48. public Byte[] GetClipData()
  49. {
  50.     if (aud.clip == null)
  51.     {
  52.         Debug.LogError("录音数据为空");
  53.         return null;
  54.     }
  55.     float[] samples = new float[aud.clip.samples];
  56.     aud.clip.GetData(samples, 0);
  57.     Byte[] outData = new byte[samples.Length * 2];
  58.     int rescaleFactor = 32767; //to convert float to Int16   
  59.     for (int i = 0; i < samples.Length; i  )
  60.     {
  61.         short temshort = (short)(samples[i] * rescaleFactor);
  62.         Byte[] temdata = System.BitConverter.GetBytes(temshort);
  63.         outData[i * 2] = temdata[0];
  64.         outData[i * 2   1] = temdata[1];
  65.     }
  66.     if (outData == null || outData.Length <= 0)
  67.     {
  68.         Debug.LogError("录音数据为空");
  69.         return null;
  70.     }
  71.     return outData;
  72. }
  73. ///
  74. /// 获取百度用户令牌
  75. ///
  76. /// 获取的url
  77. ///
  78. private IEnumerator GetToken(string url)
  79. {
  80.     WWW getTW = new WWW(url);
  81.     yield return getTW;
  82.     if (getTW.isDone)
  83.     {
  84.         if (getTW.error == null)
  85.         {
  86.             token = getTW.text;
  87.             StartCoroutine(GetAudioString(baiduAPI));
  88.         }
  89.         else
  90.         {
  91.             Debug.LogError("获取令牌出错"   getTW.error);
  92.         }
  93.     }
  94.     else
  95.     {
  96.         Debug.LogError("下载出错"   getTW.error);
  97.     }
  98. }
  99. ///
  100. /// 把语音转换为文字
  101. ///
  102. ///
  103. ///
  104. private IEnumerator GetAudioString(string url)
  105. {
  106.     JsonWriter jw = new JsonWriter();
  107.     jw.WriteObjectStart();
  108.     jw.WritePropertyName("format");
  109.     jw.Write(format);
  110.     jw.WritePropertyName("rate");
  111.     jw.Write(rate);
  112.     jw.WritePropertyName("channel");
  113.     jw.Write(channel);
  114.     jw.WritePropertyName("token");
  115.     jw.Write(token);
  116.     jw.WritePropertyName("cuid");
  117.     jw.Write(cuid);
  118.     jw.WritePropertyName("len");
  119.     jw.Write(len);
  120.     jw.WritePropertyName("speech");
  121.     jw.Write(speech);
  122.     jw.WriteObjectEnd();
  123.     WWW getASW = new WWW(url, Encoding.Default.GetBytes(jw.ToString()));
  124.     yield return getASW;
  125.     if (getASW.isDone)
  126.     {
  127.         if (getASW.error == null)
  128.         {
  129.             JsonData getASWJson = JsonMapper.ToObject(getASW.text);
  130.             if (getASWJson["err_msg"].ToString() == "success.")
  131.             {
  132.                 audioToString = getASWJson["result"][0].ToString();
  133.                 if (audioToString.Substring(audioToString.Length - 1) == ",")
  134.                     audioToString = audioToString.Substring(0, audioToString.Length - 1);
  135.                 Debug.Log("说话的问题是:"   audioToString);
  136.                 GetAnswer(audioToString);
  137.             }
  138.             else
  139.             {
  140.                 Debug.LogWarning("没有成功:"   getASWJson["err_msg"].ToString());
  141.             }
  142.         }
  143.         else
  144.         {
  145.             Debug.LogError(getASW.error);
  146.         }
  147.     }
  148. }

复制代码


首先你要去百度语音官网申请一个开发者权限,这些细节在这里就不讨论了,很简单。重要的就是两个参数 client_ID和client_Secret 的内容,填写到获取token的网页中发上去,获取到token后百度就知道我们是哪个用户哪个软件在调用语音识别了。这里在ios运行QQ转让的时候有时候会显示获取不到token,原因是苹果不给我们这样发信息获取内容,说不安全,需要用其他办法。其他办法有很多,最笨的方法就是提前获取到token,然后写死在程序,坏处就是一个月会变一次,比较麻烦。好了,现在我们的语音已经传上百度然后转成文字下来, 下一步我采用的是图灵的文字问答系统。图灵这个做的也是非常好用的,直接把我们的问题文字写到url里面打开链接,就可以返回答案了。

  1. private string url = "http://www.tuling123.com/openapi/api?key=d91b25b8866fef13f82cd28c0d523c8a&info=";
  2. private string QuestionUrl= "http://www.tuling123.com/openapi/api?key=d91b25b8866fef13f82cd28c0d523c8a&info=";
  3. public string msg = "";
  4. ///
  5. /// 获取图灵返回的答案
  6. ///
  7. /// 提问的问题
  8. public void GetAnswer(string msg)
  9. {
  10.     StartCoroutine(GetTuLingtoken(url   msg));
  11. }
  12. private string TuLingtoken = "";
  13. ///
  14. /// 图灵的问答系统
  15. ///
  16. /// 要问的问题
  17. ///
  18. private IEnumerator GetTuLingtoken(string url)
  19. {
  20.     WWW getTW = new WWW(url);
  21.     yield return getTW;
  22.     if (getTW.isDone)
  23.     {
  24.         if (getTW.error == null)
  25.         {
  26.             TuLingtoken = getTW.text;
  27.             TuLingtoken = JsonMapper.ToObject(getTW.text)["text"].ToString();
  28.             PlayAudio(TuLingtoken);
  29.         }
  30.         else
  31.         {
  32.             Debug.LogError(getTW.error);
  33.         }
  34.     }
  35. }

复制代码


到这里我们就获取到我们要回答用户答案的音频了,回答的答案特别骚气。
 

用Unity制作一个Siri问答软件_第1张图片


我相信到这一步很多做类似项目的也有不少人做到了,可就是这里没办法做下去了,为什么呢,因为要用unity把网络的音频下载下来播放是非常麻烦的事情,我至今都没找到办法,就算用c#的办法解决了,可是安卓和ios呢,根本调用不了,再者就是就算你下载下来了,unity好像没有提供外部播放音频的功能,我不知道是我能力不足还是unity的问题,感觉很基本的事情居然没有解决方案。自我怀疑中。。。。

你可能要问了,那么有没有一种办法能解决这个多平台播放音频的问题呢? 当然有了,我用了FMOD这个插件,可以直接播放网页的在线音频,用了一个插件集成进来,最后一步就把网页音频地址传进去,就解决了播放的问题,而且是跨平台的,ios ,安卓,pc都能用。
 

  1. //要播放的语音文字
  2. public string AudioMsg = "";
  3. private string urlForward = @"http://tsn.baidu.com/text2audio?tex=";
  4. public string AudioUrl = "";
  5. public AudioStreamDemo asDemo;
  6. public void PlayAudio(string content)
  7. {
  8.     Debug.Log(content);
  9.     AudioUrl = @"http://tsn.baidu.com/text2audio?tex="   content    "&lan=zh&cuid=随便写的&ctp=1&tok="   token;//
  10.     string[] arrPunc = { ",", "。", "”", ";", "“", " " };
  11.     for (int i = 0; i < arrPunc.Length;   i)
  12.     {
  13.         //用空白字符来替换指定的标点符号,也就相当于删除掉了标点符号
  14.         AudioUrl = AudioUrl.Replace(arrPunc[i], " ");
  15.     }  
  16.     asDemo.SetUrlContext(AudioUrl);
  17.     asDemo.OnPlay();
  18. }

复制代码


就这么点代码,当初找不到方案的时候头疼了2个星期。 好在在不放弃的坚持下还是搞定了,中间走了不少弯路,好在有 MemoryC 大神的帮助下, 也一个个解决了,为 MemoryC大神打call 我把这三步骤已经缩减到一个类里面,方便大家查阅,提一下目前这个功能的一点短处,就是说完话以后要尽快点结束,让语音不浪费那么多空间, 增快识别速度,最快的时候可以在2秒内读出答案,网速好的话比较好。我测试在安卓读的最快,然后是pc,最慢是ios,测试的机器是iphone5,不知道和这个有没有关系。再说点这个东西的拓展,比如一些项目有一些运行了windows系统的机器人,展厅的一些全息 投影之类的项目,都可以接入这个功能,让项目更有可玩性。

你可能感兴趣的:(https,xml,c#)