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<version>-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

      获取SDK版本号。

      CreateNlsToken

      3.1.9

      创建Token获取对象,用于获取阿里云token,详见下方NlsToken相关表格。

      ReleaseNlsToken

      3.1.9

      销毁CreateNlsToken所建立的NlsToken对象。

      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获取对象。

      CreateRecognizerRequest

      3.1.9

      创建一句话识别对象,线程安全,支持高并发请求。

      ReleaseRecognizerRequest

      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回调的事件信息。

      result

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

  • 识别接口

    SpeechRecognizerRequest:一句话识别请求对象,用于短语音识别。

    接口名

    功能描述

    SetOnTaskFailed

    设置错误回调函数。

    SetOnRecognitionStarted

    设置一句话识别开始回调函数。

    SetOnRecognitionResultChanged

    设置一句话识别中间结果回调函数。

    SetOnRecognitionCompleted

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

    SetOnChannelClosed

    设置通道关闭回调函数。

    SetAppKey

    设置Appkey。

    SetToken

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

    SetUrl

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

    SetIntermediateResult

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

    SetPunctuationPrediction

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

    SetInverseTextNormalization

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

    SetEnableVoiceDetection

    设置是否启动自定义静音检测。

    SetMaxStartSilence

    允许的最大开始静音,可选,单位为毫秒。超出后服务端将会发送RecognitionCompleted事件,开始本次识别。需要先设置SetEnableVoiceDetection为true. 建议设置为2000~5000。

    SetMaxEndSilence

    允许的最大结束静音,可选,单位是毫秒。超出后服务端将会发送RecognitionCompleted事件,结束本次识别。需要先设置SetEnableVoiceDetection为true. 建议时间0~5000。

    SetFormat

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

    SetSampleRate

    音频采样率设置。

    SetCustomizationId

    设置定制模型,例如("TestId_123")

    SetVocabularyId

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

    SetTimeout

    设置Socket接收超时时间。

    SetOutputFormat

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

    SetPayloadParam

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

    SetContextParam

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

    AppendHttpHeaderParam

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

    Start

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

    Stop

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

    Cancel

    不会与服务端确认关闭,直接关闭链接。

    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文件大小参数。

-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

系统错误。

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

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

成功

服务端响应状态码

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

