C++ SDK

更新时间:
复制 MD 格式

使用 C++ SDK 发送音频流,获取中间识别结果和句子级最终结果。本文的 Linux x86_64 示例使用 3.3.0b。

快速接入

准备 Linux 编译环境,获取 SDK,配置访问凭证,然后运行完整示例。请求参数和返回事件的详细定义,请参见实时语音识别接口说明

原 API 2.0 已下线,与本文使用的 API 3.1 定义不同。

准备环境

Linux 编译工具最低版本要求:CMake 3.0、glibc 2.5、GCC 4.8.5。

本示例使用 Linux x86_64、GCC 10.2.1 和 glibc 2.32。

获取和安装 SDK

源码仓库

从 SDK 的 GitHub 仓库获取源码。仓库默认分支可能与下载包版本不同,编译方法以对应版本的 readme.md 为准。

git clone --depth 1 https://github.com/aliyun/alibabacloud-nls-cpp-sdk

下载包

源码包需要编译后生成库文件;平台预编译包包含库文件和头文件。

SDK 包

平台

MD5

alibabacloud-nls-cpp-sdk3.3.0b-master_cbcac53.zip

SDK源码

7257c0998654e611cf2e8ca9867670ef

NlsCppSdk_Linux-x86_64_3.3.0b_cbcac53.tar.gz

Linux x86_64

9a93df607f26f1558bc1043a425af6d1

NlsCppSdk_Linux-aarch64_3.1.15_fa30fba.tar.gz

Linux aarch64

76c34a3ab397d7285963a139b9270ff4

alibabacloud-nls-cpp-sdk<version>-master_<github commit id>.zip 是源码包;NlsCppSdk_<平台>_<版本号>_<github commit id>.tar.gz 是平台预编译包,使用方法见包内 readme.md。

编译与集成

源码编译

在源码根目录执行以下命令,生成 SDK 库文件及 srDemo(一句话识别)、stDemo(实时语音识别)、syDemo(语音合成)、daDemo(语音对话)等示例程序。

./scripts/build_linux.sh

生成的头文件和库位于 build/install/NlsSdk3.X_LINUX。在 build/demo 目录运行 ./stDemo 可查看包内示例的使用方式。

预编译包

解压与 CPU 架构匹配的 SDK 包,再选择所需的 Debug 或 Release 包。使用与应用一致的 C++ ABI;CXX11 包对应 _GLIBCXX_USE_CXX11_ABI=1

重要

3.3.0b 的 Linux x86_64 Release CXX11 包内,demo/build_linux_demo.sh 仍指定 ABI=0。使用此包时,将示例编译参数调整为 ABI=1,避免链接错误。

配置访问凭证

运行示例前,为当前进程设置以下环境变量。AppKey 和 AccessKey 必须属于实际使用的服务账号。不要在代码或日志中写入凭证明文。

环境变量

说明

NLS_APPKEY_ENV

项目的 AppKey。

NLS_AK_ENV

AccessKey ID。

NLS_SK_ENV

AccessKey Secret。

export NLS_APPKEY_ENV='<appkey>'
export NLS_AK_ENV='<accesskey-id>'
export NLS_SK_ENV='<accesskey-secret>'

在 IDE 中运行时,也需在运行配置中设置这些环境变量。示例通过 NlsToken 获取访问令牌,缓存令牌及过期时间,并在临近过期时更新;多个并发请求可共用有效令牌。

代码示例

以下完整示例包含客户端初始化、Token 获取、异步事件处理、音频发送及资源释放。使用 SDK 包中的 test0.wav,并将文件放在程序工作目录;并发示例还使用 test1.wav

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

示例等待启动事件后发送音频,收到失败事件后停止发送,并将失败状态传递为非零进程退出码。正常完成时,依次收到 TranscriptionCompleted 和通道关闭事件。

#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
#include <ctime>
#include <string>
#include <iostream>
#include <vector>
#include <fstream>
#include <sys/time.h>
#include <errno.h>
#include <atomic>
#include "nlsClient.h"
#include "nlsEvent.h"
#include "nlsToken.h"
#include "speechTranscriberRequest.h"

#define FRAME_SIZE 3200
#define SAMPLE_RATE 16000

using namespace AlibabaNlsCommon; 
using AlibabaNls::NlsClient; 
using AlibabaNls::NlsEvent; 
using AlibabaNls::LogDebug; 
using AlibabaNls::LogInfo; 
using AlibabaNls::LogError;
using AlibabaNls::SpeechTranscriberRequest;

