C# SDK

本文介绍如何使用阿里云智能语音服务提供的C# SDK,包括SDK的安装方法及SDK代码示例。

SDK下载

说明
  • 当前最新版本:3.1.17,该版本基于C++ SDK API 3.1.x。发布日期:2023年08月28日。

  • 此SDK是底层C++包裹了C#组成,仅支持Windows平台,不支持跨平台能力。

  • 使用SDK前,请先阅读接口说明,详情请参见接口说明

可通过以下两种方法获取SDK。

  • 方法一:从GitHub获取最新源码,详细编译和运行方式可见下文,或查看源码中的readme.md。

    git clone --depth 1 https://github.com/aliyun/alibabacloud-nls-cpp-sdk
  • 方法二:直接从下文表中选取需要SDK包进行下载。其中SDK源码包为SDK原始代码,需要通过下文编译方法生成集成所需的库文件。其他对应平台的SDK包内含相关库文件、头文件,无需编译。

    最新SDK包

    平台

    MD5

    alibabacloud-nls-cpp-sdk3.1.17-master_fd7c7d8.zip

    SDK源码

    8beeaad832d4b249464f4338429c3b15

    NlsCsharpSdk_Windows_3.1.17_fd7c7d8.zip

    Windows

    81fdd1579e103d8c93b4dab98ee0f71c

    其中:

    • alibabacloud-nls-cpp-sdk-master_<github commit id>.zip为SDK源码包。

    • NlsCsharpSdk_Windows_<版本号>_<github commit id>.zip为Windows平台下开发需要的SDK包,详见内部readme.md。

Windows平台C#编译运行

推荐直接使用已经编译好的库NlsCsharpSdk_Windows_<version>_<github commit id>.zip进行集成。若有编译需求,请下载alibabacloud-nls-cpp-sdk<version>-master_<github commit id>.zip并解压到本地,或从GitHub获取最新代码,然后参考其中readme.md的编译步骤。

关键接口

