本文介绍如何使用阿里云智能语音服务提供的C++ SDK,包括SDK的安装方法及SDK代码示例。
使用说明
-
当前最新版本:3.0.10,支持Linux及Windows平台。发布日期:2021年06月29日。本文以当前版本为例进行介绍。
-
请先阅读接口说明,详情请参见 接口说明 。
下载安装
SDK下载:
可以 下载已经编译完成的SDK ,也可以使用 SDK源码 进行编译。
-
build.sh:示例编译脚本。
-
CMakeLists.txt:示例代码工程的CMakeList文件。
-
demo:示例,语音服务配置文件,如下表所示。
文件名
描述
speechRecognizerDemo.cpp
一句话识别示例
speechSynthesizerDemo.cpp
语音合成示例
speechTranscriberDemo.cpp
实时语音识别示例
speechLongSynthesizerDemo.cpp
长文本语音合成示例
test0.wav/test1.wav
测试音频(16k采样频率、16bit采样位数的音频文件)
-
include:SDK头文件,如下表所示。
文件名
描述
nlsClient.h
SDK实例
nlsEvent.h
回调事件说明
speechRecognizerRequest.h
一句话识别
speechSynthesizerRequest.h
语音合成/长文本语音合成
speechTranscriberRequest.h
实时音频流识别
-
lib:SDK库文件。
-
readme.md:SDK说明。
-
release.log:版本说明。
-
version:版本号。
编译运行:
-
安装工具的最低版本要求如下:
-
Cmake 3.1
-
Glibc 2.5
-
Gcc 4.1.2
-
-
在Linux终端运行如下脚本。
mkdir build cd build && cmake .. && make cd ../demo #生成示例可执行程序:srDemo(一句话识别)、stDemo(实时语音识别)、syDemo(语音合成)、syLongDemo(长文本语音合成)。 ./syLongDemo appkey <yourAccessKey Id> <yourAccessKey Secret> # 测试使用。
关键接口
-
基础接口
-
NlsClient:语音处理客户端,利用该客户端可以进行一句话识别、实时语音识别和语音合成的语音处理任务。该客户端为线程安全,建议全局仅创建一个实例。
-
NlsEvent:事件对象,您可以从中获取Request状态码、云端返回结果、失败信息等。
-
-
识别接口
SpeechSynthesizerRequest:语音合成请求对象,用于语音合成及长文本语音合成。
C++ SDK错误码
状态码 |
状态消息 |
原因 |
解决方案 |
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. |
JISO格式异常, 请通过日志查看具体的错误点。 |
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转码失败,常为系统问题,建议重新尝试。 |
服务端响应状态码
关于服务状态码,请参见 服务状态码 。
代码示例
-
示例中使用了SDK内置的默认外网访问服务URL,如果您使用阿里云上海ECS且需要使用内网访问服务URL,则在创建SpeechSynthesizerRequest的对象中设置内网访问的URL:
request->setUrl("ws://nls-gateway.cn-shanghai-internal.aliyuncs.com/ws/v1");
-
示例中将合成的音频保存在文件中,如果您需要播放音频且对实时性要求较高,建议使用流式播放,即边接收语音数据边播放,减少延时,而无需等待合成结束后再处理语音流。
-
完整示例参见SDK文件中demo目录的speechLongSynthesizerDemo.cpp文件。
#include <pthread.h>
#include <unistd.h>
#include <sys/time.h>
#include <ctime>
#include <string>
#include <vector>
#include <fstream>
#include "nlsClient.h"
#include "nlsEvent.h"
#include "speechSynthesizerRequest.h"
#include "nlsCommonSdk/Token.h"
using namespace AlibabaNlsCommon;
using AlibabaNls::NlsClient;
using AlibabaNls::NlsEvent;
using AlibabaNls::LogDebug;
using AlibabaNls::LogInfo;
using AlibabaNls::SpeechSynthesizerRequest;
// 自定义线程参数
struct ParamStruct {
std::string text;
std::string token;
std::string appkey;
std::string audioFile;
// 自定义事件回调参数
struct ParamCallBack {
std::string binAudioFile;
std::ofstream audioFile;
uint64_t startMs;
//全局维护一个服务鉴权Token和其对应的有效期时间戳,
//每次调用服务之前,首先判断Token是否已经过期,
//如果已经过期,则根据AccessKey ID和AccessKey Secret重新生成一个Token,并更新该全局的Token和其有效期时间戳。
//获取Token具体操作,请参见:https://help.aliyun.com/document_detail/450514.html
//注意:只需在Token即将过期时重新生成即可,所有服务并发可共用一个Token。
std::string g_akId = "";
std::string g_akSecret = "";
std::string g_token = "";
long g_expireTime = -1;
uint64_t getNow() {
struct timeval now;
gettimeofday(&now, NULL);
return now.tv_sec * 1000 * 1000 + now.tv_usec;
int generateToken(std::string akId, std::string akSecret, std::string* token, long* expireTime) {
NlsToken nlsTokenRequest;
nlsTokenRequest.setAccessKeyId(akId);
nlsTokenRequest.setKeySecret(akSecret);
if (-1 == nlsTokenRequest.applyNlsToken()) {
// 获取失败原因
printf("generateToken Failed: %s\n", nlsTokenRequest.getErrorMsg());
return -1;
*token = nlsTokenRequest.getToken();
*expireTime = nlsTokenRequest.getExpireTime();
return 0;
//@brief SDK在接收到云端返回合成结束消息时,其内部线程上报Completed事件。
//@note 上报Completed事件之后,SDK内部会关闭识别连接通道。
//@param cbEvent 回调事件结构,详见nlsEvent.h。
//@param cbParam 回调自定义参数,默认为NULL,可以根据需求自定义参数。
void OnSynthesisCompleted(NlsEvent* cbEvent, void* cbParam) {
ParamCallBack* tmpParam = (ParamCallBack*)cbParam;
// 演示如何打印/使用用户自定义参数示例。
printf("OnSynthesisCompleted: %s\n", tmpParam->binAudioFile.c_str());
// 获取消息的状态码,成功为0或者20000000,失败时对应失败的错误码。
// 当前任务的task id。
printf("OnSynthesisCompleted: status code=%d, task id=%s\n", cbEvent->getStatusCode(), cbEvent->getTaskId());
// 获取服务端返回的全部信息。
//printf("OnSynthesisCompleted: all response=%s\n", cbEvent->getAllResponse());
//@brief 合成过程发生异常时,SDK内部线程上报TaskFailed事件。
//@note 上报TaskFailed事件后,SDK内部会关闭识别连接通道。
//@param cbEvent 回调事件结构,详见nlsEvent.h。
//@param cbParam 回调自定义参数,默认为NULL,可以根据需求自定义参数。
void OnSynthesisTaskFailed(NlsEvent* cbEvent, void* cbParam) {
ParamCallBack* tmpParam = (ParamCallBack*)cbParam;
// 演示如何打印/使用用户自定义参数示例。
printf("OnSynthesisTaskFailed: %s\n", tmpParam->binAudioFile.c_str());
// 当前任务的task id。
printf("OnSynthesisTaskFailed: status code=%d, task id=%s, error message=%s\n", cbEvent->getStatusCode(), cbEvent->getTaskId(), cbEvent->getErrorMessage());
//@brief 识别结束或发生异常时,会关闭连接通道,SDK内部线程上报ChannelCloseed事件。
//@param cbEvent 回调事件结构,详见nlsEvent.h。
//@param cbParam 回调自定义参数,默认为NULL,可以根据需求自定义参数。
void OnSynthesisChannelClosed(NlsEvent* cbEvent, void* cbParam) {
ParamCallBack* tmpParam = (ParamCallBack*)cbParam;
// 演示如何打印/使用用户自定义参数示例。
printf("OnSynthesisChannelClosed: %s\n", tmpParam->binAudioFile.c_str());
printf("OnSynthesisChannelClosed: %s\n", cbEvent->getAllResponse());
tmpParam->audioFile.close();
delete tmpParam; //识别流程结束,释放回调参数。
//@brief 文本上报服务端之后,收到服务端返回的二进制音频数据,SDK内部线程通过BinaryDataRecved事件上报给您。
//@param cbEvent 回调事件结构,详见nlsEvent.h。
//@param cbParam 回调自定义参数,默认为NULL,可以根据需求自定义参数。
void OnBinaryDataRecved(NlsEvent* cbEvent, void* cbParam) {
ParamCallBack* tmpParam = (ParamCallBack*)cbParam;
if(tmpParam->startMs > 0 ) {
// 说明:一旦获取到语音流,如第一次从服务端返回合成语音流,即可开始进行播放或者其他处理,本示例为保存到本地文件。
// 第一次收到语音流数据,计算TTS合成首包延迟。另外此处计算首包延迟时也包括了start操作(即本程序连接公共云服务端的时间),而该时间受不同网络因素影响可能有较大差异。
uint64_t now = getNow();
printf("first latency = %lld ms, task id = %s\n", (now - tmpParam->startMs) / 1000, cbEvent->getTaskId());
tmpParam->startMs = 0;
// 演示如何打印/使用用户自定义参数示例。
printf("OnBinaryDataRecved: %s\n", tmpParam->binAudioFile.c_str());
const std::vector<unsigned char>& data = cbEvent->getBinaryData(); // getBinaryData()为获取文本合成的二进制音频数据。
printf("OnBinaryDataRecved: status code=%d, task id=%s, data size=%d\n", cbEvent->getStatusCode(), cbEvent->getTaskId(), data.size());
// 以追加形式将二进制音频数据写入文件。
if (data.size() > 0) {
tmpParam->audioFile.write((char*)&data[0], data.size());
//@brief 返回tts文本对应的日志信息,增量返回对应的字幕信息。
//@param cbEvent 回调事件结构,详见nlsEvent.h。
//@param cbParam 回调自定义参数,默认为NULL,可以根据需求自定义参数。
void OnMetaInfo(NlsEvent* cbEvent, void* cbParam) {
ParamCallBack* tmpParam = (ParamCallBack*)cbParam;
// 演示如何打印/使用用户自定义参数示例。
printf("OnBinaryDataRecved: %s\n", tmpParam->binAudioFile.c_str());
printf("OnMetaInfo: task id=%s, respose=%s\n", cbEvent->getTaskId(), cbEvent->getAllResponse());
// 工作线程
void* pthreadFunc(void* arg) {
// 0:从自定义线程参数中获取token,配置文件等参数。
ParamStruct* tst = (ParamStruct*)arg;
if (tst == NULL) {
printf("arg is not valid\n");
return NULL;
// 1:初始化自定义回调参数。
ParamCallBack* cbParam = new ParamCallBack;
cbParam->binAudioFile = tst->audioFile;
cbParam->audioFile.open(cbParam->binAudioFile.c_str(), std::ios::binary | std::ios::out);
// 2:创建语音识别SpeechSynthesizerRequest对象。
SpeechSynthesizerRequest* request = NlsClient::getInstance()->createSynthesizerRequest(AlibabaNls::LongTts);
if (request == NULL) {
printf("createSynthesizerRequest failed.\n");
cbParam->audioFile.close();
return NULL;
request->setOnSynthesisCompleted(OnSynthesisCompleted, cbParam); // 设置音频合成结束回调函数
request->setOnChannelClosed(OnSynthesisChannelClosed, cbParam); // 设置音频合成通道关闭回调函数
request->setOnTaskFailed(OnSynthesisTaskFailed, cbParam); // 设置异常失败回调函数
request->setOnBinaryDataReceived(OnBinaryDataRecved, cbParam); // 设置文本音频数据接收回调函数
request->setOnMetaInfo(OnMetaInfo, cbParam); // 设置字幕信息
request->setAppKey(tst->appkey.c_str());
request->setText(tst->text.c_str()); // 设置待合成文本,必填参数。文本内容必须为UTF-8编码。
request->setVoice("siqi"); // 发音人。可选参数,默认是xiaoyun。注意:C++ SDK不支持调用超高清场景对应的发音人(例如"zhiqi"),如需调用请使用restfulAPI方式。
request->setVolume(50); // 音量,范围是0~100,可选参数,默认50。
request->setFormat("wav"); // 音频编码格式,可选参数,默认是wav。支持的格式PCM、WAV和MP3。
request->setSampleRate(8000); // 音频采样率:8000/16000。可选参数,默认是16000。
request->setSpeechRate(0); // 语速,范围是-500~500,可选参数,默认是0。
request->setPitchRate(0); // 语调,范围是-500~500,可选参数,默认是0。
//request->setEnableSubtitle(true); //是否开启字幕,非必须,需要注意的是并不是所有发音人都支持字幕功能。
request->setToken(tst->token.c_str()); // 设置账号校验token,必填参数。
cbParam->startMs = getNow();
// 3:start()为异步操作。成功返回BinaryRecv事件。失败返回TaskFailed事件。
if (request->start() < 0) {
printf("start() failed. may be can not connect server. please check network or firewalld\n");
NlsClient::getInstance()->releaseSynthesizerRequest(request); // start()失败,释放request对象
cbParam->audioFile.close();
return NULL;
//4:通知云端数据发送结束。
//stop()为异步操作,失败返回TaskFailed事件。
request->stop();
// 5:识别结束,释放request对象。
NlsClient::getInstance()->releaseSynthesizerRequest(request);
return NULL;
// 合成单个文本数据
int speechLongSynthesizerFile(const char* appkey) {
//获取当前系统时间戳,判断token是否过期。
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 (-1 == generateToken(g_akId, g_akSecret, &g_token, &g_expireTime)) {
return -1;
ParamStruct pa;
pa.token = g_token;
pa.appkey = appkey;
// 注意:Windows平台下,合成文本中如果包含中文,请将本CPP文件设置为带签名的UTF-8编码或者GB2312编码。
pa.text = "今天天气很棒,适合去户外旅行.";
pa.audioFile = "syAudio.wav";
pthread_t pthreadId;
// 启动一个工作线程,用于识别。
pthread_create(&pthreadId, NULL, &pthreadFunc, (void *)&pa);
pthread_join(pthreadId, NULL);
return 0;
// 合成多个文本数据
// SDK多线程指一个文本数据对应一个线程,非一个文本数据对应多个线程。
// 示例代码为同时开启2个线程,合成2个文件。
#define AUDIO_TEXT_NUMS 2
#define AUDIO_TEXT_LENGTH 64
#define AUDIO_FILE_NAME_LENGTH 32
int speechLongSynthesizerMultFile(const char* appkey) {
//获取当前系统时间戳,判断token是否过期。
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 (-1 == generateToken(g_akId, g_akSecret, &g_token, &g_expireTime)) {
return -1;
const char syAudioFiles[AUDIO_TEXT_NUMS][AUDIO_FILE_NAME_LENGTH] = {"syAudio0.wav", "syAudio1.wav"};
const char texts[AUDIO_TEXT_NUMS][AUDIO_TEXT_LENGTH] = {"今日天气真不错,我想去操作踢足球.", "明天有大暴雨,还是宅在家里看电影吧."};
ParamStruct pa[AUDIO_TEXT_NUMS];
for (int i = 0; i < AUDIO_TEXT_NUMS; i ++) {
pa[i].token = g_token;
pa[i].appkey = appkey;
pa[i].text = texts[i];
pa[i].audioFile = syAudioFiles[i];
std::vector<pthread_t> pthreadId(AUDIO_TEXT_NUMS);
// 启动工作线程,同时识别音频文件。
for (int j = 0; j < AUDIO_TEXT_NUMS; j++) {
pthread_create(&pthreadId[j], NULL, &pthreadFunc, (void *)&(pa[j]));
for (int j = 0; j < AUDIO_TEXT_NUMS; j++) {
pthread_join(pthreadId[j], NULL);
return 0;
int main(int arc, char* argv[]) {
if (arc < 4) {
printf("params is not valid. Usage: ./demo <your appkey> <your AccessKey ID> <your AccessKey Secret>\n");
return -1;
std::string appkey = argv[1];
g_akId = argv[2];
g_akSecret = argv[3];
// 根据需要设置SDK输出日志,可选。此处表示SDK日志输出至log-Synthesizer.txt,LogDebug表示输出所有级别日志。
int ret = NlsClient::getInstance()->setLogConfig("log-longsynthesizer", LogDebug);
if (-1 == ret) {
printf("set log failed\n");
return -1;
//启动工作线程
NlsClient::getInstance()->startWorkThread(4);
/// 注意:长文本语音合成目前没有免费试用版,必须开通商业版长文本语音合成之后才能进行测试。
// 合成单个文本
speechLongSynthesizerFile(appkey.c_str());
// 合成多个文本
// speechLongSynthesizerMultFile(appkey.c_str());
// 所有工作完成,进程退出前,释放nlsClient。请注意,releaseInstance()非线程安全。
NlsClient::releaseInstance();
return 0;
}