struct ParamStruct {
  std::string fileName;
  std::string appkey; 
  std::string token;
  int result = -1; 
};

struct ParamCallBack {
 public:
  ParamCallBack() {
    pthread_mutex_init(&mtxWord, NULL);
    pthread_cond_init(&cvWord, NULL);
  };
  ~ParamCallBack() {
    pthread_mutex_destroy(&mtxWord);
    pthread_cond_destroy(&cvWord);
  };

  std::atomic<bool> started{false}, closed{false}, failed{false}, completed{false};
  int userId;
  char userInfo[8];
  pthread_mutex_t mtxWord;
  pthread_cond_t cvWord;
};

std::string g_akId = "";
std::string g_akSecret = "";
std::string g_token = "";
long g_expireTime = -1;
int g_sync_timeout = 0;
struct timeval tv;
struct timeval tv1;

int generateToken(std::string akId, std::string akSecret,
                  std::string* token, long* expireTime) {
  NlsToken nlsTokenRequest;
  nlsTokenRequest.setAccessKeyId(akId); 
  nlsTokenRequest.setKeySecret(akSecret); 

  int ret = nlsTokenRequest.applyNlsToken();
  if (ret < 0) {

    printf("generateToken Failed, error code:%d msg:%s\n",
        ret, nlsTokenRequest.getErrorMsg());
    return ret;
  }
  *token = nlsTokenRequest.getToken();
  *expireTime = nlsTokenRequest.getExpireTime();
  return 0;
}

unsigned int getSendAudioSleepTime(int dataSize,
                                   int sampleRate,
                                   int compressRate) { 

  const int sampleBytes = 16;     
  const int soundChannel = 1;     
  int bytes = (sampleRate * sampleBytes * soundChannel) / 8;     
  int bytesMs = bytes / 1000;     
  int sleepMs = (dataSize * compressRate) / bytesMs;
  return sleepMs; 
}

void onTranscriptionStarted(NlsEvent* cbEvent, void* cbParam) {
  ParamCallBack* tmpParam = (ParamCallBack*)cbParam;

  printf("onTranscriptionStarted: %d\n", tmpParam->userId);

  printf("onTranscriptionStarted: status code=%d, task id=%s\n",
      cbEvent->getStatusCode(), cbEvent->getTaskId());

  pthread_mutex_lock(&(tmpParam->mtxWord));
  tmpParam->started = true;
  pthread_cond_signal(&(tmpParam->cvWord));
  pthread_mutex_unlock(&(tmpParam->mtxWord));
} 

void onSentenceBegin(NlsEvent* cbEvent, void* cbParam) {
  ParamCallBack* tmpParam = (ParamCallBack*)cbParam;

  printf("onSentenceBegin: %d\n", tmpParam->userId);
  printf("onSentenceBegin: status code=%d, task id=%s, index=%d, time=%d\n",
      cbEvent->getStatusCode(), cbEvent->getTaskId(),
      cbEvent->getSentenceIndex(), 
      cbEvent->getSentenceTime() 
  );

}

void onSentenceEnd(NlsEvent* cbEvent, void* cbParam) {
  ParamCallBack* tmpParam = (ParamCallBack*)cbParam;

  printf("onSentenceEnd: %d\n", tmpParam->userId);
  printf("onSentenceEnd: status code=%d, task id=%s, index=%d, time=%d, begin_time=%d, result=%s\n",
      cbEvent->getStatusCode(),
      cbEvent->getTaskId(),
      cbEvent->getSentenceIndex(), 
      cbEvent->getSentenceTime(), 
      cbEvent->getSentenceBeginTime(), 
      cbEvent->getResult()    
  );

}

void onTranscriptionResultChanged(NlsEvent* cbEvent, void* cbParam) {
  ParamCallBack* tmpParam = (ParamCallBack*)cbParam;

  printf("onTranscriptionResultChanged: %d\n", tmpParam->userId);
  printf("onTranscriptionResultChanged: status code=%d, task id=%s, index=%d, time=%d, result=%s\n",
      cbEvent->getStatusCode(),
      cbEvent->getTaskId(),
      cbEvent->getSentenceIndex(), 
      cbEvent->getSentenceTime(), 
      cbEvent->getResult()    
  );

}

void onTranscriptionCompleted(NlsEvent* cbEvent, void* cbParam) {
  ParamCallBack* tmpParam = (ParamCallBack*)cbParam;
  tmpParam->completed = true;

  printf("onTranscriptionCompleted: %d\n", tmpParam->userId);
  printf("onTranscriptionCompleted: status code=%d, task id=%s\n",
      cbEvent->getStatusCode(),
      cbEvent->getTaskId());
}

