C++ SDK

Updated at:

This topic describes how to use the C++ software development kit (SDK) for Alibaba Cloud Intelligent Speech Interaction. It includes installation methods and code examples.

Prerequisites

Download and install

Download the SDK

You can obtain the SDK in one of the following two ways.

  • Method 1: Obtain the latest source code from GitHub. For more information about how to compile and run the code, see the following sections or the readme.md file in the source code.

    git clone --depth 1 https://github.com/aliyun/alibabacloud-nls-cpp-sdk
  • Method 2: Download the required SDK package from the following table. The SDK source code package contains the original code. You must compile the code to generate the library files required for integration. The other packages for specific platforms contain the required library files and header files and do not require compilation.

    Latest SDK package

    Platform

    MD5

    alibabacloud-nls-cpp-sdk3.3.0c-master_58ba245.zip

    SDK source code

    4dbd8c7fc5581f5e6c46fd14bb68998e

    NlsCppSdk_Linux-x86_64_3.3.0c_58ba245.tar.gz

    Linux x86_64

    f43507b82cf4ba1a9f902429dd780fac

    Note

    The Linux-x86_64 version is compiled with gcc 8.4.0 and _GLIBCXX_USE_CXX11_ABI=0. You can recompile the source code package by following the instructions in the readme.md file.

    Where:

    • alibabacloud-nls-cpp-sdk<version>-master_<github commit id>.zip is the SDK source code package.

    • NlsCppSdk_<Platform>_<Version>_<github commit id>.tar.gz is the SDK package for the corresponding platform. For more information, see the readme.md file in the package.

SDK package files

  • scripts/build_linux.sh: An example compilation script for the Linux platform.

  • CMakeLists.txt: The CMakeLists.txt file for an example code-based project for the Linux or Android platform.

  • demo folder: Contains integration code examples in the SDK package. The following table uses the Linux platform as an example.

    File name

    Description

    speechRecognizerDemo.cpp

    Short sentence recognition example.

    speechSynthesizerDemo.cpp

    Speech synthesis example.

    speechTranscriberDemo.cpp

    Real-time speech recognition example.

    flowingSynthesizerDemo.cpp

    Streaming/Long-text-to-speech synthesis example.

    fileTransferDemo.cpp

    Audio file recognition example.

  • resource folder: Contains sample audio files for the Voice Service. You can use these files for functional testing.

    File name

    Description

    • test0.wav

    • test1.wav

    • test2.wav

    • test3.wav

    Test audio files (16 kHz audio sampling rate, 16-bit audio bit depth).

  • include: Contains the SDK header files.

    File name

    Description

    nlsClient.h

    SDK instance.

    nlsEvent.h

    Callback event descriptions.

    nlsGlobal.h

    Global SDK header file.

    nlsToken.h

    SDK Access Token instance.

    iNlsRequest.h

    Basic NLS request header file.

    speechRecognizerRequest.h

    Short sentence recognition.

    speechSynthesizerRequest.h

    Speech synthesis and long-text-to-speech synthesis.

    speechTranscriberRequest.h

    Real-time audio stream recognition.

    flowingSynthesizerRequest.h

    Streaming/Long-text-to-speech synthesis.

    FileTrans.h

    Audio file recognition.

  • lib: Contains the SDK library files.

  • readme.md: The SDK instructions.

  • release.log: The release notes.

  • version: The version number.

Compile and run (Linux platform)

  1. The following minimum tool versions are required:

    • CMake 3.0

    • Glibc 2.5

    • Gcc 4.8.5

  2. Run the following scripts in the Linux terminal.

    1. Go to the root directory of the SDK source code.

    2. Generate the SDK library files and executable programs: srDemo (short sentence recognition), stDemo (real-time speech recognition), syDemo (speech synthesis), daDemo (speech interaction), and fsDemo (streaming or long-text-to-speech synthesis).

      ./scripts/build_linux.sh
    3. View the example usage.

      cd build/demo 
      ./fsDemo 

Key interfaces

Basic interfaces

  • NlsClient: The speech processing client. You can use this client for speech processing tasks such as short sentence recognition, real-time speech recognition, and speech synthesis. This client is thread-safe. We recommend that you create only one global instance.

    Interface

    Version enabled

    Description

    getInstance

    2.x

    Gets (creates) an NlsClient instance.

    setLogConfig

    2.x

    Sets the log file and storage path.

    setDirectHost

    3.x

    Skips DNS resolution and directly sets the server's IPv4 address. If you call this interface, call it before startWorkThread.

    setAddrInFamily

    3.1.12

    Sets the type of the socket address structure. The default value is AF_INET, which returns only IPv4-related address information. Call this interface before startWorkThread.

    setUseSysGetAddrInfo

    3.1.13

    If the DNS of libevent does not meet requirements and cannot complete DNS resolution, call this interface to switch to the system's interface. Call this interface before startWorkThread.

    calculateUtf8Chars

    3.1.14

    Counts the number of characters in a text. You must pass text content with UTF-8 encoding. One Chinese character, one English letter, or one punctuation mark is counted as one character.

    setSyncCallTimeout

    3.1.17

    Sets the timeout period (in ms) for synchronous call mode. The default value is 0, which disables synchronous mode. In this mode, start() returns only after all server-side results are received, and stop() returns only after the close() callback is received. Call this interface before startWorkThread. Note: Enabling synchronous mode significantly reduces interface efficiency. Do not enable this mode in high-concurrency scenarios.

    setPreconnectedPool

    3.3.0

    Sets a pre-connection pool for each domain name URL. After each request is complete, the connection is internally maintained and reused. This reduces the connection time before each request and significantly lowers the first-packet latency. This setting conflicts with the long-connection mode and will disable it if it is already set. Do not use this mode for Tingwu scenarios. Call this interface before startWorkThread.

    startWorkThread

    3.x

    Starts the worker threads. The default value is 1, which starts one thread. A value of -1 starts a number of threads equal to the number of CPU cores. In high-concurrency scenarios, we recommend that you set this to -1. This can be considered the initialization of the NlsClient instance and must be called.

    getVersion

    3.x

    Gets the SDK version number.

    releaseInstance

    2.x

    Destroys the NlsClient object instance.

    createFlowingSynthesizerRequest

    3.2

    Creates a long-text-to-speech synthesis object. It is thread-safe and supports high-concurrency requests.

    releaseFlowingSynthesizerRequest

    3.2

    Destroys the long-text-to-speech synthesis object. Call this after the closed event of the current request.

  • NlsToken: Creates a Token object to request a token ID. When you request a new token, you must first obtain a valid timestamp. If the token expires, you must request a new one. Requesting a token multiple times within its validity period can return an incorrect token ID, which makes the token unusable.

    Interface

    Description

    setAccessKeyId

    Sets the AccessKey ID of your Alibaba Cloud account.

    setKeySecret

    Sets the AccessKey secret of your Alibaba Cloud account.

    setDomain

    Sets the domain name. Optional.

    setServerVersion

    Sets the API version. Optional.

    setServerResourcePath

    Sets the service path. Optional.

    setRegionId

    Sets the service region ID. Optional.

    setAction

    Sets the feature. Optional.

    applyNlsToken

    Requests a Token ID.

    getToken

    Gets the Token ID.

    getExpireTime

    Gets the token's expiration UNIX timestamp (in seconds).

    getErrorMsg

    Gets the error message.

  • NlsEvent: The event object. You can obtain the request status code, server-side response, failure information, and other details from this object.

    Interface

    Description

    getStatusCode

    Gets the status code. A normal status is 0 or 20000000. A failure corresponds to a specific error code.

    getErrorMessage

    In the TaskFailed callback, gets the error message for a failed NlsRequest operation.

    getTaskId

    Gets the TaskId of the task.

    getBinaryData

    Gets the binary data returned from the cloud.

    getAllResponse

    Gets the full response from the cloud.

