双缓冲.txt 10.9 KB
#include <fstream>
#include <assert.h>
#include <cstring>
#include <atomic>
#include <unistd.h>
#include <string>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <vector>
#include <algorithm>

#include "JZsdkLib.h"
#include "version_choose.h"
#include "JZsdk_base/JZring/JZring.h"

#ifdef IFLAY_TTS_2_CONFIG_STATUS_ON

#include "aikit_biz_api.h"
#include "aikit_constant.h"
#include "aikit_biz_config.h"
#include "iflytek_tts.h"
#include "AudioDeal/AudioDeal.h"
#include "../../Megaphone.h"
#include "iflytek_tts.h"

using namespace std;
using namespace AIKIT;

// ---------- 全局变量 ----------
static const char* ABILITY = "e2e44feff";
static AIKIT_HANDLE* g_AikitHandle = nullptr;           // 当前合成句柄
static std::atomic_bool ttsFinished(false);             // 合成完成标志
static std::atomic_bool g_stopRequested(false);         // 外部停止请求(用于线程退出)

// 双缓冲环形缓冲区
#define RING_BUF_SIZE (1024 * 100)  // 每个缓冲区100KB
static T_JZringHandle g_ringHandleA = nullptr;   // 一级缓冲(生产者写入)
static T_JZringHandle g_ringHandleB = nullptr;   // 二级缓冲(播放线程读取)
static char* g_ringBufA = nullptr;
static char* g_ringBufB = nullptr;

// 同步机制
static std::mutex g_doubleMutex;
static std::condition_variable g_doubleCV;

// 线程对象(可 join)
static std::thread g_transferThread;
static std::thread g_playThread;

// 函数声明
int IflytekLib_2_StopTts();

// ---------- 双缓冲播放线程(常驻)----------
void PlaybackThreadFunc()
{
    U8_t buffer[1536];
    U32_t readSize = 0;
    T_JZsdkReturnCode ret;

    while (!g_stopRequested)
    {
        // 等待B缓冲有数据(使用条件变量精确等待)
        {
            std::unique_lock<std::mutex> lock(g_doubleMutex);
            g_doubleCV.wait(lock, [] {
                U32_t count = 0;
                JZring_GetDataCount(g_ringHandleB, &count);
                return count > 0 || g_stopRequested;
                });
            if (g_stopRequested) break;
        }

        // 读取数据
        ret = JZring_Read(g_ringHandleB, buffer, sizeof(buffer), &readSize);
        if (ret != JZ_ERROR_SYSTEM_MODULE_CODE_SUCCESS || readSize == 0) {
            usleep(10000);
            continue;
        }

        JZSDK_LOG_DEBUG("从b池读取:%d", readSize);

        // 播放数据
        AudioDeal_PcmDataInput_TextSteam(16000, buffer, readSize);
    }

    JZSDK_LOG_INFO("播放线程退出");
}

// ---------- 双缓冲搬运线程(常驻)----------
void TransferThreadFunc()
{
    U8_t buffer[2048];
    U32_t readSize = 0;
    T_JZsdkReturnCode ret;

    while (!g_stopRequested)
    {
        // 等待A缓冲有数据
        {
            std::unique_lock<std::mutex> lock(g_doubleMutex);
            g_doubleCV.wait(lock, [] {
                U32_t count = 0;
                JZring_GetDataCount(g_ringHandleA, &count);
                return count > 0 || g_stopRequested;
                });
            if (g_stopRequested) break;
        }

        ret = JZring_Read(g_ringHandleA, buffer, sizeof(buffer), &readSize);
        if (ret != JZ_ERROR_SYSTEM_MODULE_CODE_SUCCESS || readSize == 0) {
            usleep(10000);
            continue;
        }

        // 将数据写入B缓冲
        U32_t written = 0;
        while (written < readSize && !g_stopRequested)
        {
            U32_t chunkSize = std::min(readSize - written, static_cast<U32_t>(2048));
            ret = JZring_Write(g_ringHandleB, buffer + written, chunkSize);

            //JZSDK_LOG_DEBUG("写入数据到b池:%d", chunkSize);

            if (ret == JZ_ERROR_SYSTEM_MODULE_CODE_BUFFER_SIZE_NOT_ENOUGH) {
                // B缓冲满,等待播放线程消耗
                usleep(10000);
                continue;
            }
            if (ret != JZ_ERROR_SYSTEM_MODULE_CODE_SUCCESS) {
                JZSDK_LOG_ERROR("写入B缓冲失败: %d", ret);
                break;
            }
            written += chunkSize;
        }

        // 通知播放线程有新数据
        g_doubleCV.notify_one();
    }

    JZSDK_LOG_INFO("搬运线程退出");
}