void onTaskFailed(NlsEvent* cbEvent, void* cbParam) { 
  ParamCallBack* tmpParam = (ParamCallBack*)cbParam;
  pthread_mutex_lock(&tmpParam->mtxWord);
  tmpParam->failed = true;
  pthread_cond_broadcast(&tmpParam->cvWord);
  pthread_mutex_unlock(&tmpParam->mtxWord); 

  printf("onTaskFailed: %d\n", tmpParam->userId); 
  printf("onTaskFailed: status code=%d, task id=%s, error message=%s\n", 
      cbEvent->getStatusCode(), 
      cbEvent->getTaskId(), 
      cbEvent->getErrorMessage()
  );     

}

void onChannelClosed(NlsEvent* cbEvent, void* cbParam) {     
  ParamCallBack* tmpParam = (ParamCallBack*)cbParam;     

  printf("onChannelClosed: %d, %s\n", tmpParam->userId, tmpParam->userInfo); 
  printf("onChannelClosed: response=%s\n", cbEvent->getAllResponse());

  pthread_mutex_lock(&(tmpParam->mtxWord));
  tmpParam->closed = true;
  pthread_cond_broadcast(&(tmpParam->cvWord));
  pthread_mutex_unlock(&(tmpParam->mtxWord));
}

void* pthreadFunction(void* arg) {
  int sleepMs = 0;
  int ret = 0;
  ParamCallBack *cbParam = NULL;

  cbParam = new ParamCallBack();
  cbParam->userId = rand() % 100;
  strcpy(cbParam->userInfo, "User.");

  ParamStruct *tst = (ParamStruct *) arg; 
  if (tst == NULL) {
    printf("arg is not valid\n"); 
    delete cbParam;
    return NULL;
  } 

  std::ifstream fs;
  fs.open(tst->fileName.c_str(), std::ios::binary | std::ios::in);
  if (!fs) {
    printf("Cannot open %s\n", tst->fileName.c_str());
    delete cbParam;
    return NULL;
  }
  
  SpeechTranscriberRequest* request =
      NlsClient::getInstance()->createTranscriberRequest();
  if (request == NULL) {
    printf("createTranscriberRequest failed\n");
    delete cbParam;
    return NULL;
  }

  request->setOnTranscriptionStarted(onTranscriptionStarted, cbParam);

  request->setOnTranscriptionResultChanged(onTranscriptionResultChanged, cbParam);

  request->setOnTranscriptionCompleted(onTranscriptionCompleted, cbParam);

  request->setOnSentenceBegin(onSentenceBegin, cbParam);

  request->setOnSentenceEnd(onSentenceEnd, cbParam);

  request->setOnTaskFailed(onTaskFailed, cbParam);

  request->setOnChannelClosed(onChannelClosed, cbParam);

  request->setAppKey(tst->appkey.c_str());

  request->setToken(tst->token.c_str());

  request->setUrl("wss://nls-gateway-cn-shanghai.aliyuncs.com/ws/v1");
  request->setFormat("opus");

  request->setSampleRate(SAMPLE_RATE);

  request->setIntermediateResult(true);

  request->setPunctuationPrediction(true);

  request->setInverseTextNormalization(true);

  struct timespec outtime;
  struct timeval now;
  
  ret = request->start();
  if (ret < 0) {
    printf("start() failed. may be can not connect server. please check network or firewalld\n");
    NlsClient::getInstance()->releaseTranscriberRequest(request); 
    delete cbParam;
    return NULL;
  } else {
    if (g_sync_timeout == 0) {

      printf("wait started callback.\n");

      gettimeofday(&now, NULL);
      outtime.tv_sec = now.tv_sec + 5;
      outtime.tv_nsec = now.tv_usec * 1000;
      pthread_mutex_lock(&(cbParam->mtxWord));
      while (!cbParam->started && !cbParam->failed && !cbParam->closed) {
      if (ETIMEDOUT == pthread_cond_timedwait(&(cbParam->cvWord), &(cbParam->mtxWord), &outtime)) {
        printf("start timeout.\n");
        pthread_mutex_unlock(&(cbParam->mtxWord));
        request->cancel();
        NlsClient::getInstance()->releaseTranscriberRequest(request);
        delete cbParam;
        return NULL;
      }
      }
      pthread_mutex_unlock(&(cbParam->mtxWord));
    } else {
      
    }
  }

  while (cbParam->started && !cbParam->failed && !cbParam->closed && !fs.eof()) {
    uint8_t data[FRAME_SIZE] = {0};
    fs.read((char *) data, sizeof(uint8_t) * FRAME_SIZE); 
    size_t nlen = fs.gcount();
    if (nlen <= 0) {
      continue;
    }

    ret = request->sendAudio(data, nlen, ENCODER_OPUS); 
    if (ret < 0) {

      printf("send data fail.\n");
      cbParam->failed = true; 
      break;
    } 

    sleepMs = getSendAudioSleepTime(nlen, SAMPLE_RATE, 1); 
    
    usleep(sleepMs * 1000);
  } 

  printf("sendAudio done.\n");

  fs.close();

  ret = cbParam->failed || cbParam->closed ? -1 : request->stop();
  if (ret == 0) {
      if (g_sync_timeout == 0) {

        printf("wait closed callback.\n");

        gettimeofday(&now, NULL);
        outtime.tv_sec = now.tv_sec + 5;
        outtime.tv_nsec = now.tv_usec * 1000;

        pthread_mutex_lock(&(cbParam->mtxWord));
        while (!cbParam->closed) {
        if (ETIMEDOUT == pthread_cond_timedwait(&(cbParam->cvWord), &(cbParam->mtxWord), &outtime)) {
          printf("stop timeout\n");
          pthread_mutex_unlock(&(cbParam->mtxWord));
          NlsClient::getInstance()->releaseTranscriberRequest(request);
          delete cbParam;
          return NULL;
        }
        }
        pthread_mutex_unlock(&(cbParam->mtxWord));
      } else {
        
      }
  } else {
    printf("stop ret is %d\n", ret);
    cbParam->failed = true;
    if (!cbParam->closed) request->cancel();
  }

  NlsClient::getInstance()->releaseTranscriberRequest(request);
  tst->result = (!cbParam->failed && cbParam->completed) ? 0 : -1;
  delete cbParam;
  return NULL;
}