using nlsCsharpSdk;
  • 基础接口

    • NlsClient:语音处理客户端,利用该客户端可以进行一句话识别、实时语音识别和语音合成的语音处理任务。该客户端为线程安全,建议全局仅创建一个实例。

      接口名

      启用版本

      功能描述

      SetLogConfig

      3.1.9

      设置日志文件与存储路径。越早调用记录的信息越详细。

      GetVersion

      3.1.9

      设置日志文件与存储路径。越早调用记录的信息越详细。

      CreateNlsToken

      3.1.9

      设置日志文件与存储路径。越早调用记录的信息越详细。

      ReleaseNlsToken

      3.1.9

      设置日志文件与存储路径。越早调用记录的信息越详细。

      SetDirectHost

      3.1.16

      跳过DNS域名解析直接设置服务器IP地址, 若调用则需要在StartWorkThread之前。

      StartWorkThread

      3.1.9

      启动工作线程数,默认1即启动一个线程,若-1则启动CPU核数的线程数。在高并发的情况下建议选择-1。这一接口有初始化NlsClient的作用。

      ReleaseInstance

      3.1.9

      销毁NlsClient对象实例,即释放NLS SDK。此操作需要在销毁所有请求后再调用。

      CreateNlsToken

      3.1.9

      创建Token获取对象,用于申请获取TokenId。详见下方NlsToken相关说明。

      ReleaseNlsToken

      3.1.9

      销毁Token获取对象。

      CreateTranscriberRequest

      3.1.9

      创建实时语音识别对象,线程安全,支持高并发请求。

      ReleaseTranscriberRequest

      3.1.9

      销毁实时语音识别对象,需要在当前请求的closed事件后调用。

    • NlsToken:创建Token对象,用于申请获取TokenId。申请新Token时需要先获取有效时间戳,若超过有效时间则再申请。若在有效时间内多次申请Token会导致TokenId错误而无法使用。

      接口名

      功能描述

      SetAccessKeyId

      设置阿里云账号AccessKey ID。

      SetKeySecret

      设置阿里云账号AccessKey Secret。

      ApplyNlsToken

      申请获取TokenId。此TokenId在有效期内对所有此阿里云账号AccessKey ID下的所有语音请求有效,无需在每个语音请求前申请获得。

      GetToken

      获取TokenId。

      GetExpireTime

      获取Token有效期时间戳(秒)。

      GetErrorMsg

      获取TokenId失败的错误信息。

    • NlsEvent:事件对象NLS_EVENT_STRUCT,通过CallbackDelegate返回。您可以从中获取Request状态码、云端返回结果、失败信息等。

      参数名

      功能描述

      statusCode

      获取状态码,正常情况为0或者20000000,失败时对应失败的错误码。

      taskId

      获取任务的TaskId。

      msg

      获取云端返回的response结果,用于获得事件信息,如Started、Closed、TaskFailed、SentenceBegin、SentenceEnd回调的事件信息。

      result

      获取云端返回的识别结果,用于获得识别结果,如ResultChanged、SentenceEnd回调的事件信息。

  • 识别接口

    SpeechTranscriberRequest:实时语音识别请求对象,用于长语音实时识别。

    接口名

    功能描述

    SetOnTaskFailed

    设置错误回调函数。

    SetOnTranscriptionStarted

    设置实时音频流识别开始回调函数。

    SetOnSentenceBegin

    设置一句话开始回调。

    SetOnSentenceEnd

    设置一句话结束回调函数。

    SetOnTranscriptionResultChanged

    设置实时音频流识别中间结果回调函数。

    SetOnTranscriptionCompleted

    设置服务端结束服务回调函数。

    SetOnChannelClosed

    设置通道关闭回调函数。

    SetAppKey

    设置Appkey

    SetToken

    口令认证。所有的请求都必须通过SetToken方法认证通过,才可以使用。

    SetUrl

    设置服务URL地址,默认可不设置则调用公有云上海域名的服务。

    SetIntermediateResult

    设置是否返回中间识别结果,结果会从onTranscriptionResultChanged回调返回。

    SetPunctuationPrediction

    设置是否在后处理中添加标点。

    SetInverseTextNormalization

    设置是否在后处理中执行数字转换。

    SetFormat

    设置音频数据编码格式,Windows环境推荐用PCM格式。

    SetSampleRate

    音频采样率设置。

    SetSemanticSentenceDetection

    设置是否使用语义断句。

    SetMaxSentenceSilence

    设置vad阈值。与语义断句为互斥关系, 不能同时使用. 调用此设置前, 请将语义断句SetSemanticSentenceDetection设置为false。

    SetCustomizationId

    设置定制模型。

    SetVocabularyId

    设置泛热词,例如("TestId_456")

    SetTimeout

    设置Socket接收超时时间。

    SetEnableNlp

    设置是否开启NLP服务。

    SetNlpModel

    设置NLP模型名称,开启NLP服务后必填。

    SetSessionId

    设置Session ID。

    SetOutputFormat

    设置输出文本的编码格式。Windows平台下默认为GBK。非中文语种情况下,请设置成UTF-8,否则会出现乱码。

    SetPayloadParam

    参数设置,入参为JSON格式字符串。如指定声学模型,例如"{\"model\":\"test-regression-model\"}")

    SetContextParam

    设置用户自定义参数,入参为JSON格式字符串。

    AppendHttpHeaderParam

    设置用户自定义ws阶段http header参数。

    Start

    异步接口,启动SpeechTranscriberRequest。需要等待OnTranscriptionStarted回调以确保请求进入工作状态。

    Stop

    异步接口,会与服务端确认关闭,正常停止链接操作。需要等待OnChannelClosed回调以确保请求结束工作状态。

    Cancel

    不会与服务端确认关闭,直接关闭实时音频流识别过程。

    Control

    要求服务端更新识别参数。

    SendAudio

    异步接口,发送语音数据。为保证实时性,每次发送数据不建议太长,建议一次发送100ms时长以内的音频数据,范围为640字节~16384字节

SDK错误码

状态码

状态消息

原因

解决方案

0

Success

成功

-10

DefaultError

默认错误

暂未使用。

-11

JsonParseFailed

错误的JSON格式

请检查传入的JSON字符串是否符合JSON格式。

-12

JsonObjectError

错误的JSON对象

建议重新尝试。

-13

MallocFailed

Malloc失败

请检查内存是否充足。

-14

ReallocFailed

Realloc失败

请检查内存是否充足。

-15

InvalidInputParam

传入无效的参数

暂未使用。

-50

InvalidLogLevel

无效日志级别

请检查设置的Log级别。

-51

InvalidLogFileSize

无效日志文件大小

请检查设置的Log文件大小参数。

-52

InvalidLogFileNum

无效日志文件数量

请检查设置的Log文件数量参数。

-100

EncoderExistent

NLS的编码器已存在

建议重新尝试。

-101

EncoderInexistent

