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; // 选择的录音设备下标。多个设备时可设置为用户选择 /// /// 单例模式 /// 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"); } /// /// 检查麦克风 /// /// /// 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"); } /// /// 时间戳 /// /// 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后再返回 /// /// HMACSHA1加密 /// /// 要加密的原串 ///私钥 /// 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 } }