int speechTranscriberFile(const char* appkey) {

  std::time_t curTime = std::time(0);
  if (g_expireTime - curTime < 10) {
    printf("the token will be expired, please generate new token by AccessKey-ID and AccessKey-Secret.\n");
    if (generateToken(g_akId, g_akSecret, &g_token, &g_expireTime) < 0) {
      return -1;
    } 
  }

  ParamStruct pa;
  pa.token = g_token;
  pa.appkey = appkey;
  pa.fileName = "test0.wav";

  pthread_t pthreadId;
  if (pthread_create(&pthreadId, NULL, &pthreadFunction, (void *)&pa) != 0) return -1;
  pthread_join(pthreadId, NULL);
  return pa.result;
}

#define AUDIO_FILE_NUMS 2
#define AUDIO_FILE_NAME_LENGTH 32
int speechTranscriberMultFile(const char* appkey) {

  std::time_t curTime = std::time(0);
  if (g_expireTime - curTime < 10) {
    printf("the token will be expired, please generate new token by AccessKey-ID and AccessKey-Secret.\n");
    if (generateToken(g_akId, g_akSecret, &g_token, &g_expireTime) < 0) {
      return -1;
    }
  }

  char audioFileNames[AUDIO_FILE_NUMS][AUDIO_FILE_NAME_LENGTH] = {
      "test0.wav", 
      "test1.wav"
  };
  ParamStruct pa[AUDIO_FILE_NUMS];
  for (int i = 0; i < AUDIO_FILE_NUMS; i ++) {
    pa[i].token = g_token; 
    pa[i].appkey = appkey;
    pa[i].fileName = audioFileNames[i]; 
  } 

  std::vector<pthread_t> pthreadId(AUDIO_FILE_NUMS);
  int created = 0;
  for (; created < AUDIO_FILE_NUMS; ++created) {
    if (pthread_create(&pthreadId[created], NULL, &pthreadFunction, &pa[created]) != 0) break;
  }
  int result = created == AUDIO_FILE_NUMS ? 0 : -1;
  for (int j = 0; j < created; ++j) {
    pthread_join(pthreadId[j], NULL);
    if (pa[j].result != 0) result = -1;
  }
  return result;
}

