全部产品
云市场
云游戏

C++ SDK(新)

更新时间:2020-09-16 17:49:08

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

说明

  • 当前最新版本:3.0.8。发布日期:2020年1月9日。

    该版本只支持Linux平台,暂不支持Windows平台。

  • 请先阅读接口说明,详情请参见接口说明

  • 之前版本C++ SDK不支持长文本语音合成,本文以当前版本为例进行介绍。

下载安装

SDK下载:

下载CppSdk,文件包含如下几部分:

  • CMakeLists.txt:示例代码工程的CMakeList文件。

  • readme.txt:SDK说明。

  • release.log:版本说明。

  • version:版本号。

  • build.sh:示例编译脚本。

  • lib:SDK库文件。

  • build:编译目录。

  • 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

    实时音频流识别

编译运行:

  1. 安装工具的最低版本要求如下:

    1. Cmake 3.1

    2. Glibc 2.5

    3. Gcc 4.1.2

  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

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方法失败。请调用stop方法释放资源,重新开始识别流程。

服务端响应状态码

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

代码示例

说明

  • 示例中使用了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即将过期时重新生成即可,所有服务并发可共用一个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。
    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;
}