天天看點

Unity 百度SDK 之 線上語音識别ASR WebAPI 功能的實作Unity 百度SDK 之 線上語音識别ASR  WebAPI 功能的實作

Unity 百度SDK 之 線上語音識别ASR  WebAPI 功能的實作

目錄

Unity 百度SDK 之 線上語音識别ASR WebAPI 功能的實作

一、簡單介紹

二、百度官網關于線上語音識别的的介紹

三、 線上識别 Access Token 的擷取

四、效果預覽

五、實作步驟

六、關鍵代碼

一、簡單介紹

Unity 工具類,自己整理的一些遊戲開發可能用到的子產品,單獨獨立使用,友善遊戲開發。

本節使用Baidu API 進行語音識别功能的簡單的實作。

二、百度官網關于線上語音識别的的介紹

網址:https://ai.baidu.com/ai-doc/SPEECH/Vk38lxily

簡介

百度短語音識别可以将60秒以下的音頻識别為文字。适用于語音對話、語音控制、語音輸入等場景。

  • 接口類型:通過 REST API 的方式提供的通用的 HTTP 接口。适用于任意作業系統,任意程式設計語言
  • 接口限制:需要上傳完整的錄音檔案,錄音檔案時長不超過60秒。浏覽器由于無法跨域請求百度語音伺服器的域名,是以無法直接調用API接口。
  • 支援音頻格式:pcm、wav、amr、m4a
  • 音頻編碼要求:采樣率 16000,16bit 位深,單聲道(音頻格式檢視及轉換)
  • 語言及模型設定:支援中文國語(能識别簡單的常用英語)、英語、粵語、四川話識别。通過在請求時配置不同的pid參數,選擇對應模型,詳見請求說明

調用流程

  1. 建立賬号及應用:在ai.baidu.com控制台中,建立應用,勾選開通”語音技術“-”短語音識别、短語音識别極速版“能力。擷取AppID、API Key、Secret Key,并通過請求鑒權接口換取 token ,詳細見“接入指南”。
  2. 建立識别請求:POST方式,音頻可通過JSON和RAW兩種方式送出。JSON方式音頻資料由于base64編碼,資料會增大1/3。其他填寫具體請求參數 ,詳見 ”請求說明“。
  3. 傳回識别結果:識别結果會即刻傳回,采用 JSON 格式封裝,如果識别成功,識别結果放在 JSON的“result”字段中,統一采用 utf-8 方式編碼。詳見”傳回說明“。

三、 線上識别 Access Token 的擷取

網址介紹: https://ai.baidu.com/ai-doc/REFERENCE/Ck3dwjhhu

webAPI 擷取的方式

Unity 百度SDK 之 線上語音識别ASR WebAPI 功能的實作Unity 百度SDK 之 線上語音識别ASR  WebAPI 功能的實作

四、效果預覽

Unity 百度SDK 之 線上語音識别ASR WebAPI 功能的實作Unity 百度SDK 之 線上語音識别ASR  WebAPI 功能的實作

五、實作步驟

1、打開Unity,建立一個工程

Unity 百度SDK 之 線上語音識别ASR WebAPI 功能的實作Unity 百度SDK 之 線上語音識别ASR  WebAPI 功能的實作

2、在工程中添加一個腳本,

Unity 百度SDK 之 線上語音識别ASR WebAPI 功能的實作Unity 百度SDK 之 線上語音識别ASR  WebAPI 功能的實作

3、編寫腳本,實作打開麥克風錄音,然後傳給 REST API  進行識别,記得識别前,要擷取以下 Access Token

Unity 百度SDK 之 線上語音識别ASR WebAPI 功能的實作Unity 百度SDK 之 線上語音識别ASR  WebAPI 功能的實作

4、在場景中,添加一個按鈕和Text

Unity 百度SDK 之 線上語音識别ASR WebAPI 功能的實作Unity 百度SDK 之 線上語音識别ASR  WebAPI 功能的實作