int main(int argc, char* argv[]) {
  const char* appkeyEnv = getenv("NLS_APPKEY_ENV");
  const char* akEnv = getenv("NLS_AK_ENV");
  const char* skEnv = getenv("NLS_SK_ENV");
  if (!appkeyEnv || !*appkeyEnv || !akEnv || !*akEnv || !skEnv || !*skEnv) {
    std::cerr << "Set NLS_APPKEY_ENV, NLS_AK_ENV, and NLS_SK_ENV." << std::endl;
    return EXIT_FAILURE;
  }
  std::string appkey = appkeyEnv;
  g_akId = akEnv;
  g_akSecret = skEnv;

  int ret = NlsClient::getInstance()->setLogConfig(
      "log-transcriber", LogError, 10, 3);
  if (ret < 0) {
    printf("set log failed.\n");
    NlsClient::releaseInstance();
    return EXIT_FAILURE;
  }

  if (g_sync_timeout > 0) {
    NlsClient::getInstance()->setSyncCallTimeout(g_sync_timeout);
  }

  NlsClient::getInstance()->startWorkThread(1);

  int result = speechTranscriberFile(appkey.c_str());
  // To transcribe two files concurrently, replace the preceding call with:
  // int result = speechTranscriberMultFile(appkey.c_str());

  NlsClient::releaseInstance();

  return result == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}
说明

本示例不启用预连接池。使用 3.3.0b 的 Linux x86_64 Release CXX11 预编译包时,启用预连接池(例如调用 setPreconnectedPool(2))可能触发内存释放异常,导致进程退出。使用此预编译包时,请勿启用预连接池。如需使用预连接池,可从源码构建 Debug 库。

将 SDK 的 include 和 lib 目录加入项目编译、链接配置。以当前目录下的 NlsSdk3.X_LINUX 为例,示例文件 demo.cpp 可使用以下命令编译:

g++ -std=c++11 -D_GLIBCXX_USE_CXX11_ABI=1 demo.cpp   -I NlsSdk3.X_LINUX/include -L NlsSdk3.X_LINUX/lib   -lalibabacloud-idst-speech -lpthread -o demo
export LD_LIBRARY_PATH="$PWD/NlsSdk3.X_LINUX/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"

使用说明

  • 示例按 16000 Hz、16 位、单声道音频计算发送间隔。8 kHz PCM 每发送 1600 字节可等待 100 ms,16 kHz PCM 每发送 3200 字节可等待 100 ms。OPUS/OPU 模式传给 SDK 的仍是 PCM 数据;建议为每次 640 字节、等待 20 ms。实时录音按采集速率发送,文件模拟录音时才需要控制发送速率。

  • sendAudio 返回负值时停止发送;收到 TranscriptionCompletedTaskFailed 后,SDK 关闭通道,不再继续发送音频。

  • 异步模式下,start() 返回不代表启动成功,应等待启动事件或失败事件;stop() 返回不代表交互结束,应等待通道关闭事件。调用 setSyncCallTimeout 后启用同步模式。

  • SDK 多线程指一个音频源对应一个线程,不是多个线程同时发送同一音频源。完整示例提供单文件及双文件并发入口;所有请求结束后再释放全局客户端,releaseInstance() 非线程安全。

免费用户并发连接不能超过 2 个。工作线程数在 200 并发以下建议设置为 1,更高并发的建议配置见 SDK readme.md。

语音断句检测阈值为一句话之后的静音长度,取值范围为 200~2000 ms,默认 800 ms。启用语义断句后,语音断句检测不生效;语义断句需要同时开启中间识别结果。

通过 setPayloadParam 传递定制或高级参数,参数为 JSON 格式字符串,例如 {"vad_model":"farfield"}

内网访问

在阿里云上海 ECS 上使用内网访问时,创建 SpeechTranscriberRequest 后设置以下 URL。

request->setUrl("ws://nls-gateway-cn-shanghai-internal.aliyuncs.com/ws/v1");

关键接口

NlsClient

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

接口名

启用版本

功能描述

getInstance

2.x

获取(创建)NlsClient实例。

setLogConfig

2.x

设置日志文件与存储路径。

支持 LogDebug、LogInfo、LogWarning 和 LogError 日志级别,可设置单个日志文件大小和循环记录的文件数量。

setDirectHost

3.x

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

setAddrInFamily

3.1.12

设置套接口地址结构的类型,默认为AF_INET仅返回IPV4相关的地址信息,需要在startWorkThread之前调用。

setUseSysGetAddrInfo

3.1.13

如果 libevent 无法完成 DNS 解析,可调用此接口切换为系统解析接口。需在 startWorkThread 之前调用。

setSyncCallTimeout

3.1.17

设置同步调用模式的超时时间(ms)。默认值为0(即关闭同步模式)。

使用同步调用模式时:

  • start()阻塞直至接收服务端结果

  • stop()阻塞直至触发close()回调

setPreconnectedPool

3.3.0