NLS的编码器不存在

建议重新初始化。

-102

OpusEncoderCreateFailed

Opus编码器创建失败

建议重新初始化。

-103

OggOpusEncoderCreateFailed

OggOpus编码器创建失败

建议重新初始化。

-150

EventClientEmpty

主工作线程空指针, 已释放

建议重新初始化,即startWorkThread()。

-151

SelectThreadFailed

工作线程选择失败, 未初始化

建议重新初始化,即startWorkThread()。

-160

StartCommandFailed

发送start命令失败

建议重新尝试。

-161

InvokeStartFailed

请求状态机不对, 导致start失败

请检查当前请求是否未创建或者已经完成。

-162

InvokeSendAudioFailed

请求状态机不对, 导致sendAudio失败

请检查当前请求是否已经启动(即收到started事件回调)或者已经完成。

-163

InvalidOpusFrameSize

opus帧长无效, 默认为640字节

OPU编码模式下,sendAudio一帧只接收640字节数据。

-164

InvokeStopFailed

请求状态机不对, 导致stop失败

请检查当前请求是否未启动(即收到started事件回调)或者已经完成。

-165

InvokeCancelFailed

请求状态机不对, 导致stop失败

请检查当前请求是否未启动(即收到started事件回调)或者已经完成。

-166

InvokeStControlFailed

请求状态机不对, 导致stControl失败

请检查当前请求是否未启动(即收到started事件回调)或者已经完成。

-200

NlsEventEmpty

NLS事件为空

SDK内部使用, NlsEvent帧丢失。

-201

NewNlsEventFailed

创建NlsEvent失败

SDK内部使用, NlsEvent帧创建失败。

-202

NlsEventMsgEmpty

NLS事件中消息为空

parseJsonMsg()进行解析时发现消息字符串为空。

-203

InvalidNlsEventMsgType

无效的NLS事件中消息类型

SDK内部使用, NlsEvent帧的事件类型不合法。

-204

InvalidNlsEventMsgStatusCode

无效的NLS事件中消息状态码

SDK内部使用, NlsEvent帧的事件消息状态不合法。

-205

InvalidNlsEventMsgHeader

无效的NLS事件中消息头

SDK内部使用, NlsEvent帧的事件消息头不合法。

-250

CancelledExitStatus

已调用cancel

暂未使用。

-251

InvalidWorkStatus

无效的工作状态

SDK内部使用, 当前请求内部状态不合法。

-252

InvalidNodeQueue

workThread中NodeQueue无效

SDK内部使用, 当前待运行的请求不合法,建议释放当前请求重新尝试。

-300

InvalidRequestParams

请求的入参无效

sendAudio传入的数据为空。

-301

RequestEmpty

请求是空指针

SDK内部使用, 当前请求已经释放,建议释放当前请求重新尝试。

-302

InvalidRequest

无效的请求

SDK内部使用, 当前请求已经释放,建议释放当前请求重新尝试。

-303

SetParamsEmpty

设置传入的参数为空

请检查传入的参数是否为空。

-350

GetHttpHeaderFailed

获得http头失败

SDK内部使用, 根据日志中反馈信息详细定位。

-351

HttpGotBadStatus

http错误的状态

SDK内部使用, 根据日志中反馈信息详细定位。

-352

WsResponsePackageFailed

解析websocket返回包失败

SDK内部使用, 根据日志中反馈信息详细定位。

-353

WsResponsePackageEmpty

解析websocket返回包为空

SDK内部使用, 根据日志中反馈信息详细定位。

-354

WsRequestPackageEmpty

websocket请求包为空

SDK内部使用, 根据日志中反馈信息详细定位。

-355

UnknownWsFrameHeadType

未知websocket帧头类型

SDK内部使用, 根据日志中反馈信息详细定位。

-356

InvalidWsFrameHeaderSize

无效的websocket帧头大小

SDK内部使用, 根据日志中反馈信息详细定位。

-357

InvalidWsFrameHeaderBody

无效的websocket帧头本体

SDK内部使用, 根据日志中反馈信息详细定位。

-358

InvalidWsFrameBody

无效的websocket帧本体

SDK内部使用, 根据日志中反馈信息详细定位。

-359

WsFrameBodyEmpty

帧数据为空, 常见为收到了脏数据

SDK内部使用, 根据日志中反馈信息详细定位。

-400

NodeEmpty

node为空指针

建议释放当前请求重新尝试。

-401

InvaildNodeStatus

node所处状态无效

