第一句子网 - 唯美句子、句子迷、好句子大全
第一句子网 > Unity 百度SDK 之 语音合成 TTS WebAPI 功能的实现

Unity 百度SDK 之 语音合成 TTS WebAPI 功能的实现

时间:2021-09-12 21:05:53

相关推荐

Unity 百度SDK 之 语音合成 TTS  WebAPI 功能的实现

Unity 百度SDK 之 在线语音合成 TTS WebAPI 功能的实现

目录

Unity 百度SDK 之 在线语音合成 TTS WebAPI 功能的实现

一、简单介绍

二、百度官网关于在线语音合成的介绍

三、 在线识别 Access Token 的获取

四、注意事项

五、效果预览

六、实现步骤

七、关键代码

一、简单介绍

Unity 工具类,自己整理的一些游戏开发可能用到的模块,单独独立使用,方便游戏开发。

本节使用Baidu API 进行语音合成功能的简单的实现。

二、百度官网关于在线语音合成的介绍

网址:/ai-doc/SPEECH/Gk38y8lzk

百度语音合成服务,基于HTTP请求的REST API接口,将文本转换为可以播放的音频文件

合成的文件格式为 mp3,pcm(8k及16k),wav(16k),具体见aue参数。 若您需要其它格式,音频文件的转换方法请参考“语音识别工具”=>“音频文件转码”一节

本文档描述了使用语音合成服务REST API的方法。

多音字可以通过标注自行定义发音。格式如:重(chong2)报集团。

目前只有中英文混合这一种语言,优先中文发音。示例: " I bought 3 books” 发音 “three”; “ 3 books are bought” 发音 “three”; “我们买了 3 books” 发音“三”

语音合成示例代码: /Baidu-AIP/speech-demo/tree/master/rest-api-tts

三、 在线识别 Access Token 的获取

网址介绍: /ai-doc/REFERENCE/Ck3dwjhhu

webAPI 获取的方式

四、注意事项

1、如果你的是安卓Android 系统版本过高的话,可能会报错,而不能实现语音合成功能

java.io.IOException: Cleartext HTTP traffic to not permitted

2、正对上面过高版本Android的报错,只要在 AndroidMainfest.xml ,添加 android:usesCleartextTraffic="true"即可

(Unity AndroidManifest.xml路径:Unity\Editor\Data\PlaybackEngines\AndroidPlayer\Apk 路径下 )

2、注意添加 litjson 和 NAudio 插件

五、效果预览

六、实现步骤

1、打开Unity,新建一个工程

2、在工程中添加一个脚本

3、编写脚本,获取输入文本,传给 TTS 语音合成,在把合成的转为Unity能播放的格式,然后播放出来,记得合成之前,获取Access Token

4、在场景中,添加一个按钮和输入框

5、把脚本,添加到场景中,并且把按钮添加监听事件

6、运行场景,即可测试效果

七、关键代码

