C#調用科大訊飛離線語音合成實現文本轉語音

来源:https://www.cnblogs.com/DriftingLeaf/archive/2023/08/22/17648569.html
-Advertisement-
Play Games

# C#文本轉語音(科大訊飛離線版) ### 引言 文本轉語音(Text To Speech),簡稱TTS,在很多業務場景會用到,比如廣播大廳,人機互動等。C#要實現TTS有不少選擇,比如調用System.Speech,此處就不細說了,下麵主要介紹一下C#調用科大訊飛的離線語音合成SDK來實現文本轉 ...


C#文本轉語音(科大訊飛離線版)

引言

文本轉語音(Text To Speech),簡稱TTS,在很多業務場景會用到,比如廣播大廳,人機互動等。C#要實現TTS有不少選擇,比如調用System.Speech,此處就不細說了,下麵主要介紹一下C#調用科大訊飛的離線語音合成SDK來實現文本轉語音。

產品介紹

地址:[https://www.xfyun.cn/service/offline_tts]

步驟

一、創建科大訊飛應用

進入科大訊飛控制台創建一個應用:[https://console.xfyun.cn/app/myapp],沒有賬號的可以先註冊一個。

img

創建應用後會有一個APPID,先記下來,後面寫代碼會用到。

二、下載對應平臺的SDK

點擊剛創建的應用,進去應用服務界面,在左側點擊語音合成下麵的離線語音合成(普通版/高品質版),如下

img

如果是windows平臺,那麼只能選擇普通版。Linux的話可以選擇高品質版,兩者的效果在我看來相差還是比較大的。

img

下載完解壓出來,目錄如下

img

裡面我們需要用到的是bin目錄內的文件,當然你也可以參考samples裡面的c語言代碼,跟後面我提供的C#代碼差不多

三、編寫C#代碼

Windows版

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.IO;

namespace Speech
{
    /// <summary>
    /// 訊飛語音合成
    /// Author:String Lee
    /// Date:2021年12月15日 14:06:00
    /// </summary>
    public class WindowsXFTTSUtils
    {
        #region TTS枚舉常量

        public enum ErrorCode
        {
            MSP_SUCCESS = 0,
            MSP_ERROR_FAIL = -1,
            MSP_ERROR_EXCEPTION = -2,

            /* General errors 10100(0x2774) */
            MSP_ERROR_GENERAL = 10100,     /* 0x2774 */
            MSP_ERROR_OUT_OF_MEMORY = 10101,     /* 0x2775 */
            MSP_ERROR_FILE_NOT_FOUND = 10102,     /* 0x2776 */
            MSP_ERROR_NOT_SUPPORT = 10103,     /* 0x2777 */
            MSP_ERROR_NOT_IMPLEMENT = 10104,     /* 0x2778 */
            MSP_ERROR_ACCESS = 10105,     /* 0x2779 */
            MSP_ERROR_INVALID_PARA = 10106,     /* 0x277A */
            MSP_ERROR_INVALID_PARA_VALUE = 10107,     /* 0x277B */
            MSP_ERROR_INVALID_HANDLE = 10108,     /* 0x277C */
            MSP_ERROR_INVALID_DATA = 10109,     /* 0x277D */
            MSP_ERROR_NO_LICENSE = 10110,     /* 0x277E */
            MSP_ERROR_NOT_INIT = 10111,     /* 0x277F */
            MSP_ERROR_NULL_HANDLE = 10112,     /* 0x2780 */
            MSP_ERROR_OVERFLOW = 10113,     /* 0x2781 */
            MSP_ERROR_TIME_OUT = 10114,     /* 0x2782 */
            MSP_ERROR_OPEN_FILE = 10115,     /* 0x2783 */
            MSP_ERROR_NOT_FOUND = 10116,     /* 0x2784 */
            MSP_ERROR_NO_ENOUGH_BUFFER = 10117,     /* 0x2785 */
            MSP_ERROR_NO_DATA = 10118,     /* 0x2786 */
            MSP_ERROR_NO_MORE_DATA = 10119,     /* 0x2787 */
            MSP_ERROR_SKIPPED = 10120,     /* 0x2788 */
            MSP_ERROR_ALREADY_EXIST = 10121,     /* 0x2789 */
            MSP_ERROR_LOAD_MODULE = 10122,     /* 0x278A */
            MSP_ERROR_BUSY = 10123,     /* 0x278B */
            MSP_ERROR_INVALID_CONFIG = 10124,     /* 0x278C */
            MSP_ERROR_VERSION_CHECK = 10125,     /* 0x278D */
            MSP_ERROR_CANCELED = 10126,     /* 0x278E */
            MSP_ERROR_INVALID_MEDIA_TYPE = 10127,     /* 0x278F */
            MSP_ERROR_CONFIG_INITIALIZE = 10128,     /* 0x2790 */
            MSP_ERROR_CREATE_HANDLE = 10129,     /* 0x2791 */
            MSP_ERROR_CODING_LIB_NOT_LOAD = 10130,     /* 0x2792 */

            /* Error codes of network 10200(0x27D8)*/
            MSP_ERROR_NET_GENERAL = 10200,     /* 0x27D8 */
            MSP_ERROR_NET_OPENSOCK = 10201,     /* 0x27D9 */   /* Open socket */
            MSP_ERROR_NET_CONNECTSOCK = 10202,     /* 0x27DA */   /* Connect socket */
            MSP_ERROR_NET_ACCEPTSOCK = 10203,     /* 0x27DB */   /* Accept socket */
            MSP_ERROR_NET_SENDSOCK = 10204,     /* 0x27DC */   /* Send socket data */
            MSP_ERROR_NET_RECVSOCK = 10205,     /* 0x27DD */   /* Recv socket data */
            MSP_ERROR_NET_INVALIDSOCK = 10206,     /* 0x27DE */   /* Invalid socket handle */
            MSP_ERROR_NET_BADADDRESS = 10207,     /* 0x27EF */   /* Bad network address */
            MSP_ERROR_NET_BINDSEQUENCE = 10208,     /* 0x27E0 */   /* Bind after listen/connect */
            MSP_ERROR_NET_NOTOPENSOCK = 10209,     /* 0x27E1 */   /* Socket is not opened */
            MSP_ERROR_NET_NOTBIND = 10210,     /* 0x27E2 */   /* Socket is not bind to an address */
            MSP_ERROR_NET_NOTLISTEN = 10211,     /* 0x27E3 */   /* Socket is not listenning */
            MSP_ERROR_NET_CONNECTCLOSE = 10212,     /* 0x27E4 */   /* The other side of connection is closed */
            MSP_ERROR_NET_NOTDGRAMSOCK = 10213,     /* 0x27E5 */   /* The socket is not datagram type */

            /* Error codes of mssp message 10300(0x283C) */
            MSP_ERROR_MSG_GENERAL = 10300,     /* 0x283C */
            MSP_ERROR_MSG_PARSE_ERROR = 10301,     /* 0x283D */
            MSP_ERROR_MSG_BUILD_ERROR = 10302,     /* 0x283E */
            MSP_ERROR_MSG_PARAM_ERROR = 10303,     /* 0x283F */
            MSP_ERROR_MSG_CONTENT_EMPTY = 10304,     /* 0x2840 */
            MSP_ERROR_MSG_INVALID_CONTENT_TYPE = 10305,     /* 0x2841 */
            MSP_ERROR_MSG_INVALID_CONTENT_LENGTH = 10306,     /* 0x2842 */
            MSP_ERROR_MSG_INVALID_CONTENT_ENCODE = 10307,     /* 0x2843 */
            MSP_ERROR_MSG_INVALID_KEY = 10308,     /* 0x2844 */
            MSP_ERROR_MSG_KEY_EMPTY = 10309,     /* 0x2845 */
            MSP_ERROR_MSG_SESSION_ID_EMPTY = 10310,     /* 0x2846 */
            MSP_ERROR_MSG_LOGIN_ID_EMPTY = 10311,     /* 0x2847 */
            MSP_ERROR_MSG_SYNC_ID_EMPTY = 10312,     /* 0x2848 */
            MSP_ERROR_MSG_APP_ID_EMPTY = 10313,     /* 0x2849 */
            MSP_ERROR_MSG_EXTERN_ID_EMPTY = 10314,     /* 0x284A */
            MSP_ERROR_MSG_INVALID_CMD = 10315,     /* 0x284B */
            MSP_ERROR_MSG_INVALID_SUBJECT = 10316,     /* 0x284C */
            MSP_ERROR_MSG_INVALID_VERSION = 10317,     /* 0x284D */
            MSP_ERROR_MSG_NO_CMD = 10318,     /* 0x284E */
            MSP_ERROR_MSG_NO_SUBJECT = 10319,     /* 0x284F */
            MSP_ERROR_MSG_NO_VERSION = 10320,     /* 0x2850 */
            MSP_ERROR_MSG_MSSP_EMPTY = 10321,     /* 0x2851 */
            MSP_ERROR_MSG_NEW_RESPONSE = 10322,     /* 0x2852 */
            MSP_ERROR_MSG_NEW_CONTENT = 10323,     /* 0x2853 */
            MSP_ERROR_MSG_INVALID_SESSION_ID = 10324,     /* 0x2854 */

            /* Error codes of DataBase 10400(0x28A0)*/
            MSP_ERROR_DB_GENERAL = 10400,     /* 0x28A0 */
            MSP_ERROR_DB_EXCEPTION = 10401,     /* 0x28A1 */
            MSP_ERROR_DB_NO_RESULT = 10402,     /* 0x28A2 */
            MSP_ERROR_DB_INVALID_USER = 10403,     /* 0x28A3 */
            MSP_ERROR_DB_INVALID_PWD = 10404,     /* 0x28A4 */
            MSP_ERROR_DB_CONNECT = 10405,     /* 0x28A5 */
            MSP_ERROR_DB_INVALID_SQL = 10406,     /* 0x28A6 */
            MSP_ERROR_DB_INVALID_APPID = 10407,    /* 0x28A7 */

            /* Error codes of Resource 10500(0x2904)*/
            MSP_ERROR_RES_GENERAL = 10500,     /* 0x2904 */
            MSP_ERROR_RES_LOAD = 10501,     /* 0x2905 */   /* Load resource */
            MSP_ERROR_RES_FREE = 10502,     /* 0x2906 */   /* Free resource */
            MSP_ERROR_RES_MISSING = 10503,     /* 0x2907 */   /* Resource File Missing */
            MSP_ERROR_RES_INVALID_NAME = 10504,     /* 0x2908 */   /* Invalid resource file name */
            MSP_ERROR_RES_INVALID_ID = 10505,     /* 0x2909 */   /* Invalid resource ID */
            MSP_ERROR_RES_INVALID_IMG = 10506,     /* 0x290A */   /* Invalid resource image pointer */
            MSP_ERROR_RES_WRITE = 10507,     /* 0x290B */   /* Write read-only resource */
            MSP_ERROR_RES_LEAK = 10508,     /* 0x290C */   /* Resource leak out */
            MSP_ERROR_RES_HEAD = 10509,     /* 0x290D */   /* Resource head currupt */
            MSP_ERROR_RES_DATA = 10510,     /* 0x290E */   /* Resource data currupt */
            MSP_ERROR_RES_SKIP = 10511,     /* 0x290F */   /* Resource file skipped */

            /* Error codes of TTS 10600(0x2968)*/
            MSP_ERROR_TTS_GENERAL = 10600,     /* 0x2968 */
            MSP_ERROR_TTS_TEXTEND = 10601,     /* 0x2969 */  /* Meet text end */
            MSP_ERROR_TTS_TEXT_EMPTY = 10602,     /* 0x296A */  /* no synth text */

            /* Error codes of Recognizer 10700(0x29CC) */
            MSP_ERROR_REC_GENERAL = 10700,     /* 0x29CC */
            MSP_ERROR_REC_INACTIVE = 10701,     /* 0x29CD */
            MSP_ERROR_REC_GRAMMAR_ERROR = 10702,     /* 0x29CE */
            MSP_ERROR_REC_NO_ACTIVE_GRAMMARS = 10703,     /* 0x29CF */
            MSP_ERROR_REC_DUPLICATE_GRAMMAR = 10704,     /* 0x29D0 */
            MSP_ERROR_REC_INVALID_MEDIA_TYPE = 10705,     /* 0x29D1 */
            MSP_ERROR_REC_INVALID_LANGUAGE = 10706,     /* 0x29D2 */
            MSP_ERROR_REC_URI_NOT_FOUND = 10707,     /* 0x29D3 */
            MSP_ERROR_REC_URI_TIMEOUT = 10708,     /* 0x29D4 */
            MSP_ERROR_REC_URI_FETCH_ERROR = 10709,     /* 0x29D5 */

            /* Error codes of Speech Detector 10800(0x2A30) */
            MSP_ERROR_EP_GENERAL = 10800,     /* 0x2A30 */
            MSP_ERROR_EP_NO_SESSION_NAME = 10801,     /* 0x2A31 */
            MSP_ERROR_EP_INACTIVE = 10802,     /* 0x2A32 */
            MSP_ERROR_EP_INITIALIZED = 10803,     /* 0x2A33 */

            /* Error codes of TUV */
            MSP_ERROR_TUV_GENERAL = 10900,     /* 0x2A94 */
            MSP_ERROR_TUV_GETHIDPARAM = 10901,     /* 0x2A95 */   /* Get Busin Param huanid*/
            MSP_ERROR_TUV_TOKEN = 10902,     /* 0x2A96 */   /* Get Token */
            MSP_ERROR_TUV_CFGFILE = 10903,     /* 0x2A97 */   /* Open cfg file */
            MSP_ERROR_TUV_RECV_CONTENT = 10904,     /* 0x2A98 */   /* received content is error */
            MSP_ERROR_TUV_VERFAIL = 10905,     /* 0x2A99 */   /* Verify failure */

            /* Error codes of IMTV */
            MSP_ERROR_IMTV_SUCCESS = 11000,     /* 0x2AF8 */   /* 成功 */
            MSP_ERROR_IMTV_NO_LICENSE = 11001,     /* 0x2AF9 */   /* 試用次數結束,用戶需要付費 */
            MSP_ERROR_IMTV_SESSIONID_INVALID = 11002,     /* 0x2AFA */   /* SessionId失效,需要重新登錄通行證 */
            MSP_ERROR_IMTV_SESSIONID_ERROR = 11003,     /* 0x2AFB */   /* SessionId為空,或者非法 */
            MSP_ERROR_IMTV_UNLOGIN = 11004,     /* 0x2AFC */   /* 未登錄通行證 */
            MSP_ERROR_IMTV_SYSTEM_ERROR = 11005,     /* 0x2AFD */   /* 系統錯誤 */

            /* Error codes of HCR */
            MSP_ERROR_HCR_GENERAL = 11100,
            MSP_ERROR_HCR_RESOURCE_NOT_EXIST = 11101,

            /* Error codes of http 12000(0x2EE0) */
            MSP_ERROR_HTTP_BASE = 12000,    /* 0x2EE0 */

            /*Error codes of ISV */
            MSP_ERROR_ISV_NO_USER = 13000,    /* 32C8 */    /* the user doesn't exist */
        }

        public enum SynthStatus
        {
            MSP_TTS_FLAG_STILL_HAVE_DATA = 1,
            MSP_TTS_FLAG_DATA_END = 2,
            MSP_TTS_FLAG_CMD_CANCELED = 0
        }

        #endregion

        #region TTS dll import

        [DllImport("msc.dll", CallingConvention = CallingConvention.Winapi)]
        public static extern int MSPLogin(string user, string password, string configs);

        [DllImport("msc.dll", CallingConvention = CallingConvention.Winapi)]
        public static extern int MSPLogout();

        [DllImport("msc.dll", CallingConvention = CallingConvention.Winapi)]
        public static extern IntPtr QTTSSessionBegin(string _params, ref int errorCode);

        [DllImport("msc.dll", CallingConvention = CallingConvention.Winapi)]
        public static extern int QTTSTextPut(string sessionID, string textString, uint textLen, string _params);

        [DllImport("msc.dll", CallingConvention = CallingConvention.Winapi)]
        public static extern IntPtr QTTSAudioGet(string sessionID, ref uint audioLen, ref SynthStatus synthStatus, ref int errorCode);

        [DllImport("msc.dll", CallingConvention = CallingConvention.Winapi)]
        public static extern IntPtr QTTSAudioInfo(string sessionID);

        [DllImport("msc.dll", CallingConvention = CallingConvention.Winapi)]
        public static extern int QTTSSessionEnd(string sessionID, string hints);
        #endregion

        /// <summary>
        /// 生成語音文件
        /// </summary>
        /// <param name="text"></param>
        /// <param name="filename"></param>
        /// <param name="speaker"></param>
        /// <param name="speed"></param>
        /// <returns></returns>
        public static bool GetAudio(string text, string filename, string speaker, int speed)
        {
            IntPtr session_ID = IntPtr.Zero;
            try
            {
                string login_configs = $"appid =xxx ";//登錄參數,自己註冊後獲取的appid

                string type = speaker;
                uint audio_len = 0;

                SynthStatus synth_status = SynthStatus.MSP_TTS_FLAG_STILL_HAVE_DATA;
                var ret = WindowsXFTTSUtils.MSPLogin(string.Empty, string.Empty, login_configs);

                //MSPLogin方法返回失敗
                if (ret != (int)ErrorCode.MSP_SUCCESS)
                {
                    return false;
                }

                string _params = $"engine_type = local,rdn = 2, speed = {speed}, volume = 100, rcn = 0, voice_name={type}, tts_res_path =fo|res\\tts\\{type}.jet;fo|res\\tts\\common.jet, sample_rate = 16000";
                //string _params = "ssm=1,ent=sms16k,vcn=xiaoyan,spd=medium,aue=speex-wb;7,vol=x-loud,auf=audio/L16;rate=16000";
                //string @params = "engine_type = local,voice_name=xiaoyan,speed=50,volume=50,pitch=50,rcn=1, text_encoding = UTF8, background_sound=1,sample_rate = 16000";
                session_ID = WindowsXFTTSUtils.QTTSSessionBegin(_params, ref ret);
                //QTTSSessionBegin方法返回失敗
                if (ret != (int)ErrorCode.MSP_SUCCESS)
                {
                    return false;
                }

                ret = WindowsXFTTSUtils.QTTSTextPut(Ptr2Str(session_ID), text, (uint)Encoding.Default.GetByteCount(text), string.Empty);
                //QTTSTextPut方法返回失敗
                if (ret != (int)ErrorCode.MSP_SUCCESS)
                {
                    return false;
                }

                MemoryStream memoryStream = new MemoryStream();
                memoryStream.Write(new byte[44], 0, 44);
                while (true)
                {
                    IntPtr source = WindowsXFTTSUtils.QTTSAudioGet(Ptr2Str(session_ID), ref audio_len, ref synth_status, ref ret);
                    if (source != IntPtr.Zero)
                    {
                        byte[] array = new byte[(int)audio_len];
                        if (audio_len > 0)
                        {
                            Marshal.Copy(source, array, 0, (int)audio_len);
                        }
                        memoryStream.Write(array, 0, array.Length);
                    }

                    if (synth_status == SynthStatus.MSP_TTS_FLAG_DATA_END || ret != 0)
                        break;
                }
                WAVE_Header wave_Header = GetWave_Header((int)memoryStream.Length - 44);
                byte[] array2 = StructToBytes(wave_Header);
                memoryStream.Position = 0L;
                memoryStream.Write(array2, 0, array2.Length);
                memoryStream.Position = 0L;
                if (filename != null)
                {
                    var dir = Path.GetDirectoryName(filename);
                    if (!Directory.Exists(dir))
                        Directory.CreateDirectory(dir);

                    FileStream fileStream = new FileStream(filename, FileMode.Create, FileAccess.Write);
                    memoryStream.WriteTo(fileStream);
                    memoryStream.Close();
                    fileStream.Close();
                }

                return true;
            }
            catch (Exception ex)
            {
                return false;
            }
            finally
            {
                WindowsXFTTSUtils.QTTSSessionEnd(Ptr2Str(session_ID), "");
                WindowsXFTTSUtils.MSPLogout();//退出登錄
            }
        }

        /// <summary>
        /// 結構體轉字元串
        /// </summary>
        /// <param name="structure"></param>
        /// <returns></returns>
        private static byte[] StructToBytes(object structure)
        {
            int num = Marshal.SizeOf(structure);
            IntPtr intPtr = Marshal.AllocHGlobal(num);
            byte[] result;
            try
            {
                Marshal.StructureToPtr(structure, intPtr, false);
                byte[] array = new byte[num];
                Marshal.Copy(intPtr, array, 0, num);
                result = array;
            }
            finally
            {
                Marshal.FreeHGlobal(intPtr);
            }
            return result;
        }

        /// <summary>
        /// 結構體初始化賦值
        /// </summary>
        /// <param name="data_len"></param>
        /// <returns></returns>
        private static WAVE_Header GetWave_Header(int data_len)
        {
            return new WAVE_Header
            {
                RIFF_ID = 1179011410,
                File_Size = data_len + 36,
                RIFF_Type = 1163280727,
                FMT_ID = 544501094,
                FMT_Size = 16,
                FMT_Tag = 1,
                FMT_Channel = 1,
                FMT_SamplesPerSec = 16000,
                AvgBytesPerSec = 32000,
                BlockAlign = 2,
                BitsPerSample = 16,
                DATA_ID = 1635017060,
                DATA_Size = data_len
            };
        }

        /// <summary>
        /// 語音音頻頭
        /// </summary>
        private struct WAVE_Header
        {
            public int RIFF_ID;
            public int File_Size;
            public int RIFF_Type;
            public int FMT_ID;
            public int FMT_Size;
            public short FMT_Tag;
            public ushort FMT_Channel;
            public int FMT_SamplesPerSec;
            public int AvgBytesPerSec;
            public ushort BlockAlign;
            public ushort BitsPerSample;
            public int DATA_ID;
            public int DATA_Size;
        }

        /// 指針轉字元串
        /// </summary>
        /// <param name="p">指向非托管代碼字元串的指針</param>
        /// <returns>返回指針指向的字元串</returns>
        public static string Ptr2Str(IntPtr p)
        {
            List<byte> lb = new List<byte>();
            while (Marshal.ReadByte(p) != 0)
            {
                lb.Add(Marshal.ReadByte(p));
                p = p + 1;
            }

            return Encoding.Default.GetString(lb.ToArray());
        }
    }
}

Linux版

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.IO;

namespace Speech
{
    /// <summary>
    /// 訊飛語音合成
    /// Author:String Lee
    /// Date:2021年12月15日 14:06:00
    /// </summary>
    public class LinuxXFTTSUtils
    {
        #region TTS枚舉常量

        public enum ErrorCode
        {
            MSP_SUCCESS = 0,
            MSP_ERROR_FAIL = -1,
            MSP_ERROR_EXCEPTION = -2,

            /* General errors 10100(0x2774) */
            MSP_ERROR_GENERAL = 10100,     /* 0x2774 */
            MSP_ERROR_OUT_OF_MEMORY = 10101,     /* 0x2775 */
            MSP_ERROR_FILE_NOT_FOUND = 10102,     /* 0x2776 */
            MSP_ERROR_NOT_SUPPORT = 10103,     /* 0x2777 */
            MSP_ERROR_NOT_IMPLEMENT = 10104,     /* 0x2778 */
            MSP_ERROR_ACCESS = 10105,     /* 0x2779 */
            MSP_ERROR_INVALID_PARA = 10106,     /* 0x277A */
            MSP_ERROR_INVALID_PARA_VALUE = 10107,     /* 0x277B */
            MSP_ERROR_INVALID_HANDLE = 10108,     /* 0x277C */
            MSP_ERROR_INVALID_DATA = 10109,     /* 0x277D */
            MSP_ERROR_NO_LICENSE = 10110,     /* 0x277E */
            MSP_ERROR_NOT_INIT = 10111,     /* 0x277F */
            MSP_ERROR_NULL_HANDLE = 10112,     /* 0x2780 */
            MSP_ERROR_OVERFLOW = 10113,     /* 0x2781 */
            MSP_ERROR_TIME_OUT = 10114,     /* 0x2782 */
            MSP_ERROR_OPEN_FILE = 10115,     /* 0x2783 */
            MSP_ERROR_NOT_FOUND = 10116,     /* 0x2784 */
            MSP_ERROR_NO_ENOUGH_BUFFER = 10117,     /* 0x2785 */
            MSP_ERROR_NO_DATA = 10118,     /* 0x2786 */
            MSP_ERROR_NO_MORE_DATA = 10119,     /* 0x2787 */
            MSP_ERROR_SKIPPED = 10120,     /* 0x2788 */
            MSP_ERROR_ALREADY_EXIST = 10121,     /* 0x2789 */
            MSP_ERROR_LOAD_MODULE = 10122,     /* 0x278A */
            MSP_ERROR_BUSY = 10123,     /* 0x278B */
            MSP_ERROR_INVALID_CONFIG = 10124,     /* 0x278C */
            MSP_ERROR_VERSION_CHECK = 10125,     /* 0x278D */
            MSP_ERROR_CANCELED = 10126,     /* 0x278E */
            MSP_ERROR_INVALID_MEDIA_TYPE = 10127,     /* 0x278F */
            MSP_ERROR_CONFIG_INITIALIZE = 10128,     /* 0x2790 */
            MSP_ERROR_CREATE_HANDLE = 10129,     /* 0x2791 */
            MSP_ERROR_CODING_LIB_NOT_LOAD = 10130,     /* 0x2792 */

            /* Error codes of network 10200(0x27D8)*/
            MSP_ERROR_NET_GENERAL = 10200,     /* 0x27D8 */
            MSP_ERROR_NET_OPENSOCK = 10201,     /* 0x27D9 */   /* Open socket */
            MSP_ERROR_NET_CONNECTSOCK = 10202,     /* 0x27DA */   /* Connect socket */
            MSP_ERROR_NET_ACCEPTSOCK = 10203,     /* 0x27DB */   /* Accept socket */
            MSP_ERROR_NET_SENDSOCK = 10204,     /* 0x27DC */   /* Send socket data */
            MSP_ERROR_NET_RECVSOCK = 10205,     /* 0x27DD */   /* Recv socket data */
            MSP_ERROR_NET_INVALIDSOCK = 10206,     /* 0x27DE */   /* Invalid socket handle */
            MSP_ERROR_NET_BADADDRESS = 10207,     /* 0x27EF */   /* Bad network address */
            MSP_ERROR_NET_BINDSEQUENCE = 10208,     /* 0x27E0 */   /* Bind after listen/connect */
            MSP_ERROR_NET_NOTOPENSOCK = 10209,     /* 0x27E1 */   /* Socket is not opened */
            MSP_ERROR_NET_NOTBIND = 10210,     /* 0x27E2 */   /* Socket is not bind to an address */
            MSP_ERROR_NET_NOTLISTEN = 10211,     /* 0x27E3 */   /* Socket is not listenning */
            MSP_ERROR_NET_CONNECTCLOSE = 10212,     /* 0x27E4 */   /* The other side of connection is closed */
            MSP_ERROR_NET_NOTDGRAMSOCK = 10213,     /* 0x27E5 */   /* The socket is not datagram type */

            /* Error codes of mssp message 10300(0x283C) */
            MSP_ERROR_MSG_GENERAL = 10300,     /* 0x283C */
            MSP_ERROR_MSG_PARSE_ERROR = 10301,     /* 0x283D */
            MSP_ERROR_MSG_BUILD_ERROR = 10302,     /* 0x283E */
            MSP_ERROR_MSG_PARAM_ERROR = 10303,     /* 0x283F */
            MSP_ERROR_MSG_CONTENT_EMPTY = 10304,     /* 0x2840 */
            MSP_ERROR_MSG_INVALID_CONTENT_TYPE = 10305,     /* 0x2841 */
            MSP_ERROR_MSG_INVALID_CONTENT_LENGTH = 10306,     /* 0x2842 */
            MSP_ERROR_MSG_INVALID_CONTENT_ENCODE = 10307,     /* 0x2843 */
            MSP_ERROR_MSG_INVALID_KEY = 10308,     /* 0x2844 */
            MSP_ERROR_MSG_KEY_EMPTY = 10309,     /* 0x2845 */
            MSP_ERROR_MSG_SESSION_ID_EMPTY = 10310,     /* 0x2846 */
            MSP_ERROR_MSG_LOGIN_ID_EMPTY = 10311,     /* 0x2847 */
            MSP_ERROR_MSG_SYNC_ID_EMPTY = 10312,     /* 0x2848 */
            MSP_ERROR_MSG_APP_ID_EMPTY = 10313,     /* 0x2849 */
            MSP_ERROR_MSG_EXTERN_ID_EMPTY = 10314,     /* 0x284A */
            MSP_ERROR_MSG_INVALID_CMD = 10315,     /* 0x284B */
            MSP_ERROR_MSG_INVALID_SUBJECT = 10316,     /* 0x284C */
            MSP_ERROR_MSG_INVALID_VERSION = 10317,     /* 0x284D */
            MSP_ERROR_MSG_NO_CMD = 10318,     /* 0x284E */
            MSP_ERROR_MSG_NO_SUBJECT = 10319,     /* 0x284F */
            MSP_ERROR_MSG_NO_VERSION = 10320,     /* 0x2850 */
            MSP_ERROR_MSG_MSSP_EMPTY = 10321,     /* 0x2851 */
            MSP_ERROR_MSG_NEW_RESPONSE = 10322,     /* 0x2852 */
            MSP_ERROR_MSG_NEW_CONTENT = 10323,     /* 0x2853 */
            MSP_ERROR_MSG_INVALID_SESSION_ID = 10324,     /* 0x2854 */

            /* Error codes of DataBase 10400(0x28A0)*/
            MSP_ERROR_DB_GENERAL = 10400,     /* 0x28A0 */
            MSP_ERROR_DB_EXCEPTION = 10401,     /* 0x28A1 */
            MSP_ERROR_DB_NO_RESULT = 10402,     /* 0x28A2 */
            MSP_ERROR_DB_INVALID_USER = 10403,     /* 0x28A3 */
            MSP_ERROR_DB_INVALID_PWD = 10404,     /* 0x28A4 */
            MSP_ERROR_DB_CONNECT = 10405,     /* 0x28A5 */
            MSP_ERROR_DB_INVALID_SQL = 10406,     /* 0x28A6 */
            MSP_ERROR_DB_INVALID_APPID = 10407,    /* 0x28A7 */

            /* Error codes of Resource 10500(0x2904)*/
            MSP_ERROR_RES_GENERAL = 10500,     /* 0x2904 */
            MSP_ERROR_RES_LOAD = 10501,     /* 0x2905 */   /* Load resource */
            MSP_ERROR_RES_FREE = 10502,     /* 0x2906 */   /* Free resource */
            MSP_ERROR_RES_MISSING = 10503,     /* 0x2907 */   /* Resource File Missing */
            MSP_ERROR_RES_INVALID_NAME = 10504,     /* 0x2908 */   /* Invalid resource file name */
            MSP_ERROR_RES_INVALID_ID = 10505,     /* 0x2909 */   /* Invalid resource ID */
            MSP_ERROR_RES_INVALID_IMG = 10506,     /* 0x290A */   /* Invalid resource image pointer */
            MSP_ERROR_RES_WRITE = 10507,     /* 0x290B */   /* Write read-only resource */
            MSP_ERROR_RES_LEAK = 10508,     /* 0x290C */   /* Resource leak out */
            MSP_ERROR_RES_HEAD = 10509,     /* 0x290D */   /* Resource head currupt */
            MSP_ERROR_RES_DATA = 10510,     /* 0x290E */   /* Resource data currupt */
            MSP_ERROR_RES_SKIP = 10511,     /* 0x290F */   /* Resource file skipped */

            /* Error codes of TTS 10600(0x2968)*/
            MSP_ERROR_TTS_GENERAL = 10600,     /* 0x2968 */
            MSP_ERROR_TTS_TEXTEND = 10601,     /* 0x2969 */  /* Meet text end */
            MSP_ERROR_TTS_TEXT_EMPTY = 10602,     /* 0x296A */  /* no synth text */

            /* Error codes of Recognizer 10700(0x29CC) */
            MSP_ERROR_REC_GENERAL = 10700,     /* 0x29CC */
            MSP_ERROR_REC_INACTIVE = 10701,     /* 0x29CD */
            MSP_ERROR_REC_GRAMMAR_ERROR = 10702,     /* 0x29CE */
            MSP_ERROR_REC_NO_ACTIVE_GRAMMARS = 10703,     /* 0x29CF */
            MSP_ERROR_REC_DUPLICATE_GRAMMAR = 10704,     /* 0x29D0 */
            MSP_ERROR_REC_INVALID_MEDIA_TYPE = 10705,     /* 0x29D1 */
            MSP_ERROR_REC_INVALID_LANGUAGE = 10706,     /* 0x29D2 */
            MSP_ERROR_REC_URI_NOT_FOUND = 10707,     /* 0x29D3 */
            MSP_ERROR_REC_URI_TIMEOUT = 10708,     /* 0x29D4 */
            MSP_ERROR_REC_URI_FETCH_ERROR = 10709,     /* 0x29D5 */

            /* Error codes of Speech Detector 10800(0x2A30) */
            MSP_ERROR_EP_GENERAL = 10800,     /* 0x2A30 */
            MSP_ERROR_EP_NO_SESSION_NAME = 10801,     /* 0x2A31 */
            MSP_ERROR_EP_INACTIVE = 10802,     /* 0x2A32 */
            MSP_ERROR_EP_INITIALIZED = 10803,     /* 0x2A33 */

            /* Error codes of TUV */
            MSP_ERROR_TUV_GENERAL = 10900,     /* 0x2A94 */
            MSP_ERROR_TUV_GETHIDPARAM = 10901,     /* 0x2A95 */   /* Get Busin Param huanid*/
            MSP_ERROR_TUV_TOKEN = 10902,     /* 0x2A96 */   /* Get Token */
            MSP_ERROR_TUV_CFGFILE = 10903,     /* 0x2A97 */   /* Open cfg file */
            MSP_ERROR_TUV_RECV_CONTENT = 10904,     /* 0x2A98 */   /* received content is error */
            MSP_ERROR_TUV_VERFAIL = 10905,     /* 0x2A99 */   /* Verify failure */

            /* Error codes of IMTV */
            MSP_ERROR_IMTV_SUCCESS = 11000,     /* 0x2AF8 */   /* 成功 */
            MSP_ERROR_IMTV_NO_LICENSE = 11001,     /* 0x2AF9 */   /* 試用次數結束,用戶需要付費 */
            MSP_ERROR_IMTV_SESSIONID_INVALID = 11002,     /* 0x2AFA */   /* SessionId失效,需要重新登錄通行證 */
            MSP_ERROR_IMTV_SESSIONID_ERROR = 11003,     /* 0x2AFB */   /* SessionId為空,或者非法 */
            MSP_ERROR_IMTV_UNLOGIN = 11004,     /* 0x2AFC */   /* 未登錄通行證 */
            MSP_ERROR_IMTV_SYSTEM_ERROR = 11005,     /* 0x2AFD */   /* 系統錯誤 */

            /* Error codes of HCR */
            MSP_ERROR_HCR_GENERAL = 11100,
            MSP_ERROR_HCR_RESOURCE_NOT_EXIST = 11101,

            /* Error codes of http 12000(0x2EE0) */
            MSP_ERROR_HTTP_BASE = 12000,    /* 0x2EE0 */

            /*Error codes of ISV */
            MSP_ERROR_ISV_NO_USER = 13000,    /* 32C8 */    /* the user doesn't exist */
        }

        public enum SynthStatus
        {
            MSP_TTS_FLAG_STILL_HAVE_DATA = 1,
            MSP_TTS_FLAG_DATA_END = 2,
            MSP_TTS_FLAG_CMD_CANCELED = 0
        }

        #endregion

        #region TTS dll import

        [DllImport("libmsc.so", EntryPoint = "MSPLogin", CallingConvention = CallingConvention.Cdecl)]
        public static extern int MSPLogin(string user, string password, string configs);

        [DllImport("libmsc.so", EntryPoint = "MSPLogout", CallingConvention = CallingConvention.Cdecl)]
        public static extern int MSPLogout();

        [DllImport("libmsc.so", EntryPoint = "QTTSSessionBegin", CallingConvention = CallingConvention.Cdecl)]
        public static extern IntPtr QTTSSessionBegin(string _params, ref int errorCode);

        [DllImport("libmsc.so", EntryPoint = "QTTSTextPut", CallingConvention = CallingConvention.Cdecl)]
        public static extern int QTTSTextPut(string sessionID, string textString, uint textLen, string _params);

        [DllImport("libmsc.so", EntryPoint = "QTTSAudioGet", CallingConvention = CallingConvention.Cdecl)]
        public static extern IntPtr QTTSAudioGet(string sessionID, ref uint audioLen, ref SynthStatus synthStatus, ref int errorCode);

        [DllImport("libmsc.so", EntryPoint = "QTTSAudioInfo", CallingConvention = CallingConvention.Cdecl)]
        public static extern IntPtr QTTSAudioInfo(string sessionID);

        [DllImport("libmsc.so", EntryPoint = "QTTSSessionEnd", CallingConvention = CallingConvention.Cdecl)]
        public static extern int QTTSSessionEnd(string sessionID, string hints);
        #endregion

        /// <summary>
        /// 生成語音文件
        /// </summary>
        /// <param name="text"></param>
        /// <param name="filename"></param>
        /// <param name="speaker"></param>
        /// <param name="speed"></param>
        /// <returns></returns>
        public static bool GetAudio(string text, string filename, string speaker, int speed)
        {
            IntPtr session_ID = IntPtr.Zero;
            try
            {
                string login_configs = $"appid =xxx ";//登錄參數,自己註冊後獲取的appid

                string type = speaker;
                uint audio_len = 0;

                SynthStatus synth_status = SynthStatus.MSP_TTS_FLAG_STILL_HAVE_DATA;
                var ret = LinuxXFTTSUtils.MSPLogin(string.Empty, string.Empty, login_configs);

                //MSPLogin方法返回失敗
                if (ret != (int)ErrorCode.MSP_SUCCESS)
                {
                    Console.WriteLine("1");
                    return false;
                }

                //string _params = $"engine_type = purextts,rdn = 2, speed = {speed}, volume = 100, rcn = 0, voice_name={type}, tts_res_path =fo|res\\xtts\\{type}.jet;fo|res\\xtts\\common.jet, sample_rate = 16000";
                string _params = $"engine_type = purextts,voice_name={speaker}, text_encoding = UTF8, tts_res_path = fo|res/xtts/{speaker}.jet;fo|res/xtts/common.jet, sample_rate = 16000, speed = {speed}, volume = 100, pitch = 50, rdn = 2";
                //string _params = "ssm=1,ent=sms16k,vcn=xiaoyan,spd=medium,aue=speex-wb;7,vol=x-loud,auf=audio/L16;rate=16000";
                //string @params = "engine_type = local,voice_name=xiaoyan,speed=50,volume=50,pitch=50,rcn=1, text_encoding = UTF8, background_sound=1,sample_rate = 16000";
                session_ID = LinuxXFTTSUtils.QTTSSessionBegin(_params, ref ret);
                //QTTSSessionBegin方法返回失敗
                if (ret != (int)ErrorCode.MSP_SUCCESS)
                {
                    return false;
                }

                ret = LinuxXFTTSUtils.QTTSTextPut(Ptr2Str(session_ID), text, (uint)Encoding.Default.GetByteCount(text), string.Empty);
                //QTTSTextPut方法返回失敗
                if (ret != (int)ErrorCode.MSP_SUCCESS)
                {
                    return false;
                }

                MemoryStream memoryStream = new MemoryStream();
                memoryStream.Write(new byte[44], 0, 44);
                while (true)
                {
                    IntPtr source = LinuxXFTTSUtils.QTTSAudioGet(Ptr2Str(session_ID), ref audio_len, ref synth_status, ref ret);
                    if (source != IntPtr.Zero)
                    {
                        byte[] array = new byte[(int)audio_len];
                        if (audio_len > 0)
                        {
                            Marshal.Copy(source, array, 0, (int)audio_len);
                        }
                        memoryStream.Write(array, 0, array.Length);
                    }

                    if (synth_status == SynthStatus.MSP_TTS_FLAG_DATA_END || ret != 0)
                        break;
                }
                WAVE_Header wave_Header = GetWave_Header((int)memoryStream.Length - 44);
                byte[] array2 = StructToBytes(wave_Header);
                memoryStream.Position = 0L;
                memoryStream.Write(array2, 0, array2.Length);
                memoryStream.Position = 0L;
                if (filename != null)
                {
                    var dir = Path.GetDirectoryName(filename);
                    if (!Directory.Exists(dir))
                        Directory.CreateDirectory(dir);

                    FileStream fileStream = new FileStream(filename, FileMode.Create, FileAccess.Write);
                    memoryStream.WriteTo(fileStream);
                    memoryStream.Close();
                    fileStream.Close();
                }

                return true;
            }
            catch (Exception ex)
            {
                return false;
            }
            finally
            {
                LinuxXFTTSUtils.QTTSSessionEnd(Ptr2Str(session_ID), "");
                LinuxXFTTSUtils.MSPLogout();//退出登錄
            }
        }

        /// <summary>
        /// 結構體轉字元串
        /// </summary>
        /// <param name="structure"></param>
        /// <returns></returns>
        private static byte[] StructToBytes(object structure)
        {
            int num = Marshal.SizeOf(structure);
            IntPtr intPtr = Marshal.AllocHGlobal(num);
            byte[] result;
            try
            {
                Marshal.StructureToPtr(structure, intPtr, false);
                byte[] array = new byte[num];
                Marshal.Copy(intPtr, array, 0, num);
                result = array;
            }
            finally
            {
                Marshal.FreeHGlobal(intPtr);
            }
            return result;
        }

        /// <summary>
        /// 結構體初始化賦值
        /// </summary>
        /// <param name="data_len"></param>
        /// <returns></returns>
        private static WAVE_Header GetWave_Header(int data_len)
        {
            return new WAVE_Header
            {
                RIFF_ID = 1179011410,
                File_Size = data_len + 36,
                RIFF_Type = 1163280727,
                FMT_ID = 544501094,
                FMT_Size = 16,
                FMT_Tag = 1,
                FMT_Channel = 1,
                FMT_SamplesPerSec = 16000,
                AvgBytesPerSec = 32000,
                BlockAlign = 2,
                BitsPerSample = 16,
                DATA_ID = 1635017060,
                DATA_Size = data_len
            };
        }

        /// <summary>
        /// 語音音頻頭
        /// </summary>
        private struct WAVE_Header
        {
            public int RIFF_ID;
            public int File_Size;
            public int RIFF_Type;
            public int FMT_ID;
            public int FMT_Size;
            public short FMT_Tag;
            public ushort FMT_Channel;
            public int FMT_SamplesPerSec;
            public int AvgBytesPerSec;
            public ushort BlockAlign;
            public ushort BitsPerSample;
            public int DATA_ID;
            public int DATA_Size;
        }

        /// 指針轉字元串
        /// </summary>
        /// <param name="p">指向非托管代碼字元串的指針</param>
        /// <returns>返回指針指向的字元串</returns>
        public static string Ptr2Str(IntPtr p)
        {
            List<byte> lb = new List<byte>();
            while (Marshal.ReadByte(p) != 0)
            {
                lb.Add(Marshal.ReadByte(p));
                p = p + 1;
            }

            return Encoding.Default.GetString(lb.ToArray());
        }
    }
}

上面分別是Windows的普通版與Linux高品質版的調用代碼,比較凌亂,用的時候可以再封裝一下,還要記得把之前記下來的APPID填到代碼里。

四、調用

[ApiController]
[Route("[controller]")]
public class TextToSpeechController : ControllerBase
{
    [HttpGet]
    public FileStreamResult GetWav(string text,string speaker)
    {
        var fileName = Guid.NewGuid().ToString() + ".wav";
        var mimeType = "application/....";

        if(string.IsNullOrEmpty(speaker))
            speaker = "xiaofeng";

        if(RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
            LinuxXFTTSUtils.GetAudio(text, fileName, speaker, 50);
        else if(RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            WindowsXFTTSUtils.GetAudio(text, fileName, speaker, 50);

        Stream stream = System.IO.File.OpenRead(fileName);
        System.IO.File.Delete(fileName);

        return new FileStreamResult(stream, mimeType)
        {
            FileDownloadName = fileName
        };
    }
}

註意需要把下載的SDK包裡面的bin目錄內的文件拷貝到運行目錄,如果是linux版,需要從libs裡面拷貝libmsc.so庫,speaker可以填xiaofeng或者xiaoyan,分別是男女聲,需要更多的發音人可以去購買。

代碼基本完整提供了,各位直接複製粘貼使用即可,離線SDK可以免費體驗30天,過期了需要購買或者重新註冊創建新應用來延續使用。不過既然都是離線版了,直接把電腦時間固定住應該也能繼續白嫖。哈哈,個人學習使用的話可以,商用版還是要購買噢。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 函數是任何一門高級語言中必須要存在的,使用函數式編程可以讓程式可讀性更高,充分發揮了模塊化設計思想的精髓,今天我將帶大家一起來探索函數的實現機理,探索編譯器到底是如何對函數這個關鍵字進行實現的,並使用彙編語言模擬實現函數編程中的參數傳遞調用規範等。說到函數我們必須要提起調用約定這個名詞,而調用約定離... ...
  • 本文是區塊鏈瀏覽器系列的第四篇。 在[上一篇文章](https://mengbin.top/2023-08-13-blockBrowser/)介紹如何解析區塊數據時,使用`session`對客戶端上傳的pb文件進行區分,到期後自動刪除。 在這片文章中,會著重介紹下認證系統的實現,主要分為三部分: - ...
  • 需求背景 需要在前端頁面展示當前表欄位的所有上下游血緣關係,以進一步做數據診斷治理。大致效果圖如下: 首先這裡解釋什麼是表欄位血緣關係,SQL 示例: CREATE TABLE IF NOT EXISTS table_b AS SELECT order_id, order_status FROM t ...
  • 由於本人太弱,可能講解有誤,請讀者指出。 # 什麼是網路流 網路流是通過構建從源點到匯點的有向圖模型來解決圖論問題。從理論上講,網路流可以處理所有二分圖問題。 二分圖和網路流的難度都在於問題建模,一般不會特意去卡演算法效率,所以只需要背一兩個簡單演算法的模板就能應付大部分題目了。 # 最大流問題 ## ...
  • 1.已解密的登錄請求 推理: AppScan 識別了不是通過 SSL 發送的登錄請求。 測試請求和響應: 1.1.1 產生的原因 登錄介面,前端傳入的密碼參數沒有經過md5的加密就直接傳給了後端 1.1.2 解決方法 前端代碼傳參的時候做md5加密處理 2.會話標識未更新 推理: 測試結果似乎指示存 ...
  • ### 1. 建立工程 bh003_ble [源碼](https://github.com/densen2014/BlazorHybrid/tree/master/bh100days/bh003_ble?WT.mc_id=DT-MVP-5005078) ### 2. 添加 nuget 包 ``` ` ...
  • ## zhontai 項目 > 基於 .Net7.x + Vue 等技術的前後端分離後臺許可權管理系統,想你所想的開發理念,希望減少工作量,幫助大家實現快速開發 後端地址:https://github.com/zhontai/Admin.Core 前端地址:https://github.com/zho ...
  • 最近建立一個新項目準備寫一個小demo,新建項目時選的時.Net7。寫代碼的時候發現。Net7沒有系統的中文註釋,去官網下載的時候發現沒有關於.Net7的漢化文件包,最新的漢化包是。Net5的,可能是我沒找到,我這裡是把.Net5漢化包放到了.Net7下麵了,測試好用。 一、.NET Framewo ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...