代码示例​

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

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

      SpeechRecognizerRequest srPtr;
      srPtr.SetUrl(srPtr, "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 SpeechRecognizerRequest srPtr;
            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 string cur_nls_result;
    
            static bool sr_send_audio_flag = false;
            static bool sr_audio_loop_flag = false;
            static Thread sr_send_audio;
            static string cur_sr_result;
            static string cur_sr_completed;
            static string cur_sr_closed;
    
            private void FlushLab()
            {
                while (running)
                {
                    if (cur_sr_result != null && cur_sr_result.Length > 0)
                    {
                        srResult.Text = cur_sr_result;
                    }
                    if (cur_sr_completed != null && cur_sr_completed.Length > 0)
                    {
                        srCompleted.Text = cur_sr_completed;
                    }
                    if (cur_sr_closed != null && cur_sr_closed.Length > 0)
                    {
                        srClosed.Text = cur_sr_closed;
                    }
    
                    Thread.Sleep(200);
                }
            }
    
            private void SRAudioLab()
            {
                string file_name = System.Environment.CurrentDirectory + @"\audio_files\test3.wav";
                System.Diagnostics.Debug.WriteLine("sr audio file_name = {0}", file_name);
                FileStream fs = new FileStream(file_name, FileMode.Open, FileAccess.Read);
                BinaryReader br = new BinaryReader(fs);
    
                while (sr_audio_loop_flag)
                {
                    if (sr_send_audio_flag)
                    {
                        byte[] byData = br.ReadBytes((int)3200);
                        if (byData.Length > 0)
                        {
                            // 音频数据以PCM格式,byData.Length字节进行推送。
                            srPtr.SendAudio(srPtr, 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 RecognizerCallback
            private CallbackDelegate DemoOnRecognitionStarted =
                (ref NLS_EVENT_STRUCT e, ref string uuid) =>
            {
                System.Diagnostics.Debug.WriteLine("DemoOnRecognitionStarted user uuid = {0}", uuid);
                string msg = System.Text.Encoding.Default.GetString(e.msg).TrimEnd('\0');
                System.Diagnostics.Debug.WriteLine("DemoOnRecognitionStarted msg = {0}", msg);
                cur_sr_completed = "msg : " + msg;
    
                sr_send_audio_flag = true;
            };
            private CallbackDelegate DemoOnRecognitionClosed =
                (ref NLS_EVENT_STRUCT e, ref string uuid) =>
            {
                string msg = System.Text.Encoding.Default.GetString(e.msg).TrimEnd('\0');
                System.Diagnostics.Debug.WriteLine("DemoOnRecognitionClosed = {0}", msg);
                cur_sr_closed = "msg : " + msg;
            };
            private CallbackDelegate DemoOnRecognitionTaskFailed =
                (ref NLS_EVENT_STRUCT e, ref string uuid) =>
            {
                System.Diagnostics.Debug.WriteLine("DemoOnRecognitionTaskFailed user uuid = {0}", uuid);
                string msg = System.Text.Encoding.Default.GetString(e.msg).TrimEnd('\0');
                System.Diagnostics.Debug.WriteLine("DemoOnRecognitionTaskFailed = {0}", msg);
                cur_sr_completed = "msg : " + msg;
    
                sr_send_audio_flag = false;
                sr_audio_loop_flag = false;
            };
            private CallbackDelegate DemoOnRecognitionResultChanged =
                (ref NLS_EVENT_STRUCT e, ref string uuid) =>
            {
                System.Diagnostics.Debug.WriteLine("DemoOnRecognitionResultChanged 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');
                cur_sr_result = "middle result : " + result;
            };
            private CallbackDelegate DemoOnRecognitionCompleted =
                (ref NLS_EVENT_STRUCT e, ref string uuid) =>
            {
                System.Diagnostics.Debug.WriteLine("DemoOnRecognitionCompleted 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');
                cur_sr_completed = "final result : " + result;
            };
            #endregion
    
                // create recognizer
                private void button8_Click(object sender, EventArgs e)
            {
                srPtr = nlsClient.CreateRecognizerRequest();
                if (srPtr.native_request != IntPtr.Zero)
                {
                    nlsResult.Text = "CreateRecognizerRequest Success";
                }
                else
                {
                    nlsResult.Text = "CreateRecognizerRequest Failed";
                }
                cur_sr_result = "null";
                cur_sr_closed = "null";
                cur_sr_completed = "null";
            }
    
            // start recognizer
            private void button6_Click(object sender, EventArgs e)
            {
                int ret = -1;
                if (srPtr.native_request != IntPtr.Zero)
                {
                    srPtr.SetAppKey(srPtr, appKey);
                    srPtr.SetToken(srPtr, token);
                    srPtr.SetUrl(srPtr, url);
                    srPtr.SetFormat(srPtr, "pcm");
                    srPtr.SetSampleRate(srPtr, 16000);
                    srPtr.SetIntermediateResult(srPtr, true);
                    srPtr.SetPunctuationPrediction(srPtr, true);
                    srPtr.SetInverseTextNormalization(srPtr, true);
    
                    string uuid = System.Guid.NewGuid().ToString("N");
    
                    srPtr.SetOnRecognitionStarted(srPtr, DemoOnRecognitionStarted, uuid);
                    srPtr.SetOnChannelClosed(srPtr, DemoOnRecognitionClosed, uuid);
                    srPtr.SetOnTaskFailed(srPtr, DemoOnRecognitionTaskFailed, uuid);
                    srPtr.SetOnRecognitionResultChanged(srPtr, DemoOnRecognitionResultChanged, uuid);
                    srPtr.SetOnRecognitionCompleted(srPtr, DemoOnRecognitionCompleted, uuid);
    
                    ret = srPtr.Start(srPtr);
    
                    if (sr_audio_loop_flag == false)
                    {
                        sr_audio_loop_flag = true;
                        sr_send_audio = new Thread(SRAudioLab);
                        sr_send_audio.Start();
                    }
                }
    
                if (ret != 0)
                {
                    nlsResult.Text = "Recognizer Start failed";
                }
                else
                {
                    nlsResult.Text = "Recognizer Start success";
                }
            }
    
         // stop recognizer
         private void button5_Click(object sender, EventArgs e)
         {
             int ret = -1;
             if (srPtr.native_request != IntPtr.Zero)
                 ret = srPtr.Stop(srPtr);
    
             sr_send_audio_flag = false;
             sr_audio_loop_flag = false;
             if (ret != 0)
             {
                 nlsResult.Text = "Recognizer Stop failed";
             }
             else
             {
                 nlsResult.Text = "Recognizer Stop success";
             }
         }
    
         // release recognizer
         private void button7_Click(object sender, EventArgs e)
         {
             if (srPtr.native_request != IntPtr.Zero)
             {
                 nlsClient.ReleaseRecognizerRequest(srPtr);
                 srPtr.native_request = IntPtr.Zero;
                 nlsResult.Text = "ReleaseRecognizerRequest Success";
             }
             else
             {
                 nlsResult.Text = "ReleaseRecognizerRequest is nullptr";
             }
             cur_sr_result = "null";
             cur_sr_closed = "null";
             cur_sr_completed = "null";
         }
     }
    }