SDK内部使用, 建议释放当前请求重新尝试。

-402

GetAddrinfoFailed

通过DNS解析地址识别

SDK内部使用, 请检查当前环境的DNS是否可用。

-403

ConnectFailed

联网失败

请检查当前网络环境是否可用。

-404

InvalidDnsSource

当前设备无DNS

SDK内部使用, 请检查当前环境的DNS是否可用。

-405

ParseUrlFailed

无效URL

请检查设置的URL是否有效。

-406

SslHandshakeFailed

SSL握手失败

SDK内部使用, 请检查当前网络环境是否可用,并再次尝试。

-407

SslCtxEmpty

SSL_CTX未空

SDK内部使用, 请检查当前网络环境是否可用,并再次尝试。

-408

SslNewFailed

SSL_new失败

SDK内部使用, 请检查当前网络环境是否可用,并再次尝试。

-409

SslSetFailed

SSL设置参数失败

SDK内部使用, 请检查当前网络环境是否可用,并再次尝试。

-410

SslConnectFailed

SSL_connect失败

SDK内部使用, 请检查当前网络环境是否可用,并再次尝试。

-411

SslWriteFailed

SSL发送数据失败

SDK内部使用, 请检查当前网络环境是否可用,并再次尝试。

-412

SslReadSysError

SSL接收数据收到SYSCALL错误

SDK内部使用, 请检查当前网络环境是否可用,并再次尝试。

-413

SslReadFailed

SSL接收数据失败

SDK内部使用, 请检查当前网络环境是否可用,并再次尝试。

-414

SocketFailed

创建socket失败

SDK内部使用, 请检查当前网络环境是否可用,并再次尝试。

-415

SetSocketoptFailed

设置socket参数失败

SDK内部使用, 请检查当前网络环境是否可用,并再次尝试。

-416

SocketConnectFailed

进行socket链接失败

SDK内部使用, 请检查当前网络环境是否可用,并再次尝试。

-417

SocketWriteFailed

socket发送数据失败

SDK内部使用, 请检查当前网络环境是否可用,并再次尝试。

-418

SocketReadFailed

socket接收数据失败

SDK内部使用, 请检查当前网络环境是否可用,并再次尝试。

-430

NlsReceiveFailed

NLS接收帧数据失败

SDK内部使用, 请检查当前网络环境是否可用,并再次尝试。

-431

NlsReceiveEmpty

NLS接收帧数据为空

SDK内部使用, 请检查当前网络环境是否可用,并再次尝试。

-432

ReadFailed

接收数据失败

SDK内部使用, 请检查当前网络环境是否可用,并再次尝试。

-433

NlsSendFailed

NLS发送数据失败

SDK内部使用, 请检查当前网络环境是否可用,并再次尝试。

-434

NewOutputBufferFailed

创建buffer失败

SDK内部使用, 请检查内存是否充足。

-435

NlsEncodingFailed

音频编码失败

SDK内部使用, 建议释放当前请求重新尝试。

-436

EventEmpty

event为空

SDK内部使用, 建议释放当前请求重新尝试。

-437

EvbufferTooMuch

evbuffer中数据太多

SDK内部使用, 发送数据缓存已满(16K音频最大缓存320000,8K音频最大缓存160000),请检查是否发送音频数据过频或一次发送过多数据。

-438

EvutilSocketFailed

evutil设置参数失败

SDK内部使用, 建议释放当前请求重新尝试。

-439

InvalidExitStatus

无效的退出状态

请检查是否已经cancel了当前请求。

-450

InvalidAkId

阿里云账号ak id无效

请检查阿里云账号ak id是否为空。

-451

InvalidAkSecret

阿里云账号ak secret无效

请检查阿里云账号ak secret是否为空。

-452

InvalidAppKey

项目appKey无效

请检查阿里云项目appKey是否为空。

-453

InvalidDomain

domain无效

请检查输入的domain是否为空。

-454

InvalidAction

action无效

请检查输入的action是否为空。

-455

InvalidServerVersion

ServerVersion无效

请检查输入的ServerVersion是否为空。

-456

InvalidServerResource

ServerResource无效

请检查输入的ServerResource是否为空。

-457

InvalidRegionId

RegionId无效

请检查输入的RegionId是否为空。

-500

InvalidFileLink

无效的录音文件链接

录音文件转写文件链接为空。

-501

ErrorStatusCode

错误的状态码

录音文件转写返回错误,详见错误码。

-502

IconvOpenFailed