5、把 腳本挂載到按鈕上

Unity 百度SDK 之 線上語音識别ASR WebAPI 功能的實作Unity 百度SDK 之 線上語音識别ASR  WebAPI 功能的實作

6、運作場景,按下按鈕,說完,松開進行識别

Unity 百度SDK 之 線上語音識别ASR WebAPI 功能的實作Unity 百度SDK 之 線上語音識别ASR  WebAPI 功能的實作

六、關鍵代碼

using System;
using System.Collections;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Networking;
using UnityEngine.UI;

//RequireComponent的這兩個元件主要用于播放自己錄制的聲音,不需要刻意删除,同時注意删除使用元件的代碼
[RequireComponent(typeof(AudioListener)), RequireComponent(typeof(AudioSource))]
public class BaiduASR : MonoBehaviour, IPointerDownHandler, IPointerUpHandler
{
    //百度語音識别相關key
    //string appId = "";
    string apiKey = "你的apiKey";              //填寫自己的apiKey
    string secretKey = "你的secretKey";         //填寫自己的secretKey

    //記錄accesstoken令牌
    string accessToken = string.Empty;

    //語音識别的結果
    string asrResult = string.Empty;

    //标記是否有麥克風
    private bool isHaveMic = false;

    //目前錄音裝置名稱
    string currentDeviceName = string.Empty;

    //錄音頻率,控制錄音品質(8000,16000)
    int recordFrequency = 8000;

    //上次按下時間戳
    double lastPressTimestamp = 0;

    //表示錄音的最大時長
    int recordMaxLength = 10;

    //實際錄音長度(由于unity的錄音需先指定長度,導緻識别上傳時候會上傳多餘的無效位元組)
    //通過該字段,擷取有效錄音長度,上傳時候剪切到無效的位元組資料即可
    int trueLength = 0;

    //存儲錄音的片段
    [HideInInspector]
    public AudioClip saveAudioClip;

    //目前按鈕下的文本
    Text textBtn;

    //顯示結果的文本
    Text textResult;

    //音源
    AudioSource audioSource;

    void Start()
    {
        //擷取麥克風裝置,判斷是否有麥克風裝置
        if (Microphone.devices.Length > 0)
        {
            isHaveMic = true;
            currentDeviceName = Microphone.devices[0];
        }

        //擷取相關元件
        textBtn = this.transform.GetChild(0).GetComponent<Text>();
        audioSource = this.GetComponent<AudioSource>();
        textResult = this.transform.parent.GetChild(1).GetComponent<Text>();
    }

    /// <summary>
    /// 開始錄音
    /// </summary>
    /// <param name="isLoop"></param>
    /// <param name="lengthSec"></param>
    /// <param name="frequency"></param>
    /// <returns></returns>
    public bool StartRecording(bool isLoop = false) //8000,16000
    {
        if (isHaveMic == false || Microphone.IsRecording(currentDeviceName))
        {
            return false;
        }

        //開始錄音
        /*
         * public static AudioClip Start(string deviceName, bool loop, int lengthSec, int frequency);
         * deviceName   錄音裝置名稱.
         * loop         如果達到長度,是否繼續記錄
         * lengthSec    指定錄音的長度.
         * frequency    音頻采樣率   
         */

        lastPressTimestamp = GetTimestampOfNowWithMillisecond();

        saveAudioClip = Microphone.Start(currentDeviceName, isLoop, recordMaxLength, recordFrequency);

        return true;
    }

    /// <summary>
    /// 錄音結束,傳回實際的錄音時長
    /// </summary>
    /// <returns></returns>
    public int EndRecording()
    {
        if (isHaveMic == false || !Microphone.IsRecording(currentDeviceName))
        {
            return 0;
        }

        //結束錄音
        Microphone.End(currentDeviceName);

        //向上取整,避免遺漏錄音末尾
        return Mathf.CeilToInt((float)(GetTimestampOfNowWithMillisecond() - lastPressTimestamp) / 1000f);
    }

