因为个人学习研究的需要,需要使用语音识别功能,特定针对语音识别,语音翻译做了个研究,主要是针对几种语音识别的优缺点,使用代码进行解析下。 目前市面上存在的常用语音识别大厂国内有讯飞、百度、阿里,腾讯,思必驰等, 国外的有微软,谷歌等。

国内识别率高的我们都首推讯飞,毕竟人家就是做语音识别起家的,百度最近做了一些语音识别免费使用的策略,如果想要白嫖的话,百度也是一个不错的选择。

我主要做的是语音转文字实时识别的用途,主要要求,识别率高,速度快,联网环境。 这些厂商大都是有离线识别模块的,但是离线识别的话不能做到根据语义实时纠错,所以,在识别率上是远低于在线实时识别的。


这里首先说讯飞,最近讯飞出的推广策略,可以免费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获取麦克风的音频流,传到接口。 详细的文档,可以到官方的文档查看。 测试返回结果: 关于市面上的语音实时识别转文字测试使用攻略,讯飞,微软智能认知,百度等-LMLPHP


关于百度,因为没有给出接口调用的方式,只能采用下载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

05-30 15:11