申请转换描述失败

UTF8与GBK转换失败。

-503

IconvFailed

编码转换失败

UTF8与GBK转换失败。

-504

ClientRequestFaild

账号客户端请求失败

录音文件转写返回失败。

-999

NlsMaxErrorCode

其他状态码

状态消息

原因

解决方案

10000001

NewSslCtxFailed

SSL: couldn't create a context!

建议重新初始化。

10000002

DefaultErrorCode

return of SSL_read: error:00000000:lib(0):func(0):reason(0)

建议重新尝试。

return of SSL_read: error:140E0197:SSL routines:SSL_shutdown:shutdown while in init

10000003

SysErrorCode

系统错误。

根据系统反馈的错误信息进行处理。

10000004

EmptyUrl

URL: The url is empty.

传入的URL为空, 请重新填写正确URL。

10000005

InvalidWsUrl

Could not parse WebSocket url:

传入的URL格式错误, 请重新填写正确URL。

10000007

JsonStringParseFailed

JSON: Json parse failed.

JSON格式异常, 请通过日志查看具体的错误点。

10000008

UnknownWsHeadType

WEBSOCKET: unkown head type.

联网失败,请检查本机DNS解析和URL是否有效。

10000009

HttpConnectFailed

HTTP: connect failed.

与云端连接失败,请检查网络后,重试。

10000010

MemNotEnough

内存不足。

请检查内存是否充足。

10000015

SysConnectFailed

connect failed.

联网失败,请检查本机DNS解析和URL是否有效。

10000100

HttpGotBadStatusWith403

Got bad status host=xxxxx line=HTTP/1.1 403 Forbidden

链接被拒,请检查账号特别是Token是否过期。

10000101

EvSendTimeout

Send timeout. socket error:

libevent发送event超时,请检查回调中是否有耗时任务,或并发过大导致无法及时处理事件。

10000102

EvRecvTimeout

Recv timeout. socket error:

libevent接收event超时,请检查回调中是否有耗时任务,或并发过大导致无法及时处理事件。

10000103

EvUnknownEvent

Unknown event:

未知的libevent事件,建议重新尝试。

10000104

OpNowInProgress

Operation now in progress

链接正在进行中,建议重新尝试。

10000105

BrokenPipe

Broken pipe

pipe处理不过来,建议重新尝试。

10000110

TokenHasExpired

Gateway:ACCESS_DENIED:The token 'xxx' has expired!

请更新Token。

10000111

TokenIsInvalid

Meta:ACCESS_DENIED:The token 'xxx' is invalid!

请检查Token的有效性。

10000112

NoPrivilegeToVoice

Gateway:ACCESS_DENIED:No privilege to this voice! (voice: zhinan, privilege: 0)

此发音人无权使用。

10000113

MissAuthHeader

Gateway:ACCESS_DENIED:Missing authorization header!

请检查账号是否有权限,或并发是否在限度内。

10000120

Utf8ConvertError

utf8ToGbk failed

utf8转码失败,常为系统问题,建议重新尝试。

20000000

SuccessStatusCode

成功

服务端响应状态码

关于服务状态码,请参见服务状态码