    /// <summary>
    /// 擷取毫秒級别的時間戳,用于計算按下錄音時長
    /// </summary>
    /// <returns></returns>
    public double GetTimestampOfNowWithMillisecond()
    {
        return (DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000;
    }

    /// <summary>
    /// 按下錄音按鈕
    /// </summary>
    /// <param name="eventData"></param>
    public void OnPointerDown(PointerEventData eventData)
    {
        textBtn.text = "松開識别";
        StartRecording();
    }

    /// <summary>
    /// 放開錄音按鈕
    /// </summary>
    /// <param name="eventData"></param>
    public void OnPointerUp(PointerEventData eventData)
    {
        textBtn.text = "按住說話";
        trueLength = EndRecording();
        if (trueLength > 1)
        {
            audioSource.PlayOneShot(saveAudioClip);
            StartCoroutine(_StartBaiduYuYin());
        }
        else
        {
            textResult.text = "錄音時長過短";
        }
    }

    /// <summary>
    /// 擷取accessToken請求令牌
    /// </summary>
    /// <returns></returns>
    IEnumerator _GetAccessToken()
    {
        var uri =
            string.Format(
                
                "https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id={0}&client_secret={1}",
                apiKey, secretKey);


        UnityWebRequest unityWebRequest = UnityWebRequest.Get(uri);
        yield return unityWebRequest.SendWebRequest();

        if (unityWebRequest.isDone)
        {
            //這裡可以考慮用Json,本人比較懶是以用正則比對出accessToken
            Match match = Regex.Match(unityWebRequest.downloadHandler.text, @"access_token.:.(.*?).,");
            if (match.Success)
            {
                //表示正則比對到了accessToken
                accessToken = match.Groups[1].ToString();
                Debug.Log("accessToken : "+ accessToken);
            }
            else
            {
                textResult.text = "驗證錯誤,擷取AccessToken失敗!!!";
            }
        }
    }

    /// <summary>
    /// 發起語音識别請求
    /// </summary>
    /// <returns></returns>
    IEnumerator _StartBaiduYuYin()
    {
        if (string.IsNullOrEmpty(accessToken))
        {
            yield return _GetAccessToken();
        }

        asrResult = string.Empty;

        //處理目前錄音資料為PCM16
        float[] samples = new float[recordFrequency * trueLength * saveAudioClip.channels];
        saveAudioClip.GetData(samples, 0);
        var samplesShort = new short[samples.Length];
        for (var index = 0; index < samples.Length; index++)
        {
            samplesShort[index] = (short)(samples[index] * short.MaxValue);
        }
        byte[] datas = new byte[samplesShort.Length * 2];
        Buffer.BlockCopy(samplesShort, 0, datas, 0, datas.Length);

        string url = string.Format("{0}?cuid={1}&token={2}", "https://vop.baidu.com/server_api", SystemInfo.deviceUniqueIdentifier, accessToken);

        WWWForm wwwForm = new WWWForm();
        wwwForm.AddBinaryData("audio", datas);

        UnityWebRequest unityWebRequest = UnityWebRequest.Post(url, wwwForm);

        unityWebRequest.SetRequestHeader("Content-Type", "audio/pcm;rate=" + recordFrequency);

        yield return unityWebRequest.SendWebRequest();

        if (string.IsNullOrEmpty(unityWebRequest.error))
        {
            asrResult = unityWebRequest.downloadHandler.text;
            if (Regex.IsMatch(asrResult, @"err_msg.:.success"))
            {
                Match match = Regex.Match(asrResult, "result.:..(.*?)..]");
                if (match.Success)
                {
                    asrResult = match.Groups[1].ToString();
                }
            }
            else
            {
                asrResult = "識别結果為空";
            }
            textResult.text = asrResult;
        }
    }
}
           

繼續閱讀