批量語音識別

在這裏插入圖片描述
總體思路
FFmpeg對音視頻進行處理獲取wav音頻文件,對音頻文件按時間切片(30秒),每個片段調用百度或科大訊飛API進行識別(這裏主要是用免費的那種),得到結果後再拼接,保存到.TXT中。

FFmpeg音視頻處理
使用FFmpeg對音視頻進行格式轉換、切片

  public class FFmpeg
    {
        private Process FFmpegProcess;
        /// <summary>
        /// ffmpeg
        /// </summary>
        /// <param name="arg"></param>
        public void StartFfmpeg(string arg)
        {
            string path = Environment.CurrentDirectory + "\\ffmpeg.exe";
            try
            {
                FFmpegProcess = new Process();//建立外部調用線程
                FFmpegProcess.StartInfo.FileName = path;//要調用外部程序的絕對路徑
                FFmpegProcess.StartInfo.Arguments = arg;
                FFmpegProcess.StartInfo.UseShellExecute = false;//不使用操作系統外殼程序啓動線程(一定爲FALSE,詳細的請看MSDN)
                FFmpegProcess.StartInfo.RedirectStandardError = true;//把外部程序錯誤輸出寫到StandardError流中(這個一定要注意,FFMPEG的所有輸出信息,都爲錯誤輸出流,用StandardOutput是捕獲不到任何消息的...這是我耗費了2個多月得出來的經驗...mencoder就是用standardOutput來捕獲的)
                FFmpegProcess.StartInfo.CreateNoWindow = true;//不創建進程窗口
                FFmpegProcess.ErrorDataReceived += new DataReceivedEventHandler(Output);//外部程序(這裏是FFMPEG)輸出流時候產生的事件,這裏是把流的處理過程轉移到下面的方法中,詳細請查閱MSDN
                FFmpegProcess.Start();//啓動線程
                FFmpegProcess.BeginErrorReadLine();//開始異步讀取
                FFmpegProcess.WaitForExit();//阻塞等待進程結束
                FFmpegProcess.Close();//關閉進程
                FFmpegProcess.Dispose();//釋放資源
            }
            catch (Exception)
            {
                FFmpegProcess.Close();
                FFmpegProcess.Dispose();
                //Log(ex.ToString());
            }
        }
        /// <summary>
        /// ffmpeg輸出
        /// </summary>
        /// <param name="sendProcess"></param>
        /// <param name="output"></param>
        private void Output(object sendProcess, DataReceivedEventArgs output)
        {
            if (!string.IsNullOrEmpty(output.Data))
            {
                string msg = output.Data.ToString();
                Console.WriteLine(msg);

            }
        }

        public void Stop()
        {
            if (FFmpegProcess != null)
            {
                FFmpegProcess.Close();
            }
        }


        /// <summary>
        /// 轉成wav格式並切片,存放在臨時文件夾
        /// </summary>
        /// <param name="filePath"></param>
        public void ToWav(string filePath, string fileName)
        {      
            //轉成wav
            string arg = $"-i \"{filePath}\"  -ar 16000 -ac 1 -acodec pcm_s16le \"{fileName}.wav\"";
            StartFfmpeg(arg);

            var wav = fileName + ".wav";
            //30秒切片
            arg = $"-i \"{wav}\" -c copy -f segment -segment_time 30 \"{fileName}-%03d.wav\"";
            StartFfmpeg(arg);
            if (File.Exists(wav))
            {
                File.Delete(wav);
            }
        }

    }

將FFmpeg處理後得到的wav音頻片段逐個傳入下列識別接口中,得到識別結果後再拼接即可。