Long-text synthesis interfaces

The interface descriptions are based on the content of flowingSynthesizerRequest.h.

Interface

Version enabled

Description

setOnSynthesisStarted

3.2

Sets the callback function for when long-text-to-speech synthesis starts.

setOnSynthesisCompleted

3.2

Sets the callback function for when speech synthesis is completed.

setOnChannelClosed

2.x

Sets the callback function for when the channel is closed.

setOnTaskFailed

2.x

Sets the callback function for errors.

setOnSentenceBegin

3.2

The callback function for when the server detects the beginning of a sentence.

setOnSentenceEnd

3.2

The callback function for when the server detects the end of a sentence. It returns the full timestamp for that sentence.

setOnBinaryDataReceived

2.x

Sets the callback function for receiving binary audio data from speech synthesis.

setOnSentenceSynthesis

3.2

The callback function that incrementally returns the speech synthesis result. It includes the latest audio and timestamp, full intra-sentence data, and incremental inter-sentence data.

setOnMessage

3.1.16

Sets the callback function for the server-side response message. All callbacks are output from this callback for you to parse. Optional. After setting, you must call setEnableOnMessage to enable it.

setAppKey

2.x

Sets the AppKey.

setToken

2.x

Token authentication. All requests must be authenticated using the SetToken method.

setTokenExpirationTime

3.3.0

Sets the expiration time of the token. This is effective only when the pre-connection pool feature is enabled. It is used to refresh the nodes within the pre-connection pool. If not set, the expiration time is the timestamp when the token was added to the pool plus 12 hours.

setUrl

2.x

Sets the service URL. Optional.

sendText

3.2

In a single streaming TTS session, a single synthesis request cannot exceed 5,000 characters, and the total cannot exceed 100,000 characters. One Chinese character, one English letter, one punctuation mark, or one space between sentences is counted as one character.

setVoice

2.x

Sets the voice.

setVolume

2.x

Sets the volume.

setFormat

2.x

Sets the audio data encoding format. The default is PCM. Supported formats include PCM, WAV, and MP3.

setSampleRate

2.x

Sets the audio sampling rate.

setSpeechRate

2.x

Sets the speech rate.

setPitchRate

2.x

Sets the pitch.

setEnableSubtitle

2.x

Specifies whether to enable the caption feature.

setPayloadParam

2.x

Sets parameters. The input parameter is a JSON-formatted string.

setTimeout

2.x

Sets the connection timeout period. The default is 5000 ms.

setContextParam

2.x

Sets custom user parameters. The input parameter is a JSON-formatted string.

AppendHttpHeaderParam

2.x

Sets custom HTTP header parameters for the WebSocket phase.

setSendTimeout

3.1.14

Sets the sending timeout period. The default is 5000 ms.

setEnableOnMessage

3.1.16

Enables the callback for messages returned by the server.

setSingleRoundText

3.3.0

Sets the text for the current long-text-to-speech synthesis.

getTaskId

3.1.17

Gets the task_id of the current request.

start

2.x

Starts the FlowingSynthesizerRequest.

stop

3.2

Ends the synthesis task. You must wait for the synthesis to complete. You do not need to call this interface in long-text-to-speech synthesis.

cancel

2.x

Directly closes the speech synthesis process without confirming with the server.

Code example

Note
  • The example saves the synthesized audio to a file. To play the audio with high real-time performance, we recommend that you use streaming playback. This method plays audio data as it is received, which reduces latency because you do not need to wait for the synthesis to finish before you process the audio stream.

  • For the complete example, see the flowingSynthesizerRequest.cpp file in the demo folder of the SDK package.

  • Before you call the interface, you must configure the environment variables to read the access credentials. The environment variable names for the Intelligent Speech Interaction AccessKey ID, AccessKey secret, and AppKey are NLS_AK_ENV, NLS_SK_ENV, and NLS_APPKEY_ENV.

Code example

#include <errno.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <ctime>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>

#include "nlsClient.h"
#include "nlsEvent.h"
#include "nlsToken.h"
#include "flowingSynthesizerRequest.h"

#define SAMPLE_RATE_16K 16000
#define DEFAULT_STRING_LEN 512
#define AUDIO_TEXT_LENGTH 2048