// ---------- 合成数据回调 ----------
void OnOutput(AIKIT_HANDLE* handle, const AIKIT_OutputData* output)
{
    // 检测外部停止信号
    if (Megaphone_MegDataGenFlag(JZ_FLAGCODE_GET, 0) == JZ_FLAGCODE_OFF) {
        IflytekLib_2_StopTts();
        return;
    }

    if (!output->node->value) return;

    // 写入一级缓冲A
    T_JZsdkReturnCode ret;
    U32_t written = 0;
    while (written < static_cast<U32_t>(output->node->len) && !g_stopRequested) {
        U32_t chunkSize = std::min(static_cast<U32_t>(output->node->len) - written,
            static_cast<U32_t>(4096));
        ret = JZring_Write(g_ringHandleA,
            reinterpret_cast<U8_t*>(output->node->value) + written,
            chunkSize);

        JZSDK_LOG_DEBUG("写入数据到a缓冲池 :%d", chunkSize);

        if (ret == JZ_ERROR_SYSTEM_MODULE_CODE_BUFFER_SIZE_NOT_ENOUGH) {
            // A缓冲满,等待搬运线程消耗
            usleep(10000);
            continue;
        }
        if (ret != JZ_ERROR_SYSTEM_MODULE_CODE_SUCCESS) {
            JZSDK_LOG_ERROR("写入A缓冲失败: %d", ret);
            break;
        }
        written += chunkSize;
    }

    //JZSDK_LOG_DEBUG("生成了数据 :%d", output->node->len);

    // 通知搬运线程有新数据
    g_doubleCV.notify_one();
}

// ---------- 合成事件回调 ----------
void OnEvent(AIKIT_HANDLE* handle, AIKIT_EVENT eventType, const AIKIT_OutputEvent* eventValue)
{
    if (eventType == AIKIT_Event_End) {
        ttsFinished = true;
        JZSDK_LOG_INFO("合成完成");
        // 唤醒可能等待的线程(实际线程不依赖此标志,但唤醒无害)
        g_doubleCV.notify_all();
    }
}

// ---------- 错误回调 ----------
void OnError(AIKIT_HANDLE* handle, int32_t err, const char* desc)
{
    JZSDK_LOG_ERROR("TTS错误: %d, %s", err, desc);
    IflytekLib_2_StopTts();
}

// ---------- 核心TTS合成函数 ----------
int IflytekLib_2_TextToTts(int language,
    const char* TtsRole,
    const char* text,
    int speed,
    int volume)
{
    AIKIT_ParamBuilder* paramBuilder = nullptr;
    AIKIT_DataBuilder* dataBuilder = nullptr;
    AiText* aiText_raw = nullptr;
    int ret = 0;

    // 重置状态
    ttsFinished = false;

    // 重置环形缓冲区(确保每次调用都是全新状态)
    JZring_Reset(g_ringHandleA);
    JZring_Reset(g_ringHandleB);

    // 构建TTS参数
    paramBuilder = AIKIT_ParamBuilder::create();
    paramBuilder->clear();
    paramBuilder->param("vcn", TtsRole, strlen(TtsRole));
    paramBuilder->param("vcnModel", TtsRole, strlen(TtsRole));
    paramBuilder->param("language", language);
    paramBuilder->param("textEncoding", "UTF-8", strlen("UTF-8"));
    paramBuilder->param("volume", volume);
    paramBuilder->param("speed", speed);

    JZSDK_LOG_DEBUG("TTS Role:%s, Text:%s, language:%d", TtsRole, text, language);

    // 开始合成
    ret = AIKIT_Start(ABILITY, AIKIT_Builder::build(paramBuilder), nullptr, &g_AikitHandle);
    if (ret != 0) {
        JZSDK_LOG_ERROR("AIKIT_Start failed: %d", ret);
        goto exit;
    }

    // 写入文本
    dataBuilder = AIKIT_DataBuilder::create();
    dataBuilder->clear();
    aiText_raw = AiText::get("text")->data(text, strlen(text))->once()->valid();
    dataBuilder->payload(aiText_raw);

    ret = AIKIT_Write(g_AikitHandle, AIKIT_Builder::build(dataBuilder));
    if (ret != 0) {
        JZSDK_LOG_ERROR("AIKIT_Write failed: %d", ret);
        goto exit;
    }

    // 等待合成完成(ttsFinished 由 OnEvent 设置)
    while (!ttsFinished) {
        if (Megaphone_MegDataGenFlag(JZ_FLAGCODE_GET, 0) == JZ_FLAGCODE_OFF) {
            IflytekLib_2_StopTts();
            break;
        }
        usleep(10000);
    }

    JZSDK_LOG_INFO("合成完成,等待缓冲区数据播放...");

    // 等待两个缓冲区都为空(所有数据播放完毕)
    while (JZring_GetDataCount(g_ringHandleA, nullptr) > 0 ||
        JZring_GetDataCount(g_ringHandleB, nullptr) > 0) {
        if (Megaphone_MegDataGenFlag(JZ_FLAGCODE_GET, 0) == JZ_FLAGCODE_OFF) {
            break;
        }
        usleep(10000);
    }

exit:
    // 结束合成
    if (g_AikitHandle) {
        AIKIT_End(g_AikitHandle);
        g_AikitHandle = nullptr;
    }

    if (paramBuilder) delete paramBuilder;
    if (dataBuilder) delete dataBuilder;

    return ret;
}

