依圖語音API的C#封裝以及調用進行語音轉寫的處理

對於語音識別,一般有實時語音識別和語音文件的識別處理等方式,如在會議、培訓等場景中,可以對錄製的文件進行文字的轉錄,對於轉錄文字的成功率來說,如果能夠轉換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的應用,用於讓依圖語音平台的伺服器進行回調處理結果。

 首先第一步我們需要處理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 = "//www.***.com/downloads/iqidi.mp3", //語音文件
                callback = "//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(其他阿里、百度也都是),因此在商用的時候,需要考慮好即可。