设置每个域名URL的预连接池。

  • 作用:

    为域名URL建立持久连接池,请求结束后自动复用连接

    • 降低每次发起请求前的连接时间

    • 大幅降低首包延迟

  • 与长链接模式冲突,会关闭已设置的长链接模式。

  • 禁用场景:听悟场景

  • 调用约束:需要在startWorkThread之前调用

startWorkThread

3.x

启动工作线程。默认值为 1,即启动一个线程;设置为 -1 时,启动与 CPU 核数相同数量的线程。高并发时建议设置为 -1。此接口用于初始化 NlsClient 实例,必须调用。

releaseInstance

3.x

销毁NlsClient对象实例。

getVersion

2.x

获取SDK版本号。

createTranscriberRequest

2.x

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

releaseTranscriberRequest

2.x

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

另外,请注意在代码中增加异常处理机制,并在异常发生时及时调用该方法以确保资源释放,否则将影响正常的使用,比如无识别结果返回。

NlsToken

NlsToken:创建Token对象,用于申请获取TokenId。使用 getExpireTime 获取过期时间戳,在临近过期时重新申请。有效期内申请新 Token 不会使已有 Token 自动失效。

接口名

功能描述

setAccessKeyId

设置阿里云账号AccessKey ID。

setKeySecret

设置阿里云账号AccessKey Secret。

setDomain

设置域名,非必填。

setServerVersion

设置API版本,非必填。

setServerResourcePath

设置服务路径,非必填。

setRegionId

设置服务地域 ID,非必填。

setAction

设置功能,非必填。

applyNlsToken

申请获取TokenId。

getToken

获取TokenId。

getExpireTime

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

getErrorMsg

获得错误信息。

NlsEvent

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

接口名

功能描述

getStatusCode

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

getErrorMessage

TaskFailed回调中,获取NlsRequest操作过程中出现失败时的错误信息。

getTaskId

获取任务的TaskId。

getAllResponse

获取云端返回的识别结果。

getResult

获取中间识别结果和最终结果。

getSentenceIndex

获取实时语音检测的句子编号。

getSentenceTime

获取当前已处理的音频时间位置,单位是毫秒。

getSentenceBeginTime

对应的SentenceBegin事件的时间,单位是毫秒。

SpeechTranscriberRequest

SpeechTranscriberRequest:实时语音识别请求对象,用于长语音实时识别。接口说明以speechTranscriberRequest.h内容为准。

接口名

启用版本

功能描述

setOnTaskFailed

2.x

设置错误回调函数。

setOnTranscriptionStarted

2.x

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

setOnSentenceBegin

2.x

设置一句话开始回调。

setOnSentenceEnd

2.x

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

setOnTranscriptionResultChanged

2.x

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

setOnTranscriptionCompleted

2.x

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

setOnChannelClosed

2.x

设置通道关闭回调函数。

setOnMessage

3.1.16

设置服务端response message回调函数,所有回调从此回调输出由用户自行解析。非必填。设置后需setEnableOnMessage启动。

setAppKey

2.x

设置AppKey。

setToken

2.x

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

setUrl

2.x

设置服务URL地址。

setIntermediateResult

2.x

设置是否返回中间识别结果。

默认值为 false。

setPunctuationPrediction

2.x

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

默认值为 false。

setInverseTextNormalization

2.x

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

默认值为 false。

setFormat

2.x

设置音频数据编码格式(PCM、OPUS、OPU,默认是PCM,推荐OPUS)。

setSampleRate

2.x

音频采样率设置。

支持 16000 和 8000 Hz,默认值为 16000 Hz。

setSemanticSentenceDetection

2.x

设置是否使用语义断句。

setMaxSentenceSilence

2.x

设置vad阈值。

setCustomizationId

2.x

设置定制模型。

setVocabularyId

2.x

为当前请求设置热词表 ID。如果同时配置了控制台热词和 SDK 热词,则 SDK 配置优先,将覆盖控制台热词配置。

setTimeout

2.x

设置链接超时时间,默认5000ms。

setSessionId

2.x

设置Session ID。

setOutputFormat

2.x

设置输出文本的编码格式:UTF-8 或 GBK。

setPayloadParam

2.x

参数设置,入参为JSON格式字符串。

setContextParam

2.x

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

AppendHttpHeaderParam

2.x

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

setSendTimeout

3.1.14

设置发送超时时间,默认5000ms。

setRecvTimeout

3.1.14

设置接收超时时间, 默认15000ms,需setEnableRecvTimeout开启后生效。

setEnableRecvTimeout

3.1.16

设置是否启用接收超时检测,默认false,即默认关闭接收超时时间,启用后长时间未收到服务端数据则报错。