/**
 * Maintain a global service authentication token and its expiration timestamp.
 * Before each service call, check if the token has expired.
 * If it has, regenerate a token using the AccessKey ID and AccessKey
 * secret, and update the global token and its expiration timestamp.
 *
 * Note: Do not regenerate a new token before every service call. Regenerate it only when it is about to expire. All concurrent service calls can share one token.
 */
// Custom thread parameters
struct ParamStruct {
  char text[AUDIO_TEXT_LENGTH];
  char token[DEFAULT_STRING_LEN];
  char appkey[DEFAULT_STRING_LEN];
  char url[DEFAULT_STRING_LEN];

  pthread_mutex_t mtx;
};

// Custom callback parameters
struct ParamCallBack {
 public:
  explicit ParamCallBack(ParamStruct* param) {
    tParam = param;
    pthread_mutex_init(&mtxWord, NULL);
    pthread_cond_init(&cvWord, NULL);
  };
  ~ParamCallBack() {
    tParam = NULL;
    pthread_mutex_destroy(&mtxWord);
    pthread_cond_destroy(&cvWord);
  };

  pthread_mutex_t mtxWord;
  pthread_cond_t cvWord;

  ParamStruct* tParam;
};

std::string g_appkey = "";
std::string g_akId = "";
std::string g_akSecret = "";
std::string g_token = "";
std::string g_url = "";
std::string g_voice = "xiaoyun";
int g_threads = 1;
bool g_save_audio = true;
std::string g_text = "";
std::string g_format = "wav";
long g_expireTime = -1;
static int sample_rate = SAMPLE_RATE_16K;
static bool enableSubtitle = false;

/**
 * Regenerate a token and get its expiration timestamp based on the AccessKey ID and AccessKey secret
 */
int generateToken(std::string akId, std::string akSecret, std::string* token,
                  long* expireTime) {
  AlibabaNlsCommon::NlsToken nlsTokenRequest;
  nlsTokenRequest.setAccessKeyId(akId);
  nlsTokenRequest.setKeySecret(akSecret);

  int retCode = nlsTokenRequest.applyNlsToken();
  /* Get the failure cause */
  if (retCode < 0) {
    std::cout << "Failed error code: " << retCode
              << "  error msg: " << nlsTokenRequest.getErrorMsg() << std::endl;
    return retCode;
  }

  *token = nlsTokenRequest.getToken();
  *expireTime = nlsTokenRequest.getExpireTime();

  return 0;
}

void OnSynthesisStarted(AlibabaNls::NlsEvent* cbEvent, void* cbParam) {
  std::cout << "OnSynthesisStarted:"
            << "  status code: " << cbEvent->getStatusCode()
            << "  task id: " << cbEvent->getTaskId()
            << "  all response:" << cbEvent->getAllResponse() << std::endl;
}

/**
 * @brief When the SDK receives a synthesis completion message from the cloud, the SDK's internal thread reports a Completed event.
 * @note After the Completed event is reported, the SDK closes the recognition channel.
 * @param cbEvent Callback event structure. For more information, see nlsEvent.h.
 * @param cbParam Custom callback parameters. The default is NULL. You can customize parameters as needed.
 * @return
 */
void OnSynthesisCompleted(AlibabaNls::NlsEvent* cbEvent, void* cbParam) {
  std::cout
      << "OnSynthesisCompleted: "
      << ", status code: "
      << cbEvent
             ->getStatusCode()  // Get the status code of the message. 0 or 20000000 indicates success. A failure corresponds to a specific error code.
      << ", task id: "
      << cbEvent->getTaskId()  // The task ID of the current task. We recommend that you print this for troubleshooting.
      << std::endl;
  std::cout << "OnSynthesisCompleted: All response:"
            << cbEvent->getAllResponse()
            << std::endl;  // Get the complete information returned by the server.
}

/**
 * @brief If an exception occurs during synthesis, the SDK's internal thread reports a TaskFailed event.
 * @note After the TaskFailed event is reported, the SDK closes the recognition channel.
 * @param cbEvent Callback event structure. For more information, see nlsEvent.h.
 * @param cbParam Custom callback parameters. The default is NULL. You can customize parameters as needed.
 * @return
 */
void OnSynthesisTaskFailed(AlibabaNls::NlsEvent* cbEvent, void* cbParam) {
  FILE* failed_stream = fopen("synthesisTaskFailed.log", "a+");
  if (failed_stream) {
    char outbuf[1024] = {0};
    snprintf(outbuf, sizeof(outbuf),
             "OnSynthesisTaskFailed status code:%d task id:%s error mesg:%s\n",
             cbEvent->getStatusCode(), cbEvent->getTaskId(),
             cbEvent->getErrorMessage());
    std::cout << outbuf << std::endl;
    fwrite(outbuf, strlen(outbuf), 1, failed_stream);
    fclose(failed_stream);
  }
}

/**
 * @brief When recognition ends or an exception occurs, the connection channel is closed,
 * and the SDK's internal thread reports a ChannelClosed event.
 * @param cbEvent Callback event structure. For more information, see nlsEvent.h.
 * @param cbParam Custom callback parameters. The default is NULL. You can customize parameters as needed.
 * @return
 */
void OnSynthesisChannelClosed(AlibabaNls::NlsEvent* cbEvent, void* cbParam) {
  ParamCallBack* tmpParam = static_cast<ParamCallBack*>(cbParam);
  if (tmpParam) {
    std::cout << "OnSynthesisChannelClosed: "
              << ", All response: " << cbEvent->getAllResponse()
              << std::endl;  // Get the complete information returned by the server.
  }
}

/**
 * @brief After the text is sent to the server, the SDK receives binary audio data from the server.
 * The SDK's internal thread reports this to the user through a BinaryDataRecved event.
 * @param cbEvent Callback event structure. For more information, see nlsEvent.h.
 * @param cbParam Custom callback parameters. The default is NULL. You can customize parameters as needed.
 * @return
 * @notice Note: Do not perform blocking operations here. Only transfer the audio data. Performing too many operations in this callback
 *         will block subsequent data callbacks and the completed event callback.
 */