百度語音識別
Nuget添加BaiduAI:
在這裏插入圖片描述
百度雲AI開放平臺 申請語音識別應用,得到APP_ID API_KEY SECRET_KEY
這裏選擇wav格式

  public static class BaiduAI
    {
        // 設置APPID/AK/SK
        static readonly string APP_ID;
        static readonly string API_KEY;
        static readonly string SECRET_KEY;
        static readonly Asr client;

        static BaiduAI()
        {
            APP_ID = System.Configuration.ConfigurationManager.AppSettings["Baidu_APP_ID"];
            API_KEY = System.Configuration.ConfigurationManager.AppSettings["Baidu_API_KEY"];
            SECRET_KEY = System.Configuration.ConfigurationManager.AppSettings["Baidu_SECRET_KEY"];
            client = new Asr(APP_ID, API_KEY, SECRET_KEY)
            {
                Timeout = 120000  // 修改超時時間
            };
        }
        public static string AsrData(string path)
        {
            Console.WriteLine(path);
            var data = File.ReadAllBytes(path);

            // 可選參數
            var options = new Dictionary<string, object>
             {
                {"dev_pid", 1537}
             };
            var result = client.Recognize(data, "wav", 16000, options);
            Console.WriteLine(result);
            //File.Delete(pcm);
            if (result["err_no"].ToString() == "0")
            {
                return result["result"][0].ToString();
            }
            else
            {
                return "";//result["err_no"].ToString()+"------"+ result["err_msg"].ToString();
            }
        }
    }

科大訊飛
訊飛雲平臺申請應用
有兩種方式:1、通過SDK調用。2、通過WebAPI調用。
第一種方式,需要在雲平臺相應的應用中下載SDK。
第二種方式,需要在雲平臺相應的應用中加入白名單。

