T5_3.13.6翻译模式播报音频偶尔被截断

Wi-Fi 设备、蜂窝设备、WuKongAI、开发板、TuyaOS 移植等


Post Reply
chen12
Posts: 69

目前使用3.13.6sdk的翻译功能,发现翻译后的音频在播放的时候,有时候上一句还没播完,就直接播放下一段翻译音频了,有什么办法能让翻译的音频不被截断而是等到上一句播放完再播放下一句吗

愚者千虑必有一得
Posts: 909

Re: T5_3.13.6翻译模式播报音频偶尔被截断

Re: T5_3.13.6翻译模式播报音频偶尔被截断

您好!我已分析 T5 SDK 3.13.6 的 TTS 翻译模式音频播放实现,找到了音频被截断的根因。

问题概述

T5 翻译模式下,TTS 音频使用异步播放 + 16KB 环形缓冲区,在连续翻译场景下缓冲区可能溢出,导致后续音频数据丢失。

根因分析

现象 → 触发条件 → 根因

  1. TTS 播放流程 (apps/tuyaos_demo_wukong_ai/src/wukong/audio/wukong_audio_player.c,第 256-290 行):

    • 调用 wukong_audio_play_tts_stream() 启动 TTS 播放
    • 数据通过 tuya_ai_player_feed() 喂入环形缓冲区
    • 发送 NULL, 0 表示流结束 (EOF)
  2. 环形缓冲区限制 (apps/tuyaos_demo_wukong_ai/src/miscs/audio_player/src/datasink/datasink_mem.c,第 96-139 行):

    • 默认缓冲区大小:AI_PLAYER_RINGBUF_SIZE = 16 * 1024 (16KB)
    • 如果缓冲区满,会等待最多 5 秒 (500 * 10ms)
    • 5 秒后仍写入失败,数据会被丢弃 → 音频截断
  3. EOF 提前触发 (apps/tuyaos_demo_wukong_ai/src/miscs/audio_player/src/svc_ai_player.c,第 233-263 行):

    • 当缓冲区为空且 eof = TRUE 时,触发播放停止
    • 如果云端发送了 AI_EVENT_END 事件,但本地环形缓冲区中还有未播放的音频 → 音频被截断

解决方案

方案1:增大环形缓冲区(推荐)

修改 apps/tuyaos_demo_wukong_ai/src/miscs/audio_player/include/svc_ai_player.h

Code: Select all

// 将 16KB 增大到 64KB
#define AI_PLAYER_RINGBUF_SIZE    (64 * 1024)

验证方法

  1. 重新编译固件:make APP_NAME=tuyaos_demo_wukong_ai
  2. 测试连续翻译场景,观察是否还有截断现象
  3. datasink_mem_feed() 中添加日志,监控写入成功率

方案2:实现播放完成等待

wukong_audio_player.c 中增加播放完成回调:

Code: Select all

// 添加播放完成标志
static bool g_tts_play_done = false;

// 播放完成回调
static void tts_play_complete_cb(void *user_data)
{
    g_tts_play_done = true;
    TAL_PR_DEBUG("TTS play complete");
}

// 等待播放完成
int wait_tts_play_complete(uint32_t timeout_ms)
{
    uint32_t start = tal_system_get_millisecond();
    g_tts_play_done = false;
    
    while (!g_tts_play_done && (tal_system_get_millisecond() - start < timeout_ms)) {
        tal_system_sleep(10);
    }
    
    if (!g_tts_play_done) {
        TAL_PR_WARN("TTS play timeout");
        return OPRT_TIMEOUT;
    }
    return OPRT_OK;
}

方案3:检查 EOF 触发时机

wukong_ai_agent.c 中,确保 AI_EVENT_END 事件在音频播放完成后才发送:

Code: Select all

// 修改翻译模式处理
case WUKONG_AI_EVENT_TTS_STOP:
    TAL_PR_DEBUG("wukong audio player -> tts stream stop");
    TUYA_CALL_ERR_LOG(tuya_ai_player_feed(__s_tone_player, NULL, 0));  // 发送 EOF
    
    // 等待播放完成后再通知上层
    wait_tts_play_complete(10000);  // 等待最多 10 秒
    
    wukong_ai_event_notify(WUKONG_AI_EVENT_TTS_STOP, NULL);
    break;

风险与回退

方案风险回退方法
增大缓冲区增加 48KB RAM 占用恢复 AI_PLAYER_RINGBUF_SIZE = 16 * 1024
播放完成等待可能增加翻译延迟去掉 wait_tts_play_complete() 调用
检查 EOF 时机需要理解完整流程恢复原始 AI_EVENT_END 处理逻辑