void OnBinaryDataRecved(AlibabaNls::NlsEvent* cbEvent, void* cbParam) {
  std::vector<unsigned char> data =
      cbEvent->getBinaryData();  // getBinaryData() gets the binary audio data of the synthesized text.
  std::cout
      << "  OnBinaryDataRecved: status code: "
      << cbEvent
             ->getStatusCode()  // Get the status code of the message. 0 or 20000000 indicates success. A failure corresponds to a specific error code.
      << ", taskId: "
      << cbEvent->getTaskId()  // The task ID of the current task. We recommend that you print this for troubleshooting.
      << ", data size: " << data.size()  // The size of the data.
      << std::endl;

  if (g_save_audio && data.size() > 0) {
    // Append the binary audio data to a file.
    std::string dir = "./tts_audio";
    if (access(dir.c_str(), 0) == -1) {
      mkdir(dir.c_str(), S_IRWXU);
    }
    char file_name[256] = {0};
    snprintf(file_name, 256, "%s/%s.%s", dir.c_str(), cbEvent->getTaskId(),
             g_format.c_str());
    FILE* tts_stream = fopen(file_name, "a+");
    if (tts_stream) {
      fwrite((char*)&data[0], data.size(), 1, tts_stream);
      fclose(tts_stream);
    }
  }
}

void OnSentenceBegin(AlibabaNls::NlsEvent* cbEvent, void* cbParam) {
  std::cout
      << "OnSentenceBegin "
      << "Response: "
      << cbEvent
             ->getAllResponse()  // Get the status code of the message. 0 or 20000000 indicates success. A failure corresponds to a specific error code.
      << std::endl;
}

void OnSentenceEnd(AlibabaNls::NlsEvent* cbEvent, void* cbParam) {
  std::cout
      << "OnSentenceEnd "
      << "Response: "
      << cbEvent
             ->getAllResponse()  // Get the status code of the message. 0 or 20000000 indicates success. A failure corresponds to a specific error code.
      << std::endl;
}

/**
 * @brief Returns log information corresponding to the TTS text and incrementally returns the corresponding caption information.
 * @param cbEvent Callback event structure. For more information, see nlsEvent.h.
 * @param cbParam Custom callback parameters. The default is NULL. You can customize parameters as needed.
 * @return
 */
void OnSentenceSynthesis(AlibabaNls::NlsEvent* cbEvent, void* cbParam) {
  std::cout
      << "OnSentenceSynthesis "
      << "Response: "
      << cbEvent
             ->getAllResponse()  // Get the status code of the message. 0 or 20000000 indicates success. A failure corresponds to a specific error code.
      << std::endl;
}

/**
 * @brief All information returned by the server is provided through this callback.
 * @param cbEvent Callback event structure. For more information, see nlsEvent.h.
 * @param cbParam Custom callback parameters. The default is NULL. You can customize parameters as needed.
 * @return
 */
void onMessage(AlibabaNls::NlsEvent* cbEvent, void* cbParam) {
  std::cout << "onMessage: All response:" << cbEvent->getAllResponse()
            << std::endl;
}

/**
 * @brief Worker thread in short-connection mode.
 *        The process loops as follows:
 *        createFlowingSynthesizerRequest   <----|
 *                   |                              |
 *           request->setSingleRoundText(text to merge)  |
 *                   |                              |
 *           request->start()                       |
 *                   |                              |
 *           Receive OnSynthesisChannelClosed callback |
 *                   |                              |
 *      releaseFlowingSynthesizerRequest(request) --|
 */