代码示例

  • 示例中使用的音频文件为16000 Hz采样率,管控台设置的模型为通用模型。如果使用其他音频,请设置为支持该音频场景的模型。关于模型设置,请参见管理项目

  • 示例中使用了SDK内置的默认实时语音识别服务的外网访问服务URL,如果您使用阿里云上海ECS且需要使用内网访问URL,则在创建SpeechTranscriberRequest的对象中设置内网访问的URL。

    SpeechTranscriberRequest stPtr;
    stPtr.SetUrl(stPtr, "ws://nls-gateway.cn-shanghai-internal.aliyuncs.com/ws/v1")
  • 以下为简要示例,完整示例请参见SDK压缩包中demo目录的nlsCsharpSdkDemo.cs文件。

    using System;
    using System.IO;
    using System.Threading;
    using System.Windows.Forms;
    using nlsCsharpSdk;
    
    namespace nlsCsharpSdkDemo
    {
        public partial class nlsCsharpSdkDemo : Form
     {
            private NlsClient nlsClient;
            private SpeechTranscriberRequest stPtr;
            private NlsToken tokenPtr;
            private UInt64 expireTime;
    
            private string appKey;
            private string akId;
            private string akSecret;
            private string token;
            private string url;
    
            static bool running;
    
            static bool st_send_audio_flag = false;
            static bool st_audio_loop_flag = false;
            static Thread st_send_audio;
            static string cur_st_result;
            static string cur_st_completed;
            static string cur_st_closed;
    
            private void FlushLab()
            {
                while (running)
                {
                    if (cur_st_result != null && cur_st_result.Length > 0)
                    {
                        stResult.Text = cur_st_result;
                    }
                    if (cur_st_completed != null && cur_st_completed.Length > 0)
                    {
                        stCompleted.Text = cur_st_completed;
                    }
                    if (cur_st_closed != null && cur_st_closed.Length > 0)
                    {
                        stClosed.Text = cur_st_closed;
                    }
    
                    Thread.Sleep(200);
                }
            }
    
            /// <summary>
            /// 实时识别的音频推送线程.
            /// </summary>
            private void STAudioLab()
            {
                string file_name = System.Environment.CurrentDirectory + @"\audio_files\test3.wav";
                System.Diagnostics.Debug.WriteLine("st audio file_name = {0}", file_name);
                FileStream fs = new FileStream(file_name, FileMode.Open, FileAccess.Read);
                BinaryReader br = new BinaryReader(fs);
    
                while (st_audio_loop_flag)
                {
                    if (st_send_audio_flag)
                    {
                        byte[] byData = br.ReadBytes((int)3200);
                        if (byData.Length > 0)
                        {
                            // 音频数据以PCM格式,byData.Length字节进行推送。
                            stPtr.SendAudio(stPtr, byData, (UInt64)byData.Length, EncoderType.ENCODER_PCM);
                        }
                        else
                        {
                            /*
                             * 音频推送完成,重新打开循环继续
                             */
                            br.Close();
                            fs.Dispose();
                            fs = new FileStream(file_name, FileMode.Open, FileAccess.Read);
                            br = new BinaryReader(fs);
                        }
                    }
                    /*
                     * 上面推送3200字节音频数据,相当于模拟100MS的音频。
                     * 真实环境从录音获得音频数据,不需要调用此Sleep。
                     */
                    Thread.Sleep(100);
                }
                br.Close();
                fs.Dispose();
            }
    
            public nlsCsharpSdkDemo()
            {
                InitializeComponent();
                System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;
    
                nlsClient = new NlsClient();
            }
    
            private void button1_Click(object sender, EventArgs e)
            {
                // 开启日志系统,以Debug级别将名字为nlsLog.log的日志以单个400MB 10个日志文件循环的形式储存。
                int ret = nlsClient.SetLogConfig("nlsLog", LogLevel.LogDebug, 400, 10);
                if (ret == 0)
                    nlsResult.Text = "OpenLog Success";
                else
                    nlsResult.Text = "OpenLog Failed";
            }
    
            private void button1_Click_1(object sender, EventArgs e)
            {
                // 获取当前SDK的版本号
                string version = nlsClient.GetVersion();
                nlsResult.Text = version;
            }
    
            private void button1_Click_2(object sender, EventArgs e)
            {            
                // -1则表示启动CPU核数的事件池数量,用于进行内部事件的处理。
                // 单路调用的情况下,建议入参为1,即启动单个事件池进行处理。
                // 高并发(几百路)的情况下,入参建议4 ~ CPU核数,入参越大,处理延迟越低但是CPU占用越高。
                nlsClient.StartWorkThread(1);
                nlsResult.Text = "StartWorkThread and init NLS success.";
                running = true;
                Thread t = new Thread(FlushLab);
                t.Start();
            }
    
            private void button1_Click_3(object sender, EventArgs e)
            {
                // 释放NlsClient,与StartWorkThread互为反操作
                nlsClient.ReleaseInstance();
                nlsResult.Text = "Release NLS success.";
            }
    
            #region Info
            private void textBox1_TextChanged(object sender, EventArgs e)
            {
                akId = tAkId.Text;
            }
    
            private void tAppKey_TextChanged(object sender, EventArgs e)
            {
                appKey = tAppKey.Text;
            }
    
            private void tAkSecret_TextChanged(object sender, EventArgs e)
            {
                akSecret = tAkSecret.Text;
            }
    
            private void tToken_TextChanged(object sender, EventArgs e)
            {
                token = tToken.Text;
            }
    
            private void tUrl_TextChanged(object sender, EventArgs e)
            {
                url = tUrl.Text;
            }
            #endregion
    
            #region TokenButton
            // create token
            private void button3_Click_1(object sender, EventArgs e)
            {
                int ret = 0;
                tokenPtr = nlsClient.CreateNlsToken();
                if (tokenPtr.native_token != IntPtr.Zero)
                {
                    if (akId != null && akSecret != null & akId.Length > 0 && akSecret.Length > 0)
                    {
                        tokenPtr.SetAccessKeyId(tokenPtr, akId);
                        tokenPtr.SetKeySecret(tokenPtr, akSecret);
    
                        ret = tokenPtr.ApplyNlsToken(tokenPtr);
                        if (ret < 0)
                        {
                            System.Diagnostics.Debug.WriteLine("ApplyNlsToken failed");
                            nlsResult.Text = tokenPtr.GetErrorMsg(tokenPtr);
                        }
                        else
                        {
                            System.Diagnostics.Debug.WriteLine("ApplyNlsToken success");
                            token = tokenPtr.GetToken(tokenPtr);
                            tToken.Text = token;
                            expireTime = tokenPtr.GetExpireTime(tokenPtr);
                            nlsResult.Text = "ExpireTime:" + expireTime.ToString();
                        }
                    }
                    else
                    {
                        nlsResult.Text = "CreateToken Failed, akId or Secret is null";
                    }
                }
                else
                {
                    nlsResult.Text = "CreateToken Failed";
                }
            }
    
            // release token
            private void button4_Click(object sender, EventArgs e)
            {
                if (tokenPtr.native_token != IntPtr.Zero)
                {
                    nlsClient.ReleaseNlsToken(tokenPtr);
                    tokenPtr.native_token = IntPtr.Zero;
                    nlsResult.Text = "ReleaseNlsToken Success";
                }
                else
                {
                    nlsResult.Text = "ReleaseNlsToken is nullptr";
                }
            }
            #endregion
    
            #region TranscriberCallback
            private CallbackDelegate DemoOnTranscriptionStarted =
                (ref NLS_EVENT_STRUCT e, ref string uuid) =>
            {
                System.Diagnostics.Debug.WriteLine("DemoOnTranscriptionStarted user uuid = {0}", uuid);
                string msg = System.Text.Encoding.Default.GetString(e.msg).TrimEnd('\0');
                System.Diagnostics.Debug.WriteLine("DemoOnTranscriptionStarted msg = {0}", msg);
                cur_st_completed = "msg : " + msg;
    
                st_send_audio_flag = true;
            };
            private CallbackDelegate DemoOnTranscriptionClosed =
                (ref NLS_EVENT_STRUCT e, ref string uuid) =>
            {
                string msg = System.Text.Encoding.Default.GetString(e.msg).TrimEnd('\0');
                System.Diagnostics.Debug.WriteLine("DemoOnTranscriptionClosed = {0}", msg);
                cur_st_closed = "msg : " + msg;
            };
            private CallbackDelegate DemoOnTranscriptionTaskFailed =
                (ref NLS_EVENT_STRUCT e, ref string uuid) =>
            {
                System.Diagnostics.Debug.WriteLine("DemoOnTranscriptionStarted user uuid = {0}", uuid);
                string msg = System.Text.Encoding.Default.GetString(e.msg).TrimEnd('\0');
                System.Diagnostics.Debug.WriteLine("DemoOnTranscriptionTaskFailed = {0}", msg);
                cur_st_completed = "msg : " + msg;
                st_send_audio_flag = false;
                st_audio_loop_flag = false;
            };
            private CallbackDelegate DemoOnTranscriptionResultChanged =
                (ref NLS_EVENT_STRUCT e, ref string uuid) =>
            {
                System.Diagnostics.Debug.WriteLine("DemoOnTranscriptionStarted user uuid = {0}", uuid);
                // 若识别为非中文,请设置UTF-8格式,并用UTF-8解码,否则可能乱码。
                // string result = System.Text.Encoding.UTF8.GetString(e.result).TrimEnd('\0');
                string result = System.Text.Encoding.Default.GetString(e.result).TrimEnd('\0');
                System.Diagnostics.Debug.WriteLine("DemoOnTranscriptionResultChanged = {0}", result);
                cur_st_result = "middle result : " + result;
            };
            private CallbackDelegate DemoOnSentenceBegin =
                (ref NLS_EVENT_STRUCT e, ref string uuid) =>
            {
                System.Diagnostics.Debug.WriteLine("DemoOnSentenceBegin user uuid = {0}", uuid);
                // 若识别为非中文,请设置UTF-8格式,并用UTF-8解码,否则可能乱码。
                // string msg = System.Text.Encoding.UTF8.GetString(e.msg).TrimEnd('\0');
                string msg = System.Text.Encoding.Default.GetString(e.msg).TrimEnd('\0');
                System.Diagnostics.Debug.WriteLine("DemoOnSentenceBegin msg = {0}", msg);
                cur_st_completed = "sentenceBegin : " + msg;
            };
            private CallbackDelegate DemoOnSentenceEnd =
                (ref NLS_EVENT_STRUCT e, ref string uuid) =>
            {
                System.Diagnostics.Debug.WriteLine("DemoOnSentenceEnd user uuid = {0}", uuid);
                // 若识别为非中文,请设置UTF-8格式,并用UTF-8解码,否则可能乱码。
                // string msg = System.Text.Encoding.UTF8.GetString(e.msg).TrimEnd('\0');
                string msg = System.Text.Encoding.Default.GetString(e.msg).TrimEnd('\0');
                System.Diagnostics.Debug.WriteLine("DemoOnSentenceEnd msg = {0}", msg);
                cur_st_completed = "sentenceEnd : " + msg;
            };
            #endregion
    
            // create transcriber
            private void button1_Click_4(object sender, EventArgs e)
            {
                stPtr = nlsClient.CreateTranscriberRequest();
                if (stPtr.native_request != IntPtr.Zero)
                {
                    nlsResult.Text = "CreateTranscriberRequest Success";
                }
                else
                {
                    nlsResult.Text = "CreateTranscriberRequest Failed";
                }
                cur_st_result = "null";
                cur_st_closed = "null";
                cur_st_completed = "null";
            }
    
            // start transcriber
            private void button3_Click(object sender, EventArgs e)
            {
                int ret = -1;
                if (stPtr.native_request != IntPtr.Zero)
                {
                    stPtr.SetAppKey(stPtr, appKey);
                    stPtr.SetToken(stPtr, token);
                    stPtr.SetUrl(stPtr, url);
                    stPtr.SetFormat(stPtr, "pcm");
                    stPtr.SetSampleRate(stPtr, 16000);
                    stPtr.SetIntermediateResult(stPtr, true);
                    stPtr.SetPunctuationPrediction(stPtr, true);
                    stPtr.SetInverseTextNormalization(stPtr, true);
    
                    string uuid = System.Guid.NewGuid().ToString("N");
    
                    stPtr.SetOnTranscriptionStarted(stPtr, DemoOnTranscriptionStarted, uuid);
                    stPtr.SetOnChannelClosed(stPtr, DemoOnTranscriptionClosed, uuid);
                    stPtr.SetOnTaskFailed(stPtr, DemoOnTranscriptionTaskFailed, uuid);
                    stPtr.SetOnSentenceEnd(stPtr, DemoOnSentenceEnd, uuid);
                    stPtr.SetOnTranscriptionResultChanged(stPtr, DemoOnTranscriptionResultChanged, uuid);
    
                    ret = stPtr.Start(stPtr);
    
                    if (st_audio_loop_flag == false)
                    {
                        st_audio_loop_flag = true;
                        st_send_audio = new Thread(STAudioLab);
                        st_send_audio.Start();
                    }
                }
    
                if (ret != 0)
                {
                    nlsResult.Text = "Transcriber Start failed";
                }
                else
                {
                    nlsResult.Text = "Transcriber Start success";
                }
            }
    
         	// stop transcriber
            private void btnSTstop_Click(object sender, EventArgs e)
            {
                int ret = -1;
                if (stPtr.native_request != IntPtr.Zero)
                    ret = stPtr.Stop(stPtr);
    
                st_send_audio_flag = false;
                st_audio_loop_flag = false;
                if (ret != 0)
                {
                    nlsResult.Text = "Transcriber Stop failed";
                }
                else
                {
                    nlsResult.Text = "Transcriber Stop success";
                }
            }
    
         	// release transcriber
            private void button2_Click(object sender, EventArgs e)
            {
                if (stPtr.native_request != IntPtr.Zero)
                {
                    nlsClient.ReleaseTranscriberRequest(stPtr);
                    stPtr.native_request = IntPtr.Zero;
                    nlsResult.Text = "ReleaseTranscriberRequest Success";
                }
                else
                {
                    nlsResult.Text = "TranscriberRequest is nullptr";
                }
                cur_st_result = "null";
                cur_st_closed = "null";
                cur_st_completed = "null";
            }
    	}
    }