前言

讯飞语音是国内的智能语音前沿,有语音合成及语音识别还有一些其他高级的语音服务。前面已经写过一篇unity中使用在线语音的方式,不过由于是国外的网站不稳定,速度相对也慢,目前貌似已经不能用了。所以看好讯飞在线免费使用的优点,同时也看靠c++的而不是直接通过http请求的方式,在这里简要分享下在unity3d开发环境下,基于windows平台的在线语音生成。值得说明的讯飞的语音选择面广,还支持部分方言!但是对于创业型公司来说,离线版本的收费还是比较昂贵的,也是为什么本文只会涉及在线语音生成。

准备SDK

在讯飞语音官方网站(http://www.xfyun.cn/),可以下载到最新的SDK,不过要先注册,而注册后创建应用后生成的apiKey就是使用这些SDK的钥匙。解压后,应该能看到doc文件夹,如果你精通c语言,同时也熟练使用C#调用C的库,那么这篇文字对你意义不大,你需要的只是马上查看doc中的api去写在c#中实现c的接口了。当然因为本人对调用c的dll不是很熟练,所以才想把一些遇到的小问题记录下来,防止和我一样不熟悉的人会卡壳。在simple文件夹中有一些官方的demo,都是c写的,直接调用在bin中的msc.dll中的方法。在往下阅读之前,你或许可以先去学习下官方的例子。

提取接口

查看doc中的iFlytek MSC Reference Manual.html网页,能直接看到msc.dll中所有的api。如果你只关心语音生成,那么我们直接跳到qtts.h部分,这里面一共就只有5个接口,也就是说我们的工作量并不大,只需要搞清楚这几个接口就好了。

将这些接口转换到C#中,应该看起来是这样的:

        [DllImport(mscdll, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]public static extern IntPtr QTTSSessionBegin(string _params, ref int errorCode);[DllImport(mscdll, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]public static extern int QTTSTextPut(string sessionID, string textString, uint textLen, string _params);[DllImport(mscdll, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]public static extern IntPtr QTTSAudioGet(string sessionID, ref int audioLen, ref SynthStatus synthStatus, ref int errorCode);[DllImport(mscdll, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]public static extern int QTTSSessionEnd(string sessionID, string hints);[DllImport(mscdll, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]public static extern int QTTSGetParam(string sessionID, string paramName, string paramValue, ref uint valueLen);

值得注意的是,因为涉及到中文章

QTTSTextPut

接口中CharSet必须要设置为

CharSet.Unicode

这是因为,如果在C#中调用dll,如果不指定的话,默认应该就传入Ascii码了,这样就会出现接收端为乱码的问题。

调用接口

在使用这几个接口的时候,可以参考官方demo中的tts.c文件,而这里实现的逻辑也和其中相差无几,唯一的区别就是,用的是C#无言也调用那几个接口。要说明的是本人很久没有用c++和c了,所以下面这段代码也不算原创,只是其中部分功能稍微调整了一下。

 public void Speak(string speekText, string szParams, string outWaveFlie){byte[] bytes = null;int ret = 0;try{sessionID = Marshal.PtrToStringAuto(MSPAPI.QTTSSessionBegin(szParams, ref ret));if (ret != 0){if (ttsSpeakErrorEvent != null) ttsSpeakErrorEvent.Invoke("初始化TTS引会话错误,错误代码:" + ret);return;}ret = MSPAPI.QTTSTextPut(sessionID, speekText, (uint)Encoding.Unicode.GetByteCount(speekText), string.Empty);if (ret != 0){if (ttsSpeakErrorEvent != null) ttsSpeakErrorEvent.Invoke("向服务器发送数据,错误代码:" + ret);return;}IntPtr audio_data;int audio_len = 0;SynthStatus synth_status = SynthStatus.MSP_TTS_FLAG_STILL_HAVE_DATA;using (MemoryStream ms = new MemoryStream()){ms.Write(new byte[44], 0, 44);//写44字节的空文件头while (synth_status == SynthStatus.MSP_TTS_FLAG_STILL_HAVE_DATA){audio_data = MSPAPI.QTTSAudioGet(sessionID, ref audio_len, ref synth_status, ref ret);if (audio_data != IntPtr.Zero){byte[] data = new byte[audio_len];Marshal.Copy(audio_data, data, 0, audio_len);ms.Write(data, 0, data.Length);if (synth_status == SynthStatus.MSP_TTS_FLAG_DATA_END || ret != 0){if (ret != 0){if (ttsSpeakErrorEvent != null) ttsSpeakErrorEvent.Invoke("下载TTS文件错误,错误代码:" + ret);return;}break;}}Thread.Sleep(150);}System.Diagnostics.Debug.WriteLine("wav header");WAVE_Header header = getWave_Header((int)ms.Length - 44);     //创建wav文件头byte[] headerByte = StructToBytes(header);                         //把文件头结构转化为字节数组                      //写入文件头ms.Position = 0;                                                        //定位到文件头ms.Write(headerByte, 0, headerByte.Length);                             //写入文件头bytes = ms.ToArray();ms.Close();}if (outWaveFlie != null){if (File.Exists(outWaveFlie)){File.Delete(outWaveFlie);}File.WriteAllBytes(outWaveFlie, bytes);}}catch (Exception ex){if (ttsSpeakErrorEvent != null) ttsSpeakErrorEvent.Invoke("Error:" + ex.Message);return;}finally{ret = MSPAPI.QTTSSessionEnd(sessionID, "");if (ret != 0){if (ttsSpeakErrorEvent != null) ttsSpeakErrorEvent.Invoke("结束TTS会话错误,错误代码:" + ret);}else{if (tts_SpeakFinishedEvent != null) tts_SpeakFinishedEvent.Invoke(speekText, bytes);}}}

其中要注意的一点是,因为c的dll中返回char*,在c#中,接收到的只能是指针,所以用了一个指针转换为字符串的方法:

Marshal.PtrToStringAuto

在对于字符串的长度问题,也没有直接使用string.Length,而是使用

(uint)Encoding.Unicode.GetByteCount(speekText)

封装为Unity模块

在上面已经实现了文字转语音功能的基础上,可以将这些功能封装为unity主线程中可以直接调用的一个模块,便于程序的使用。和前面一篇文章实现的接口是一样的,只是添加了一个批量下载的功能。当然你也可以自己去封装,毕竟这里的功能未必适合你的项目。下面是封装后预留的接口:

  public interface ITextToAudio{event UnityAction<string> onError;IEnumerator GetAudioClip(string text, UnityAction<AudioClip> OnGet, Params param = null);IEnumerator Downland(string[] text,UnityAction<float> onProgressChanged ,Params param = null);void CleanUpCatchs();}

其中,下载的时候都是使用协程,可以利用WWW直接将得到的AudioClip返回回来(值得注意的是,如果你的音频足够了解应该可以直接从byte中创建audioClip,就没有必要使用www了,但目前也就暂时这样使用着)。而Params就是对官方参数的解析类,可以自行定义。基于官方的字符串结构,这样生成比较理想(直接重写ToString):

  public override string ToString(){var fields = typeof(Params).GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.GetField | System.Reflection.BindingFlags.Instance);var param = new string[fields.Length];for (int i = 0; i < fields.Length; i++){param[i] = fields[i].Name + "=" + fields[i].GetValue(this);}return string.Join(",",param);}

源码及引用

因为这部分资料在网上还是比较少的,而且多数开发都是移动端和对语音识别相对较高,对于还在windows端开发的我,也只能默默羡慕他们了,所以和我一样在window端的你,少遇到点坑算是我的小小贡献了。

下面是github的地址:Text2Audio_Unity

对于本文来说,这里面部分代码并不是来自于自己的脑子,下面的我引用的开源地址:

c#(非unity环境)iflytek-csharp-demo

  unitySDK(多平台)IFlySDKForUnity

讯飞文字转语音_unity3D相关推荐

  1. 【Qbot】6.讯飞文字转语音Api使用/VITS派蒙复读机实现

    该项目计划长期进行维护更新,欢迎star:https://github.com/zstar1003/Qbot 前言 看完流浪地球2之后,萌生了一个想法:我想给机器人完整的一生.作为一个完整的机器人,声 ...

  2. uni 讯飞文字转语音 详解

    嗯, 使用讯飞的这个转语音是有bug的. 安卓8以上会出现合成错误的情况.所以我在网上找了一些简单的解决方法 话不多说上代码 test() {var main = plus.android.runti ...

  3. tp5手动整合讯飞文字转语音

    只需三步,轻松整合: 1 把讯飞插件放到项目的extend下面 2 新建一个类文件,用于 文字转语音 3 应用(记得use video.php use app\common\library\Video ...

  4. 长话无需短说 讯飞输入法超长语音输入不限时

    原标题:长话无需短说 讯飞输入法超长语音输入不限时 对用户来说,衡量语音输入好坏无外乎两个指标,一个是输入准确率,另一个是识别速度.搭载自然语言理解(NLU)优化模型的讯飞输入法语音识别率提升至98% ...

  5. 讯飞小车比赛语音控制(基础)

    讯飞小车比赛语音控制 第一次写这玩意可能写的不咋地,主要也是看了一些网上的资料后面给了连接,千万不要骂人,不行我删掉.谢谢 1.硬件层面 本次比赛所使用的麦克风为ucar小车自带的环形六麦克风阵列,该 ...

  6. 讯飞语音识别之语音转文字------java

    最近项目要用到语音识别的功能,网上找别人写的都不行,二话不说直接去讯飞官网注册下载了一个sdk,但是个人觉得sdk中并不是很明确的能运行实现功能,于是自己参考下载的sdk中的代码做了一个改动和封装.添 ...

  7. 科大讯飞:讯飞输入法日语音交互次数超10亿,将发力5G和AIoT领域

    蓝鲸TMT频道10月20日,讯飞输入法在10周年沙龙活动中发布了10.0版本,据介绍,该版本搭载了"动态自适应编解码语音识别引擎",新增的唇形辅助输入,能够提高嘈杂环境及近距离多人 ...

  8. android之基于百度语音合讯飞语音识别的语音交互

    app:http://fir.im/gval 这里面包含拨盘UI 开发平台:android studio 模拟一个  原始需求如下: 1)  在界面上,通过声音提示用户讲话: 2)  将语音内容转换为 ...

  9. 讯飞文本转语音(语音合成TTS)中添加控制静音和停顿方法

    讯飞的文本转语音和语音合成TTS中,用标点符号.回车换行可以实现一定的停顿效果.如果想要更长时间的静音或停顿怎么办? 插入停顿 这里有个特殊的标记实现静音或停顿,在文字中需要停顿的地方加入 [p100 ...

最新文章

  1. 优化老手分享网络优化中的4大方法
  2. Android性能优化面试题,与性能优化相关面试题 - 与IPC机制相关面试题 - 《Android面试宝典》 - 书栈网 · BookStack...
  3. Error - section 'InterruptVectorLow' can not fit the absolute section. Section 'InterruptVectorLow'
  4. Codeforces Round #490 (Div. 3)
  5. 从 class 文件 看 synchronize 锁膨胀过程(偏向锁 轻量级锁 自旋锁 重量级锁)
  6. 经典面试题(10):关于this指向,以下代码将输出什么?
  7. 狂神说学习笔记 Java流程控制
  8. Flask make_response(*args)
  9. Django 模板中变量、过滤器、标签的使用方法
  10. 什么是WAF防火墙以及具体作用
  11. 使用outlook 2007配置microsoft exchange邮箱方法步骤
  12. 小米路由器的服务器无响应怎么回事,小米路由器常见问题与解决方法(高级功能) | 192路由网...
  13. 白杨SEO:微信视频号直播功能怎么开通?视频号有什么价值?
  14. EXCEL 2016常用知识--Excel数据透视表
  15. phpcms教程:PHPCMS v9循环显示多个栏目及栏目数据列表
  16. 7-1 字符转换 (15分)C语言
  17. 【STM32F4】八、外部中断
  18. 我的团队——风信子网络工作室简介
  19. ADSL组建局域网快捷方案 (转)
  20. 干法读书心得:第一章 7、坚持“愚直地、认真地、诚实地”工作

热门文章

  1. 前端Base64编码知识,一文打尽
  2. Excel中去除单元格内左上角的绿色小三角
  3. MFC编译错误:#error: Building MFC application with /MD[d\] (CRT dll version) requires MFC shared dll vers
  4. ChatGPT是怎样介绍自己的?
  5. 微信支付成功,如何刷新用户当前页面的余额
  6. STM32F0 Error: Flash Download failed - Cortex-M0解决
  7. 除硼树脂-HPB119
  8. OpenCV高斯滤波GaussianBlur
  9. matlab 大写变小写,matlab 修改文件夹下所有文件名大写为小写
  10. 用PS制作旋转按钮图标,UI设计教程