5

依图语音API的C#封装以及调用进行语音转写的处理

 3 years ago
source link: https://www.cnblogs.com/wuhuacong/p/16038116.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

对于语音识别,一般有实时语音识别和语音文件的识别处理等方式,如在会议、培训等场景中,可以对录制的文件进行文字的转录,对于转录文字的成功率来说,如果能够转换90%以上的正确语音内容,肯定能减轻很多相关语音文本编辑的繁琐工作,而目前大多数语音转录的接口基本都能够保证在这个成功率上,有些甚至超过98%以上,非常不错,因为他们对于各种场景的滤波,可以更加提供文字的准确性。本篇随笔对各种语音开发平台做一个介绍,并针对依图语音API的C#封装以及调用进行语音转写进行介绍。

1、语音识别的接口提供商及API情况

语音识别有很多提供商,如常见的百度、阿里云、依图语音等,他们都提供了不同的API接口来实现外部的调用处理,本篇随笔主要针对依图语音(号称能够识别99%以上的智能转录)进行测试,对于其他类型的语音API,由于接口处理方式大同小异,并没有一一测试。

1)百度语音转录

2)阿里云语音转录

3)依云语音开发平台 

对于短语音的测试,依图语音的转录基本上在98%以上,偶尔有一两个词语文字因为不常见而出错而已,非常不错。

由于依图语音平台虽然提供了Java的案例代码,但是没有对应C #API调用案例,因此需要根据接口的说明进行转换。

对于依图语音的API说明,还是介绍的比较详细的,如下所示。

一般根据这些很容易编写对应的接口API封装函数,我这里主要用来封装对应的C#调用API。

2、基于依图语音API的C#封装

我们针对API进行了封装,然后在界面上进行测试对应的功能,如下Winform界面测试所示。

一个短句,发现一个错误的字,不过整体效果还是比较好,断句以及内容都算很好的。

这个项目分为三部分,第一是对请求接口数据和返回数据的对象封装(简称DTO对象),第二是对依图长语音和短语音接口的API接口封装,包括授权信息的处理;长语音转写的任务创建、任务查询、任务停止几个部分;以及短语音的转写处理。第三是Winform界面的功能测试和日志处理操作,方便了解功能的实际处理情况。

【创建长语音转录任务】主要用于演示如何生成签名并调用接口创建转写任务的操作,操作后提示是否成功,并记录下任务ID,供查询任务或停止任务使用。同时在日志和文本框里面生成相关的JSON数据信息,日志在运行目录的log/log.txt中记录。

 对于依图语音转换来说,我们一般使用异步的处理方式,所以更好的是等待任务处理完后回调告诉我们处理结果更好,因此适合于部署Web API的应用,用于让依图语音平台的服务器进行回调处理结果。

8867-20220322103351838-1087827567.png

 首先第一步我们需要处理API的授权签名,用于后续接口的调用,我们可以根据签名的规则进行编写代码即可。 

    /// <summary>
    /// 签名处理
    /// </summary>
    public class SignatureHelper
    {
        /// <summary>
        /// 生成授权签名信息
        /// </summary>
        /// <param name="data"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static string CalculateRFC2104HMAC(string data, string key)
        {
            key = key ?? "";
            var encoding = new System.Text.UTF8Encoding();
            byte[] keyByte = encoding.GetBytes(key);
            byte[] messageBytes = encoding.GetBytes(data);
            using (var hmacsha256 = new HMACSHA256(keyByte))
            {
                byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
                StringBuilder builder = new StringBuilder();
                for (int i = 0; i < hashmessage.Length; i++)
                {
                    builder.Append(hashmessage[i].ToString("x2"));
                }
                return builder.ToString();
            }
        }

        /// <summary>
        /// 生成请求授权信息
        /// </summary>
        /// <returns></returns>
        public static AuthRequestDto GetAuthDto()
        {
            var builder = new ConfigurationBuilder()
               .SetBasePath(Directory.GetCurrentDirectory())
               .AddJsonFile("appsettings.json", true, reloadOnChange: true);
            var config = builder.Build();

            //读取配置
            var DevId = config["YITU:DevId"];
            var DevKey = config["YITU:DevKey"];

            var dto = new AuthRequestDto();
            dto.DevId = DevId;
            dto.Timestamp = DateTime.Now.DateTimeToInt();

            var accessKey = DevKey;
            var signKey = dto.DevId + dto.Timestamp; //签名加密键
            dto.Signature = SignatureHelper.CalculateRFC2104HMAC(signKey, accessKey);

            return dto;
        }

        /// <summary>
        /// 对请求的HttpClient设置相关的授权请求Header信息
        /// </summary>
        /// <param name="client"></param>
        /// <param name="dto"></param>
        public static void SetSigature(HttpClient client, AuthRequestDto dto)
        {
            client.DefaultRequestHeaders.Add("x-dev-id", dto.DevId);
            client.DefaultRequestHeaders.Add("x-request-send-timestamp", dto.Timestamp.ToString());
            client.DefaultRequestHeaders.Add("x-signature", dto.Signature);

            client.DefaultRequestHeaders.Add("Date", DateTime.Now.AddHours(-8).ToString("R"));
        }
    }

 这个按钮功能,主要测试一下使用辅助类生成的授权认证信息,授权认证信息是根据加密规则构建的签名信息和账号信息。

这个主要是演示辅助类生成签名,并获得的数据信息:

 然后我们定义一个控制器,用于接收依图语音的服务器API回调处理,用于记录转换的结果内容。

