全部产品
云市场
云游戏

C++ SDK - 新

更新时间:2020-07-12 22:29:08

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

注意事项

  • 在使用SDK前,请先阅读接口说明,详情请参见接口说明
  • 当前最新版本:3.0.8,发布日期:2020年1月9日。该版本只支持Linux平台,暂不支持Windows平台。
  • 该版本C++ SDK API和上一版本API(已下线)定义有区别,本文以当前版本为例进行介绍。

下载安装

SDK下载

下载C++ SDK CppSdk,压缩文件包含如下几部分:

  • CMakeLists.txt:示例代码工程的CMakeList文件。
  • build:编译目录。
  • readme.txt:SDK说明。
  • release.log:版本说明。
  • version:版本号。
  • build.sh:demo编译脚本。
  • lib:SDK库文件。
  • demo:示例,各语音服务配置文件。各文件描述见下表。
文件名 描述
speechRecognizerDemo.cpp 一句话识别demo
speechSynthesizerDemo.cpp 语音合成demo
speechTranscriberDemo.cpp 实时语音识别demo
speechLongSynthesizerDemo.cpp 长文本语音合成demo
test0.wav/test1.wav 测试音频(16k 16bit音频文件)
  • include:SDK头文件。
文件名 描述
nlsClient.h SDK实例
nlsEvent.h 回调事件说明
speechRecognizerRequest.h 一句话识别
speechSynthesizerRequest.h 语音合成&长文本语音合成
speechTranscriberRequest.h 实时音频流识别

编译运行

Cmake编译

  1. 确认本地系统已安装Cmake 3.1、Glibc 2.5、Gcc 4.1.2及以上版本。
  2. 运行如下脚本。
    1. mkdir build
    2. cd build && cmake .. && make
    3. cd ../demo #生成demo可执行程序:srDemo(一句话识别)、stDemo(实时语音识别)、syDemo(语音合成)、syLongDemo(长文本语音合成)。
    4. ./srDemo appkey AccessKey Id AccessKey Secret # 测试使用

关键接口

基础接口

  • NlsClient:语音处理Client,相当于所有语音相关处理类的Factory,全局只需创建一个实例。
  • NlsEvent:事件对象,您可以从中获取Request状态码、云端返回结果、失败信息等。

识别接口

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

C++ SDK自定义错误码

错误码 错误描述 解决方案
10000001 SSL: couldn’t create a …! 建议重试。
10000002 openssl官方错误描述 根据描述提示处理后,重试。
10000003 系统错误描述 根据系统错误描述提示处理。
10000004 URL: The url is empty. 检查是否设置云端URL地址。
10000005 URL: Could not parse WebSocket url 检查是否正确设置云端URL地址。
10000006 MODE: unsupport mode. 检查是否正确设置了语音功能模式。
10000007 JSON: Json parse failed. 服务端发送错误响应内容,请提供task_id,并反馈给阿里云。
10000008 WEBSOCKET: unkown head type. 服务端发送错误WebSocket类型,请提供task_id,并反馈给阿里云。
10000009 HTTP: connect failed. 与云端连接失败,请检查网络后,重试。
HTTP协议官方状态码 HTTP: Got bad status. 根据HTTP协议官方描述提示处理。
系统错误码 IP: ip address is not valid. 根据系统错误描述提示处理。
系统错误码 ENCODE: convert to utf8 error. 根据系统错误描述提示处理。
10000010 please check if the memory is enough 内存不足,请检查本地机器内存。
10000011 Please check the order of execution 接口调用顺序错误(接收到Failed/complete事件时,SDK内部会关闭连接。此时再调用send会上报错误) 。
10000012 StartCommand/StopCommand Send failed 参数错误。请检查参数设置是否正确。
10000013 The sent data is null or dataSize <= 0. 发送错误。请检查发送参数是否正确。
10000014 Start invoke failed. start超时错误。请调用stop,释放资源,重新开始识别流程。
10000015 connect failed connect失败。释放资源,重新开始识别流程。

服务端响应状态码

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

代码示例