這裏採用第二種,下列代碼後面註釋掉的部分是第一種方式

 /// <summary>
    /// 科大訊飛語音識別
    /// </summary>
    public static class Xfyun
    {
        readonly static string x_appid;
        readonly static string api_key;

        static Xfyun()
        {
            x_appid = System.Configuration.ConfigurationManager.AppSettings["Xfyun_x_appid"];
            api_key = System.Configuration.ConfigurationManager.AppSettings["Xfyun_x_api_key"];
        }
        /// <summary>
        /// WebAPI調用
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public static string Request(string path)
        {
            Console.WriteLine(path);
            string aue = "raw", engine_type = "sms16k";
            string param = "{\"aue\":\"" + aue + "\"" + ",\"engine_type\":\"" + engine_type + "\"}";

            System.Text.Encoding encode = System.Text.Encoding.ASCII;
            byte[] bytedata = encode.GetBytes(param);
            string x_param = Convert.ToBase64String(bytedata);


            TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
            string curTime = Convert.ToInt64(ts.TotalSeconds).ToString();

           // MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
            string result = string.Format("{0}{1}{2}", api_key, curTime, x_param);
            //Console.WriteLine(Program.Md5(x_param));
            string X_checksum =Helper.Md5(result);
            //Console.WriteLine(X_checksum);

            byte[] arr = File.ReadAllBytes(path);
            string cc = Convert.ToBase64String(arr);
            string data = "audio=" + cc;

            string Url = "http://api.xfyun.cn/v1/service/v1/iat";
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url);
            request.Method = "POST";
            request.ContentType = "application/x-www-form-urlencoded";
            request.Headers["X-Appid"] = x_appid;
            request.Headers["X-CurTime"] = curTime;
            request.Headers["X-Param"] = x_param;
            request.Headers["X-CheckSum"] = X_checksum;

            request.ContentLength = Encoding.UTF8.GetByteCount(data);
            Stream requestStream = request.GetRequestStream();
            StreamWriter streamWriter = new StreamWriter(requestStream, Encoding.GetEncoding("gb2312"));
            streamWriter.Write(data);
            streamWriter.Close();

            string htmlStr = string.Empty;
            HttpWebResponse response = request.GetResponse() as HttpWebResponse;
            Stream responseStream = response.GetResponseStream();
            using (StreamReader reader = new StreamReader(responseStream, Encoding.GetEncoding("UTF-8")))
            {
                htmlStr = reader.ReadToEnd();
            }
            responseStream.Close();

            Console.WriteLine(htmlStr);
            var json = Newtonsoft.Json.JsonConvert.DeserializeObject<XfyunResult>(htmlStr);
            if (json.code == "0")
            {
                return json.data;
            }
            else
            {
                Console.WriteLine(json.desc);
                return string.Empty;
            }
        }

       

        class XfyunResult
        {
            public string code { get; set; }

            public string data { get; set; }

            public string desc { get; set; }

            public string sid { get; set; }
        }

        #region 客戶端SDK調用
        //const string my_appid = "appid = 5cc5121a";
        //const string session_begin_params = "sub = iat, domain = iat, language = zh_cn, accent = mandarin, sample_rate = 16000, result_type = plain, result_encoding =gb2312";

        ///// <summary>
        ///// 初始化(登錄科大訊飛)
        ///// </summary>
        ///// <returns></returns>
        //public static bool Init_audio()
        //{
        //    int res = mscDLL.MSPLogin(null, null, my_appid);//用戶名,密碼,登陸信息,前兩個均爲空
        //    if (res != (int)Errors.MSP_SUCCESS)
        //    {//說明登陸失敗
        //        Console.WriteLine("登陸失敗!");
        //        MessageBox.Show("網絡不穩定,請檢查網絡!", "提示");
        //        //this.Close();
        //        return false;
        //    }
        //    Console.WriteLine("登陸成功!");
        //    return true;
        //}
        ///// <summary>
        ///// 語音識別
        ///// </summary>
        ///// <param name="audio_path"></param>
        ///// <param name="session_begin_params"></param>
        ///// <returns></returns>
        //public static string Audio_iat(string audio_path)
        //{
        //    if (audio_path == null || audio_path == "") return "";
        //    IntPtr session_id;
        //    StringBuilder result = new StringBuilder();//存儲最終識別的結果
        //    var aud_stat = AudioStatus.MSP_AUDIO_SAMPLE_CONTINUE;//音頻狀態
        //    var ep_stat = EpStatus.MSP_EP_LOOKING_FOR_SPEECH;//端點狀態
        //    var rec_stat = RecogStatus.MSP_REC_STATUS_SUCCESS;//識別狀態
        //    int errcode = (int)Errors.MSP_SUCCESS;
        //    byte[] audio_content;  //用來存儲音頻文件的二進制數據
        //    int totalLength = 0;//用來記錄總的識別後的結果的長度,判斷是否超過緩存最大值
        //    try
        //    {
        //        audio_content = File.ReadAllBytes(audio_path);
        //        //SoundPlayer player = new SoundPlayer(audio_path);
        //        //player.Play();
        //    }
        //    catch (Exception e)
        //    {
        //        Console.WriteLine(e);
        //        audio_content = null;
        //    }
        //    if (audio_content == null)
        //    {
        //        Console.WriteLine("沒有讀取到任何內容");
        //        return "";
        //    }
        //    Console.WriteLine("開始進行語音聽寫.......");
        //    /*
        //    * QISRSessionBegin();
        //    * 功能:開始一次語音識別
        //    * 參數一:定義關鍵詞識別||語法識別||連續語音識別(null)
        //    * 參數2:設置識別的參數:語言、領域、語言區域。。。。
        //    * 參數3:帶回語音識別的結果,成功||錯誤代碼
        //    * 返回值intPtr類型,後面會用到這個返回值
        //    */
        //    session_id = mscDLL.QISRSessionBegin(null, session_begin_params, ref errcode);
        //    if (errcode != (int)Errors.MSP_SUCCESS)
        //    {
        //        if (errcode == 10114)
        //        {
        //            MessageBox.Show("網絡故障!請檢查網絡", "提示");
        //        }
        //        Console.WriteLine("開始一次語音識別失敗!");
        //        return "";
        //    }
        //    /*
        //      QISRAudioWrite();
        //      功能:寫入本次識別的音頻
        //      參數1:之前已經得到的sessionID
        //      參數2:音頻數據緩衝區起始地址
        //      參數3:音頻數據長度,單位字節。
        //       參數4:用來告知MSC音頻發送是否完成     MSP_AUDIO_SAMPLE_FIRST = 1	第一塊音頻
        //                                               MSP_AUDIO_SAMPLE_CONTINUE = 2	還有後繼音頻
        //                                                MSP_AUDIO_SAMPLE_LAST = 4	最後一塊音頻
        //      參數5:端點檢測(End-point detected)器所處的狀態
        //                                             MSP_EP_LOOKING_FOR_SPEECH = 0	還沒有檢測到音頻的前端點。
        //                                              MSP_EP_IN_SPEECH = 1	已經檢測到了音頻前端點,正在進行正常的音頻處理。
        //                                              MSP_EP_AFTER_SPEECH = 3	檢測到音頻的後端點,後繼的音頻會被MSC忽略。
        //                                               MSP_EP_TIMEOUT = 4	超時。
        //                                              MSP_EP_ERROR = 5	出現錯誤。
        //                                              MSP_EP_MAX_SPEECH = 6	音頻過大。
        //      參數6:識別器返回的狀態,提醒用戶及時開始\停止獲取識別結果
        //                                    MSP_REC_STATUS_SUCCESS = 0	識別成功,此時用戶可以調用QISRGetResult來獲取(部分)結果。
        //                                     MSP_REC_STATUS_NO_MATCH = 1	識別結束,沒有識別結果。
        //                                   MSP_REC_STATUS_INCOMPLETE = 2	正在識別中。
        //                                   MSP_REC_STATUS_COMPLETE = 5	識別結束。
        //      返回值:函數調用成功則其值爲MSP_SUCCESS,否則返回錯誤代碼。
        //        本接口需不斷調用,直到音頻全部寫入爲止。上傳音頻時,需更新audioStatus的值。具體來說:
        //        當寫入首塊音頻時,將audioStatus置爲MSP_AUDIO_SAMPLE_FIRST
        //        當寫入最後一塊音頻時,將audioStatus置爲MSP_AUDIO_SAMPLE_LAST
        //        其餘情況下,將audioStatus置爲MSP_AUDIO_SAMPLE_CONTINUE
        //        同時,需定時檢查兩個變量:epStatus和rsltStatus。具體來說:
        //        當epStatus顯示已檢測到後端點時,MSC已不再接收音頻,應及時停止音頻寫入
        //        當rsltStatus顯示有識別結果返回時,即可從MSC緩存中獲取結果*/
        //    int res = mscDLL.QISRAudioWrite(session_id, audio_content, (uint)audio_content.Length, aud_stat, ref ep_stat, ref rec_stat);
        //    if (res != (int)Errors.MSP_SUCCESS)
        //    {
        //        Console.WriteLine("寫入識別的音頻失敗!" + res);
        //        return "";
        //    }
        //    res = mscDLL.QISRAudioWrite(session_id, null, 0, AudioStatus.MSP_AUDIO_SAMPLE_LAST, ref ep_stat, ref rec_stat);
        //    if (res != (int)Errors.MSP_SUCCESS)
        //    {
        //        Console.WriteLine("寫入音頻失敗!" + res);
        //        return "";
        //    }
        //    while (RecogStatus.MSP_REC_STATUS_COMPLETE != rec_stat)
        //    {//如果沒有完成就一直繼續獲取結果
        //     /*
        //      QISRGetResult();
        //      功能:獲取識別結果
        //      參數1:session,之前已獲得
        //      參數2:識別結果的狀態
        //      參數3:waitTime[in]	此參數做保留用
        //      參數4:錯誤編碼||成功
        //      返回值:函數執行成功且有識別結果時,返回結果字符串指針;其他情況(失敗或無結果)返回NULL。
        //      */
        //        IntPtr now_result = mscDLL.QISRGetResult(session_id, ref rec_stat, 0, ref errcode);
        //        if (errcode != (int)Errors.MSP_SUCCESS)
        //        {
        //            Console.WriteLine("獲取結果失敗:" + errcode);
        //            return "";
        //        }
        //        if (now_result != null)
        //        {
        //            int length = now_result.ToString().Length;
        //            totalLength += length;
        //            if (totalLength > 4096)
        //            {
        //                Console.WriteLine("緩存空間不夠" + totalLength);
        //                return "";
        //            }
        //            result.Append(Marshal.PtrToStringAnsi(now_result));
        //        }
        //        Thread.Sleep(150);//防止頻繁佔用cpu
        //    }
        //    mscDLL.QISRSessionEnd(session_id, "normal end");

        //    Console.WriteLine("語音聽寫結束");
        //    Console.WriteLine("結果:\n");
        //    Console.WriteLine(result);


        //    return result.ToString();

        //}
        #endregion
    }

總結
1、若帶有背景音樂之類的干擾,識別不了或者效果很差。
2、容易出現同音錯別字
3、語氣詞準確率不高
4、由於是按30秒切片,在切片的時間點易造成識別出現問題,不準確
5、總體還是不錯的,畢竟免費~~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章