void* pthreadSingleRoundFunc(void* arg) {
  int testCount = 0;  // Counts the number of runs to exit after the configured number of loops.
  bool timedwait_flag = false;

  // Get the token, configuration files, and other parameters from the custom thread parameters.
  ParamStruct* tst = static_cast<ParamStruct*>(arg);
  if (tst == NULL) {
    std::cout << "arg is not valid." << std::endl;
    return NULL;
  }

  pthread_mutex_init(&(tst->mtx), NULL);

  // Initialize custom callback parameters.
  ParamCallBack cbParam(tst);

  /*
   * 1. Create a FlowingSynthesizerRequest object for long-text-to-speech synthesis.
   *
   * For more information about long-text-to-speech synthesis, see:
   * https://help.aliyun.com/zh/isi/developer-reference/streaming-text-to-speech-synthesis/
   */

  AlibabaNls::FlowingSynthesizerRequest* request =
      AlibabaNls::NlsClient::getInstance()->createFlowingSynthesizerRequest();
  if (request == NULL) {
    std::cout << "createFlowingSynthesizerRequest failed." << std::endl;
    return NULL;
  }

  /*
   * 2. Set the callbacks for receiving results.
   */
  // Set the callback function for when audio synthesis can start.
  request->setOnSynthesisStarted(OnSynthesisStarted, &cbParam);
  // Set the callback function for when audio synthesis is completed.
  request->setOnSynthesisCompleted(OnSynthesisCompleted, &cbParam);
  // Set the callback function for when the audio synthesis channel is closed.
  request->setOnChannelClosed(OnSynthesisChannelClosed, &cbParam);
  // Set the callback function for exceptions and failures.
  request->setOnTaskFailed(OnSynthesisTaskFailed, &cbParam);
  // Set the callback function for receiving text and audio data.
  request->setOnBinaryDataReceived(OnBinaryDataRecved, &cbParam);
  // Set the caption information.
  request->setOnSentenceSynthesis(OnSentenceSynthesis, &cbParam);
  // Beginning of a sentence.
  request->setOnSentenceBegin(OnSentenceBegin, &cbParam);
  // End of a sentence.
  request->setOnSentenceEnd(OnSentenceEnd, &cbParam);
  // Set the callback function for all messages returned by the server.
  // request->setOnMessage(onMessage, &cbParam);
  // Enable the callback function for all messages returned by the server. Other callbacks (except OnBinaryDataRecved) will be disabled.
  // request->setEnableOnMessage(true);

  /*
   * 3. Set the relevant parameters for the request.
   */
  // Voice, including "xiaoyun", "ruoxi", "xiaogang", etc. Optional. The default is xiaoyun.
  request->setVoice(g_voice.c_str());
  // Volume, range: 0-100. Optional. The default is 50.
  request->setVolume(50);
  // Audio encoding format. Optional. The default is wav. Supported formats: pcm, wav, mp3.
  request->setFormat("wav");
  // Audio sampling rate, including 8000 and 16000. Optional. The default is 16000.
  request->setSampleRate(sample_rate);
  // Speech rate, range: -500 to 500. Optional. The default is 0.
  request->setSpeechRate(0);
  // Pitch, range: -500 to 500. Optional. The default is 0.
  request->setPitchRate(0);
  // Enable captions.
  request->setEnableSubtitle(enableSubtitle);

  // Set the AppKey. Required. Apply for it on the official website.
  if (strlen(tst->appkey) > 0) {
    request->setAppKey(tst->appkey);
  }
  // Set the account verification token. Required.
  if (strlen(tst->token) > 0) {
    request->setToken(tst->token);
  }

  if (strlen(tst->url) > 0) {
    request->setUrl(tst->url);
  }
    
  // Set the connection timeout to 500 ms.
  // request->setTimeout(500);
  // Get the encoding format of the returned text.
  // const char* output_format = request->getOutputFormat();
  // std::cout << "text format: " << output_format << std::endl;

  /*
   * 4.
   * Set the text to be synthesized for long-text-to-speech synthesis.
   */
  if (request->setSingleRoundText(tst->text) != 0) {
    AlibabaNls::NlsClient::getInstance()->releaseFlowingSynthesizerRequest(
        request);  // If start() fails, release the request object.
    return NULL;
  }
    
  /*
   * 5.
   * start() is an asynchronous operation. If successful, it starts returning BinaryRecv events. If it fails, it returns a TaskFailed event.
   */
  std::cout << "start -> pid " << pthread_self() << std::endl;
  struct timespec outtime;
  struct timeval now;
  int ret = request->start();
  testCount++;
  if (ret < 0) {
    std::cout << "start failed. pid:" << pthread_self() << std::endl;
    const char* request_info = request->dumpAllInfo();
    if (request_info) {
      std::cout << "  all info: " << request_info << std::endl;
    }
    AlibabaNls::NlsClient::getInstance()->releaseFlowingSynthesizerRequest(
        request);  // If start() fails, release the request object.
    return NULL;
  } else {
    std::cout << "start success. pid " << pthread_self() << std::endl;
    /*
     * Wait for the started event to return, which indicates that start() was successful, and then send the audio data.
     * The voice server might not be able to process the current request in time, causing no callback to be returned within 10 seconds.
     * After 10 seconds, a TaskFailed callback is returned. Therefore, a timeout mechanism is required.
     */
    std::cout << "wait started callback." << std::endl;
    gettimeofday(&now, NULL);
    outtime.tv_sec = now.tv_sec + 10;
    outtime.tv_nsec = now.tv_usec * 1000;
    pthread_mutex_lock(&(cbParam.mtxWord));
    if (ETIMEDOUT == pthread_cond_timedwait(&(cbParam.cvWord),
                                            &(cbParam.mtxWord), &outtime)) {
      std::cout << "start timeout" << std::endl;
      timedwait_flag = true;
      pthread_mutex_unlock(&(cbParam.mtxWord));
      // If the start() call times out, cancel() cancels the current request.
      request->cancel();
      return NULL;
    }
    pthread_mutex_unlock(&(cbParam.mtxWord));
  }

  /*
   * 6. If start() is successful, wait to receive all synthesized data.
   *    The stop() interface is not meaningful here. The process will run to completion whether it is called or not.
   *    cancel() stops the task immediately and does not return a callback. A TaskFailed event is returned on failure.
   */
  //    ret = request->cancel();
  ret = request->stop();

  /*
   * Start waiting to receive all synthesized data.
   */
  if (ret == 0) {
    /*
     * Wait for the started event to return, which indicates that start() was successful, and then send the audio data.
     * The voice server might not be able to process the current request in time, causing no callback to be returned within 10 seconds.
     * After 10 seconds, a TaskFailed callback is returned. Therefore, a timeout mechanism is required.
     */
    // Wait for the closed event before releasing, otherwise a crash may occur.
    std::cout << "wait closed callback." << std::endl;
    /*
     * The voice server might not be able to process the current request in time, causing no callback to be returned within 10 seconds.
     * After 10 seconds, a TaskFailed callback is returned with the error message:
     * "Gateway:IDLE_TIMEOUT:Websocket session is idle for too long time,
     * the last directive is 'XXXX'!" Therefore, a timeout mechanism is required.
     */
    gettimeofday(&now, NULL);
    outtime.tv_sec = now.tv_sec + 30;
    outtime.tv_nsec = now.tv_usec * 1000;
    // Wait for the closed event before releasing, otherwise a crash may occur.
    pthread_mutex_lock(&(cbParam.mtxWord));
    if (ETIMEDOUT == pthread_cond_timedwait(&(cbParam.cvWord),
                                            &(cbParam.mtxWord), &outtime)) {
      std::cout << "stop timeout" << std::endl;
      pthread_mutex_unlock(&(cbParam.mtxWord));
      return NULL;
    }
    pthread_mutex_unlock(&(cbParam.mtxWord));
  } else {
    std::cout << "ret is " << ret << ", pid " << pthread_self() << std::endl;
  }
  gettimeofday(&now, NULL);
  std::cout << "current request task_id:" << request->getTaskId() << std::endl;
  std::cout << "stop finished. pid " << pthread_self() << " tv: " << now.tv_sec
            << std::endl;

  /*
   * 7. After all tasks are complete, release the current request.
   *    Release the request only after the closed event (which confirms all work is done) to avoid disrupting the internal state machine,
   * which could forcibly uninstall a running request.
   */
  const char* request_info = request->dumpAllInfo();
  if (request_info) {
    std::cout << "  all info: " << request_info << std::endl;
  }
  AlibabaNls::NlsClient::getInstance()->releaseFlowingSynthesizerRequest(
      request);
  std::cout << "release Synthesizer success. pid " << pthread_self()
            << std::endl;

  pthread_mutex_destroy(&(tst->mtx));

  return NULL;
}

/**
 * Synthesize multiple text data.
 * In the SDK, multi-threading means one thread per text data, not multiple threads for one text data.
 * The example code starts four threads to synthesize four files simultaneously.
 * Free-tier users cannot have more than two concurrent connections.
 */
#define AUDIO_TEXT_NUMS 4
#define AUDIO_FILE_NAME_LENGTH 32
int flowingSynthesizerMultFile(const char* appkey, int threads) {
  /**
   * Get the current system timestamp to check if the token has expired.
   */
  std::time_t curTime = std::time(0);
  if (g_token.empty()) {
    if (g_expireTime - curTime < 10) {
      std::cout << "the token will be expired, please generate new token by "
                   "AccessKey-ID and AccessKey-Secret."
                << std::endl;
      int ret = generateToken(g_akId, g_akSecret, &g_token, &g_expireTime);
      if (ret < 0) {
        std::cout << "generate token failed" << std::endl;
        return -1;
      } else {
        if (g_token.empty() || g_expireTime < 0) {
          std::cout << "generate empty token" << std::endl;
          return -2;
        }
        std::cout << "token: " << g_token << std::endl;
      }
    }
  }

  /* Do not exceed AUDIO_TEXT_LENGTH */
  const char texts[AUDIO_TEXT_LENGTH] = {
      "This is a long text for speech synthesis. It demonstrates the capability of the streaming and long-text-to-speech synthesis feature of Alibaba Cloud Intelligent Speech Interaction. The quick brown fox jumps over the lazy dog."};

  ParamStruct pa[threads];

  for (int i = 0; i < threads; i++) {
    memset(pa[i].token, 0, DEFAULT_STRING_LEN);
    memcpy(pa[i].token, g_token.c_str(), g_token.length());

    memset(pa[i].appkey, 0, DEFAULT_STRING_LEN);
    memcpy(pa[i].appkey, appkey, strlen(appkey));

    memset(pa[i].text, 0, AUDIO_TEXT_LENGTH);
    if (g_text.empty()) {
      memcpy(pa[i].text, texts, strlen(texts));
    } else {
      memcpy(pa[i].text, g_text.data(), strlen(g_text.data()));
    }

    memset(pa[i].url, 0, DEFAULT_STRING_LEN);
    if (!g_url.empty()) {
      memcpy(pa[i].url, g_url.c_str(), g_url.length());
    }
  }

  std::vector<pthread_t> pthreadId(threads);
  for (int j = 0; j < threads; j++) {
    pthread_create(&pthreadId[j], NULL, &pthreadSingleRoundFunc,
                   (void*)&(pa[j]));
  }

  std::cout << "start pthread_join..." << std::endl;

  for (int j = 0; j < threads; j++) {
    pthread_join(pthreadId[j], NULL);
  }

  std::cout << "flowingSynthesizerMultFile exit..." << std::endl;
  return 0;
}

int main(int argc, char* argv[]) {
  std::string g_appkey = getenv("NLS_APPKEY_ENV");
  g_akId = getenv("NLS_AK_ENV");
  g_akSecret = getenv("NLS_SK_ENV");

  std::cout << " appKey: " << g_appkey << std::endl;
  std::cout << " akId: " << g_akId << std::endl;
  std::cout << " akSecret: " << g_akSecret << std::endl;
  std::cout << " voice: " << g_voice << std::endl;
  std::cout << "\n" << std::endl;

  int ret = AlibabaNls::NlsClient::getInstance()->setLogConfig(
      "log-flowingSynthesizer", AlibabaNls::LogDebug, 400, 50, NULL);
  if (ret < 0) {
    std::cout << "set log failed." << std::endl;
    return -1;
  }

  // Set the socket address type required by the operating environment. The default is AF_INET.
  // This must be called before startWorkThread().
  // AlibabaNls::NlsClient::getInstance()->setAddrInFamily("AF_INET");

  // Set the direct connection IP in a private cloud deployment.
  // This must be called before startWorkThread().
  // AlibabaNls::NlsClient::getInstance()->setDirectHost("xxx.xxx.xxx.xxx");

  std::cout << "startWorkThread begin... " << std::endl;

  // Start the worker thread. This function must be called before creating and starting a request.
  // If the input parameter is negative, the number of available cores in the current system is started.
  // For high concurrency, 4 is recommended. For a single request, 1 is recommended.
  // If CPU usage is high with high concurrency, you can enter -1 to enable all CPU cores.
  AlibabaNls::NlsClient::getInstance()->startWorkThread(1);

  std::cout << "startWorkThread finish" << std::endl;

  // Synthesize multiple texts.
  ret = flowingSynthesizerMultFile(g_appkey.c_str(), g_threads);
  if (ret) {
    std::cout << "flowingSynthesizerMultFile failed." << std::endl;
    AlibabaNls::NlsClient::releaseInstance();
    return -2;
  }

  // After all work is done, release nlsClient before the process exits.
  // Note that releaseInstance() is not thread-safe.
  std::cout << "releaseInstance -> " << std::endl;
  AlibabaNls::NlsClient::releaseInstance();
  std::cout << "releaseInstance done." << std::endl;

  return 0;
}

Status codes

C++ SDK status codes

Status code

Status message

Cause

Solution

0

Success

Success.

None.

-10

DefaultError

Default error.

Not currently in use.

-11

JsonParseFailed

Incorrect JSON format.

Check if the input JSON string is in the correct JSON format.

-12

JsonObjectError

Incorrect JSON object.

Try again.

-13

MallocFailed

Malloc failed.

Check if there is sufficient memory.

-14

ReallocFailed

Realloc failed.

Check if there is sufficient memory.

-15

InvalidInputParam

An invalid parameter was passed.

Not currently in use.

-50

InvalidLogLevel

Invalid log level.

Check the configured log level.

-51

InvalidLogFileSize

Invalid log file size.

Check the configured log file size parameter.

-52

InvalidLogFileNum

Number of invalid log files

Check the configured parameter for the number of log files.

-100

EncoderExistent

The NLS encoder already exists.

Try again.

-101