namespace CallBackApi.Controllers
{
    /// <summary>
    /// 用于接收长语音信息回调的处理
    /// </summary>
    [ApiController]
    [Route("[controller]")]
    public class LongVoiceController : ControllerBase

在控制器中编写存储的逻辑,写入数据库。

        /// <summary>
        /// 回调写入结果, 路由:post /longvoice
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<CommonResultDto> CallBack(LongVoiceResultDto input)
        {
            CommonResultDto result = null;
            var json = JsonConvert.SerializeObject(input, Formatting.Indented);
            _logger.LogInformation(json);

            try
            {
                var helper = new LongVoiceDbHelper();
                var bResult = await helper.SaveData(input);
                result = new CommonResultDto(0, bResult ? "操作成功" : "操作失败");
            }
            catch(Exception ex)
            {
                result = new CommonResultDto(1, ex.Message);
                _logger.LogError(ex, ex.Message);
            }

            return result;
        }

我们根据依图语音的对象模型,构建对应的对象信息属性,然后保存起来即可。

        /// <summary>
        /// 保存数据到数据库
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<bool> SaveData(LongVoiceResultDto dto)
        {
            bool result = false;
            if(dto != null)
            {
                using(var db = CreateDb())
                {
                    var info = new ConsultationInfo();
                    info.DiscernStatus = dto.taskId;
                    info.OperateStatus = "未识别";
                    if (dto.data != null && dto.data.speechResult != null)
                    {
                        if (dto.data.statusCode == 3)
                        {
                            info.OperateStatus = "已识别";
                        }
                        var speechResult = dto.data.speechResult;
                        info.DiscernText = speechResult.resultText;
                    }

                    result = await db.Insertable(info).ExecuteCommandAsync() > 0;
                }
            }
            return result;
        }

回调接口主要用于长语音任务执行完成后,服务器的回调处理,我这里使用.net core5的Web API项目来处理,通过使用Restful规则的API控制处理,接收服务器的回调数据请求,并把请求数据记录数据库或者在log/log.txt文件中。

我们先发起语音转录请求,如下代码所示。

       private LongVoiceRequestDto GetVoice1()
        { 
            var input = new LongVoiceRequestDto()
            {
                audioUrl = "https://www.***.com/downloads/iqidi.mp3", //语音文件
                callback = "http://www.***.com:8080/longvoice", //回调地址
                fileData = new FileData
                {
                    aue = "mpeg2",
                    audioName = "iqidi.mp3",
                    sampleRateHertz = 16000,
                    lang = "MANDARIN"
                },
                speechConfig = new SpeechConfig
                {
                    scene = "GENERAL",
                    byWords = true,
                    customWords = new List<string>()
                    {
                        //"开发框架提供商",
                        //"广州爱奇迪软件科技有限公司"
                    },
                    useCustomWordsIds = new List<int>()
                    {
                    },
                    addPunctuation = true,
                    wordsReplace = true,
                    numOfSpeakers = 1,
                    convertNumber = true,
                    //disfluency = true
                },
                wordsReplace = new List<WordsReplace>()
                {
                    new WordsReplace()
                    {
                         keywords = "他妈的",
                         replace = "*"
                    },
                    new WordsReplace()
                    {
                        keywords = "破坏",
                        replace = "##"
                    }
                }
            };
            return input;
        }

对于长语音的转换,我们只是发起请求,有时候需要等待一点时间,服务器才会根据请求的回调地址进行会写操作的。发起长语音的请求如下代码所示。

        private async void btnTransfer_Click(object sender, EventArgs e)
        {
            try
            {
                var input = this.radSmall.Checked ? GetVoice1() : GetVoice2();
                var result = await longApi.Transfer(input);
                if (result != null)
                {
                    this.txtTaskId.Text = result.taskId;//任务ID,可供查询,停止
                    MessageUtil.ShowTips($"任务创建{(result.rtn == 0 ? "成功" : "异常:" + result.message)},任务ID:{result.taskId}");
                }

                var json = JsonConvert.SerializeObject(result, Formatting.Indented);
                this.richLog.AppendText(json);
                this.richLog.AppendText(Environment.NewLine);
                Serilog.Log.Information(json);
            }
            catch(Exception ex)
            {
                MessageUtil.ShowError(ex.Message);
            }
        }

回调接口主要用于长语音任务执行完成后,服务器的回调处理。

而如果是短语音转写,就和我们前面说到的一样,马上就可以出结果了。

        private async void btnShort_Click(object sender, EventArgs e)
        {
            try
            {
                var input = GetShortVoice();
                var result = await shortApi.Transfer(input);
                if (result != null)
                {
                    MessageUtil.ShowTips($"短语音听写, 操作{(result.rtn == 0 ? "成功" : "异常:" + result.message)}");
                }

                var json = JsonConvert.SerializeObject(result, Formatting.Indented);
                this.richLog.AppendText(json);
                this.richLog.AppendText(Environment.NewLine);
                Serilog.Log.Information(json);
            }
            catch (Exception ex)
            {
                MessageUtil.ShowError(ex.Message);
            }
        }

我们只需要把返回的结果信息,转换为相关的模型对象,然后进行显示或者存储即可完成。

项目可以通过VS发布方式发布一个文件包,部署到服务器上即可。

配置项目后台一直运行。

以上就是关于依图语音的测试处理,对应后端的回调处理,我们可以转录长语音文件,不过依图语音平台对于身份的审核比较严格,也是收费的API(其他阿里、百度也都是),因此在商用的时候,需要考虑好即可。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK