Unity connects to iFlytek AIUI (Windows SDK)

1. What is AIUI
AIUI is a set of human-computer interaction solutions that integrates voice wake-up, speech recognition, semantic understanding, content platform, speech synthesis (one more speaker than ordinary speech synthesis) and other capabilities.
New users have 20 free installations and 500 interactions per day

2. Create AIUI

  1. Create your own test project in the console → My Application → Create New Application
    Insert image description here
    3. Enter the AIUI console
    Insert image description here
    Insert image description here

Click Service Management to enter the AIUI console

4. AIUI settings

Insert image description here

默认自带有语音识别的,如果需要AIUI回答你,则需要开启语义理解,如果需要有声音则需要开启语音合成,否则调用SDK的时候是不会返回语义理解和语音合成的数据的

Insert image description here
You can directly modify the speaker in the above position, and the returned speech synthesis data will also change.

5. Import projects

1. Download SDK
Insert image description here

2. We need to import aiui.dll into Unity’s Plugins folder
Insert image description hereInsert image description here

3. We need to put the AIUI folder into the StreamingAssets folder. Some configuration information saved here, such as APPID, speech synthesis, etc., only need to read the configuration file at the beginning of the program. There is no need to do it like iFlytek. Speech synthesis, speech recognition and other modules also need to log in and out.
Insert image description here

4. Here are some tool classes, which are extensions to aiui.dll and use the official c# demo
Insert image description hereInsert image description here
5. Initialization configuration

 void Start()
    {
    
    
        // 为每一个设备设置对应唯一的SN(最好使用设备硬件信息(mac地址,设备序列号等)生成)
        // 以便正确统计装机量,避免刷机或者应用卸载重装导致装机量重复计数
        AIUISetting.setSystemInfo(AIUIConstant.KEY_SERIAL_NUM, GetMac());
        string cfg = File.ReadAllText(Application.streamingAssetsPath + "\\AIUI\\cfg\\aiui.cfg");
        agent = IAIUIAgent.Create(cfg, onEvent);

        IAIUIMessage msg_start = IAIUIMessage.Create(AIUIConstant.CMD_START, 0, 0, "", IBuffer.Zero);
        agent.SendMessage(msg_start);
        msg_start.Destroy();
        msg_start = null;
    }

6. Send a message. Here, the information entered by the microphone is converted into byte[] and then sent to the SDK.

 public IEnumerator SendMassage(byte[] data)
    {
    
    
        //唤醒
        IAIUIMessage msg_wakeup = IAIUIMessage.Create(AIUIConstant.CMD_WAKEUP, 0, 0, "", IBuffer.Zero);
        agent.SendMessage(msg_wakeup);
        msg_wakeup.Destroy();
        msg_wakeup = null;
        yield return new WaitForSeconds(0.2f);
        print("wakeup");

        //开始录音
        IAIUIMessage msg_start_r = IAIUIMessage.Create(AIUIConstant.CMD_START_RECORD, 0, 0,
            "data_type=audio,interact_mode=oneshot", IBuffer.Zero);
        agent.SendMessage(msg_start_r);
        msg_start_r.Destroy();
        msg_start_r = null;

        //发送语音
        IBuffer buf_1 = IBuffer.FromData(data, data.Length);
        IAIUIMessage msg_write_audio = IAIUIMessage.Create(AIUIConstant.CMD_WRITE, 0, 0, "data_type=audio", buf_1);
        agent.SendMessage(msg_write_audio);
        msg_write_audio.Destroy();
        msg_write_audio = null;
        buf_1 = null;
        yield return new WaitForSeconds(0.04f);
    }

7.Return message

private  void onEvent(IAIUIEvent ev)
    {
    
    
        switch (ev.GetEventType())
        {
    
    
            case AIUIConstant.EVENT_STATE:
                {
    
    
                    switch (ev.GetArg1())
                    {
    
    
                        case AIUIConstant.STATE_IDLE:
                            {
    
    
                                print("EVENT_STATE: IDLE");
                            }
                            break;

                        case AIUIConstant.STATE_READY:
                            {
    
    
                                print("EVENT_STATE: READY");
                            }
                            break;

                        case AIUIConstant.STATE_WORKING:
                            {
    
    
                                print("EVENT_STATE: WORKING");
                            }
                            break;
                    }
                }
                break;
            case AIUIConstant.EVENT_WAKEUP:
                {
    
    
                    Debug.LogFormat("EVENT_WAKEUP: {0}", ev.GetInfo());

                }
                break;
            case AIUIConstant.EVENT_SLEEP:
                {
    
    
                    Debug.LogFormat("EVENT_WAKEUP: arg1={0}", ev.GetArg1());
                }
                break;
            case AIUIConstant.EVENT_VAD:
                {
    
    
                    switch (ev.GetArg1())
                    {
    
    
                        case AIUIConstant.VAD_BOS:
                            {
    
    
                                print("EVENT_VAD: BOS");
                            }
                            break;

                        case AIUIConstant.VAD_EOS:
                            {
    
    
                                print("EVENT_VAD: EOS");
                            }
                            break;
                    }
                }
                break;

            case AIUIConstant.EVENT_RESULT://返回成功时传入的数据
                {
    
    
                    try
                    {
    
    
                        var info = JsonConvert.DeserializeObject<Dictionary<object, object>>(ev.GetInfo());
                         
                        var datas = info["data"] as JArray;
                        var data = datas[0] as JObject;
                        var param = data["params"] as JObject;
                        var contents = data["content"] as JArray;
                        var content = contents[0] as JObject;

                        string sub = param["sub"].ToString();
                        string cnt_id = content["cnt_id"].ToString();
                        int dataLen = 0;
                        byte[] buffer = ev.GetData().GetBinary(cnt_id, ref dataLen);
                        Debug.LogFormat("sub: {0} ,info: {1}", sub, ev.GetInfo());
                        switch (sub)
                        {
    
                                   
                            case "iat":
                                string jsonObject = Encoding.UTF8.GetString(buffer).Replace('\0', ' ');
                                JsonData deJson = JsonMapper.ToObject(jsonObject);

                                StringBuilder cont = new StringBuilder();
                                foreach (JsonData item in deJson["text"]["ws"])
                                {
    
    

                                    cont.Append(item["cw"][0]["w"]);
                                }
                                //切换的主线程
                                Loom.QueueOnMainThread(() => {
    
    
                                    sendText.text = cont.ToString();
                                });

                                break;
                            case "nlp":
                                string jsonObject1 = Encoding.UTF8.GetString(buffer).Replace('\0',' ');
                                JsonData deJson1 = JsonMapper.ToObject(jsonObject1);
                                //切换的主线程
                                Loom.QueueOnMainThread(() => {
    
    
                                    returnText.text = deJson1["intent"]["answer"]["text"].ToString();
                                });
 
                                break;
                            case "tts":
                                string jsonObject2 = ev.GetInfo().Replace('\0', ' ');
                                JsonData deJson2 = JsonMapper.ToObject(jsonObject2);
                                //复制数据
                                for (int i = 0; i < buffer.Length; i++)
                                {
    
    
                                    audioData.Add(buffer[i]);
                                }
                              
                                Debug.Log("音频数据长度:"+ buffer.Length +"   总长度:"+ audioData.Count);
                                //判断是否结束
                                if (int.Parse(deJson2["data"][0]["content"][0]["dts"].ToString()) == 2)
                                {
    
    
                                    Debug.Log(audioData.Count);
                                    //切换的主线程
                                    Loom.QueueOnMainThread(() => {
    
    
                                        Save();
                                        //audioData = new List<byte>();
                                        //语音合成
                                        //audioSource.clip = Clip(audioData.ToArray());
                                        //audioSource.loop = false;
                                        //audioSource.Play();

                                    });
                                }
                                break;
                            case "asr":
                                break;
                            default:
                                break;
                        }

                        datas = null;
                        data = null;
                        param = null;
                        contents = null;
                        content = null;
                        info = null;
                    }
                    catch (Exception e)
                    {
    
    

                        print(e.Message); ;
                    }


                }
                break;
            case AIUIConstant.EVENT_ERROR:
                Debug.LogFormat("EVENT_ERROR: {0} {1}", ev.GetArg1(), ev.GetInfo());
                break;

        }
    }

AIUIManager all code

using aiui;
using LitJson;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net.NetworkInformation;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using UnityEngine;
using UnityEngine.UI;

//讯飞AIUI
public class AIUIManager : MonoBehaviour
{
    
    
    public static AIUIManager Instance;
    private IAIUIAgent agent;
    public AudioSource audioSource;

    public Text returnText;
    public Text sendText;

    private void Awake()
    {
    
    
        Instance = this;
    }

    // Start is called before the first frame update
    void Start()
    {
    
    
        // 为每一个设备设置对应唯一的SN(最好使用设备硬件信息(mac地址,设备序列号等)生成)
        // 以便正确统计装机量,避免刷机或者应用卸载重装导致装机量重复计数
        AIUISetting.setSystemInfo(AIUIConstant.KEY_SERIAL_NUM, GetMac());
        string cfg = File.ReadAllText(Application.streamingAssetsPath + "\\AIUI\\cfg\\aiui.cfg");
        agent = IAIUIAgent.Create(cfg, onEvent);

        IAIUIMessage msg_start = IAIUIMessage.Create(AIUIConstant.CMD_START, 0, 0, "", IBuffer.Zero);
        agent.SendMessage(msg_start);
        msg_start.Destroy();
        msg_start = null;
    }

     public IEnumerator SendMassage(byte[] data)
    {
    
    
        //唤醒
        IAIUIMessage msg_wakeup = IAIUIMessage.Create(AIUIConstant.CMD_WAKEUP, 0, 0, "", IBuffer.Zero);
        agent.SendMessage(msg_wakeup);
        msg_wakeup.Destroy();
        msg_wakeup = null;
        yield return new WaitForSeconds(0.2f);
        print("wakeup");

        //开始录音
        IAIUIMessage msg_start_r = IAIUIMessage.Create(AIUIConstant.CMD_START_RECORD, 0, 0,
            "data_type=audio,interact_mode=oneshot", IBuffer.Zero);
        agent.SendMessage(msg_start_r);
        msg_start_r.Destroy();
        msg_start_r = null;

        //发送语音
        IBuffer buf_1 = IBuffer.FromData(data, data.Length);
        IAIUIMessage msg_write_audio = IAIUIMessage.Create(AIUIConstant.CMD_WRITE, 0, 0, "data_type=audio", buf_1);
        agent.SendMessage(msg_write_audio);
        msg_write_audio.Destroy();
        msg_write_audio = null;
        buf_1 = null;
        yield return new WaitForSeconds(0.04f);
    }

    public  string GetMac()
    {
    
    
        NetworkInterface[] interfaces = NetworkInterface.GetAllNetworkInterfaces();

        string mac = "";
        foreach (NetworkInterface ni in interfaces)
        {
    
    
            if (ni.NetworkInterfaceType != NetworkInterfaceType.Loopback)
            {
    
    
                mac += ni.GetPhysicalAddress().ToString();
            }
        }

        byte[] result = Encoding.Default.GetBytes(mac);
        result = new MD5CryptoServiceProvider().ComputeHash(result);

        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < result.Length; i++)
        {
    
    
            builder.Append(result[i].ToString("x2"));
        }

        return builder.ToString();
    }

    private void OnDisable()
    {
    
    
        agent.Destroy();
    }

    //返回事件
    private  void onEvent(IAIUIEvent ev)
    {
    
    
        switch (ev.GetEventType())
        {
    
    
            case AIUIConstant.EVENT_STATE:
                {
    
    
                    switch (ev.GetArg1())
                    {
    
    
                        case AIUIConstant.STATE_IDLE:
                            {
    
    
                                print("EVENT_STATE: IDLE");
                            }
                            break;

                        case AIUIConstant.STATE_READY:
                            {
    
    
                                print("EVENT_STATE: READY");
                            }
                            break;

                        case AIUIConstant.STATE_WORKING:
                            {
    
    
                                print("EVENT_STATE: WORKING");
                            }
                            break;
                    }
                }
                break;
            case AIUIConstant.EVENT_WAKEUP:
                {
    
    
                    Debug.LogFormat("EVENT_WAKEUP: {0}", ev.GetInfo());

                }
                break;
            case AIUIConstant.EVENT_SLEEP:
                {
    
    
                    Debug.LogFormat("EVENT_WAKEUP: arg1={0}", ev.GetArg1());
                }
                break;
            case AIUIConstant.EVENT_VAD:
                {
    
    
                    switch (ev.GetArg1())
                    {
    
    
                        case AIUIConstant.VAD_BOS:
                            {
    
    
                                print("EVENT_VAD: BOS");
                            }
                            break;

                        case AIUIConstant.VAD_EOS:
                            {
    
    
                                print("EVENT_VAD: EOS");
                            }
                            break;
                    }
                }
                break;

            case AIUIConstant.EVENT_RESULT:
                {
    
    
                    try
                    {
    
    
                        var info = JsonConvert.DeserializeObject<Dictionary<object, object>>(ev.GetInfo());
                         
                        var datas = info["data"] as JArray;
                        var data = datas[0] as JObject;
                        var param = data["params"] as JObject;
                        var contents = data["content"] as JArray;
                        var content = contents[0] as JObject;

                        string sub = param["sub"].ToString();
                        string cnt_id = content["cnt_id"].ToString();
                        int dataLen = 0;
                        byte[] buffer = ev.GetData().GetBinary(cnt_id, ref dataLen);
                        Debug.LogFormat("sub: {0} ,info: {1}", sub, ev.GetInfo());
                        switch (sub)
                        {
    
                                   
                            case "iat":
                                string jsonObject = Encoding.UTF8.GetString(buffer).Replace('\0', ' ');
                                JsonData deJson = JsonMapper.ToObject(jsonObject);

                                StringBuilder cont = new StringBuilder();
                                foreach (JsonData item in deJson["text"]["ws"])
                                {
    
    

                                    cont.Append(item["cw"][0]["w"]);
                                }
                                //切换的主线程
                                Loom.QueueOnMainThread(() => {
    
    
                                    sendText.text = cont.ToString();
                                });

                                break;
                            case "nlp":
                                string jsonObject1 = Encoding.UTF8.GetString(buffer).Replace('\0',' ');
                                JsonData deJson1 = JsonMapper.ToObject(jsonObject1);
                                //切换的主线程
                                Loom.QueueOnMainThread(() => {
    
    
                                    returnText.text = deJson1["intent"]["answer"]["text"].ToString();
                                });
 
                                break;
                            case "tts":
                                string jsonObject2 = ev.GetInfo().Replace('\0', ' ');
                                JsonData deJson2 = JsonMapper.ToObject(jsonObject2);

                                if (int.Parse(deJson2["data"][0]["content"][0]["dts"].ToString()) == 2)
                                {
    
    
                                    for (int i = 0; i < 44; i++)
                                    {
    
    
                                        audioData.Add(0);
                                    }
                                }

                                    //复制数据
                                for (int i = 0; i < buffer.Length; i++)
                                {
    
    
                                    audioData.Add(buffer[i]);
                                }
                              
                                Debug.Log("音频数据长度:"+ buffer.Length +"   总长度:"+ audioData.Count);
                                //判断是否结束
                                if (int.Parse(deJson2["data"][0]["content"][0]["dts"].ToString()) == 2)
                                {
    
    
                                    Debug.Log(audioData.Count);

                                    //写入wav头
                                    //创建wav文件头
                                    WAVE_Header header = getWave_Header(audioData.Count-44);
                                    //把文件头结构转化为字节数组                      
                                    byte[] headerByte = StructToBytes(header);
                                    for (int i = 0; i < headerByte.Length; i++)
                                    {
    
    
                                        audioData[i] = headerByte[i];
                                    }

                                    //切换的主线程
                                    Loom.QueueOnMainThread(() => {
    
    
                                        //Save();
                                       
                                        //语音合成
                                        audioSource.clip = Clip(audioData.ToArray());
                                        //audioSource.loop = false;
                                        audioSource.Play();
                                        audioData = new List<byte>();
                                    });
                                }
                                break;
                            case "asr":
                                break;
                            default:
                                break;
                        }

                        datas = null;
                        data = null;
                        param = null;
                        contents = null;
                        content = null;
                        info = null;
                    }
                    catch (Exception e)
                    {
    
    

                        print(e.Message); ;
                    }


                }
                break;
            case AIUIConstant.EVENT_ERROR:
                Debug.LogFormat("EVENT_ERROR: {0} {1}", ev.GetArg1(), ev.GetInfo());
                break;

        }
    }
    private List<byte> audioData = new List<byte>();

    /// <summary>
    /// 把结构体转化为字节序列
    /// </summary>
    /// <param name="structure">被转化的结构体</param>
    /// <returns>返回字节序列</returns>
    public static byte[] StructToBytes(object structure)
    {
    
    
        int size = Marshal.SizeOf(structure);
        IntPtr buffer = Marshal.AllocHGlobal(size);
        try
        {
    
    
            Marshal.StructureToPtr(structure, buffer, false);
            Byte[] bytes = new Byte[size];
            Marshal.Copy(buffer, bytes, 0, size);
            return bytes;
        }
        finally
        {
    
    
            Marshal.FreeHGlobal(buffer);
        }
    }

/// <summary>
/// 根据数据段的长度,生产文件头
/// </summary>
/// <param name="data_len">音频数据长度</param>
/// <returns>返回wav文件头结构体</returns>
public static WAVE_Header getWave_Header(int data_len)
    {
    
    
        WAVE_Header wav_Header = new WAVE_Header();
        wav_Header.RIFF_ID = 0x46464952;        //字符RIFF
        wav_Header.File_Size = data_len + 36;
        wav_Header.RIFF_Type = 0x45564157;      //字符WAVE

        wav_Header.FMT_ID = 0x20746D66;         //字符fmt
        wav_Header.FMT_Size = 16;
        wav_Header.FMT_Tag = 0x0001;
        wav_Header.FMT_Channel = 1;             //单声道
        wav_Header.FMT_SamplesPerSec = 16000;   //采样频率
        wav_Header.AvgBytesPerSec = 32000;      //每秒所需字节数
        wav_Header.BlockAlign = 2;              //每个采样1个字节
        wav_Header.BitsPerSample = 16;           //每个采样8bit

        wav_Header.DATA_ID = 0x61746164;        //字符data
        wav_Header.DATA_Size = data_len;

        return wav_Header;
    }
    /// <summary>
    /// wave文件头
    /// </summary>
    public struct WAVE_Header
    {
    
    
        public int RIFF_ID;           //4 byte , 'RIFF'
        public int File_Size;         //4 byte , 文件长度
        public int RIFF_Type;         //4 byte , 'WAVE'

        public int FMT_ID;            //4 byte , 'fmt'
        public int FMT_Size;          //4 byte , 数值为16或18,18则最后又附加信息
        public short FMT_Tag;         //2 byte , 编码方式,一般为0x0001
        public ushort FMT_Channel;    //2 byte , 声道数目,1--单声道;2--双声道
        public int FMT_SamplesPerSec; //4 byte , 采样频率
        public int AvgBytesPerSec;    //4 byte , 每秒所需字节数,记录每秒的数据量
        public ushort BlockAlign;     //2 byte , 数据块对齐单位(每个采样需要的字节数)
        public ushort BitsPerSample;  //2 byte , 每个采样需要的bit数

        public int DATA_ID;           //4 byte , 'data'
        public int DATA_Size;         //4 byte , 
    }

    #region 播放录音
    public static AudioClip Clip(byte[] fileBytes, int offsetSamples = 0, string name = "ifly")
    {
    
    
        //string riff = Encoding.ASCII.GetString (fileBytes, 0, 4);
        //string wave = Encoding.ASCII.GetString (fileBytes, 8, 4);
        int subchunk1 = BitConverter.ToInt32(fileBytes, 16);
        ushort audioFormat = BitConverter.ToUInt16(fileBytes, 20);

        // NB: Only uncompressed PCM wav files are supported.
        string formatCode = FormatCode(audioFormat);
        //Debug.AssertFormat(audioFormat == 1 || audioFormat == 65534, "Detected format code '{0}' {1}, but only PCM and WaveFormatExtensable uncompressed formats are currently supported.", audioFormat, formatCode);

        ushort channels = BitConverter.ToUInt16(fileBytes, 22);
        int sampleRate = BitConverter.ToInt32(fileBytes, 24);
        //int byteRate = BitConverter.ToInt32 (fileBytes, 28);
        //UInt16 blockAlign = BitConverter.ToUInt16 (fileBytes, 32);
        ushort bitDepth = BitConverter.ToUInt16(fileBytes, 34);

        int headerOffset = 16 + 4 + subchunk1 + 4;
        int subchunk2 = BitConverter.ToInt32(fileBytes, headerOffset);
        //Debug.LogFormat ("riff={0} wave={1} subchunk1={2} format={3} channels={4} sampleRate={5} byteRate={6} blockAlign={7} bitDepth={8} headerOffset={9} subchunk2={10} filesize={11}", riff, wave, subchunk1, formatCode, channels, sampleRate, byteRate, blockAlign, bitDepth, headerOffset, subchunk2, fileBytes.Length);

        //Log.Info(bitDepth);

        float[] data;
        switch (bitDepth)
        {
    
    
            case 8:
                data = Convert8BitByteArrayToAudioClipData(fileBytes, headerOffset, subchunk2);
                break;
            case 16:
                data = Convert16BitByteArrayToAudioClipData(fileBytes, headerOffset, subchunk2);
                break;
            case 24:
                data = Convert24BitByteArrayToAudioClipData(fileBytes, headerOffset, subchunk2);
                break;
            case 32:
                data = Convert32BitByteArrayToAudioClipData(fileBytes, headerOffset, subchunk2);
                break;
            default:
                throw new Exception(bitDepth + " bit depth is not supported.");
        }

        AudioClip audioClip = AudioClip.Create(name, data.Length, channels, sampleRate, false);
        audioClip.SetData(data, 0);
        return audioClip;
    }

    private static string FormatCode(UInt16 code)
    {
    
    
        switch (code)
        {
    
    
            case 1:
                return "PCM";
            case 2:
                return "ADPCM";
            case 3:
                return "IEEE";
            case 7:
                return "μ-law";
            case 65534:
                return "WaveFormatExtensable";
            default:
                Debug.LogWarning("Unknown wav code format:" + code);
                return "";
        }
    }

    #region wav file bytes to Unity AudioClip conversion methods

    private static float[] Convert8BitByteArrayToAudioClipData(byte[] source, int headerOffset, int dataSize)
    {
    
    
        int wavSize = BitConverter.ToInt32(source, headerOffset);
        headerOffset += sizeof(int);
        Debug.AssertFormat(wavSize > 0 && wavSize == dataSize, "Failed to get valid 8-bit wav size: {0} from data bytes: {1} at offset: {2}", wavSize, dataSize, headerOffset);

        float[] data = new float[wavSize];

        sbyte maxValue = sbyte.MaxValue;

        int i = 0;
        while (i < wavSize)
        {
    
    
            data[i] = (float)source[i] / maxValue;
            ++i;
        }

        return data;
    }

    private static float[] Convert16BitByteArrayToAudioClipData(byte[] source, int headerOffset, int dataSize)
    {
    
    
        int wavSize = BitConverter.ToInt32(source, headerOffset);
        headerOffset += sizeof(int);
        Debug.AssertFormat(wavSize > 0 && wavSize == dataSize, "Failed to get valid 16-bit wav size: {0} from data bytes: {1} at offset: {2}", wavSize, dataSize, headerOffset);

        int x = sizeof(Int16); // block size = 2
        int convertedSize = wavSize / x;

        float[] data = new float[convertedSize];

        Int16 maxValue = Int16.MaxValue;

        int offset = 0;
        int i = 0;
        while (i < convertedSize)
        {
    
    
            offset = i * x + headerOffset;
            data[i] = (float)BitConverter.ToInt16(source, offset) / maxValue;
            ++i;
        }

        Debug.AssertFormat(data.Length == convertedSize, "AudioClip .wav data is wrong size: {0} == {1}", data.Length, convertedSize);

        return data;
    }

    private static float[] Convert24BitByteArrayToAudioClipData(byte[] source, int headerOffset, int dataSize)
    {
    
    
        int wavSize = BitConverter.ToInt32(source, headerOffset);
        headerOffset += sizeof(int);
        Debug.AssertFormat(wavSize > 0 && wavSize == dataSize, "Failed to get valid 24-bit wav size: {0} from data bytes: {1} at offset: {2}", wavSize, dataSize, headerOffset);

        int x = 3; // block size = 3
        int convertedSize = wavSize / x;

        int maxValue = Int32.MaxValue;

        float[] data = new float[convertedSize];

        byte[] block = new byte[sizeof(int)]; // using a 4 byte block for copying 3 bytes, then copy bytes with 1 offset

        int offset = 0;
        int i = 0;
        while (i < convertedSize)
        {
    
    
            offset = i * x + headerOffset;
            Buffer.BlockCopy(source, offset, block, 1, x);
            data[i] = (float)BitConverter.ToInt32(block, 0) / maxValue;
            ++i;
        }

        Debug.AssertFormat(data.Length == convertedSize, "AudioClip .wav data is wrong size: {0} == {1}", data.Length, convertedSize);

        return data;
    }

    private static float[] Convert32BitByteArrayToAudioClipData(byte[] source, int headerOffset, int dataSize)
    {
    
    
        int wavSize = BitConverter.ToInt32(source, headerOffset);
        headerOffset += sizeof(int);
        Debug.AssertFormat(wavSize > 0 && wavSize == dataSize, "Failed to get valid 32-bit wav size: {0} from data bytes: {1} at offset: {2}", wavSize, dataSize, headerOffset);

        int x = sizeof(float); //  block size = 4
        int convertedSize = wavSize / x;

        Int32 maxValue = Int32.MaxValue;

        float[] data = new float[convertedSize];

        int offset = 0;
        int i = 0;
        while (i < convertedSize)
        {
    
    
            offset = i * x + headerOffset;
            data[i] = (float)BitConverter.ToInt32(source, offset) / maxValue;
            ++i;
        }

        Debug.AssertFormat(data.Length == convertedSize, "AudioClip .wav data is wrong size: {0} == {1}", data.Length, convertedSize);

        return data;
    }

    #endregion

    #endregion

    #region 保存语音
    public void Save()
    {
    
    
        using (FileStream fs = CreateEmpty(Application.streamingAssetsPath + "/dd.wav"))
        {
    
    
            ConvertAndWrite(fs);
            WriteHeader(fs);

            Debug.Log("写入完成");
        }



    }
    private void ConvertAndWrite(FileStream fileStream)
    {
    
    

        var outData = audioData.ToArray();
        fileStream.Write(outData, 0, outData.Length);
    }
    private FileStream CreateEmpty(string filepath)
    {
    
    
        FileStream fileStream = new FileStream(filepath, FileMode.Create);
        byte emptyByte = new byte();

        for (int i = 0; i < 44; i++) //preparing the header
        {
    
    
            fileStream.WriteByte(emptyByte);
        }

        return fileStream;
    }

    private void WriteHeader(FileStream stream)
    {
    
    
        int hz = 16000;
        int channels = 1;
        int samples =audioData.Count;

        stream.Seek(0, SeekOrigin.Begin);

        Byte[] riff = System.Text.Encoding.UTF8.GetBytes("RIFF");
        stream.Write(riff, 0, 4);

        Byte[] chunkSize = BitConverter.GetBytes(stream.Length - 8);
        stream.Write(chunkSize, 0, 4);

        Byte[] wave = System.Text.Encoding.UTF8.GetBytes("WAVE");
        stream.Write(wave, 0, 4);

        Byte[] fmt = System.Text.Encoding.UTF8.GetBytes("fmt ");
        stream.Write(fmt, 0, 4);

        Byte[] subChunk1 = BitConverter.GetBytes(16);
        stream.Write(subChunk1, 0, 4);

        UInt16 two = 2;
        UInt16 one = 1;

        Byte[] audioFormat = BitConverter.GetBytes(one);
        stream.Write(audioFormat, 0, 2);

        Byte[] numChannels = BitConverter.GetBytes(channels);
        stream.Write(numChannels, 0, 2);

        Byte[] sampleRate = BitConverter.GetBytes(hz);
        stream.Write(sampleRate, 0, 4);

        Byte[] byteRate = BitConverter.GetBytes(hz * channels * 2); // sampleRate * bytesPerSample*number of channels, here 44100*2*2
        stream.Write(byteRate, 0, 4);

        UInt16 blockAlign = (ushort)(channels * 2);
        stream.Write(BitConverter.GetBytes(blockAlign), 0, 2);

        UInt16 bps = 16;
        Byte[] bitsPerSample = BitConverter.GetBytes(bps);
        stream.Write(bitsPerSample, 0, 2);

        Byte[] datastring = System.Text.Encoding.UTF8.GetBytes("data");
        stream.Write(datastring, 0, 4);

        Byte[] subChunk2 = BitConverter.GetBytes(samples * channels * 2);
        stream.Write(subChunk2, 0, 4);

    }
    #endregion
}

When the message is received here, other threads are used, so you need to switch to the main thread to use Unity's API, otherwise an error will be reported. I can put the incoming data locally, and also use and play it in the memory (here The transferred data is pcm. If you want to use it directly in Unity, you need to write the WAV header). The speech synthesis data is transmitted in multiple segments, so it needs to be integrated together.

6. Project location
link: https://pan.baidu.com/s/1X83tzFAaRLFvPeleT2Pm0A
Extraction code: a1oj
Insert image description here
Just put the AIUI file in the SDK in the SteamingAssets folder and replace the aiui.dll file.

Guess you like

Origin blog.csdn.net/weixin_41132607/article/details/130575204