讯飞文字转语音_unity3D
来源:互联网 发布:淘宝网首页官网电脑版 编辑:程序博客网 时间:2024/06/07 10:52
前言
讯飞语音是国内的智能语音前沿,有语音合成及语音识别还有一些其他高级的语音服务。前面已经写过一篇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
- 讯飞文字转语音
- 第三方集成讯飞 语音识别 文字转音
- iOS 文字转语音
- 文字转语音
- Android文字转语音
- Android文字转语音
- iOS文字 转 语音
- 文字转语音AVSpeechSynthesizer
- iOS-文字转语音
- 文字转语音
- 文字转语音软件
- 文字转语音地址
- unity 文字转语音
- 文字转语音免费导出,语音来至讯飞
- .NET 语音转文字 文字转语音
- 科大讯飞文字转语音功能
- iOS文字转语音播放
- Java反射机制
- 20170922学习笔记Selenium 2第四章WebDriver API 4.1定位元素
- 利用SVD分解做协同过滤推荐
- MVP+Retrofit+RxJava
- linux内核中的GPIO系统之(2):pin control subsystem
- 讯飞文字转语音_unity3D
- 随笔
- 欢迎使用CSDN-markdown编辑器
- extern c
- 欢迎使用CSDN-markdown编辑器
- 面试题-面向对象篇
- 单例模式VS静态方法
- c#在字符中四位一组添加\u构成Unicode编码
- 移动端点击某个按钮触发的时候,出现点击状态的方法