You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

330 lines
12 KiB

using NAudio.Wave;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using WebSocket4Net;
namespace IOTContainer.Common
{
internal class BaiduSpeechForWebApi
{
private const int appid = 101476776;//到控制台-语音合成页面获取
private const String apiSecret = "GXfbPhns1Lhmq0BChJgVuUe3acQBOn4w";//到控制台-语音合成页面获取
private const String apiKey = "CTY1O4eGFxUn1rU5PRyUzdAy";//到控制台-语音合成页面获取
enum Status
{
FirstFrame = 0,
ContinueFrame = 1,
LastFrame = 2
}
private static WebSocket webSocket;
private static IWaveIn recorder;//录音机
private static volatile Status status = Status.FirstFrame;
private int deviceNumber = 0; // 选择的录音设备下标。多个设备时可设置为用户选择
/// <summary>
/// 单例模式
/// </summary>
public static BaiduSpeechForWebApi baiduSpeechForWebApi
{
get
{
if (_baiduSpeechForWebApi == null)
_baiduSpeechForWebApi = new BaiduSpeechForWebApi();
return _baiduSpeechForWebApi;
}
}
private static BaiduSpeechForWebApi _baiduSpeechForWebApi = new BaiduSpeechForWebApi();
private BaiduSpeechForWebApi() { }
public void startVoice()
{
status = Status.FirstFrame;
string url = !string.IsNullOrEmpty(ComParameters.Parameters.baiduVoiceServer) ? ComParameters.Parameters.baiduVoiceServer : "vop.baidu.com";
StartBaiduSpeech(url);
}
public void stopVoice()
{
status = Status.LastFrame;
recorder.DataAvailable -= OnDataAvailable;
dynamic frame = new JObject();
frame.type = "FINISH";
webSocket.Send(frame.ToString());
recorder.StopRecording();
Thread.Sleep(1000);
webSocket.Close();
}
public static void StartBaiduSpeech(string url)
{
try
{
Log.MyLog.WriteLogFile("StartBaiduSpeech:Enter", "BaiDuSpeech");
string ts = GenerateTimeStamp();
Log.MyLog.WriteLogFile("GenerateTimeStamp:" + ts, "BaiDuSpeech");
string yb = EncryptUtil.HMACSHA1Text(EncryptUtil.GetMd5Code(appid + ts), apiKey);
Log.MyLog.WriteLogFile("yb:" + yb, "BaiDuSpeech");
string uril = $"ws://{url}/realtime_asr?sn=" + Guid.NewGuid().ToString();
Log.MyLog.WriteLogFile("uril:" + uril, "BaiDuSpeech");
try
{
//开始请求 WebSocket
webSocket = new WebSocket(uril);
webSocket.Opened += OnOpened;
webSocket.DataReceived += OnDataReceived;
webSocket.MessageReceived += OnMessageReceived;
webSocket.Closed += OnClosed;
webSocket.Error += OnError;
webSocket.Open();
Log.MyLog.WriteLogFile("开始请求 WebSocket", "BaiDuSpeech");
}
catch (Exception ex)
{
Log.MyLog.WriteLogFile("WebSocket:" + ex.ToString(), "BaiDuSpeech"); ;
}
// 初始化录音机
try
{
recorder = new WaveInEvent { WaveFormat = new WaveFormat(16000, 1) };
recorder.DataAvailable += OnDataAvailable;
recorder.RecordingStopped += OnRecordingStopped;
Log.MyLog.WriteLogFile("录音机初始化完成", "BaiDuSpeech");
}
catch (Exception ex)
{
Log.MyLog.WriteLogFile("recorder:" + ex.ToString(), "BaiDuSpeech");
}
}
catch (Exception ex)
{
Log.MyLog.WriteLogFile("StartBaiduSpeech:" + ex.ToString(), "BaiDuSpeech");
}
}
private static void OnDataAvailable(object sender, WaveInEventArgs e)
{
try
{
//Log.MyLog.WriteLogFile("OnDataAvailable:status:" + status, "BaiDuSpeech");
switch (status)
{
case Status.FirstFrame://握手
{
dynamic frame = new JObject();
frame.type = "START";
frame.data = new JObject
{
{ "appid",appid},
{ "appkey",apiKey},
{ "dev_pid",15372},
{ "cuid","cuid-1"},
{ "format","pcm"},
{ "sample",16000},
};
webSocket.Send(frame.ToString());
status = Status.ContinueFrame;
}
break;
case Status.ContinueFrame://开始发送
{
dynamic frame = new JObject();
frame = Convert.ToBase64String(e.Buffer);
webSocket.Send(e.Buffer, 0, e.Buffer.Length);
}
break;
case Status.LastFrame://关闭
{
}
break;
default:
break;
}
}
catch (Exception ex)
{
Log.MyLog.WriteLogFile("OnDataAvailable:" + ex.ToString(), "BaiDuSpeech");
}
//Log.MyLog.WriteLogFile("status:"+ status, "BaiDuSpeech");
}
/// <summary>
/// 检查麦克风
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private static void OnOpened(object sender, EventArgs e)
{
Log.MyLog.WriteLogFile("OnOpened", "BaiDuSpeech");
try
{
recorder.StartRecording();
}
catch (Exception ex)
{
Log.MyLog.WriteLogFile("未插入麦克风,程序关闭", "BaiDuSpeech");
}
}
//返回结果处理
private static void OnMessageReceived(object sender, MessageReceivedEventArgs e)
{
//返回的JSON串"{\"err_msg\":\"OK\",\"err_no\":0,\"log_id\":1542502511,\"result\":\"你好\",\"sn\":\"c36964c8-d86c-4a06-b7cf-8939539d3eb8_ws_1\",\"type\":\"MID_TEXT\"}\n"
//Console.WriteLine(e.Message);
try
{
dynamic msg = JsonConvert.DeserializeObject(e.Message);
//Log.MyLog.WriteLogFile("OnMessageReceived:" + e.Message, "BaiDuSpeech");
if (msg.err_msg != "OK")
{
Log.MyLog.WriteLogFile($"error => {msg.message},err_no => {msg.err_no}", "BaiDuSpeech");
return;
}
string ws = null;
if (msg.err_msg == "OK" && msg.type == "FIN_TEXT")
{
ws = msg.result;
Log.MyLog.WriteLogFile(ws, "BaiDuSpeech");
ComParameters.Parameters.EdgeWindow.SendVoiceMsgBaidu("{\"code\": \"200\",\"msg\": \"" + ws + "\",\"data\": { \"ModelType\" : \"\", \"ActionType\" : \"\", \"query\":\"???\" , \"ttsMsg\":\"录音结束\"}}");
}
//var ws = msg.result;
if (ws == null)
{
return;
}
}
catch (Exception ex)
{
Log.MyLog.WriteLogFile("OnMessageReceived:" + ex.ToString(), "BaiDuSpeech");
}
}
private static void OnError(object sender, SuperSocket.ClientEngine.ErrorEventArgs e)
{
Log.MyLog.WriteLogFile("OnError", "BaiDuSpeech");
Log.MyLog.WriteLogFile(e.Exception.Message, "BaiDuSpeech");
}
private static void OnClosed(object sender, EventArgs e)
{
Log.MyLog.WriteLogFile("OnClosed", "BaiDuSpeech");
//recorder.DataAvailable -= OnDataAvailable;
}
private static void OnDataReceived(object sender, DataReceivedEventArgs e)
{
Log.MyLog.WriteLogFile("OnDataReceived", "BaiDuSpeech");
Log.MyLog.WriteLogFile(e.Data.Length.ToString(), "BaiDuSpeech");
}
private static void OnRecordingStopped(object sender, StoppedEventArgs e)
{
Log.MyLog.WriteLogFile("OnRecordingStopped", "BaiDuSpeech");
Log.MyLog.WriteLogFile(e.Exception?.Message, "BaiDuSpeech");
}
/// <summary>
/// 时间戳
/// </summary>
/// <returns></returns>
public static string GenerateTimeStamp()
{
try
{
TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
return Convert.ToInt64(ts.TotalSeconds).ToString();
}
catch (Exception ex)
{
Log.MyLog.WriteLogFile("GenerateTimeStamp:" + ex.ToString(), "BaiDuSpeech");
return null;
}
}
}
public class EncryptUtil
{
public static string GetMd5Code(string text)
{
try
{
//MD5类是抽象类
MD5 md5 = MD5.Create();
//需要将字符串转成字节数组
byte[] buffer = Encoding.Default.GetBytes(text);
//加密后是一个字节类型的数组,这里要注意编码UTF8/Unicode等的选择
byte[] md5buffer = md5.ComputeHash(buffer);
string str = null;
// 通过使用循环,将字节类型的数组转换为字符串,此字符串是常规字符格式化所得
foreach (byte b in md5buffer)
{
//得到的字符串使用十六进制类型格式。格式后的字符是小写的字母,如果使用大写(X)则格式后的字符是大写字符
//但是在和对方测试过程中,发现我这边的MD5加密编码,经常出现少一位或几位的问题;
//后来分析发现是 字符串格式符的问题, X 表示大写, x 表示小写,
//X2和x2表示不省略首位为0的十六进制数字;
str += b.ToString("x2");
}
//Console.WriteLine(str);//202cb962ac59075b964b07152d234b70
return str;
}
catch (Exception e)
{
//System.Diagnostics.Debug.WriteLine("GetMd5Code exception:" + e.Message);
}
return null;
}
#region HMACSHA1加密 对二进制数据转Base64后再返回
/// <summary>
/// HMACSHA1加密
/// </summary>
/// <param name="text">要加密的原串</param>
///<param name="key">私钥</param>
/// <returns></returns>
public static string HMACSHA1Text(string text, string key)
{
try
{
//HMACSHA1加密
HMACSHA1 hmacsha1 = new HMACSHA1
{
Key = System.Text.Encoding.UTF8.GetBytes(key)
};
byte[] dataBuffer = System.Text.Encoding.UTF8.GetBytes(text);
byte[] hashBytes = hmacsha1.ComputeHash(dataBuffer);
return Convert.ToBase64String(hashBytes);
}
catch (Exception e)
{
//System.Diagnostics.Debug.WriteLine("HMACSHA1Text exception:" + e.Message);
}
return null;
}
#endregion
}
}