如何開發一個在線朗讀的功能----科大訊飛語音合成實戰
起因
天天學習強國,不過強國APP的語音朗讀不錯,了解之后是科大訊飛支持的,於是開始擼碼。https://www.xfyun.cn/doc/tts/online_tts/API.html

注冊為開發者,接口要求這些我就不贅述了,文檔里面寫的清楚。當然具體實現是另外一回事。
聽了一下效果,怎么說呢,免費的和特色的還是有很大的差別的,免費的是剛好讓你能忍的那個級別,特色的和真人差別不大。看了一下收費,分為兩部分,一部分是接口費用,一部分是特色發音人的費用。基於擼碼的習慣,一切先從免費開始。

詳情請看這里:https://www.xfyun.cn/services/online_tts
開干
看了一圈沒有C#的demo,這就尷尬了,雖然是有文檔,但是大家都懂,好比微信公眾號的開發文檔,要變成實際的代碼,看得見的應用那是要廢一番功夫的。找了一番之后,終於發現一個開源的項目剛發布沒多久,真是喜出望外就開干了: https://github.com/zuiyuewentian/XunFeiNETSDK
訊飛的這個接口是基於websock的,我們先用控制台程序做一個demo。C#其實自帶了websocket,不過這里用的是WebSocketSharp,這個我覺得很好,System.Net.WebSockets.WebSocket 是基於異步方法的,后面我會講到,而WebSocketSharp.WebSocket 是基於事件的,很符合前端的編程習慣。
websocket = new WebSocketSharp.WebSocket(reqUrl);
websocket.OnMessage += Websocket_OnMessage;
websocket.OnOpen += Websocket_OnOpen;
websocket.Connect();
訊飛的服務器收到我們的文字內容后,會以流的形勢把音頻傳回來,在我們的服務器上把這種流轉成文件即可。
private static Stopwatch stopwatch;
public static void Main(string[] args)
{
//text要合成的文字,pathUrl域名
stopwatch = new Stopwatch();
stopwatch.Start();
var xunFeiNetSdk = new XunFeiTTS();
xunFeiNetSdk.MessageUpdate_Event += XunFeiNetSdk_MessageUpdate_Event;
xunFeiNetSdk.SendData("張家界荷花國際機場,北京大興機場,長沙黃花機場,邵陽武岡機場,所有航班全部復航!");
Console.Read();
}
static byte[] data = new byte[0];
private static void XunFeiNetSdk_MessageUpdate_Event(TTS_Data_Model message, string error = null)
{
if (error != null)
{
Console.WriteLine(error);
return;
}
try
{
//合成結束
if (message.status == 2)
{
Console.WriteLine("合成成功");
string voice = string.Format("{0}.wav", DateTime.Now.ToString("yyyyMMddHHmmssfff"));
Console.WriteLine("正在保存..."+voice);
data = data.Concat(message.audioStream).ToArray();
var mWavWriter = new WaveFileWriter(voice, new WaveFormat(16000, 1));
mWavWriter.Write(data, 0, data.Length);
mWavWriter.Close();
mWavWriter.Dispose();
Console.WriteLine("保存成功...");
var sp = stopwatch.Elapsed;
Console.WriteLine("用時" + sp);
}
else
{
data = data.Concat(message.audioStream).ToArray();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
文件的存儲用的是NAudio,XunFeiNETSDK里面的代碼我獨立出來。

(最近2個月航班太少了,工資驟減,原諒我說出我的內心話)
這樣就得到了語音了。聽一聽,還能接受。但是怎么做到web頁面里面呢?
改造成web應用
首先的思路是,前端把文字發過來,然后交給sdk去獲取音頻,得到文件的地址后返回給前端。所以最合適的方案還是前端也用websocket,因為發送消息和收到消息是分開的。那么這又需要后端有一個websocket服務了
我又不想單獨去開一個websocket服務,那就可以將這個websocket做成api的形式,如下:
View Code
var webSocket;
var player = document.getElementById("player");
function sendSocketMsg() {
var msg = $("#msg").val();
webSocket.send(msg);
showMsg("發送消息:" + msg, "blue");
}
openSocket();
function openSocket() {
if (webSocket != null && typeof (webSocket) != "undefined") {
closeSocket();
}
webSocket = new WebSocket("ws://" + location.hostname + ":" + location.port + "/api/msg");
webSocket.onopen = function () {
showMsg("連接建立");
}
webSocket.onerror = function () {
showMsg("發生異常");
}
webSocket.onmessage = function (event) {
showMsg("收到消息:" + event.data, "yellow");
if (event.data.indexOf("voice:") > -1) {
var src = event.data.split("voice:")[1];
player.src = src;
player.play();
}
}
webSocket.onclose = function () {
showMsg("連接關閉");
}
}
function closeSocket() {
if (webSocket != null && typeof (webSocket) != "undefined") {
webSocket.close();
}
}
function showMsg(msg, type) {
if (type === null || typeof (type) === "undefined") type = "gray";
$("#show").append("<span class='" + type + "'>" + msg + "</span><br>");
}

這樣就得到產品的雛形了。后續要考慮的是文字的長短、音頻播放器的展示效果,還能換一下播放的聲音等等,每次給你說一個功能,其實這個功能背后有太多細節了。
Console版源碼:https://download.csdn.net/download/stoneniqiu/12347028
Web版源碼:https://download.csdn.net/download/stoneniqiu/12347167