// ---------- 停止合成(可被外部调用)----------
int IflytekLib_2_StopTts()
{
    if (g_AikitHandle) {
        AIKIT_End(g_AikitHandle);
        g_AikitHandle = nullptr;
        ttsFinished = true;
        JZSDK_LOG_INFO("TTS合成已停止");
    }
    g_stopRequested = true;  // 同时通知线程退出
    g_doubleCV.notify_all();
    return 0;
}

// ---------- 初始化 ----------
int IflytekLib_2_Init()
{
    g_stopRequested = false;

    // 初始化两个环形缓冲区
    g_ringBufA = new char[RING_BUF_SIZE];
    g_ringBufB = new char[RING_BUF_SIZE];

    if (JZring_Init(&g_ringHandleA, reinterpret_cast<U8_t*>(g_ringBufA), RING_BUF_SIZE)
        != JZ_ERROR_SYSTEM_MODULE_CODE_SUCCESS) {
        JZSDK_LOG_ERROR("环形缓冲区A初始化失败");
        return -1;
    }
    if (JZring_Init(&g_ringHandleB, reinterpret_cast<U8_t*>(g_ringBufB), RING_BUF_SIZE)
        != JZ_ERROR_SYSTEM_MODULE_CODE_SUCCESS) {
        JZSDK_LOG_ERROR("环形缓冲区B初始化失败");
        return -1;
    }

    // 启动搬运线程和播放线程(保存为 joinable)
    g_transferThread = std::thread(TransferThreadFunc);
    g_playThread = std::thread(PlaybackThreadFunc);

    // 初始化TTS SDK
    AIKIT_Configurator::builder()
        .app()
        .appID("03857dfd")
        .apiSecret("OTA2OTEzMTVlOGYwMjllMmJkYzEwZGY5")
        .apiKey("2b2c60f8a80b8cdfe45ae1058a25149a")
        .workDir("/root/Iflytek_2")
        .auth()
        .authType(0)
        .log()
        .logLevel(LOG_LVL_OFF)
        .logMode(LOG_STDOUT);

    int ret = AIKIT_Init();
    if (ret != 0) {
        JZSDK_LOG_ERROR("AIKIT_Init failed: %d", ret);
        return -1;
    }

    // 注册回调
    AIKIT_Callbacks cbs = { OnOutput, OnEvent, OnError };
    AIKIT_RegisterAbilityCallback(ABILITY, cbs);

    return 0;
}

// ---------- 反初始化 ----------
int IflytekLib_2_UnInit()
{
    // 通知线程退出
    g_stopRequested = true;
    g_doubleCV.notify_all();

    // 等待线程结束
    if (g_transferThread.joinable()) g_transferThread.join();
    if (g_playThread.joinable()) g_playThread.join();

    // 反初始化TTS SDK
    if (g_AikitHandle) {
        AIKIT_End(g_AikitHandle);
        g_AikitHandle = nullptr;
    }
    AIKIT_UnInit();

    // 释放环形缓冲区
    if (g_ringHandleA) {
        JZring_DeInit(g_ringHandleA);
        g_ringHandleA = nullptr;
    }
    if (g_ringBufA) {
        delete[] g_ringBufA;
        g_ringBufA = nullptr;
    }
    if (g_ringHandleB) {
        JZring_DeInit(g_ringHandleB);
        g_ringHandleB = nullptr;
    }
    if (g_ringBufB) {
        delete[] g_ringBufB;
        g_ringBufB = nullptr;
    }

    return 0;
}

#endif