因为个人学习研究的需要,需要使用语音识别功能,特定针对语音识别,语音翻译做了个研究,主要是针对几种语音识别的优缺点,使用代码进行解析下。 目前市面上存在的常用语音识别大厂国内有讯飞、百度、阿里,腾讯,思必驰等, 国外的有微软,谷歌等。
国内识别率高的我们都首推讯飞,毕竟人家就是做语音识别起家的,百度最近做了一些语音识别免费使用的策略,如果想要白嫖的话,百度也是一个不错的选择。
我主要做的是语音转文字实时识别的用途,主要要求,识别率高,速度快,联网环境。 这些厂商大都是有离线识别模块的,但是离线识别的话不能做到根据语义实时纠错,所以,在识别率上是远低于在线实时识别的。
这里首先说讯飞,最近讯飞出的推广策略,可以免费15天试用总时长24小时的实时语音,楼主目前就用这个完成了开发测试。
先说体验: 1.注册简单快捷,马上就能用上。
2.可以自定义定义热词,增加识别。
3.ws协议接口,无需安装sdk等。
4.采用白名单机制,访问接口,最多只允许5个,这点其实是比较烦的,不过为了安全。
5.技术客服工作时间955,周六周末没人回答你的问题,平时提工单回复速度还可以。
使用: 楼主采用的是C#开发的,这里的实例语言全部采用C#。
1.先获取自己的APPID,APIKey,以及在白名单列表中配置自己的外网ip。没有配白名单一般会报错10105
2.接口地址+参数:如下:主要有三个参数appid,ts,signa,具体参考文档 ws://rtasr.xfyun.cn/v1/ws?appid=595f23df&ts=1512041814&signa=IrrzsJeOFk1NGfJHW6SkHUoN9CU= 这里的MD5加密一定要用官方的,这里的坑很多,很容易弄错,官方也找不到,楼主从官方其他项目里找到的。 这里放出使用时用到的Helper类:
`
public class EncryptHelper
{
/// <summary>
/// HMACSHA1算法加密并返回ToBase64String
/// </summary>
/// <param name="strText">签名参数字符串</param>
/// <param name="strKey">密钥参数</param>
/// <returns>返回一个签名值(即哈希值)</returns>
public static string ToBase64hmac(string strText, string strKey)
{
HMACSHA1 myHMACSHA1 = new HMACSHA1(Encoding.UTF8.GetBytes(strKey));
byte[] byteText = myHMACSHA1.ComputeHash(Encoding.UTF8.GetBytes(strText));
return System.Convert.ToBase64String(byteText);
}
/// <summary>
/// MD5加密
/// </summary>
/// <param name="source"></param>
/// <returns></returns>
public static String EncryptWithMD5(String source)
{
byte[] sor = Encoding.UTF8.GetBytes(source);
MD5 md5 = MD5.Create();
byte[] result = md5.ComputeHash(sor);
StringBuilder strbul = new StringBuilder(40);
for (int i = 0; i < result.Length; i++)
{
//加密结果"x2"结果为32位,"x3"结果为48位,"x4"结果为64位
strbul.Append(result[i].ToString("x2"));
}
return strbul.ToString();
}
/// <summary>
/// 获取时间戳
/// </summary>
/// <returns></returns>
public static string GetTimeStamp()
{
TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
return Convert.ToInt64(ts.TotalSeconds).ToString();
}
/// <summary>
/// 将16进制的字符串转为byte[]
/// </summary>
/// <param name="hexString"></param>
/// <returns></returns>
public static byte[] StrToHexByte(string hexString)
{
hexString = hexString.Replace(" ", "");
if ((hexString.Length % 2) != 0)
hexString += " ";
byte[] returnBytes = new byte[hexString.Length / 2];
for (int i = 0; i < returnBytes.Length; i++)
returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
return returnBytes;
}
}
3.连接websocket接口,我这边采用的是nuget上下载WebSocketSharp。 封装了一个讯飞连接的类:
public class XunFeiSocketHelper
{
public delegate void message_Event(string message);
public event message_Event messageUpdate_Event;
/// <summary>
/// baseURL
/// </summary>
string baseUrl = "ws://rtasr.xfyun.cn/v1/ws?";
/// <summary>
/// 源地址
/// </summary>
string originStr = "http://rtasr.xfyun.cn";
string appId = ConfigurationSettings.AppSettings["appId"];
string apiKey = ConfigurationSettings.AppSettings["apiKey"];
/// <summary>
/// 时间戳
/// </summary>
string ts;
/// <summary>
/// 验证码
/// </summary>
string signa = "";
/// <summary>
/// 建议音频流每40ms发送1280字节,发送过快可能导致引擎出错; 2.音频发送间隔超时时间为15秒,超时服务端报错并主动断开连接。
/// </summary>
int send_Size = 1280;
public WebSocket websocket;
/// <summary>
/// 调用讯飞接口
/// </summary>
/// <param name="voice"></param>
public void GetSocketValue(byte[] voice)
{
ts = EncryptHelper.GetTimeStamp();
signa = EncryptHelper.ToBase64hmac(EncryptHelper.EncryptWithMD5(appId + ts), apiKey);
string reqUrl = string.Format(baseUrl + "appid={0}&ts={1}&signa={2}", appId, ts, signa);
try
{
websocket = new WebSocket(reqUrl);
websocket.Origin = originStr;
websocket.OnMessage += Websocket_OnMessage;
websocket.OnOpen += Websocket_OnOpen;
websocket.Connect();
}
catch (Exception ex)
{
throw ex;
}
}
/// <summary>
/// 发送消息
/// </summary>
/// <param name="message"></param>
public void sendMessage(byte[] message)
{
try
{
if (websocket.ReadyState == WebSocketState.Open)
websocket.Send(message);
}
catch (Exception ex)
{
LoggingHelper.Error(ex.Message);
LoggingHelper.Error(ex.StackTrace);
}
}
private void Websocket_OnOpen(object sender, EventArgs e)
{
}
private void Websocket_OnMessage(object sender, WebSocketSharp.MessageEventArgs e)
{
try
{
string message = e.Data;
if (messageUpdate_Event != null)
messageUpdate_Event(message);
}
catch (Exception ex)
{
LoggingHelper.Error(ex.Message);
LoggingHelper.Error(ex.StackTrace);
}
}
}
4.OK,到这里就可以完成调用了。 PS:忘记说一点了,关于音频采样,楼主是通过NAudio获取麦克风的音频流,传到接口。 详细的文档,可以到官方的文档查看。 测试返回结果:
关于百度,因为没有给出接口调用的方式,只能采用下载sdk的模式,楼主就没有写代码,只是浅浅的研究了下。 百度语音识别有一个功能是讯飞没有的,就是可以自定义声学模型,提交测试音频和数据到服务器上训练,可以针对性的对相关音频识别。
微软,C#毕竟是亲儿子,各种中文文档,demo详细到令人发指。 如果采用C#开发,更是一步可以到位,对其他语言也是支持,包括JavaScript也有demo示例。 体验: 1.文档详细,支持多语言,多渠道 2.微软提供免费一个月的体验,如果注册,直接赠送200美元可以在云上消费产品。但是必须要有海外的信用卡才能注册支付这是一个坑。 3.上手非常便捷,2分钟即可写出一个demo。 4.支持自定义模型训练 5.官方qa没看到,不知道怎么提供服务,也没有用到。
上手: 1.Neget引用Microsoft.CognitiveServices.Speech 2.框架调整为x64 3.代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.CognitiveServices.Speech;
namespace MSSpeechToText
{
class Program
{
public static async Task RecognizeSpeechAsync()
{
// Creates an instance of a speech config with specified subscription key and service region.
// Replace with your own subscription key and service region (e.g., "westus").
var config = SpeechConfig.FromSubscription("YourSubscriptionKey", "westus");
config.SpeechRecognitionLanguage = "zh-CN";
// Creates a speech recognizer.
using (var recognizer = new SpeechRecognizer(config))
{
Console.WriteLine("Say something...");
// Starts speech recognition, and returns after a single utterance is recognized. The end of a
// single utterance is determined by listening for silence at the end or until a maximum of 15
// seconds of audio is processed. The task returns the recognition text as result.
// Note: Since RecognizeOnceAsync() returns only a single utterance, it is suitable only for single
// shot recognition like command or query.
// For long-running multi-utterance recognition, use StartContinuousRecognitionAsync() instead.
var result = await recognizer.RecognizeOnceAsync();
// Checks result.
if (result.Reason == ResultReason.RecognizedSpeech)
{
Console.WriteLine($"We recognized: {result.Text}");
}
else if (result.Reason == ResultReason.NoMatch)
{
Console.WriteLine($"NOMATCH: Speech could not be recognized.");
}
else if (result.Reason == ResultReason.Canceled)
{
var cancellation = CancellationDetails.FromResult(result);
Console.WriteLine($"CANCELED: Reason={cancellation.Reason}");
if (cancellation.Reason == CancellationReason.Error)
{
Console.WriteLine($"CANCELED: ErrorCode={cancellation.ErrorCode}");
Console.WriteLine($"CANCELED: ErrorDetails={cancellation.ErrorDetails}");
Console.WriteLine($"CANCELED: Did you update the subscription info?");
}
}
}
}
public static async Task ContinuousRecognitionAsync()
{
var config = SpeechConfig.FromSubscription("YourSubscriptionKey", "westus");
config.SpeechRecognitionLanguage = "zh-CN";
using (var recognizer = new SpeechRecognizer(config))
{
Console.WriteLine("Say something...");
await recognizer.StartContinuousRecognitionAsync();
recognizer.Recognizing += (s, e) =>
{
Console.WriteLine(e.Result.Duration+" "+ e.Result.Text);
};
recognizer.Recognized += (s, e) =>
{
//Console.WriteLine(e.Result.Duration + " " + e.Result.Text);
};
recognizer.Canceled += (s, e) =>
{
// Console.WriteLine($"\nRecognition canceled. Reason: {e.Reason}; ErrorDetails: {e.ErrorDetails}");
};
recognizer.SessionStarted += (s, e) =>
{
//Console.WriteLine("\nSession started event.");
};
recognizer.SessionStopped += (s, e) =>
{
// Console.WriteLine("\nSession stopped event.");
};
while (true)
{
}
// await recognizer.StartContinuousRecognitionAsync().ConfigureAwait(false);
}
}
static void Main()
{
ContinuousRecognitionAsync().Wait();
Console.WriteLine("Please press a key to continue.");
Console.ReadLine();
}
}
}
好了,到这里就可以跑起来了。
最后这里说一说对实时识别功能的感受: 实时识别的原理是厂商在服务器上部署了机器学习功能的语音识别,可以根据实时语音识别的结果的语义,修改之前识别的文字,只有一段话识别结束(机器认定的一句话结束有时很长),就不会修改前面的文字。
识别率提高了,但直观感觉不是很好,因为你之前阅读过得字,因为后面不停修改,所以在不停变化,你需要不断重复阅读,这个阅读体验很差,但如果不需要实时性那么高,允许有几秒误差的话,体验会好很多!
最后推广下自己做的小软件,实时语音识别翻译字幕软件:http://www.wildcaption.com