全部产品
云市场
云游戏

C++ SDK - 新

更新时间:2020-06-05 15:53:13

提示:

  • 在使用SDK之前,请先确保已阅读了 接口说明文档
  • 当前最新版本:3.0.8,发布日期:2020年01月09号。该版本只支持Linux平台,暂不支持Windows平台。
  • 该版本api和上一版本C++ SDK api(已下线)有不同定义,如果您之前在此之前使用过早期C++ SDK,注意对比区分。

下载安装

SDK下载

C++ SDK 可从CppSdk中下载,压缩文件包含以下几个部分:

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

编译运行

cmake编译:

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

关键接口

基础接口:

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

    识别接口:

  • SpeechSynthesizerRequest:语音合成请求对象,用于语音合成及长文本语音合成。

    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失败. 释放资源,重新开始识别流程.

服务端响应状态码

可以点此查看服务状态码

代码示例

  • 说明1:Demo中使用了SDK内置的默认一句话识别服务的外网访问URL,如果您使用阿里云上海ECS并想使用内网访问URL,则在创建SpeechRecognizerRequest的对象中设置内网访问的URL:
    1. request->setUrl("ws://nls-gateway.cn-shanghai-internal.aliyuncs.com/ws/v1");
  • 说明2:Demo中将合成的音频保存在了文件中,如果您需要播放音频且对实时性要求较高,建议使用流式播放,即边接收语音数据边播放,减少延时,而无需等待合成结束后再处理语音流。

语音合成示例

完整示例,详见SDK压缩包中的demo目录speechSynthesizerDemo.cpp文件。

  1. #include <pthread.h>
  2. #include <unistd.h>
  3. #include <sys/time.h>
  4. #include <ctime>
  5. #include <string>
  6. #include <vector>
  7. #include <fstream>
  8. #include "nlsClient.h"
  9. #include "nlsEvent.h"
  10. #include "speechSynthesizerRequest.h"
  11. #include "nlsCommonSdk/Token.h"
  12. using namespace AlibabaNlsCommon;
  13. using AlibabaNls::NlsClient;
  14. using AlibabaNls::NlsEvent;
  15. using AlibabaNls::LogDebug;
  16. using AlibabaNls::LogInfo;
  17. using AlibabaNls::SpeechSynthesizerRequest;
  18. // 自定义线程参数
  19. struct ParamStruct {
  20. std::string text;
  21. std::string token;
  22. std::string appkey;
  23. std::string audioFile;
  24. };
  25. // 自定义事件回调参数
  26. struct ParamCallBack {
  27. std::string binAudioFile;
  28. std::ofstream audioFile;
  29. uint64_t startMs;
  30. };
  31. //全局维护一个服务鉴权token和其对应的有效期时间戳,
  32. //每次调用服务之前,首先判断token是否已经过期,
  33. //如果已经过期,则根据AccessKey ID和AccessKey Secret重新生成一个token,并更新这个全局的token和其有效期时间戳。
  34. //注意:不要每次调用服务之前都重新生成新token,只需在token即将过期时重新生成即可。所有的服务并发可共用一个token。
  35. std::string g_akId = "";
  36. std::string g_akSecret = "";
  37. std::string g_token = "";
  38. long g_expireTime = -1;
  39. uint64_t getNow() {
  40. struct timeval now;
  41. gettimeofday(&now, NULL);
  42. return now.tv_sec * 1000 * 1000 + now.tv_usec;
  43. }
  44. // 根据AccessKey ID和AccessKey Secret重新生成一个token,并获取其有效期时间戳
  45. // token使用规则:在有效期到期前可以一直使用,且可以多个进程/多个线程/多个应用使用均可,建议在有效期快到期时提起申请新的token
  46. int generateToken(std::string akId, std::string akSecret, std::string* token, long* expireTime) {
  47. NlsToken nlsTokenRequest;
  48. nlsTokenRequest.setAccessKeyId(akId);
  49. nlsTokenRequest.setKeySecret(akSecret);
  50. if (-1 == nlsTokenRequest.applyNlsToken()) {
  51. // 获取失败原因
  52. printf("generateToken Failed: %s\n", nlsTokenRequest.getErrorMsg());
  53. return -1;
  54. }
  55. *token = nlsTokenRequest.getToken();
  56. *expireTime = nlsTokenRequest.getExpireTime();
  57. return 0;
  58. }
  59. //@brief sdk在接收到云端返回合成结束消息时, sdk内部线程上报Completed事件
  60. //@note 上报Completed事件之后,SDK内部会关闭识别连接通道.
  61. //@param cbEvent 回调事件结构, 详见nlsEvent.h
  62. //@param cbParam 回调自定义参数,默认为NULL, 可以根据需求自定义参数
  63. void OnSynthesisCompleted(NlsEvent* cbEvent, void* cbParam) {
  64. ParamCallBack* tmpParam = (ParamCallBack*)cbParam;
  65. // 演示如何打印/使用用户自定义参数示例
  66. printf("OnSynthesisCompleted: %s\n", tmpParam->binAudioFile.c_str());
  67. // 获取消息的状态码,成功为0或者20000000,失败时对应失败的错误码
  68. // 当前任务的task id,方便定位问题,建议输出,特别提醒该taskid非常重要,是和服务端交互的唯一标识,因此建议在实际使用时建议输出该taskid
  69. printf("OnSynthesisCompleted: status code=%d, task id=%s\n", cbEvent->getStatusCode(), cbEvent->getTaskId());
  70. // 获取服务端返回的全部信息
  71. //printf("OnSynthesisCompleted: all response=%s\n", cbEvent->getAllResponse());
  72. }
  73. //@brief 合成过程发生异常时, sdk内部线程上报TaskFailed事件
  74. //@note 上报TaskFailed事件之后,SDK内部会关闭识别连接通道.
  75. //@param cbEvent 回调事件结构, 详见nlsEvent.h
  76. //@param cbParam 回调自定义参数,默认为NULL, 可以根据需求自定义参数
  77. void OnSynthesisTaskFailed(NlsEvent* cbEvent, void* cbParam) {
  78. ParamCallBack* tmpParam = (ParamCallBack*)cbParam;
  79. // 演示如何打印/使用用户自定义参数示例
  80. printf("OnSynthesisTaskFailed: %s\n", tmpParam->binAudioFile.c_str());
  81. // 当前任务的task id,方便定位问题,建议输出,特别提醒该taskid非常重要,是和服务端交互的唯一标识,因此建议在实际使用时建议输出该taskid
  82. printf("OnSynthesisTaskFailed: status code=%d, task id=%s, error message=%s\n", cbEvent->getStatusCode(), cbEvent->getTaskId(), cbEvent->getErrorMessage());
  83. }
  84. //@brief 识别结束或发生异常时,会关闭连接通道, sdk内部线程上报ChannelCloseed事件
  85. //@param cbEvent 回调事件结构, 详见nlsEvent.h
  86. //@param cbParam 回调自定义参数,默认为NULL, 可以根据需求自定义参数
  87. void OnSynthesisChannelClosed(NlsEvent* cbEvent, void* cbParam) {
  88. ParamCallBack* tmpParam = (ParamCallBack*)cbParam;
  89. // 演示如何打印/使用用户自定义参数示例
  90. printf("OnSynthesisChannelClosed: %s\n", tmpParam->binAudioFile.c_str());
  91. printf("OnSynthesisChannelClosed: %s\n", cbEvent->getAllResponse());
  92. tmpParam->audioFile.close();
  93. delete tmpParam; //识别流程结束,释放回调参数
  94. }
  95. //@brief 文本上报服务端之后, 收到服务端返回的二进制音频数据, SDK内部线程通过BinaryDataRecved事件上报给用户
  96. //@param cbEvent 回调事件结构, 详见nlsEvent.h
  97. //@param cbParam 回调自定义参数,默认为NULL, 可以根据需求自定义参数
  98. void OnBinaryDataRecved(NlsEvent* cbEvent, void* cbParam) {
  99. ParamCallBack* tmpParam = (ParamCallBack*)cbParam;
  100. if(tmpParam->startMs > 0 ) {
  101. // 重要提示: 一旦能够获取到语音流,比如第一次从服务端返回合成语音流,即可以开始使用了,比如播放或者其他处理,这里简单示例为保存到本地文件
  102. // 第一次收到语音流数据,计算TTS合成首包延迟。 另外此处计算首包延迟时也包括了start操作(即本程序连接公共云服务端的时间),而该时间受不同网络因素影响可能有较大差异
  103. uint64_t now = getNow();
  104. printf("first latency = %lld ms, task id = %s\n", (now - tmpParam->startMs) / 1000, cbEvent->getTaskId());
  105. tmpParam->startMs = 0;
  106. }
  107. // 演示如何打印/使用用户自定义参数示例
  108. printf("OnBinaryDataRecved: %s\n", tmpParam->binAudioFile.c_str());
  109. const std::vector<unsigned char>& data = cbEvent->getBinaryData(); // getBinaryData() 获取文本合成的二进制音频数据
  110. printf("OnBinaryDataRecved: status code=%d, task id=%s, data size=%d\n", cbEvent->getStatusCode(), cbEvent->getTaskId(), data.size());
  111. // 以追加形式将二进制音频数据写入文件
  112. if (data.size() > 0) {
  113. tmpParam->audioFile.write((char*)&data[0], data.size());
  114. }
  115. }
  116. //@brief 返回 tts 文本对应的日志信息,增量返回对应的字幕信息
  117. //@param cbEvent 回调事件结构, 详见nlsEvent.h
  118. //@param cbParam 回调自定义参数,默认为NULL, 可以根据需求自定义参数
  119. void OnMetaInfo(NlsEvent* cbEvent, void* cbParam) {
  120. ParamCallBack* tmpParam = (ParamCallBack*)cbParam;
  121. // 演示如何打印/使用用户自定义参数示例
  122. printf("OnBinaryDataRecved: %s\n", tmpParam->binAudioFile.c_str());
  123. printf("OnMetaInfo: task id=%s, respose=%s\n", cbEvent->getTaskId(), cbEvent->getAllResponse());
  124. }
  125. // 工作线程
  126. void* pthreadFunc(void* arg) {
  127. // 0: 从自定义线程参数中获取token, 配置文件等参数.
  128. ParamStruct* tst = (ParamStruct*)arg;
  129. if (tst == NULL) {
  130. printf("arg is not valid\n");
  131. return NULL;
  132. }
  133. // 1: 初始化自定义回调参数
  134. ParamCallBack* cbParam = new ParamCallBack;
  135. cbParam->binAudioFile = tst->audioFile;
  136. cbParam->audioFile.open(cbParam->binAudioFile.c_str(), std::ios::binary | std::ios::out);
  137. // 2: 创建语音识别SpeechSynthesizerRequest对象
  138. SpeechSynthesizerRequest* request = NlsClient::getInstance()->createSynthesizerRequest();
  139. if (request == NULL) {
  140. printf("createSynthesizerRequest failed.\n");
  141. cbParam->audioFile.close();
  142. return NULL;
  143. }
  144. request->setOnSynthesisCompleted(OnSynthesisCompleted, cbParam); // 设置音频合成结束回调函数
  145. request->setOnChannelClosed(OnSynthesisChannelClosed, cbParam); // 设置音频合成通道关闭回调函数
  146. request->setOnTaskFailed(OnSynthesisTaskFailed, cbParam); // 设置异常失败回调函数
  147. request->setOnBinaryDataReceived(OnBinaryDataRecved, cbParam); // 设置文本音频数据接收回调函数
  148. request->setOnMetaInfo(OnMetaInfo, cbParam); // 设置字幕信息
  149. request->setAppKey(tst->appkey.c_str());
  150. request->setText(tst->text.c_str()); // 设置待合成文本, 必填参数. 文本内容必须为UTF-8编码
  151. request->setVoice("siqi"); // 发音人, 包含"xiaoyun", "ruoxi", "xiaogang"等. 可选参数, 默认是xiaoyun,具体可用发音人可以参考文档介绍
  152. request->setVolume(50); // 音量, 范围是0~100, 可选参数, 默认50
  153. request->setFormat("wav"); // 音频编码格式, 可选参数, 默认是wav. 支持的格式pcm, wav, mp3
  154. request->setSampleRate(8000); // 音频采样率, 包含8000, 16000. 可选参数, 默认是16000
  155. request->setSpeechRate(0); // 语速, 范围是-500~500, 可选参数, 默认是0
  156. request->setPitchRate(0); // 语调, 范围是-500~500, 可选参数, 默认是0
  157. //request->setEnableSubtitle(true); //是否开启字幕,非必须,需要注意的是并不是所有发音人都支持字幕功能
  158. request->setToken(tst->token.c_str()); // 设置账号校验token, 必填参数
  159. cbParam->startMs = getNow();
  160. // 3: start()为异步操作。成功返回BinaryRecv事件。失败返回TaskFailed事件。
  161. if (request->start() < 0) {
  162. printf("start() failed. may be can not connect server. please check network or firewalld\n");
  163. NlsClient::getInstance()->releaseSynthesizerRequest(request); // start()失败,释放request对象
  164. cbParam->audioFile.close();
  165. return NULL;
  166. }
  167. //6: 通知云端数据发送结束.
  168. //stop()为异步操作.失败返回TaskFailed事件。
  169. request->stop();
  170. // 7: 识别结束, 释放request对象
  171. NlsClient::getInstance()->releaseSynthesizerRequest(request);
  172. return NULL;
  173. }
  174. // 合成单个文本数据
  175. int speechSynthesizerFile(const char* appkey) {
  176. //获取当前系统时间戳,判断token是否过期
  177. std::time_t curTime = std::time(0);
  178. if (g_expireTime - curTime < 10) {
  179. printf("the token will be expired, please generate new token by AccessKey-ID and AccessKey-Secret.\n");
  180. if (-1 == generateToken(g_akId, g_akSecret, &g_token, &g_expireTime)) {
  181. return -1;
  182. }
  183. }
  184. ParamStruct pa;
  185. pa.token = g_token;
  186. pa.appkey = appkey;
  187. // 注意: Windows平台下,合成文本中如果包含中文,请将本CPP文件设置为带签名的UTF-8编码或者GB2312编码
  188. pa.text = "今天天气很棒,适合去户外旅行.";
  189. pa.audioFile = "syAudio.wav";
  190. pthread_t pthreadId;
  191. // 启动一个工作线程, 用于识别
  192. pthread_create(&pthreadId, NULL, &pthreadFunc, (void *)&pa);
  193. pthread_join(pthreadId, NULL);
  194. return 0;
  195. }
  196. // 合成多个文本数据;
  197. // sdk多线程指一个文本数据对应一个线程, 非一个文本数据对应多个线程.
  198. // 示例代码为同时开启2个线程合成2个文件;
  199. // 免费用户并发连接不能超过2个;
  200. #define AUDIO_TEXT_NUMS 2
  201. #define AUDIO_TEXT_LENGTH 64
  202. #define AUDIO_FILE_NAME_LENGTH 32
  203. int speechSynthesizerMultFile(const char* appkey) {
  204. //获取当前系统时间戳,判断token是否过期
  205. std::time_t curTime = std::time(0);
  206. if (g_expireTime - curTime < 10) {
  207. printf("the token will be expired, please generate new token by AccessKey-ID and AccessKey-Secret.\n");
  208. if (-1 == generateToken(g_akId, g_akSecret, &g_token, &g_expireTime)) {
  209. return -1;
  210. }
  211. }
  212. const char syAudioFiles[AUDIO_TEXT_NUMS][AUDIO_FILE_NAME_LENGTH] = {"syAudio0.wav", "syAudio1.wav"};
  213. const char texts[AUDIO_TEXT_NUMS][AUDIO_TEXT_LENGTH] = {"今日天气真不错,我想去操作踢足球.", "明天有大暴雨,还是宅在家里看电影吧."};
  214. ParamStruct pa[AUDIO_TEXT_NUMS];
  215. for (int i = 0; i < AUDIO_TEXT_NUMS; i ++) {
  216. pa[i].token = g_token;
  217. pa[i].appkey = appkey;
  218. pa[i].text = texts[i];
  219. pa[i].audioFile = syAudioFiles[i];
  220. }
  221. std::vector<pthread_t> pthreadId(AUDIO_TEXT_NUMS);
  222. // 启动工作线程, 同时识别音频文件
  223. for (int j = 0; j < AUDIO_TEXT_NUMS; j++) {
  224. pthread_create(&pthreadId[j], NULL, &pthreadFunc, (void *)&(pa[j]));
  225. }
  226. for (int j = 0; j < AUDIO_TEXT_NUMS; j++) {
  227. pthread_join(pthreadId[j], NULL);
  228. }
  229. return 0;
  230. }
  231. int main(int arc, char* argv[]) {
  232. if (arc < 4) {
  233. printf("params is not valid. Usage: ./demo <your appkey> <your AccessKey ID> <your AccessKey Secret>\n");
  234. return -1;
  235. }
  236. std::string appkey = argv[1];
  237. g_akId = argv[2];
  238. g_akSecret = argv[3];
  239. // 根据需要设置SDK输出日志, 可选. 此处表示SDK日志输出至log-Synthesizer.txt, LogDebug表示输出所有级别日志
  240. int ret = NlsClient::getInstance()->setLogConfig("log-synthesizer", LogDebug);
  241. if (-1 == ret) {
  242. printf("set log failed\n");
  243. return -1;
  244. }
  245. //启动工作线程
  246. NlsClient::getInstance()->startWorkThread(4);
  247. // 合成单个文本
  248. speechSynthesizerFile(appkey.c_str());
  249. // 合成多个文本
  250. // speechSynthesizerMultFile(appkey.c_str());
  251. // 所有工作完成,进程退出前,释放nlsClient. 请注意, releaseInstance()非线程安全.
  252. NlsClient::releaseInstance();
  253. return 0;
  254. }