Baji_Rtc_Toy/main/dzbj/bg_gif_demo.h
Rdzleo 497c1b4654 feat(rtc-only): Phase 4 - 情绪标签 → 数字人 hiyori GIF 映射 + 切换接口
按 GSD 框架 .planning/milestones/digital_human_rtc/phases/phase_04_emotion_mapping/
规划完成 Phase 4 情绪映射 + 运行时切换。

## 核心变更

### bg_gif_demo 新增运行时切换接口

```c
esp_err_t bg_gif_demo_switch_gif(const char *new_gif_path);
```

实现要点:
- 先 heap_caps_free(g_gif_data) 释放旧 PSRAM,再加载新 GIF,单峰仅一份
- 内部 static last_gif_path 去重(相同路径直接返回 ESP_OK)
- 切换后立即 lv_timer_set_period(timer, 20) 防止 lv_gif_set_src 重建为默认 10ms
- LVGL 锁保护 200ms 超时

### 22 情绪 → 3 hiyori GIF 映射表

定义在 main/dzbj/ai_chat_ui.c USE_BG_GIF_POC 包裹内:

| GIF | 情绪标签 (个数) |
|-----|----------------|
| m06 (默认/积极) | neutral, happy, laughing, funny, loving, relaxed, delicious, kissy, confident, silly, blink, curious (12) |
| m07 (思考/疲倦) | sleepy, thinking, confused, embarrassed, dizzy (5) |
| m03 (负面/严肃) | sad, crying, angry, surprised, shocked (5) |

22 个标准情绪 100% 覆盖,未映射的情绪默认 fallback 到 m06。

### ai_chat_set_emotion 改造

PoC 模式下优先切换数字人大图(不再切隐藏的 emoji 200x89):

```c
#ifdef USE_BG_GIF_POC
if (bg_gif_demo_is_running()) {
    const char *path = find_hiyori_gif(emotion);
    bg_gif_demo_switch_gif(path);
    return;
}
#endif
// 非 PoC 模式 fallback emoji 路径保留
```

## 调用链(已与现有 RTC 字幕解析对接,无需改 application.cc)

RTC 字幕 → application.cc:1419 display->SetEmotion(emo)
  → AiChatDisplay::SetEmotion (display/ai_chat_display.cc)
  → ai_chat_set_emotion (dzbj/ai_chat_ui.c)
  → bg_gif_demo_switch_gif (PoC 模式)
  → 数字人 hiyori GIF 切换

## 烧录验证(用户实测)

监控 60s 期间捕获情绪切换:
- neutral / happy → m06(已在播,去重)
- thinking → m07 切换成功 (590ms 延迟)
- confused → m07 去重跳过
- AI 回复结束 → 自动回到 neutral
- 无 abort / 重启

m06 ↔ m07 切换屏幕可视确认,m03 走相同代码路径无需重复测试。

## GSD 文档(同时提交)

- .planning/milestones/digital_human_rtc/phases/phase_04_emotion_mapping/PLAN.md
- .planning/milestones/digital_human_rtc/phases/phase_04_emotion_mapping/EMOTION_REPORT.md
2026-05-13 11:59:38 +08:00

48 lines
1.4 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* bg_gif_demo.h — 背景图 + 透明 GIF 叠加显示(方案 C
*
* 数据流:
* - 背景图JPG→ esp_jpeg 解码 RGB565 → lv_img底层
* - Hiyori GIF带 Alpha 透明)→ PSRAM → lv_gif上层叠加
*
* 优势:
* - 背景图加载一次,常驻 PSRAM不刷新
* - GIF 切换时仅刷新 GIF 区域,背景透出
* - 与 LVGL 字幕/UI 完美共存
*/
#pragma once
#include "esp_err.h"
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* 启动背景 + GIF 叠加显示
*
* @param bg_jpg_path 背景图 JPG 文件路径,例如 "/spiflash/Background_360x360.jpg"
* @param gif_path 透明 GIF 文件路径,例如 "/spiflash/hiyori_m05.gif"
* @return ESP_OK 成功
*/
esp_err_t bg_gif_demo_start(const char *bg_jpg_path, const char *gif_path);
void bg_gif_demo_stop(void);
bool bg_gif_demo_is_running(void);
/**
* 运行时切换数字人 GIF背景图保持不变
*
* 释放旧 GIF 的 PSRAM → 加载新 GIF → lv_gif_set_src → 重置定时器周期 20ms
* 内部带"相同路径去重",重复调用无副作用
*
* @param new_gif_path 新 GIF 路径,例如 "/spiflash/hiyori_m03.gif"
* @return ESP_OK 成功ESP_ERR_INVALID_STATE 未启动ESP_ERR_NO_MEM PSRAM 不足ESP_FAIL 文件错误
*/
esp_err_t bg_gif_demo_switch_gif(const char *new_gif_path);
#ifdef __cplusplus
}
#endif