using System; using System.Collections.Generic; using System.Net.WebSockets; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; using System.Threading; using UnityEngine; using Newtonsoft.Json.Linq; using System.Linq; public class XunFeiManager : MonoBehaviour { /// 单例 public static XunFeiManager Instance; /// 固定不动 private const string STTURL = "wss://iat-api.xfyun.cn/v2/iat"; /// 固定不动 private const string TTSURL = "wss://tts-api.xfyun.cn/v2/tts"; /// 改成你自己的APPID!!!!!! private const string APPID = "ff508508"; /// 改成你自己的APISecret!!!!!! private const string APISecret = "Njk1YmQwNjE3YjFmODEwZjg0ZTM4NTQ1"; /// 改成你自己的APIKey!!!!!! private const string APIKey = "d68246e4ce71ff58db89705e9f19159b"; /// WebSocket private ClientWebSocket webSocket; private void Awake() { //初始化单例 Instance = this; } public async Task SpeechToText(JObject request) { //获取请求数据 byte[] bytes = Convert.FromBase64String(request["data"].ToString()); //建立连接 await Connect(STTURL); //发送消息 await STTSendMessage(bytes); //接收消息 string text = await STTReceiveMessage(); //关闭接连 await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); //构建返回Json数据 JObject response = new JObject(); response["text"] = text; //响应Post请求 return response.ToString(); } public async Task TextToSpeech(JObject request) { //获取请求数据 string text = request["text"].ToString(); string voice = request["voice"].ToString(); //建立连接 await Connect(TTSURL); //发送消息 await TTSSendMessage(text, voice); //接收消息 string base64String = await TTSReceiveMessage(); //关闭接连 await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); //构建返回Json数据 JObject response = new JObject(); response["data"] = base64String; //响应Post请求 return response.ToString(); } /// /// 连接讯飞API /// /// private async Task Connect(string url) { //新建ClientWebSocket webSocket = new ClientWebSocket(); //使用WebSocket连接讯飞的服务 await webSocket.ConnectAsync(new Uri(GetUrl(url)), CancellationToken.None); //await Console.Out.WriteLineAsync("讯飞WebSocket连接成功"); } /// /// 讯飞语音转文本,发送消息 /// private async Task STTSendMessage(byte[] bytes) { //状态值 0代表第1帧,1代表中间帧,2代表结尾帧 int status; int remainLength = bytes.Length; //分段发送消息 while (remainLength > 0) { byte[] currBytes; if (remainLength > 1280) { status = remainLength == bytes.Length ? 0 : 1; currBytes = new byte[1280]; Array.Copy(bytes, bytes.Length - remainLength, currBytes, 0, 1280); remainLength -= 1280; } else { status = 2; currBytes = new byte[remainLength]; Array.Copy(bytes, bytes.Length - remainLength, currBytes, 0, remainLength); remainLength = 0; } JObject jsonData = STTCreateJsonData(status, currBytes); byte[] messageBytes = Encoding.UTF8.GetBytes(jsonData.ToString()); await webSocket.SendAsync(messageBytes, WebSocketMessageType.Text, true, CancellationToken.None); //等待30ms await Task.Delay(20); //Console.WriteLine("发送消息:"+jsonData.ToString()); } //Console.WriteLine("数据发送完成"); } /// /// 讯飞语音转文本,生成需要发送的Json数据 /// /// /// /// private JObject STTCreateJsonData(int status, byte[] bytes) { JObject requestObj = new JObject(); JObject commonJson = new JObject(); commonJson["app_id"] = APPID; requestObj["common"] = commonJson; JObject bussinessJson = new JObject(); bussinessJson["language"] = "zh_cn"; bussinessJson["domain"] = "iat"; bussinessJson["accent"] = "mandarin"; bussinessJson["dwa"] = "wpgs"; requestObj["business"] = bussinessJson; JObject dataJson = new JObject(); dataJson["status"] = status; dataJson["format"] = "audio/L16;rate=16000"; dataJson["encoding"] = "raw"; dataJson["audio"] = Convert.ToBase64String(bytes); requestObj["data"] = dataJson; return requestObj; } /// /// 讯飞语音转文本,接收消息 /// /// private async Task STTReceiveMessage() { //webSocket. //状态值 int status = 0; string finalText = string.Empty; while (status != 2) { byte[] buffer = new byte[8 * 1024]; WebSocketReceiveResult webSocketReceiveResult = await webSocket.ReceiveAsync(buffer, CancellationToken.None); string receivedMessage = Encoding.UTF8.GetString(buffer, 0, webSocketReceiveResult.Count); //await Console.Out.WriteLineAsync("receivedMessage:" + receivedMessage); finalText += STTParseMessage(receivedMessage, out status); } Debug.Log("讯飞语音转文本:" + finalText); return finalText; } /// /// 讯飞语音转文本,解析收到的Json消息 /// /// /// /// private string STTParseMessage(string message, out int status) { JObject jObject = JObject.Parse(message); int code = (int)jObject["code"]; if (code != 0)//失败 { string errMsg = jObject["message"].ToString(); Debug.LogError("讯飞语音转文本,解析Json消息失败,错误信息:" + errMsg); } else//成功 { string result = string.Empty; foreach (JObject i in jObject["data"]["result"]["ws"]) { foreach (JObject w in i["cw"]) { result += w["w"].ToString(); } } //状态值 //识别结果是否结束标识: //0:识别的第一块结果 //1:识别中间结果 //2:识别最后一块结果 status = (int)jObject["data"]["status"]; return result; } status = -1; return string.Empty; } /// /// 讯飞文本转语音,发送消息 /// private async Task TTSSendMessage(string text, string voice) { //构建请求需要的Json字符串 JObject jsonData = TTSCreateJsonData(text, voice); byte[] messageBytes = Encoding.UTF8.GetBytes(jsonData.ToString()); //发送消息 await webSocket.SendAsync(messageBytes, WebSocketMessageType.Text, true, CancellationToken.None); } /// /// 讯飞文本转语音,生成需要发送的Json数据 /// /// /// /// private JObject TTSCreateJsonData(string text, string voice) { JObject requestObj = new JObject(); JObject commonJson = new JObject(); commonJson["app_id"] = APPID; requestObj["common"] = commonJson; JObject bussinessJson = new JObject(); bussinessJson["aue"] = "raw"; bussinessJson["vcn"] = voice; bussinessJson["speed"] = 50; bussinessJson["volume"] = 50; bussinessJson["pitch"] = 50; bussinessJson["tte"] = "UTF8"; requestObj["business"] = bussinessJson; JObject dataJson = new JObject(); dataJson["status"] = 2; dataJson["text"] = Convert.ToBase64String(Encoding.UTF8.GetBytes(text)); requestObj["data"] = dataJson; return requestObj; } /// /// 讯飞文本转语音,接收消息 /// /// 语音合成后的base64字符串 private async Task TTSReceiveMessage() { //webSocket. //状态值 int status = 0; List bytes = new List(); while (status != 2) { bool receivedCompleted = false; string receivedMessage = string.Empty; while (!receivedCompleted) { byte[] buffer = new byte[8 * 1024]; WebSocketReceiveResult webSocketReceiveResult = await webSocket.ReceiveAsync(buffer, CancellationToken.None); receivedMessage += Encoding.UTF8.GetString(buffer, 0, webSocketReceiveResult.Count); receivedCompleted = webSocketReceiveResult.Count != 8 * 1024; } //await Console.Out.WriteLineAsync("receivedMessage:" + receivedMessage); bytes.AddRange(Convert.FromBase64String(TTSParseMessage(receivedMessage, out status)).ToList()); //finalAudioBase64String += TTSParseMessage(receivedMessage, out status).TrimEnd('='); } string finalAudioBase64String = Convert.ToBase64String(bytes.ToArray()); //await Console.Out.WriteLineAsync("讯飞语音转文本:" + finalAudioBase64String); return finalAudioBase64String; } /// /// 讯飞文本转语音,解析收到的Json消息 /// /// /// /// private string TTSParseMessage(string message, out int status) { JObject jObject = JObject.Parse(message); if (jObject["message"].ToString() == "success") { if (jObject["data"] != null) { if (jObject["data"]["audio"] != null) { if ((int)jObject["data"]["status"] == 2) { status = 2; } else { status = 1; } return jObject["data"]["audio"].ToString(); } } Debug.LogError("ERROR:TTSParseMessage失败,data为空"); status = 0; return string.Empty; } else { Debug.LogError("ERROR:TTSParseMessage失败,错误消息:" + jObject["message"].ToString()); status = 0; return string.Empty; } } #region 生成URL private string GetUrl(string url) { Uri uri = new Uri(url); //官方文档要求时间必须是UTC+0或GMT时区,RFC1123格式(Thu, 01 Aug 2019 01:53:21 GMT)。 string date = DateTime.Now.ToString("r"); //组装生成鉴权 string authorization = ComposeAuthUrl(uri, date); //生成最终鉴权 string uriStr = $"{uri}?authorization={authorization}&date={date}&host={uri.Host}"; //返回生成后的Url return uriStr; } /// /// 组装生成鉴权 /// /// /// /// 最终编码后的鉴权 private string ComposeAuthUrl(Uri uri, string date) { string signature; //最终编码后的签名 string authorization_origin; //原始鉴权 //原始签名 string signature_origin = string.Format("host: " + uri.Host + "\ndate: " + date + "\nGET " + uri.AbsolutePath + " HTTP/1.1"); //使用hmac-sha256算法加密后的signature string signature_sha = HmacSHA256(signature_origin, APISecret); //使用hmac - sha256算法结合apiSecret对signature_origin签名 signature = signature_sha; string auth = "api_key=\"{0}\", algorithm=\"{1}\", headers=\"{2}\", signature=\"{3}\""; authorization_origin = string.Format(auth, APIKey, "hmac-sha256", "host date request-line", signature); //参数介绍:APIKey,加密算法名,headers是参与签名的参数(该参数名是固定的"host date request-line"),生成的签名 return ToBase64String(authorization_origin); } /// /// 加密算法HmacSHA256 /// /// /// /// private static string HmacSHA256(string secret, string signKey) { string signRet = string.Empty; using (HMACSHA256 mac = new HMACSHA256(Encoding.UTF8.GetBytes(signKey))) { byte[] hash = mac.ComputeHash(Encoding.UTF8.GetBytes(secret)); signRet = Convert.ToBase64String(hash); } return signRet; } /// /// UTF字符串转成Base64字符串 /// /// /// private static string ToBase64String(string value) { if (value == null || value == "") { return ""; } byte[] bytes = Encoding.UTF8.GetBytes(value); return Convert.ToBase64String(bytes); } #endregion }