using System.Collections;using System.Collections.Generic;using UnityEngine;using LitJson;using System.Text;using System;using UnityEngine.UI;using System.IO;using NAudio;using NAudio.Wave;/// <summary>/// 用来转换语音,将文字转成语音。/// </summary>public class BaiduTTS : MonoBehaviour{#region 字段、属性private string tex;private string lan = "zh";private string tok;private string ctp = "1";//用户唯一标识,这里建议使用机器 MAC 地址或 IMEI 码private string cuid = "00-12-7B-16-74-8D";//待加入的功能//语速 0-9 5为中语速public string spd = "5";//音调 0-9 5为中语调public string pit = "5";//音量 0-9 5为中音量public string vol = "5";//发音 0-4 发音人选择, 0为女声,1为男声,3为情感合成-度逍遥,4为情感合成-度丫丫,默认为普通女声public string per = "103";//上传数据的url,private string url;//所需要转成语音的信息文本private string Speak = "欢迎来到百度语音合成";private string grant_Type = "client_credentials";//百度appkey[Header("Please input baidu API Key")]public string client_ID = "你的 API Key";//百度Secret Key[Header("Please input baidu Secret Key")]public string client_Secret = "你的 Secret Key";//获取百度令牌的urlprivate string getTokenAPIPath = "/oauth/2.0/token?";#endregion//测试public InputField inputField;AudioSource aud;/// <summary>/// 将所需要说的话进行编码/// </summary>/// <returns>The to encoding UT f8.</returns>/// <param name="str">String.输入您想说的话</param>private void StringToEncodingUTF8(string str){byte[] tempByte = Encoding.UTF8.GetBytes(str);for (int i = 0; i < tempByte.Length; i++){//UrlEncode编码规则tex += (@"%" + Convert.ToString(tempByte[i], 16));}//拼接上传的urlurl = "/text2audio?tex=" + tex + "&lan=zh&cuid=" + cuid + "&ctp=1&tok=" + tok + "&per=" + per + "&spd=" + spd + "&pit=" + pit + "&vol=" + vol + "";Debug.Log("Token:" + tok);}/// <summary>/// 获取百度用户令牌,否则无法使用API/// </summary>/// <param name="url">获取的url</param>/// <returns></returns>private IEnumerator GetToken(string url){WWWForm TokenForm = new WWWForm();TokenForm.AddField("grant_type", grant_Type);TokenForm.AddField("client_id", client_ID);TokenForm.AddField("client_secret", client_Secret);WWW getTW = new WWW(url, TokenForm);yield return getTW;if (getTW.isDone){//Debug.Log (getTW.text);if (getTW.error == null){tok = JsonMapper.ToObject(getTW.text)["access_token"].ToString();}else{Debug.LogError(getTW.error);}}}/// <summary>/// 上传和下载/// </summary>/// <param name="url">URL.</param>private IEnumerator Loading(string url){WWW loadingAudio = new WWW(url);yield return loadingAudio;if (loadingAudio.error == null){if (loadingAudio.isDone){//下载该音频 /* PC下需要对MP3格式转码,手机端则使用MP3*/#if UNITY_EDITOR_WINaud.clip = FromMp3Data(loadingAudio.bytes);aud.Play();#elif UNITY_STANDALONE_WINaud.clip = FromMp3Data(loadingAudio.bytes);aud.Play ();#elif UNITY_ANDROIDaud.clip = loadingAudio.GetAudioClip (false,true,AudioType.MPEG);aud.Play ();#endif}else{Debug.LogError(loadingAudio.error);}}}void Awake(){if (GetComponent<AudioSource>() == null){aud = gameObject.AddComponent<AudioSource>();}else{aud = gameObject.GetComponent<AudioSource>();}aud.playOnAwake = false;StartCoroutine(GetToken(getTokenAPIPath));}//Button响应事件public void StartStringToAudio(){tex = "";Speak = inputField.text;Debug.Log(Speak);//文本编码StringToEncodingUTF8(Speak);//Debug.Log ("编码后得到的信息:"+tex);StartCoroutine(Loading(url));} //MP3 --- wavpublic static AudioClip FromMp3Data(byte[] data){//加载数据进入流MemoryStream mp3stream = new MemoryStream(data);//流中的数据转换为WAV格式Mp3FileReader mp3audio = new Mp3FileReader(mp3stream);WaveStream waveStream = WaveFormatConversionStream.CreatePcmStream(mp3audio);//转换WAV数据WAV wav = new WAV(AudioMemStream(waveStream).ToArray());AudioClip audioClip = AudioClip.Create("testSound", wav.SampleCount, 1, wav.Frequency, false);audioClip.SetData(wav.LeftChannel, 0);return audioClip;}private static MemoryStream AudioMemStream(WaveStream waveStream){MemoryStream outputStream = new MemoryStream();using (WaveFileWriter waveFileWriter = new WaveFileWriter(outputStream, waveStream.WaveFormat)){byte[] bytes = new byte[waveStream.Length];waveStream.Position = 0;waveStream.Read(bytes, 0, Convert.ToInt32(waveStream.Length));waveFileWriter.Write(bytes, 0, bytes.Length);waveFileWriter.Flush();}return outputStream;}}public class WAV{// 两个字节转换为一个浮动范围在-1到1static float bytesToFloat(byte firstByte, byte secondByte){//两个字节转换为一个短(小端字节序)short s = (short)((secondByte << 8) | firstByte);//将范围从-1到1return s / 32768.0F;}static int bytesToInt(byte[] bytes, int offset = 0){int value = 0;for (int i = 0; i < 4; i++){value |= ((int)bytes[offset + i]) << (i * 8);}return value;}// 属性public float[] LeftChannel { get; internal set; }public float[] RightChannel { get; internal set; }public int ChannelCount { get; internal set; }public int SampleCount { get; internal set; }public int Frequency { get; internal set; } /// <summary>/// 自定义Wav格式/// </summary>/// <param name="wav">Wav.</param>public WAV(byte[] wav){//确定单声道或立体声ChannelCount = wav[22];// 23(99.999%)往后丢弃//得到的频率Frequency = bytesToInt(wav, 24);int pos = 12; //第一个子块ID从12-16// 继续迭代,直到找到数据块 (i.e. 64 61 74 61 ...... (i.e. 100 97 116 97 in decimal))while (!(wav[pos] == 100 && wav[pos + 1] == 97 && wav[pos + 2] == 116 && wav[pos + 3] == 97)){pos += 4;int chunkSize = wav[pos] + wav[pos + 1] * 256 + wav[pos + 2] * 65536 + wav[pos + 3] * 16777216;pos += 4 + chunkSize;}pos += 8;//定位实际声音开始的数据.SampleCount = (wav.Length - pos) / 2;// 2字节/采样 (16 bit 单声道)if (ChannelCount == 2) SampleCount /= 2; // 4字节/采样 (16 bit 音响)//分配内存(右将null如果只有单声道声音)LeftChannel = new float[SampleCount];if (ChannelCount == 2) RightChannel = new float[SampleCount];else RightChannel = null;//写入双数组int i = 0;while (pos < wav.Length){LeftChannel[i] = bytesToFloat(wav[pos], wav[pos + 1]);pos += 2;if (ChannelCount == 2){RightChannel[i] = bytesToFloat(wav[pos], wav[pos + 1]);pos += 2;}i++;}}}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。