EncoderInexistent

The NLS encoder does not exist.

Re-initialize.

-102

OpusEncoderCreateFailed

Failed to create the Opus encoder.

Re-initialize.

-103

OggOpusEncoderCreateFailed

Failed to create the OggOpus encoder.

Re-initialize.

-104

InvalidEncoderType

Invalid encoder type.

OPUS may have been disabled at compile-time but is still being used. Or, check the ENCODER_TYPE.

-150

EventClientEmpty

The main worker thread is a null pointer. It has been released.

Re-initialize by calling startWorkThread().

-151

SelectThreadFailed

Failed to select a worker thread. It has not been initialized.

Re-initialize by calling startWorkThread().

-160

StartCommandFailed

Failed to send the start command.

Try again.

-161

InvokeStartFailed

The request state machine is incorrect, causing start to fail.

Check if the current request has not been created or has already been completed.

-162

InvokeSendAudioFailed

The request state machine is incorrect, causing sendAudio to fail.

Check if the current request has started (received the started event callback) or has already been completed.

-163

InvalidOpusFrameSize

Invalid Opus frame size. The default is 640 bytes.

In OPU encoding mode, sendAudio accepts only 640 bytes of data per frame.

-164

InvokeStopFailed

The request state machine is incorrect, causing stop to fail.

Check if the current request has not started (received the started event callback) or has already been completed.

-165

InvokeCancelFailed

The request state machine is incorrect, causing stop to fail.

Check if the current request has not started (received the started event callback) or has already been completed.

-166

InvokeStControlFailed

The request state machine is incorrect, causing stControl to fail.

Check if the current request has not started (received the started event callback) or has already been completed.

-200

NlsEventEmpty

The NLS event is empty.

For internal SDK use. The NlsEvent frame was lost.

-201

NewNlsEventFailed

Failed to create NlsEvent.

For internal SDK use. Failed to create the NlsEvent frame.

-202

NlsEventMsgEmpty

The message in the NLS event is empty.

The message string was found to be empty during parsing by parseJsonMsg().

-203

InvalidNlsEventMsgType

Invalid message type in the NLS event.

For internal SDK use. The event type of the NlsEvent frame is invalid.

-204

InvalidNlsEventMsgStatusCode

Invalid message status code in the NLS event.

For internal SDK use. The event message status of the NlsEvent frame is invalid.

-205

InvalidNlsEventMsgHeader

Invalid message header in the NLS event.

For internal SDK use. The event message header of the NlsEvent frame is invalid.

-250

CancelledExitStatus

Cancel has been called.

Not currently in use.

-251

InvalidWorkStatus

Invalid working status.

For internal SDK use. The internal status of the current request is invalid.

-252

InvalidNodeQueue

The NodeQueue in WorkThread is invalid.

For internal SDK use. The current request to be run is invalid. Release the current request and try again.

-300

InvalidRequestParams

Invalid request parameters.

The data passed to SendAudio is empty.

-301

RequestEmpty

The request is a null pointer.

For internal SDK use. The current request has been released. Release the current request and try again.

-302

InvalidRequest

Invalid request.

For internal SDK use. The current request has been released. Release the current request and try again.

-303

SetParamsEmpty

The parameters passed for setting are empty.

Check if the passed parameters are empty.

-350

GetHttpHeaderFailed

Failed to get the HTTP header.

For internal SDK use. Locate the issue based on the feedback in the logs.

-351

HttpGotBadStatus

HTTP error status.

For internal SDK use. Locate the issue based on the feedback in the logs.

-352

WsResponsePackageFailed

Failed to parse the WebSocket response package.

For internal SDK use. Locate the issue based on the feedback in the logs.

-353

WsResponsePackageEmpty

The parsed WebSocket response package is empty.

For internal SDK use. Locate the issue based on the feedback in the logs.

-354

WsRequestPackageEmpty

The WebSocket request package is empty.

For internal SDK use. Locate the issue based on the feedback in the logs.

-355

UnknownWsFrameHeadType

Unknown WebSocket frame header type.

For internal SDK use. Locate the issue based on the feedback in the logs.

-356

InvalidWsFrameHeaderSize

Invalid WebSocket frame header size.

For internal SDK use. Locate the issue based on the feedback in the logs.

-357

InvalidWsFrameHeaderBody

Invalid WebSocket frame header body.

For internal SDK use. Locate the issue based on the feedback in the logs.

-358

InvalidWsFrameBody

Invalid WebSocket frame body.

For internal SDK use. Locate the issue based on the feedback in the logs.

-359

WsFrameBodyEmpty

The frame data is empty. This is often caused by receiving dirty data.

For internal SDK use. Locate the issue based on the feedback in the logs.

-400

NodeEmpty

The node is a null pointer.

Release the current request and try again.

-401

InvaildNodeStatus

The node is in an invalid state.

For internal SDK use. Release the current request and try again.

-402

GetAddrinfoFailed

DNS parsing detects the address.

For internal SDK use. Check if DNS is available in the current environment.

-403

ConnectFailed

Network connection failed.

Check if the network is available.

-404

InvalidDnsSource

No DNS on the current device.

For internal SDK use. Check if DNS is available in the current environment.

-405

ParseUrlFailed

Invalid URL.

Check if the configured URL is valid.

-406

SslHandshakeFailed

SSL handshake failed.

For internal SDK use. Check if the network is available and try again.

-407

SslCtxEmpty

SSL_CTX is empty.

For internal SDK use. Check if the network is available and try again.

-408

SslNewFailed

SSL_new failed.

For internal SDK use. Check if the network is available and try again.

-409

SslSetFailed

Failed to set SSL parameters.

For internal SDK use. Check if the network is available and try again.

-410

SslConnectFailed

SSL_connect failed.

For internal SDK use. Check if the network is available and try again.

-411

SslWriteFailed

Failed to send data over SSL.

For internal SDK use. Check if the network is available and try again.

-412

SslReadSysError

A SYSCALL error occurred while receiving data over SSL.

For internal SDK use. Check if the network is available and try again.

-413

SslReadFailed

Failed to receive data over SSL.