getOutputFormat

3.1.16

获得设置的输出文本的编码格式。

setEnableOnMessage

3.1.16

设置开启服务器返回消息回调。

getTaskId

3.1.17

获得当前请求的task_id。

start

2.x

启动SpeechTranscriberRequest。

stop

2.x

会与服务端确认关闭,正常停止实时音频流识别操作。

cancel

2.x

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

control

2.x

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

sendAudio

2.x

发送语音数据。建议一次发送音频数据640~16384字节。

SDK包文件说明

  • scripts/build_linux.sh:SDK源码中,以Linux平台为例的示例编译脚本。

  • CMakeLists.txt:SDK源码中,以Linux平台为例的示例代码工程CMakeList文件。

  • demo目录:SDK包中,集成示例代码,以Linux平台为例,如下表所示。

    文件名

    描述

    speechRecognizerDemo.cpp

    一句话识别示例。

    speechSynthesizerDemo.cpp

    语音合成示例。

    speechTranscriberDemo.cpp

    实时语音识别示例。

    fileTransferDemo.cpp

    录音文件识别示例。

  • resource目录:SDK源码中,语音服务范例音频,可用于功能测试,如下表所示。

    文件名

    描述

    • test0.wav

    • test1.wav

    • test2.wav

    • test3.wav

    测试音频(16k采样频率、16bit采样位数的音频文件)。

  • include:SDK源码中的头文件,如下表所示。

    文件名

    描述

    nlsClient.h

    SDK实例。

    nlsEvent.h

    回调事件说明。

    nlsGlobal.h

    SDK全局头文件。

    nlsToken.h

    SDK Access Token实例。

    iNlsRequest.h

    NLS请求基础头文件。

    speechRecognizerRequest.h

    一句话识别。

    speechSynthesizerRequest.h

    语音合成、长文本语音合成。

    speechTranscriberRequest.h

    实时音频流识别。

    FileTrans.h

    录音文件识别。

  • lib:SDK库文件。

  • readme.md:SDK说明。

  • release.log:版本说明。

  • version:版本号。

C++ 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编码器创建失败

建议重新初始化。

-104

InvalidEncoderType

encoder类型无效

编译时可能关闭OPUS但是又使用,或请检查ENCODER_TYPE。

-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

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

请检查当前请求是否未启动(即收到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

workThreadNodeQueue无效

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

申请转换描述失败

UTF8GBK转换失败。

-503

IconvFailed

编码转换失败

UTF8GBK转换失败。

-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

成功

服务端响应状态码

关于服务状态码,请参见接口说明

常见问题

场景问题

C++ SDK调用智能实时语音解析接口失败,提示 {"TaskFailed":"connect failed."} {"channeclClosed": "nls request finished."}如何解决?

  • C++ SDK3.0及以前的版本有小概率出现这个问题,无需特别关注;3.1及以后版本出现这个问题可能是运行环境的网络问题导致,建议检查本地网络环境。

  • 如果没有返回TaskId,说明在连接过程中直接断开,实时语音交互不需要重复调用接口,重复调用会有并发上限和超时时间,并发超过限制会直接返回超过限制,WebSocket连接超过10秒没有音频就会自动断开,但是会返回TaskId。

C++ SDK实时语音识别报错,提示status_text:Gateway:IDLE_TIMEOUT:Websocket session is idle for too long time, the last directive is 'StartTranscription'!如何解决?

  1. 因为超过10秒没有发送数据到服务端,空闲超时自动断连。同时请检查URI是否正确,wss://nls-gateway-cn-shanghai.aliyuncs.com/ws/v1。如果发生这种情况,可以增加重试机制再次请求。

  2. 也可能服务端瞬间收到大量请求导致单个实例无法及时处理,建议重试。

C++ SDK调用实时语音识别,集成在mrcp里面使用,部分情况下创建多个识别通道时会返回10000002如何解决?

错误码 10000002 是 SDK 默认错误码,底层错误见 Resource temporarily unavailable。一般是建立连接后未向服务端发送数据,导致 WebSocket 超时;下一个发送的指令是StartTranscription

C++ SDK(3.0及以后版本)使用语音合成和语音识别功能,可以提高GCC5.0以上的编译版本吗?

可以。Linux下支持GCC 4.8.5或以上版本。目前已验证且顺利编译运行的GCC版本包括4.8.5、5.5.0、8.4.0。

为什么连接不到framework?

framework中代码采用Objective-CC++混合编写而成,所以需要使用.mm后缀文件进行调用,同时确保工程的头文件路径与库文件路径设置正确。

C++ SDK ASR请求有DNS解析失败的情况导致异常,报错“ali-recog-skd.log:AliSpeech_C++SDK(ERROR): GetInetAddressByHostname:252 DNS: resolved timeout.ali-recog-skd.log:AliSpeech_C++SDK(ERROR): start:76 start failed: DNS: resolved timeout..unimrcpserver_current.log: [ERROR] [[./ali/AliRecogChannel.cpp:772,onTaskFailed]]Ali Task start failed Msg :DNS: resolved timeout., start finised."”如何解决?

  • 旧版(3.0及以前版本):在高并发或者电脑DNS忙碌的情况下容易出现以上问题,建议更新到3.1.X版本,或进行再次重启请求。

  • 新版(3.0及以后版本):已经对此问题进行防御,若仍然偶现此问题,则为电脑DNS忙碌,需要再次重启请求。

C++ SDK(新)集成到其他项目中时,将CMakeLists.txt中的add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0) 修改为add_definitions(-D_GLIBCXX_USE_CXX11_ABI=1)后编译不通过如何解决?