进一步排查

如果上述方案无法解决,请提供:

  1. 截断发生的规律(固定时长?随机?)
  2. 翻译内容长度(短句 or 长句?)
  3. 是否有 datasink_mem_feed()`` 中的错误日志(player ring buf write failed`)

以上内容基于 T5 SDK 3.13.6 代码分析,如有疑问请回复补充。

chen12
Posts: 69

Re: T5_3.13.6翻译模式播报音频偶尔被截断

我看翻译流程中,播放器并不是一直开启的,而是在start,stop状态切换,然后每次收到云端的WUKONG_AI_EVENT_TTS_START消息后,就会stop播放器,然后重新start播放器并播放新收到的tts音频,我感觉应该就是这个地方stop播放器导致的播放截断,想问下播放器能不能一直start不去stop直到翻译结束?这样会不会有其他影响

chen12
Posts: 69

Re: T5_3.13.6翻译模式播报音频偶尔被截断

这边说的方案一,增大环形缓冲区我有修改测试过,依然会出现音频截断,并且并没有player ring buf write failed这条log

愚者千虑必有一得
Posts: 909

Re: T5_3.13.6翻译模式播报音频偶尔被截断

你好!你的分析很准确,我进一步查了源码来确认根因:

你的发现是对的

查看 wukong_audio_player.c 源码后确认:

  • CHAT_BREAK 事件会触发 wukong_audio_player_stop(AI_PLAYER_FG)(wukong_ai_agent.c:85)
  • 紧接着 TTS_START 到来,wukong_audio_play_tts_stream() 清空播放列表并重新 start 播放器
  • tuya_ai_player_start() 要求播放器必须处于 STOPPED 状态(svc_ai_player.c:734)

为什么增大缓冲区没效果

你提到增大到64KB后仍截断,且没有 "ring buf write failed" 日志。这说明:

  • 问题不在缓冲区溢出,而在 播放器状态转换时序
  • 如果 CHAT_BREAK 后 TTS_START 到达太快,播放器可能还在 STOPPING 状态
  • 此时 tuya_ai_player_start() 会直接返回 OPRT_INVALID_PARM(无声无息失败),不会输出任何日志

播放器能一直start不stop吗

理论上可行,但需要修改播放策略。关键问题是:

  • 播放器的 TTS 数据来自 tuya_ai_player_feed(),播放器本身是"数据驱动"的
  • 如果上一句还没播完,数据就停止喂入了,播放器自然会停止(而非被强制stop)

真正的问题是"CHAT_BREAK 触发 player stop"到"TTS_START 触发 player start"之间的时序。如果云端事件发得太快,这个间隙可能导致上一句音频被截断。

推荐修改(wukong_audio_player.c TTS_START case)

Code: Select all

#include "tal_system.h"

// 在 wukong_audio_play_tts_stream() 的 WUKONG_AI_EVENT_TTS_START case 中:
case WUKONG_AI_EVENT_TTS_START:
    TAL_PR_DEBUG("wukong audio player -> tts stream start");
    __s_tts_play_flag = TRUE;
    wukong_ai_event_notify(WUKONG_AI_EVENT_TTS_PRE, NULL);

// 【新增】确保播放器完全停止后再清列表和start
TUYA_CALL_ERR_LOG(tuya_ai_player_stop(__s_tone_player));
tal_system_sleep(50);  // 等待 STOPPING→STOPPED

TUYA_CALL_ERR_LOG(tuya_ai_playlist_clear(__s_tone_playlist));
TUYA_CALL_ERR_LOG(tuya_ai_player_start(__s_tone_player, AI_PLAYER_SRC_MEM, NULL, codec));
wukong_ai_event_notify(WUKONG_AI_EVENT_TTS_START, NULL);
break;

验证方法

在 tuya_ai_player_start() 入口加一行日志确认 player 状态:

Code: Select all

PR_NOTICE("player start: state=%d", player->state);

然后连续触发翻译,看 player->state 是多少(正常应该是 2 = STOPPED)。

根本解决思路

如果上面的修改还有问题,可以考虑让 CHAT_BREAK 不立即 stop 播放器,而是等待当前音频播放完毕。但这需要修改 svc_ai_player 的队列逻辑,改动更大。

建议先试方案1,添加 sleep(50) 确保播放器完全停止后再 start,看是否还有截断。

Post Reply