For internal SDK use. Check if the network is available and try again.

-414

SocketFailed

Failed to create a socket.

For internal SDK use. Check if the network is available and try again.

-415

SetSocketoptFailed

Failed to set socket parameters.

For internal SDK use. Check if the network is available and try again.

-416

SocketConnectFailed

Failed to connect the socket.

For internal SDK use. Check if the network is available and try again.

-417

SocketWriteFailed

Failed to send data over the socket.

For internal SDK use. Check if the network is available and try again.

-418

SocketReadFailed

Failed to receive data from the socket.

For internal SDK use. Check if the network is available and try again.

-430

NlsReceiveFailed

Failed to receive NLS frame data.

For internal SDK use. Check if the network is available and try again.

-431

NlsReceiveEmpty

The received NLS frame data is empty.

For internal SDK use. Check if the network is available and try again.

-432

ReadFailed

Failed to receive data.

For internal SDK use. Check if the network is available and try again.

-433

NlsSendFailed

Failed to send NLS data.

For internal SDK use. Check if the network is available and try again.

-434

NewOutputBufferFailed

Failed to create a buffer.

For internal SDK use. Check if there is sufficient memory.

-435

NlsEncodingFailed

Audio encoding failed.

For internal SDK use. Release the current request and try again.

-436

EventEmpty

The event is empty.

For internal SDK use. Release the current request and try again.

-437

EvbufferTooMuch

Too much data in evbuffer.

For internal SDK use. The send data cache is full (max cache for 16 kHz audio is 320000, for 8 kHz audio is 160000). Check if audio data is being sent too frequently or if too much data is being sent at once.

-438

EvutilSocketFalied

Failed to set evutil parameters.

For internal SDK use. Release the current request and try again.

-439

InvalidExitStatus

Invalid exit status.

Check if the current request has been canceled.

-450

InvalidAkId

The Alibaba Cloud account AccessKey ID is invalid.

Check if the Alibaba Cloud account AccessKey ID is empty.

-451

InvalidAkSecret

The Alibaba Cloud account AccessKey secret is invalid.

Check if the Alibaba Cloud account AccessKey secret is empty.

-452

InvalidAppKey

The project AppKey is invalid.

Check if the Alibaba Cloud project AppKey is empty.

-453

InvalidDomain

The domain is invalid.

Check if the input domain is empty.

-454

InvalidAction

The action is invalid.

Check if the input action is empty.

-455

InvalidServerVersion

The ServerVersion is invalid.

Check if the input ServerVersion is empty.

-456

InvalidServerResource

The ServerResource is invalid.

Check if the input ServerResource is empty.

-457

InvalidRegionId

The RegionId is invalid.

Check if the input Region ID is empty.

-500

InvalidFileLink

Invalid audio file link.

The audio file transcription file link is empty.

-501

ErrorStatusCode

Error status code.

Audio file transcription returned an error. See the error code for details.

-502

IconvOpenFailed

Failed to request a conversion descriptor.

UTF-8 to GBK conversion failed.

-503

IconvFailed

Encoding conversion failed.

UTF-8 to GBK conversion failed.

-504

ClientRequestFaild

Account client request failed.

Audio file transcription returned a failure.

-999

NlsMaxErrorCode

None.

None.

Other status codes

Status code

Status message

Cause

Solution

10000001

NewSslCtxFailed

SSL: couldn't create a context!

Re-initialize.

10000002

DefaultErrorCode

return of SSL_read: error:00000000:lib(0):func(0):reason(0)

Try again.

return of SSL_read: error:140E0197:SSL routines:SSL_shutdown:shutdown while in init

10000003

SysErrorCode

System error.

Handle the error based on the feedback from the system.

10000004

EmptyUrl

URL: The url is empty.

The input URL is empty. Enter a valid URL.

10000005

InvalidWsUrl

Could not parse WebSocket url:

The input URL format is incorrect. Enter a valid URL.

10000007

JsonStringParseFailed

JSON: Json parse failed.

JSON format is abnormal. Check the logs for the specific error point.

10000008

UnknownWsHeadType

WEBSOCKET: unkown head type.

Network connection failed. Check if local DNS resolution is working and if the URL is valid.

10000009

HttpConnectFailed

HTTP: connect failed.

Failed to connect to the cloud. Check the network and try again.

10000010

MemNotEnough

Out of memory.

Check if there is sufficient memory.

10000015

SysConnectFailed

connect failed.

Network connection failed. Check if local DNS resolution is working and if the URL is valid.

10000100

HttpGotBadStatusWith403

Got bad status host=xxxxx line=HTTP/1.1 403 Forbidden

Connection was rejected. Check your account, especially if the token has expired.

10000101

EvSendTimeout

Send timeout. socket error:

libevent timed out while sending an event. Check for time-consuming tasks in the callback, or if high concurrency is preventing timely> event processing.

10000102

EvRecvTimeout

Recv timeout. socket error:

libevent timed out while receiving an event. Check for time-consuming tasks in the callback, or if high concurrency is preventing timely> event processing.

10000103

EvUnknownEvent

Unknown event:

Unknown libevent event. Try again.

10000104

OpNowInProgress

Operation now in progress

Connection is in progress. Try again.

10000105

BrokenPipe

Broken pipe

The pipe cannot handle the process. Try again.

10000110

TokenHasExpired

Gateway:ACCESS_DENIED:The token 'xxx' has expired!

Update the token.

10000111

TokenIsInvalid

Meta:ACCESS_DENIED:The token 'xxx' is invalid!

Check the validity of the token.

10000112

NoPrivilegeToVoice

Gateway:ACCESS_DENIED:No privilege to this voice! (voice: zhinan, privilege: 0)

You do not have permission to use this voice.

10000113

MissAuthHeader

Gateway:ACCESS_DENIED:Missing authorization header!

Check if your account has the required permissions, or if the number of concurrent connections is within the limit.

10000120

Utf8ConvertError

utf8ToGbk failed

UTF-8 transcoding failed. This is often a system issue. Try again.

20000000

SuccessStatusCode

Success.

Server-side response status codes

For more information about service status codes, see Service status codes.