除了CMakeLists.txt,全工程都需要修改该参数,例如config/linux.thirdparty.debug.cmakeconfig/linux.thirdparty.release.cmake,请在全目录搜索_GLIBCXX_USE_CXX11_ABI进行修改。

C++ SDK旧版NlsSdkCpp2.0和新版NlsSdkCpp3.X的区别是什么?

NlsSdkCpp2.0版本的SDK每一个请求为一个线程,且接口为同步接口。

NlsSdkCpp3.X 版本由第三方库 libevent 统一处理事件消息,并发性能更强,接口默认异步;3.1.17 起可通过 setSyncCallTimeout 启用同步调用。

C++版的SDK不支持实现C11规范吗? 现在导致项目无法链接SDK该如何解决?

应用和 SDK 必须使用一致的 _GLIBCXX_USE_CXX11_ABI。3.3.0b 源码默认编译脚本会根据 GCC 主版本选择 ABI;CXX11 预编译包使用 ABI=1,不能直接与 ABI=0 的应用混用。

C++ SDK测试Demo成功,集成项目报错,DNS解析失败,报错“nls-gateway-cn-shanghai.aliyuncs.com dns failed: nodename nor servname provided, or not known”如何解决?

  1. SDK中会查看当前设备开启的所有协议族(IPv4、IPv6)进行DNS解析请求,nls-gateway-cn-shanghai.aliyuncs.com不支持 IPv6,返回解析错误,从而导致SDK DNS解析失败退出。可禁用当前设备的IPv6协议族,3.1.12 及以后版本可通过 setAddrInFamily 配置协议族。

  2. 建议升级到3.1.12及以后版本。

C++ SDK测试Demo可以成功,集成项目报错,网络链接失败,报错“[dnsEventCallback:465]Node:0x7f087c001030 ai_canonname: nls-gateway-cn-shanghai.aliyuncs.com.gds.alibabadns.com[dnsEventCallback:477]Node:0x7f087c001030 IpV4:106.15.XX.XX[connectProcess:1329]Node:0x7f087c001030 sockFd:41[connectProcess:1347]Node:0x7f087c001030 new Socket ip:106.15.XX.XX port:443 Fd:41.[socketConnect:1458]Node:0x7f087c001030 Connect failed:Network is unreachable. retry...”如何解决?

以上现象为无法连接网络,查看日志发现DNS域名解析出来的IP链接不成功,进一步通过Ping判断网络不通。由于本地拦截DNS解析,导致SDK内部libeventevdns_getaddrinfo获得错误的IP。

解决办法:

  • 3.1.12版本以前可将evdns_getaddrinfo()手动替换成系统的getaddrinfo()。

  • 3.1.12版本可在CMakeLists.txt中修改add_definitions(-DNLS_USE_NATIVE_GETADDRINFO)

  • 3.1.12及以后版本增加setDirectHost()接口,可以在SDK外部进行DNS解析,获取正确IP后通过该接口设入。

  • 3.1.13及以后版本已解决此问题,若运行时仍存在上述问题,建议调用接口setUseSysGetAddrInfo(true)。

C++ SDK语音合成时传入的文本没有采用UTF-8编码会有什么错误信息?

如果传入的文本没有采用UTF-8编码,在文本中含有中文字符时,语音合成SDK调用start函数会失败,返回错误信息Socket recv failed, errorCode: 0。错误码为0表示服务端已经关闭了连接,此时应检查传入的文本是否采用UTF-8编码。