iflytek_tts.cpp 8.2 KB
#include <fstream>
#include <assert.h>
#include <cstring>
#include <atomic>
#include <unistd.h>
#include <string>
#include <thread>
#include <mutex>
#include <condition_variable>

#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 std::atomic_bool ttsFinished(false);
static std::atomic_bool g_playThreadRunning(false);
static const char *ABILITY = "e2e44feff";
static AIKIT_HANDLE *g_AikitHandle = nullptr; // 合成句柄

static int IflytekLib_2_StopTts();

#define IFLYTEK_RING_BUF_SIZE (1024 * 100)  // 100KB缓冲区
static T_JZringHandle g_ringHandle = nullptr;
static char *g_ringBuf = nullptr;
static int g_AudioPlayFinshFlag = JZ_FLAGCODE_OFF;

// 同步机制
static std::mutex g_mutex;
static std::condition_variable g_cv;

// 播放线程函数
void PlaybackThreadFunc()
{
    U8_t buffer[2048]; // 每次读取2KB
    U32_t readSize = 0;
    T_JZsdkReturnCode ret;
    
    g_playThreadRunning = true;

    while (g_playThreadRunning) 
    {
        // 等待有数据可读
        {
            std::unique_lock<std::mutex> lock(g_mutex);
            U32_t dataCount = 0;
            JZring_GetDataCount(g_ringHandle, &dataCount);

            //JZSDK_LOG_DEBUG("缓冲区数据量: %d", dataCount);
            
            if (dataCount == 0) {
                // 等待最多100ms或通知
                g_cv.wait_for(lock, std::chrono::milliseconds(100));
                continue;
            }
        }

        g_AudioPlayFinshFlag = JZ_FLAGCODE_ON;
        
        // 从环形缓冲区读取数据
        ret = JZring_Read(g_ringHandle, buffer, sizeof(buffer), &readSize);
        if (ret != JZ_ERROR_SYSTEM_MODULE_CODE_SUCCESS || readSize == 0) {
            usleep(10000); // 10ms
            continue;
        }
        
        // 发送到音频播放器
        AudioDeal_PcmDataInput_TextSteam(16000, buffer, readSize);

        g_AudioPlayFinshFlag = JZ_FLAGCODE_OFF;
    }
    
    g_playThreadRunning = false;
}

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) {
        //JZSDK_LOG_DEBUG("生产了数据%d", output->node->len);
        
        // 写入环形缓冲区
        T_JZsdkReturnCode ret;
        U32_t written = 0;
        
        while (written < static_cast<U32_t>(output->node->len)) {
            U32_t chunkSize = std::min(static_cast<U32_t>(output->node->len) - written, 
                                      static_cast<U32_t>(4096));
            
            ret = JZring_Write(g_ringHandle, 
                              reinterpret_cast<U8_t*>(output->node->value) + written, 
                              chunkSize);
            if (ret == JZ_ERROR_SYSTEM_MODULE_CODE_BUFFER_SIZE_NOT_ENOUGH)
            {
                //缓冲区暂无空间,等待一段时间再写入
                usleep(10000); // 10ms
                continue;
            }    
            
            if (ret != JZ_ERROR_SYSTEM_MODULE_CODE_SUCCESS && ret != JZ_ERROR_SYSTEM_MODULE_CODE_BUFFER_SIZE_NOT_ENOUGH) 
            {
                JZSDK_LOG_ERROR("写入环形缓冲区失败: %d", ret);
                break;
            }
            
            written += chunkSize;
        }
        
        // 通知播放线程有新数据
        g_cv.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_cv.notify_one();
    }
}

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

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_ringHandle);

    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;
    }

    // 等待合成完成或停止信号
    while(!ttsFinished || g_AudioPlayFinshFlag == JZ_FLAGCODE_ON) {
        if (Megaphone_MegDataGenFlag(JZ_FLAGCODE_GET, 0) == JZ_FLAGCODE_OFF) {
            break;
        }
        usleep(10000); // 10ms
    }

    JZSDK_LOG_INFO("合成结束");

    // 结束合成
    ret = AIKIT_End(g_AikitHandle);
    g_AikitHandle = nullptr;

exit:
    if(paramBuilder != nullptr) {
        delete paramBuilder;
    }
    if(dataBuilder != nullptr) {
        delete dataBuilder;
    }
    
    return ret;
}

int IflytekLib_2_Init()
{
    // 初始化环形缓冲区
    g_ringBuf = new char[IFLYTEK_RING_BUF_SIZE];
    if (JZring_Init(&g_ringHandle, reinterpret_cast<U8_t*>(g_ringBuf), IFLYTEK_RING_BUF_SIZE) 
        != JZ_ERROR_SYSTEM_MODULE_CODE_SUCCESS) {
        JZSDK_LOG_ERROR("环形缓冲区初始化失败");
        return -1;
    }

    // 启动播放线程
    std::thread(PlaybackThreadFunc).detach();

    // 初始化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_playThreadRunning = false;
    g_cv.notify_one();
    
    // 等待播放线程退出
    int waitCount = 0;
    while (g_playThreadRunning && waitCount++ < 50) {
        usleep(100000); // 100ms
    }
    
    // 反初始化TTS SDK
    if (g_AikitHandle) {
        AIKIT_End(g_AikitHandle);
        g_AikitHandle = nullptr;
    }
    
    AIKIT_UnInit();
    
    // 释放环形缓冲区
    if (g_ringHandle) {
        JZring_DeInit(g_ringHandle);
        g_ringHandle = nullptr;
    }
    
    if (g_ringBuf) {
        delete[] g_ringBuf;
        g_ringBuf = nullptr;
    }
    
    return 0;
}

static int IflytekLib_2_StopTts()
{
    if (g_AikitHandle) {
        AIKIT_End(g_AikitHandle);
        g_AikitHandle = nullptr;
        ttsFinished = true;
        JZSDK_LOG_INFO("TTS合成已停止");
    }
    
    // 通知播放线程
    g_cv.notify_one();
    return 0;
}

#endif