Unity 科大讯飞-文本转语音-超拟人语音合成
1、首先在unity中新建工程;设置一个输入框和按钮,我们要做的就是点击按钮,将输入的文本转语音。
2、打开讯飞的超拟人语音合成界面
讯飞超拟人语音合成-入口
3、进入服务管理,拿到老三样
4、下载通信工具-WebSocket-sharp
WebSockte-sharp-下载链接
解压后得到该目录
5、在Unity中新建Plugins文件夹,将下载下来的【websocket-sharp】文件夹整个拖入
6、正常会出现报错
没关系,只需要打开Edit-ProjectSetting-Player-OtherSettings
差不多下拉到底部位置,取消勾选该选项
报错消失
如果没有报错那就忽略以上内容!
7、编写脚本。
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using WebSocketSharp;
public class XunfeiTTS : MonoBehaviour
{
// 你的密钥
private const string APPID = "*********";
private const string APIKey = "*********";
private const string APISecret = "*********";
//固定
private const string WsUrl = "wss://cbm01.cn-huabei-1.xf-yun.com/v1/private/mcd9m97e6";
private WebSocket ws;
private string outputPath;
public InputField inputField;
public Button playButton;
private AudioSource audioSource;
private readonly Queue<Action> mainThreadActions = new Queue<Action>();
private void Start()
{
audioSource = gameObject.AddComponent<AudioSource>();
if (playButton != null)
{
playButton.onClick.AddListener(OnPlayButtonClicked);
}
}
private void Update()
{
while (mainThreadActions.Count > 0)
{
Action action = mainThreadActions.Dequeue();
action?.Invoke();
}
}
public void OnPlayButtonClicked()
{
if (inputField != null && !string.IsNullOrEmpty(inputField.text))
{
StartTTS(inputField.text, "tts_output.mp3");
}
else
{
}
}
public void StartTTS(string text, string outputFilePath = "demo.mp3")
{
outputPath = Path.Combine(Application.persistentDataPath, outputFilePath);
// Delete existing file if it exists
if (File.Exists(outputPath))
{
File.Delete(outputPath);
}
// Create auth URL
string authUrl = AssembleWsAuthUrl(WsUrl, "GET", APIKey, APISecret);
// Initialize WebSocket
ws = new WebSocket(authUrl);
ws.SslConfiguration.EnabledSslProtocols = System.Security.Authentication.SslProtocols.Tls12;
ws.SslConfiguration.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
// Set up event handlers
ws.OnMessage += (sender, e) =>
{
// This runs on a background thread
try
{
if (e.IsBinary) return;
var message = JsonUtility.FromJson<TTSResponse>(e.Data);
if (message.payload != null && message.payload.audio != null)
{
byte[] audioData = Convert.FromBase64String(message.payload.audio.audio);
// File operations are thread-safe
using (var fileStream = new FileStream(outputPath, FileMode.Append))
{
fileStream.Write(audioData, 0, audioData.Length);
}
if (message.payload.audio.status == 2)
{
// Queue main thread actions
mainThreadActions.Enqueue(() =>
{
Debug.Log("TTS completed. File saved to: " + outputPath);
StartCoroutine(PlayAudioFile(outputPath));
ws.Close();
});
}
}
else if (message.header.code != 0)
{
mainThreadActions.Enqueue(() =>
{
ws.Close();
});
}
}
catch (Exception ex)
{
mainThreadActions.Enqueue(() => Debug.LogError($"错误信息: {ex.Message}"));
}
};
ws.OnError += (sender, e) =>
mainThreadActions.Enqueue(() => Debug.LogError($"WebSocket错误: {e.Message}"));
ws.OnClose += (sender, e) =>
mainThreadActions.Enqueue(() => Debug.Log($"WebSocket已关闭: {e.Reason}"));
ws.OnOpen += (sender, e) =>
{
mainThreadActions.Enqueue(() =>
{
Debug.Log("发送中");
SendTextData(text);
});
};
// Connect
ws.Connect();
}
private void SendTextData(string text)
{
var requestData = new TTSRequest
{
header = new CommonArgs { app_id = APPID, status = 2 },
parameter = new BusinessArgs
{
tts = new TTSConfig
{
vcn = "x4_lingxiaoxuan_oral",
volume = 50,
rhy = 0,
speed = 50,
pitch = 50,
bgs = 0,
reg = 0,
rdn = 0,
audio = new AudioConfig
{
encoding = "lame",
sample_rate = 24000,
channels = 1,
bit_depth = 16,
frame_size = 0
}
}
},
payload = new DataPayload
{
text = new TextData
{
encoding = "utf8",
compress = "raw",
format = "plain",
status = 2,
seq = 0,
text = Convert.ToBase64String(Encoding.UTF8.GetBytes(text))
}
}
};
string jsonData = JsonUtility.ToJson(requestData);
ws.Send(jsonData);
}
private IEnumerator PlayAudioFile(string filePath)
{
yield return new WaitForSeconds(0.5f);
if (!File.Exists(filePath))
{
yield break;
}
string url = "file://" + filePath;
using (WWW www = new WWW(url))
{
yield return www;
if (string.IsNullOrEmpty(www.error))
{
audioSource.clip = www.GetAudioClip(false, false, AudioType.MPEG);
while (audioSource.clip.loadState == AudioDataLoadState.Loading)
{
yield return null;
}
if (audioSource.clip.loadState == AudioDataLoadState.Loaded)
{
audioSource.Play();
}
else
{
}
}
else
{
}
}
}
private string AssembleWsAuthUrl(string requestUrl, string method, string apiKey, string apiSecret)
{
Uri uri = new Uri(requestUrl);
string host = uri.Host;
string path = uri.AbsolutePath;
string date = DateTime.UtcNow.ToString("r");
string signatureOrigin = $"host: {host}\ndate: {date}\n{method} {path} HTTP/1.1";
byte[] signatureSha = new HMACSHA256(Encoding.UTF8.GetBytes(apiSecret))
.ComputeHash(Encoding.UTF8.GetBytes(signatureOrigin));
string signature = Convert.ToBase64String(signatureSha);
string authorizationOrigin = $"api_key=\"{apiKey}\", algorithm=\"hmac-sha256\", headers=\"host date request-line\", signature=\"{signature}\"";
string authorization = Convert.ToBase64String(Encoding.UTF8.GetBytes(authorizationOrigin));
string query = $"?host={Uri.EscapeDataString(host)}&date={Uri.EscapeDataString(date)}&authorization={Uri.EscapeDataString(authorization)}";
return requestUrl + query;
}
private void OnDestroy()
{
if (ws != null && ws.IsAlive)
{
ws.Close();
}
}
// Data classes for JSON serialization
[Serializable]
private class TTSRequest
{
public CommonArgs header;
public BusinessArgs parameter;
public DataPayload payload;
}
[Serializable]
private class CommonArgs
{
public string app_id;
public int status;
}
[Serializable]
private class BusinessArgs
{
public TTSConfig tts;
}
[Serializable]
private class TTSConfig
{
public string vcn;
public int volume;
public int rhy;
public int speed;
public int pitch;
public int bgs;
public int reg;
public int rdn;
public AudioConfig audio;
}
[Serializable]
private class AudioConfig
{
public string encoding;
public int sample_rate;
public int channels;
public int bit_depth;
public int frame_size;
}
[Serializable]
private class DataPayload
{
public TextData text;
}
[Serializable]
private class TextData
{
public string encoding;
public string compress;
public string format;
public int status;
public int seq;
public string text;
}
[Serializable]
private class TTSResponse
{
public ResponseHeader header;
public ResponsePayload payload;
}
[Serializable]
private class ResponseHeader
{
public int code;
public string message;
public string sid;
}
[Serializable]
private class ResponsePayload
{
public ResponseAudio audio;
}
[Serializable]
private class ResponseAudio
{
public string audio;
public int status;
}
}
8、添加脚本,添加对应组件
9、测试成功