说明:

  • Demo中使用的音频文件为16000Hz采样率,请在管控台中将appKey对应项目的模型设置为通用模型,以获取准确的识别效果。如果使用其他音频,请设置为支持该音频场景的模型,关于模型设置,请参见管理项目
  • Demo中使用了SDK内置的默认一句话识别服务的外网访问URL,如果您使用阿里云上海ECS且需要使用内网访问URL,则在创建SpeechRecognizerRequest的对象中设置内网访问的URL。
    1. request->setUrl("ws://nls-gateway.cn-shanghai-internal.aliyuncs.com/ws/v1");
  • 完整示例,详见SDK压缩包中的demo目录speechRecognizerDemo.cpp文件。
  1. #include <string.h>
  2. #include <unistd.h>
  3. #include <pthread.h>
  4. #include <stdlib.h>
  5. #include <ctime>
  6. #include <string>
  7. #include <iostream>
  8. #include <vector>
  9. #include <fstream>
  10. #include <sys/time.h>
  11. #include "nlsClient.h"
  12. #include "nlsEvent.h"
  13. #include "speechRecognizerRequest.h"
  14. #include "nlsCommonSdk/Token.h"
  15. #define FRAME_SIZE 3200
  16. #define SAMPLE_RATE 16000
  17. using namespace AlibabaNlsCommon;
  18. using AlibabaNls::NlsClient;
  19. using AlibabaNls::NlsEvent;
  20. using AlibabaNls::LogDebug;
  21. using AlibabaNls::LogInfo;
  22. using AlibabaNls::LogError;
  23. using AlibabaNls::SpeechRecognizerRequest;
  24. //自定义线程参数
  25. struct ParamStruct {
  26. std::string fileName;
  27. std::string appkey;
  28. std::string token;
  29. };
  30. //自定义事件回调参数
  31. struct ParamCallBack {
  32. int userId;
  33. char userInfo[8];
  34. };
  35. //全局维护一个服务鉴权token和其对应的有效期时间戳,
  36. //每次调用服务之前,首先判断token是否已经过期。
  37. //如果已经过期,则根据AccessKey ID和AccessKey Secret重新生成一个token,并更新这个全局的token和其有效期时间戳。
  38. //说明:只需在token即将过期时进行重新生成。所有的服务并发可共用一个token。
  39. std::string g_akId = "";
  40. std::string g_akSecret = "";
  41. std::string g_token = "";
  42. long g_expireTime = -1;
  43. struct timeval tv;
  44. struct timeval tv1;
  45. // 根据AccessKey ID和AccessKey Secret重新生成一个token,并获取其有效期时间戳。
  46. // token使用规则:在有效期内可以多个进程/多个线程/多个应用使用,建议快到期时提起申请新的token。
  47. int generateToken(std::string akId, std::string akSecret, std::string* token, long* expireTime) {
  48. NlsToken nlsTokenRequest;
  49. nlsTokenRequest.setAccessKeyId(akId);
  50. nlsTokenRequest.setKeySecret(akSecret);
  51. if (-1 == nlsTokenRequest.applyNlsToken()) {
  52. // 获取失败原因
  53. printf("generateToken Failed: %s\n", nlsTokenRequest.getErrorMsg());
  54. return -1;
  55. }
  56. *token = nlsTokenRequest.getToken();
  57. *expireTime = nlsTokenRequest.getExpireTime();
  58. return 0;
  59. }
  60. //@brief 获取sendAudio发送延时时间
  61. //@param dataSize 待发送数据大小
  62. //@param sampleRate 采样率 16k/8K
  63. //@param compressRate 数据压缩率,例如压缩比为10:1的16k OPUS编码,此时为10,非压缩数据则为1
  64. //@return 返回sendAudio之后需要sleep的时间
  65. //@note 对于8k PCM编码数据,16位采样,建议每发送1600字节sleep 100ms,
  66. // 对于16k PCM编码数据,16位采样,建议每发送3200字节sleep 100ms。
  67. // 对于其它编码格式的数据,您可以根据压缩比自行估算,比如压缩比为10:1的 16k OPUS编码数据,需要每发送3200/10=320 sleep 100ms。
  68. unsigned int getSendAudioSleepTime(int dataSize, int sampleRate, int compressRate) {
  69. // 仅支持16位采样
  70. const int sampleBytes = 16;
  71. // 仅支持单通道
  72. const int soundChannel = 1;
  73. // 当前采样率,采样位数下每秒采样数据的大小
  74. int bytes = (sampleRate * sampleBytes * soundChannel) / 8;
  75. // 当前采样率,采样位数下每毫秒采样数据的大小
  76. int bytesMs = bytes / 1000;
  77. // 待发送数据大小除以每毫秒采样数据大小,以获取sleep时间
  78. int sleepMs = (dataSize * compressRate) / bytesMs;
  79. return sleepMs;
  80. }
  81. //@brief 调用start(),成功与云端建立连接,sdk内部线程上报started事件
  82. //@param cbEvent 回调事件结构,详见nlsEvent.h
  83. //@param cbParam 回调自定义参数,默认为NULL。可以根据需求自定义参数
  84. void OnRecognitionStarted(NlsEvent* cbEvent, void* cbParam) {
  85. ParamCallBack* tmpParam = (ParamCallBack*)cbParam;
  86. // 演示如何打印/使用用户自定义参数示例
  87. printf("OnRecognitionStarted: %d, %s\n", tmpParam->userId, tmpParam->userInfo);
  88. // 获取消息的状态码,成功为0或者20000000,失败时对应失败的错误码
  89. // 当前任务的task id,方便定位问题,作为和服务端交互的唯一标识建议输出。
  90. printf("OnRecognitionStarted: status code=%d, task id=%s\n", cbEvent->getStatusCode(), cbEvent->getTaskId());
  91. // 获取服务端返回的全部信息
  92. //printf("OnRecognitionStarted: all response=%s\n", cbEvent->getAllResponse());
  93. }
  94. //@brief 设置允许返回中间结果参数,SDK在接收到云端返回到中间结果时,内部线程上报ResultChanged事件
  95. //@param cbEvent 回调事件结构,详情参见nlsEvent.h
  96. //@param cbParam 回调自定义参数,默认为NULL。可以根据需求自定义参数
  97. void OnRecognitionResultChanged(NlsEvent* cbEvent, void* cbParam) {
  98. ParamCallBack* tmpParam = (ParamCallBack*)cbParam;
  99. // 演示如何打印/使用用户自定义参数示例
  100. printf("OnRecognitionResultChanged: %d, %s\n", tmpParam->userId, tmpParam->userInfo);
  101. // 当前任务的task id,方便定位问题,作为和服务端交互的唯一标识建议输出。
  102. printf("OnRecognitionResultChanged: status code=%d, task id=%s, result=%s\n", cbEvent->getStatusCode(), cbEvent->getTaskId(), cbEvent->getResult());
  103. // 获取服务端返回的全部信息
  104. //printf("OnRecognitionResultChanged: response=%s\n", cbEvent->getAllResponse());
  105. }
  106. //@brief SDK在接收到云端返回识别结束消息时,其线程上报Completed事件
  107. //@note 上报Completed事件之后,SDK内部会关闭识别连接通道。此时调用sendAudio会返回-1。请停止发送。
  108. //@param cbEvent 回调事件结构,详情参见nlsEvent.h
  109. //@param cbParam 回调自定义参数,默认为NULL。可以根据需求自定义参数
  110. void OnRecognitionCompleted(NlsEvent* cbEvent, void* cbParam) {
  111. ParamCallBack* tmpParam = (ParamCallBack*)cbParam;
  112. // 演示如何打印/使用用户自定义参数示例
  113. printf("OnRecognitionCompleted: %d, %s\n", tmpParam->userId, tmpParam->userInfo);
  114. // 当前任务的task id,方便定位问题,作为和服务端交互的唯一标识建议输出。
  115. printf("OnRecognitionCompleted: status code=%d, task id=%s, result=%s\n", cbEvent->getStatusCode(), cbEvent->getTaskId(), cbEvent->getResult());
  116. // 获取服务端返回的全部信息
  117. //printf("OnRecognitionCompleted: response=%s\n", cbEvent->getAllResponse());
  118. }
  119. //@brief 识别过程发生异常时,SDK内部线程上报TaskFailed事件
  120. //@note 上报TaskFailed事件之后,SDK内部会关闭识别连接通道。此时调用sendAudio会返回-1,请停止发送。
  121. //@param cbEvent 回调事件结构,详情参见nlsEvent.h
  122. //@param cbParam 回调自定义参数,默认为NULL。可以根据需求自定义参数
  123. //@return
  124. void OnRecognitionTaskFailed(NlsEvent* cbEvent, void* cbParam) {
  125. ParamCallBack* tmpParam = (ParamCallBack*)cbParam;
  126. // 演示如何打印/使用用户自定义参数示例
  127. printf("OnRecognitionTaskFailed: %d, %s\n", tmpParam->userId, tmpParam->userInfo);
  128. // 当前任务的task id,方便定位问题,作为和服务端交互的唯一标识建议输出。
  129. printf("OnRecognitionTaskFailed: status code=%d, task id=%s, error message=%s\n", cbEvent->getStatusCode(), cbEvent->getTaskId(), cbEvent->getErrorMessage());
  130. // 获取服务端返回的全部信息
  131. //printf("OnRecognitionTaskFailed: response=%s\n", cbEvent->getAllResponse());
  132. }
  133. //@brief 识别结束或发生异常时,SDK会关闭连接通道,其内部线程上报ChannelCloseed事件
  134. //@param cbEvent 回调事件结构,详见nlsEvent.h
  135. //@param cbParam 回调自定义参数,默认为NULL。可以根据需求自定义参数
  136. void OnRecognitionChannelClosed(NlsEvent* cbEvent, void* cbParam) {
  137. ParamCallBack* tmpParam = (ParamCallBack*)cbParam;
  138. // 演示如何打印/使用用户自定义参数示例
  139. printf("OnRecognitionChannelClosed: %d, %s\n", tmpParam->userId, tmpParam->userInfo);
  140. // 获取服务端返回的全部信息
  141. printf("OnRecognitionChannelClosed: response=%s\n", cbEvent->getAllResponse());
  142. delete tmpParam; //识别流程结束,释放回调参数
  143. }
  144. void* pthreadFunction(void* arg) {
  145. int sleepMs = 0;
  146. ParamCallBack *cbParam = NULL;
  147. //初始化自定义回调参数,以下两变量仅作为示例表示参数传递,在demo中不起任何作用
  148. //回调参数在堆中分配之后,SDK在销毁request对象时会一并销毁,外界无需再释放
  149. cbParam = new ParamCallBack;
  150. cbParam->userId = rand() % 100;
  151. strcpy(cbParam->userInfo, "User.");
  152. // 0: 从自定义线程参数中获取token,配置文件等参数
  153. ParamStruct *tst = (ParamStruct *) arg;
  154. if (tst == NULL) {
  155. printf("arg is not valid\n");
  156. return NULL;
  157. }
  158. // 打开音频文件,获取数据
  159. std::ifstream fs;
  160. fs.open(tst->fileName.c_str(), std::ios::binary | std::ios::in);
  161. if (!fs) {
  162. printf("%s isn't exist..\n", tst->fileName.c_str());
  163. return NULL;
  164. }
  165. //1: 创建一句话识别SpeechRecognizerRequest对象
  166. SpeechRecognizerRequest *request = NlsClient::getInstance()->createRecognizerRequest();
  167. if (request == NULL) {
  168. printf("createRecognizerRequest failed\n");
  169. return NULL;
  170. }
  171. request->setOnRecognitionStarted(OnRecognitionStarted, cbParam); // 设置start()成功回调函数
  172. request->setOnTaskFailed(OnRecognitionTaskFailed, cbParam); // 设置异常识别回调函数
  173. request->setOnChannelClosed(OnRecognitionChannelClosed, cbParam); // 设置识别通道关闭回调函数
  174. request->setOnRecognitionResultChanged(OnRecognitionResultChanged, cbParam); // 设置中间结果回调函数
  175. request->setOnRecognitionCompleted(OnRecognitionCompleted, cbParam); // 设置识别结束回调函数
  176. request->setAppKey(tst->appkey.c_str()); // 设置AppKey。必填参数,请参照官网申请
  177. request->setFormat("pcm"); // 设置音频数据编码格式。可选参数,目前支持PCM、OPUS,默认为PCM
  178. request->setSampleRate(SAMPLE_RATE); // 设置音频数据采样率。可选参数,目前支持16000/8000。默认为16000
  179. request->setIntermediateResult(true); // 设置是否返回中间识别结果。可选参数,默认false
  180. request->setPunctuationPrediction(true); // 设置是否在后处理中添加标点。可选参数,默认false
  181. request->setInverseTextNormalization(true); // 设置是否在后处理中执行ITN。可选参数,默认false
  182. //request->setEnableVoiceDetection(true); //是否启动语音检测。可选,默认为False
  183. //允许的最大开始静音。可选,单位是毫秒。超出后服务端将会发送RecognitionCompleted事件,结束本次识别。注意: 需要先设置enable_voice_detection为true
  184. //request->setMaxStartSilence(5000);
  185. //允许的最大结束静音。可选,单位是毫秒。超出后服务端将会发送RecognitionCompleted事件,结束本次识别。注意: 需要先设置enable_voice_detection为true
  186. //request->setMaxEndSilence(800);
  187. //request->setCustomizationId("TestId_123"); //定制语言模型id,可选。
  188. //request->setVocabularyId("TestId_456"); //定制泛热词id,可选。
  189. // 用于传递某些定制化、高级参数设置,参数格式为json格式: {"key": "value"}
  190. //request->setPayloadParam("{\"vad_model\": \"farfield\"}");
  191. request->setToken(tst->token.c_str()); // 设置账号校验token。必填参数
  192. // 2:start()为异步操作。成功返回started事件,失败返回TaskFailed事件。
  193. if (request->start() < 0) {
  194. printf("start() failed. may be can not connect server. please check network or firewalld\n");
  195. NlsClient::getInstance()->releaseRecognizerRequest(request); // start()失败,释放request对象
  196. return NULL;
  197. }
  198. while (!fs.eof()) {
  199. uint8_t data[FRAME_SIZE] = {0};
  200. fs.read((char *) data, sizeof(uint8_t) * FRAME_SIZE);
  201. size_t nlen = fs.gcount();
  202. if (nlen <= 0) {
  203. continue;
  204. }
  205. // 3: 发送音频数据。sendAudio为异步操作,返回-1表示发送失败,需要停止发送。
  206. int ret = request->sendAudio(data, nlen);
  207. if (ret < 0) {
  208. // 发送失败,退出循环数据发送
  209. printf("send data fail.\n");
  210. break;
  211. }
  212. // 语音数据发送控制:
  213. // 语音数据是实时的,不用sleep控制速率,直接发送即可。
  214. // 语音数据来自文件(也即本示例代码模拟的语音流发送机制),发送时需要控制速率,使单位时间内发送的数据大小接近单位时间原始语音数据存储的大小。
  215. sleepMs = getSendAudioSleepTime(nlen, SAMPLE_RATE, 1); // 根据发送数据大小、采样率、数据压缩比来获取sleep时间
  216. // 4: 语音数据发送延时控制
  217. usleep(sleepMs * 1000);
  218. }
  219. printf("sendAudio done.\n");
  220. //5: 关闭音频文件
  221. fs.close();
  222. //6: 通知云端数据发送结束
  223. //stop()为异步操作。失败返回TaskFailed事件。
  224. request->stop();
  225. //7: 通知SDK释放request。
  226. NlsClient::getInstance()->releaseRecognizerRequest(request);
  227. return NULL;
  228. }
  229. //线程循环识别
  230. //需要调整count值和每次要识别的文件,Demo中默认每次识别一个文件
  231. void* multiRecognize(void* arg) {
  232. int count = 2;
  233. while (count > 0) {
  234. pthreadFunction(arg);
  235. count--;
  236. }
  237. return NULL;
  238. }
  239. // 识别单个音频数据
  240. int speechRecognizerFile(const char* appkey) {
  241. //获取当前系统时间戳,判断token是否过期
  242. std::time_t curTime = std::time(0);
  243. if (g_expireTime - curTime < 10) {
  244. printf("the token will be expired, please generate new token by AccessKey-ID and AccessKey-Secret.\n");
  245. if (-1 == generateToken(g_akId, g_akSecret, &g_token, &g_expireTime)) {
  246. return -1;
  247. }
  248. }
  249. ParamStruct pa;
  250. pa.token = g_token;
  251. pa.appkey = appkey;
  252. pa.fileName = "test0.wav";
  253. pthread_t pthreadId;
  254. // 启动一个工作线程,用于单次识别
  255. pthread_create(&pthreadId, NULL, &pthreadFunction, (void *)&pa);
  256. // 启动一个工作线程,用于循环识别
  257. // pthread_create(&pthreadId, NULL, &multiRecognize, (void *)&pa);
  258. pthread_join(pthreadId, NULL);
  259. return 0;
  260. }
  261. //识别多个音频数据
  262. //SDK多线程指一个音频数据源对应一个线程,非一个音频数据对应多个线程。
  263. //示例代码为同时开启2个线程识别2个文件
  264. //免费用户并发连接不能超过2个
  265. #define AUDIO_FILE_NUMS 2
  266. #define AUDIO_FILE_NAME_LENGTH 32
  267. int speechRecognizerMultFile(const char* appkey) {
  268. //获取当前系统时间戳判断token是否过期
  269. std::time_t curTime = std::time(0);
  270. if (g_expireTime - curTime < 10) {
  271. printf("the token will be expired, please generate new token by AccessKey-ID and AccessKey-Secret.\n");
  272. if (-1 == generateToken(g_akId, g_akSecret, &g_token, &g_expireTime)) {
  273. return -1;
  274. }
  275. }
  276. char audioFileNames[AUDIO_FILE_NUMS][AUDIO_FILE_NAME_LENGTH] = {"test0.wav", "test1.wav"};
  277. ParamStruct pa[AUDIO_FILE_NUMS];
  278. for (int i = 0; i < AUDIO_FILE_NUMS; i ++) {
  279. pa[i].token = g_token;
  280. pa[i].appkey = appkey;
  281. pa[i].fileName = audioFileNames[i];
  282. }
  283. std::vector<pthread_t> pthreadId(AUDIO_FILE_NUMS);
  284. // 启动2个工作线程,同时识别2个音频文件
  285. for (int j = 0; j < AUDIO_FILE_NUMS; j++) {
  286. pthread_create(&pthreadId[j], NULL, &pthreadFunction, (void *)&(pa[j]));
  287. }
  288. for (int j = 0; j < AUDIO_FILE_NUMS; j++) {
  289. pthread_join(pthreadId[j], NULL);
  290. }
  291. return 0;
  292. }
  293. int main(int arc, char* argv[]) {
  294. if (arc < 4) {
  295. printf("params is not valid. Usage: ./demo <your appkey> <your AccessKey ID> <your AccessKey Secret>\n");
  296. return -1;
  297. }
  298. std::string appkey = argv[1];
  299. g_akId = argv[2];
  300. g_akSecret = argv[3];
  301. // 根据需要设置SDK输出日志。可选,此处表示SDK日志输出至log-recognizer.txt。LogDebug表示输出所有级别日志,支持LogInfo、LogWarning、LogError,400表示单个文件400MB
  302. int ret = NlsClient::getInstance()->setLogConfig("log-recognizer", LogDebug, 400);
  303. if (-1 == ret) {
  304. printf("set log failed.\n");
  305. return -1;
  306. }
  307. //启动工作线程
  308. NlsClient::getInstance()->startWorkThread(4);
  309. // 识别单个音频数据
  310. speechRecognizerFile(appkey.c_str());
  311. // 并发识别多个音频数据
  312. //speechRecognizerMultFile(appkey.c_str());
  313. // 所有工作完成,进程退出前,释放nlsClient。请注意releaseInstance()非线程安全。
  314. NlsClient::releaseInstance();
  315. return 0;
  316. }