docs(architecture): add product architecture design for voice schedul… - #1
Open
jing-gou wants to merge 1 commit into
Open
docs(architecture): add product architecture design for voice schedul…#1jing-gou wants to merge 1 commit into
jing-gou wants to merge 1 commit into
Conversation
Owner
Author
1 similar comment
Owner
Author
|
Important Fenno AI is not enabled for this organization Fenno AI is currently in internal testing and is not yet open for external organizations. If you would like to request access, sign in to Fenno AI Console, choose Give Feedback in the lower-left corner, and tell us what you need. We will follow up promptly. |
1 similar comment
|
Important Fenno AI is not enabled for this organization Fenno AI is currently in internal testing and is not yet open for external organizations. If you would like to request access, sign in to Fenno AI Console, choose Give Feedback in the lower-left corner, and tell us what you need. We will follow up promptly. |
jing-gou
pushed a commit
that referenced
this pull request
Aug 6, 2026
* ✨ feat(voice): 接入 ESP32-S3 Linx WSS 传输骨架 建立平台无关的 WebSocket 分片重组与 generation 隔离,增加 ESP-IDF 6.0.2 的 WSS/TLS Transport、凭据解析边界、固定事件队列和 Linx hello 超时。同步主机 TDD、架构边界和语音模块文档,明确真实板闭环仍待 Issue 1024XEngineer#107 验收。 * 🐛 fix(voice): 固化 MVP 音频经验并保护会话代次 * ✨ feat(voice): 完善 Linx 双向协商与 ESP32-S3 回退验证 Linx 服务端可能在重连 hello 中返回不同的下行采样率或帧时长;如果继续沿用旧的播放配置,会把协议已变更的音频静默送进错误的 AudioOutput。物理链路在 hello 失败但尚未完成清理时也不能被重复 Connect。 本次分离 capture/playback 音频格式,Provider 保存已协商格式并拒绝重连时的格式变化;Connect 同时检查 Provider 和底层 Transport 状态。补充断线、重连 hello、旧 generation、TTS abort 和格式变化的主机契约测试,并把受控 ota_1 启动、otadata 恢复和 otatool 回退流程写入文档。 验证:./scripts/run_checks.sh(主机 9/9、架构、Profile、Python 4/4);ESP-IDF 6.0.2 / ESP32-S3 构建通过,voicelife.bin 172480 bytes;真实板以 115200 只写 ota_1@0x410000 后启动成功,并恢复 ota_0 与原 SQLite 数据。 风险:真实 Linx WSS、ASR、TTS、I2S/AFE/Opus 仍未通过;重连格式变化需要 Stop 后重新 Start,当前不做静默 AudioOutput 重配置。 Upstream: 78/xiaozhi-esp32@dd99da0 Refs 1024XEngineer#107 Refs 1024XEngineer#91 * 🐛 fix(test): 显式初始化语音事件测试夹具 GitHub GCC 13 在 -Wmissing-field-initializers -Werror 下拒绝 VoiceEvent 聚合初始化,导致主机测试 job 无法编译。 为 TTS start/stop、断线和重连 fixture 显式补齐 text 与 aborted 字段,保持测试意图不变。 验证:voice_session_contract_test 通过;等待远端完整 CI 复跑。 Refs 1024XEngineer#107 Refs 1024XEngineer#91 * ✨ feat(voice): 固化有界音频队列与迁移边界 把旧 MVP 中的队列满载策略、generation 清理和水位统计落成平台无关契约,新增主机 TDD 覆盖。同步 Linx、ESP32-S3、小智 AFE 与 SQLite 控制面研究证据,明确其他板卡仅按 Profile 准入。\n\nRefs 1024XEngineer#105\nRefs 1024XEngineer#91 * ✨ feat(voice): 接入会话采集音频回调契约 参考旧 voicelife-pcb-native-mvp 的采集任务边界,让 AudioInputPort 只提交格式和负载,由 VoiceSession 统一补齐 generation 与 sequence。补充 TDD 覆盖迟到帧拒绝、回调清理和格式校验,并同步语音架构与研究决策文档。 Refs 1024XEngineer#107 Refs 1024XEngineer#105 Refs 1024XEngineer#91 * 📝 docs(voice): 刷新官方接入与跨板能力边界 记录 Linx MQTT 公开资料缺口、ESP-IDF 6.0.2 I2S/PDM 约束、跨板能力矩阵和立创板迁移输入。\n\n明确当前只验证研究与契约,不把旧 MVP 的 Codec 引脚或芯片能力冒充新工程实板录放通过。\n\nRefs 1024XEngineer#107\nRefs 1024XEngineer#105\nRefs 1024XEngineer#91 * 🐛 fix(docs): 移除失效的 Zephyr 音频来源 跨板矩阵事实仍以 ESP-IDF 6.0.2 I2S 官方文档为主,Zephyr 仅保留已核对的文件系统抽象参考。\n\nRefs 1024XEngineer#107\nRefs 1024XEngineer#105\nRefs 1024XEngineer#91 * 👷 ci(quality): 统一 Linx ESP 传输格式 补齐新增语音端口的 clang-format-18 结果,保持 WebSocket 分片和会话逻辑不变。 * ✅ test(voice): 补充 Linx Codec 与 Provider Registry 错误路径测试 - 覆盖 hello 无效音频、空 abort、非 JSON、未知 tts 状态等拒绝路径 - 覆盖 \u 转义拒绝、opus/wav 音频参数解析边界 - 覆盖 Registry 空 ID、重复注册、未找到与缺能力路径 * 📝 docs(process): 将研究资料迁移到 Issue 归档 * 🔧 build(config): 修正 Flash 配置为 16MB 并启用双 OTA 分区表 sdkconfig: CONFIG_ESPTOOLPY_FLASHSIZE 2MB → 16MB sdkconfig.defaults: SINGLE_APP_LARGE → CUSTOM + 16MB 新增 partitions_voicelife_16mb.csv: nvs 24KB + otadata 8KB + ota_0 4MB + ota_1 4MB + voicelife 2MB 实板验证: ESP32-S3 16MB Flash, 之前配成 2MB 导致启动时 spi_flash 警告并浪费 14MB 空间。 Refs 1024XEngineer#106 * ♻️ refactor(runtime): 从 Scaffold 硬编码切换为 Provider Registry 驱动 将 Runtime 从旧 VoiceSessionCoordinator + ScaffoldAdapters 重构为 新 VoiceSession + SpeechProviderRegistry 架构: - 新增 ScaffoldAudioInput/ScaffoldAudioOutput/ScaffoldSpeechProvider 实现新端口接口 (AudioInputPort/AudioOutputPort/SpeechProviderAdapter) - Runtime 构造时注册 scaffold Provider 到 SpeechProviderRegistry - Start() 从 Registry 创建 Provider 并构造 VoiceSession - 移除旧 AudioDevicePort/SpeechProviderPort/硬编码 scaffold 这是 fennoai 审查 1024XEngineer#106 标记的阻塞项 #1: 之前新语音架构在启动路径 上是死代码, Runtime 从未使用 VoiceSession 或 SpeechProviderRegistry。 现在架构主干真正串联, Scaffold Provider 可被真实 Adapter 替换。 主机测试 10/10 通过。 Refs 1024XEngineer#106 * 🐛 fix(voice): 修复 Interrupt 和 EndCapture 的状态机缺陷 Interrupt: generation 递增从 Abort/Flush 之后移到之前。 旧顺序下迟到的音频帧在 Abort→Flush 窗口内仍可进入会话; 新顺序先失效旧代次, Provider/Output 拒绝后续的旧帧。 EndCapture: provider_.StopCapture() 失败时不再静默保留 kCapturing 状态(此时本地输入已停止, 状态分裂)。 改为显式转 kFailed 并上报 capture_stop_failed 证据。 这是 fennoai 审查 1024XEngineer#106 标记的阻塞项 #2 和 1024XEngineer#3。 主机测试 10/10 通过。 Refs 1024XEngineer#106 * 🐛 fix(voice): 去掉虚假 opus 声明,添加上下行帧大小上限 能力声明: DefaultCapabilities 移除未实现的 opus, 只保留 pcm。 之前 Registry 会按虚假能力选到 Linx Provider, 运行时在音频链路炸掉。 帧校验: AudioFrame 新增 kMaxPayloadBytes=16384。 AcceptFrameLocked、HandleInputAudio、HandleAudio 三层都拒绝超大 payload, 防止畸形帧或攻击性 payload 压爆嵌入式内存。 这是 fennoai 审查 1024XEngineer#106 标记的阻塞项 1024XEngineer#7、1024XEngineer#8、1024XEngineer#16、1024XEngineer#22。 主机测试 10/10 通过。 Refs 1024XEngineer#106 * 🐛 fix(voice): 修复 BeginCapture 回滚、session_id 校验和 JSON 编解码缺陷 BeginCapture: input 启动失败后不再忽略 provider_.StopCapture() 返回值, 回滚失败时 emit evidence 并优先返回回滚错误, 避免云端残留半开采集状态。 Linx OnText: 解码后校验 session_id 与当前会话匹配, 拒绝同 WebSocket 上的旧会话或误路由消息污染当前状态。 EncodeHello: sample_format/frame_size/play_buffer_duration 仅 PCM 时输出, 不再把 PCM 专用字段硬编码进 Opus hello。 Quote: 补全 \b \f 转义, 其余 ASCII 控制字符 (0x00-0x1F) 统一 输出 \u00XX 六位 hex 序列, 避免产出非法 JSON。 这是 fennoai 审查 1024XEngineer#106 标记的阻塞项 1024XEngineer#5、1024XEngineer#14、1024XEngineer#17、1024XEngineer#20。 主机测试 10/10 通过。 Refs 1024XEngineer#106 * 🐛 fix(voice): 修复 Registry 上限、hello 缺少 audio_params 和硬编码缓冲时长 Registry: kMaxProviders 8→16, 满表错误含上限值和诊断提示。 Linx hello: 服务端 hello 不含 audio_params 时拒绝而非静默使用默认格式, 避免协商结果被错误信任。 play_buffer_duration: 从硬编码 1000 改为 frame_duration_ms * 50, 格式变化时自动缩放缓冲时长。 Connect() 已阻塞等待 hello_cv_, hello_timeout_ms 已驱动 wait_for。 这是 fennoai 审查 1024XEngineer#106 标记的阻塞项 1024XEngineer#12、1024XEngineer#15、1024XEngineer#21。 主机测试 10/10 通过。 Refs 1024XEngineer#106 * 🐛 fix(voice): JSON codec 支持 \uXXXX Unicode 转义并加固字段边界 UnescapeJsonString: 新增 \uXXXX 四字节 hex 解码为 UTF-8。 支持 BMP 范围 (U+0000-U+FFFF), 拒绝代理对 (\uD800-\uDFFF)。 解决了此前中文 ASR/TTS 文本因 \uXXXX 转义导致解析失败的问题。 FindField: 匹配字段名前先验证前驱字符为 { 或 ,, 避免把值里的同名字符串误判为对象键。 Registry: 文档化线程安全约束 -- Register() 必须在调度启动前完成。 这是 fennoai 审查 1024XEngineer#106 标记的阻塞项 1024XEngineer#11、1024XEngineer#18、1024XEngineer#19(部分缓解)。 主机测试 10/10 通过。 Refs 1024XEngineer#106 * ♻️ refactor(voice): LinxJsonCodec 从字符串扫描替换为 cJSON 结构化解析 third_party/cjson: 引入 cJSON (MIT license) 源码, ESP-IDF 和主机测试共用。 LinxJsonCodec 全面重写: Encode*: std::ostringstream → cJSON_CreateObject + cJSON_PrintUnformatted DecodeText: 字符串扫描 → cJSON_ParseWithLength, 字段通过 cJSON_GetObjectItem 按类型读取 删除: ReadJsonString, SkipSpace, FindField, ReadStringField, ReadUnsignedField, ReadBoolField, ReadObjectField, Quote (~300行手动解析逻辑) 保留: CodecName, ModeName 主机测试: CMakeLists 新增 cjson 库 (LANGUAGES C CXX), linx 链接 cjson ESP-IDF: voicelife_linx 直接编译 third_party/cjson/cJSON.c 修复: GetRequired/GetOptional 类型匹配改为位掩码 (&) 以支持 cJSON_False|cJSON_True 组合查询。 这是 fennoai 审查 1024XEngineer#106 标记的阻塞项 1024XEngineer#18、1024XEngineer#19。 主机测试 10/10 通过。 ESP-IDF 编译 voicelife.bin 181KB, 实板 16MB Flash 正常启动。 Refs 1024XEngineer#106 * 🐛 fix(voice): cJSON ParseAudioParams 补回 channels=0 等范围校验 Rebase 后远程新增错误路径测试, cJSON 版 ParseAudioParams 缺少 channels/bits/duration 零值拒绝。补回与旧代码等价的范围校验。 主机测试 17/17 通过。 Refs 1024XEngineer#106 * 🔧 build(style): clang-format 修复 CI 格式检查 格式化 voice_ports.h, voice_provider_registry.cc, runtime.cc, voice_session.cc, linx_json_codec.cc, linx_speech_provider.cc 以通过 CI clang-format 门禁。 Refs 1024XEngineer#108 * 📝 docs(voice): 补齐 voice_ports.h 公开 API Doxygen 文档注释 AudioInputPort, AudioOutputPort, VoiceTransportPort, CodecStrategy, ASRAdapter, TTSAdapter, RealtimeAdapter, SpeechProviderAdapter, SpeechProviderRegistry 全部公开 类型和函数补上 /// Doxygen 注释。 通过 CI 公共 API 文档门禁。 Refs 1024XEngineer#108 * 📝 docs(voice): 补齐所有公开 API 的 Doxygen 注释 AudioInputPort, AudioOutputPort, VoiceTransportPort, SpeechProviderPort, CodecStrategy, ASRAdapter, TTSAdapter, RealtimeAdapter 所有公开析构函数和方法补上 /// 注释。 通过 CI 公共 API 文档门禁。 Refs 1024XEngineer#108 * 📝 docs(voice): 补 SpeechProviderAdapter 析构和 Entry 结构体 Doxygen * 📝 docs(voice): 重写 voice_ports.h 为中文 Doxygen 格式 全部 33 个公开头文件通过 check_public_api_docs.py 校验。 Refs 1024XEngineer#108 * 📝 docs(voice): rebase 后补齐头文件 Doxygen 与清理 CMakeLists 修复与 main 合并后丢失的注释: - voice_types.h, voice_session.h, audio_frame_queue.h 补中文 Doxygen - linx_types.h, websocket_fragment_assembler.h 恢复 Doxygen 版本 - CapabilityProfile::Has 改为 std::find, 避免公共 API 检查器误报 tests/host/CMakeLists.txt 清理重复的 linx_esp 定义。 主机测试 25/25 通过, Doxygen 33 头文件 PASS。 Refs 1024XEngineer#108 * 🔧 build(style): clang-format 修复 voice_session_contract_test.cc Refs 1024XEngineer#108
jing-gou
pushed a commit
that referenced
this pull request
Aug 6, 2026
* ✨ feat(voice): 接入 ESP32-S3 Linx WSS 传输骨架 建立平台无关的 WebSocket 分片重组与 generation 隔离,增加 ESP-IDF 6.0.2 的 WSS/TLS Transport、凭据解析边界、固定事件队列和 Linx hello 超时。同步主机 TDD、架构边界和语音模块文档,明确真实板闭环仍待 Issue 1024XEngineer#107 验收。 * 🐛 fix(voice): 固化 MVP 音频经验并保护会话代次 * ✨ feat(voice): 完善 Linx 双向协商与 ESP32-S3 回退验证 Linx 服务端可能在重连 hello 中返回不同的下行采样率或帧时长;如果继续沿用旧的播放配置,会把协议已变更的音频静默送进错误的 AudioOutput。物理链路在 hello 失败但尚未完成清理时也不能被重复 Connect。 本次分离 capture/playback 音频格式,Provider 保存已协商格式并拒绝重连时的格式变化;Connect 同时检查 Provider 和底层 Transport 状态。补充断线、重连 hello、旧 generation、TTS abort 和格式变化的主机契约测试,并把受控 ota_1 启动、otadata 恢复和 otatool 回退流程写入文档。 验证:./scripts/run_checks.sh(主机 9/9、架构、Profile、Python 4/4);ESP-IDF 6.0.2 / ESP32-S3 构建通过,voicelife.bin 172480 bytes;真实板以 115200 只写 ota_1@0x410000 后启动成功,并恢复 ota_0 与原 SQLite 数据。 风险:真实 Linx WSS、ASR、TTS、I2S/AFE/Opus 仍未通过;重连格式变化需要 Stop 后重新 Start,当前不做静默 AudioOutput 重配置。 Upstream: 78/xiaozhi-esp32@dd99da0 Refs 1024XEngineer#107 Refs 1024XEngineer#91 * 🐛 fix(test): 显式初始化语音事件测试夹具 GitHub GCC 13 在 -Wmissing-field-initializers -Werror 下拒绝 VoiceEvent 聚合初始化,导致主机测试 job 无法编译。 为 TTS start/stop、断线和重连 fixture 显式补齐 text 与 aborted 字段,保持测试意图不变。 验证:voice_session_contract_test 通过;等待远端完整 CI 复跑。 Refs 1024XEngineer#107 Refs 1024XEngineer#91 * ✨ feat(voice): 固化有界音频队列与迁移边界 把旧 MVP 中的队列满载策略、generation 清理和水位统计落成平台无关契约,新增主机 TDD 覆盖。同步 Linx、ESP32-S3、小智 AFE 与 SQLite 控制面研究证据,明确其他板卡仅按 Profile 准入。\n\nRefs 1024XEngineer#105\nRefs 1024XEngineer#91 * ✨ feat(voice): 接入会话采集音频回调契约 参考旧 voicelife-pcb-native-mvp 的采集任务边界,让 AudioInputPort 只提交格式和负载,由 VoiceSession 统一补齐 generation 与 sequence。补充 TDD 覆盖迟到帧拒绝、回调清理和格式校验,并同步语音架构与研究决策文档。 Refs 1024XEngineer#107 Refs 1024XEngineer#105 Refs 1024XEngineer#91 * 📝 docs(voice): 刷新官方接入与跨板能力边界 记录 Linx MQTT 公开资料缺口、ESP-IDF 6.0.2 I2S/PDM 约束、跨板能力矩阵和立创板迁移输入。\n\n明确当前只验证研究与契约,不把旧 MVP 的 Codec 引脚或芯片能力冒充新工程实板录放通过。\n\nRefs 1024XEngineer#107\nRefs 1024XEngineer#105\nRefs 1024XEngineer#91 * 🐛 fix(docs): 移除失效的 Zephyr 音频来源 跨板矩阵事实仍以 ESP-IDF 6.0.2 I2S 官方文档为主,Zephyr 仅保留已核对的文件系统抽象参考。\n\nRefs 1024XEngineer#107\nRefs 1024XEngineer#105\nRefs 1024XEngineer#91 * 👷 ci(quality): 统一 Linx ESP 传输格式 补齐新增语音端口的 clang-format-18 结果,保持 WebSocket 分片和会话逻辑不变。 * ✅ test(voice): 补充 Linx Codec 与 Provider Registry 错误路径测试 - 覆盖 hello 无效音频、空 abort、非 JSON、未知 tts 状态等拒绝路径 - 覆盖 \u 转义拒绝、opus/wav 音频参数解析边界 - 覆盖 Registry 空 ID、重复注册、未找到与缺能力路径 * 📝 docs(process): 将研究资料迁移到 Issue 归档 * 🔧 build(config): 修正 Flash 配置为 16MB 并启用双 OTA 分区表 sdkconfig: CONFIG_ESPTOOLPY_FLASHSIZE 2MB → 16MB sdkconfig.defaults: SINGLE_APP_LARGE → CUSTOM + 16MB 新增 partitions_voicelife_16mb.csv: nvs 24KB + otadata 8KB + ota_0 4MB + ota_1 4MB + voicelife 2MB 实板验证: ESP32-S3 16MB Flash, 之前配成 2MB 导致启动时 spi_flash 警告并浪费 14MB 空间。 Refs 1024XEngineer#106 * ♻️ refactor(runtime): 从 Scaffold 硬编码切换为 Provider Registry 驱动 将 Runtime 从旧 VoiceSessionCoordinator + ScaffoldAdapters 重构为 新 VoiceSession + SpeechProviderRegistry 架构: - 新增 ScaffoldAudioInput/ScaffoldAudioOutput/ScaffoldSpeechProvider 实现新端口接口 (AudioInputPort/AudioOutputPort/SpeechProviderAdapter) - Runtime 构造时注册 scaffold Provider 到 SpeechProviderRegistry - Start() 从 Registry 创建 Provider 并构造 VoiceSession - 移除旧 AudioDevicePort/SpeechProviderPort/硬编码 scaffold 这是 fennoai 审查 1024XEngineer#106 标记的阻塞项 #1: 之前新语音架构在启动路径 上是死代码, Runtime 从未使用 VoiceSession 或 SpeechProviderRegistry。 现在架构主干真正串联, Scaffold Provider 可被真实 Adapter 替换。 主机测试 10/10 通过。 Refs 1024XEngineer#106 * 🐛 fix(voice): 修复 Interrupt 和 EndCapture 的状态机缺陷 Interrupt: generation 递增从 Abort/Flush 之后移到之前。 旧顺序下迟到的音频帧在 Abort→Flush 窗口内仍可进入会话; 新顺序先失效旧代次, Provider/Output 拒绝后续的旧帧。 EndCapture: provider_.StopCapture() 失败时不再静默保留 kCapturing 状态(此时本地输入已停止, 状态分裂)。 改为显式转 kFailed 并上报 capture_stop_failed 证据。 这是 fennoai 审查 1024XEngineer#106 标记的阻塞项 #2 和 1024XEngineer#3。 主机测试 10/10 通过。 Refs 1024XEngineer#106 * 🐛 fix(voice): 去掉虚假 opus 声明,添加上下行帧大小上限 能力声明: DefaultCapabilities 移除未实现的 opus, 只保留 pcm。 之前 Registry 会按虚假能力选到 Linx Provider, 运行时在音频链路炸掉。 帧校验: AudioFrame 新增 kMaxPayloadBytes=16384。 AcceptFrameLocked、HandleInputAudio、HandleAudio 三层都拒绝超大 payload, 防止畸形帧或攻击性 payload 压爆嵌入式内存。 这是 fennoai 审查 1024XEngineer#106 标记的阻塞项 1024XEngineer#7、1024XEngineer#8、1024XEngineer#16、1024XEngineer#22。 主机测试 10/10 通过。 Refs 1024XEngineer#106 * 🐛 fix(voice): 修复 BeginCapture 回滚、session_id 校验和 JSON 编解码缺陷 BeginCapture: input 启动失败后不再忽略 provider_.StopCapture() 返回值, 回滚失败时 emit evidence 并优先返回回滚错误, 避免云端残留半开采集状态。 Linx OnText: 解码后校验 session_id 与当前会话匹配, 拒绝同 WebSocket 上的旧会话或误路由消息污染当前状态。 EncodeHello: sample_format/frame_size/play_buffer_duration 仅 PCM 时输出, 不再把 PCM 专用字段硬编码进 Opus hello。 Quote: 补全 \b \f 转义, 其余 ASCII 控制字符 (0x00-0x1F) 统一 输出 \u00XX 六位 hex 序列, 避免产出非法 JSON。 这是 fennoai 审查 1024XEngineer#106 标记的阻塞项 1024XEngineer#5、1024XEngineer#14、1024XEngineer#17、1024XEngineer#20。 主机测试 10/10 通过。 Refs 1024XEngineer#106 * 🐛 fix(voice): 修复 Registry 上限、hello 缺少 audio_params 和硬编码缓冲时长 Registry: kMaxProviders 8→16, 满表错误含上限值和诊断提示。 Linx hello: 服务端 hello 不含 audio_params 时拒绝而非静默使用默认格式, 避免协商结果被错误信任。 play_buffer_duration: 从硬编码 1000 改为 frame_duration_ms * 50, 格式变化时自动缩放缓冲时长。 Connect() 已阻塞等待 hello_cv_, hello_timeout_ms 已驱动 wait_for。 这是 fennoai 审查 1024XEngineer#106 标记的阻塞项 1024XEngineer#12、1024XEngineer#15、1024XEngineer#21。 主机测试 10/10 通过。 Refs 1024XEngineer#106 * 🐛 fix(voice): JSON codec 支持 \uXXXX Unicode 转义并加固字段边界 UnescapeJsonString: 新增 \uXXXX 四字节 hex 解码为 UTF-8。 支持 BMP 范围 (U+0000-U+FFFF), 拒绝代理对 (\uD800-\uDFFF)。 解决了此前中文 ASR/TTS 文本因 \uXXXX 转义导致解析失败的问题。 FindField: 匹配字段名前先验证前驱字符为 { 或 ,, 避免把值里的同名字符串误判为对象键。 Registry: 文档化线程安全约束 -- Register() 必须在调度启动前完成。 这是 fennoai 审查 1024XEngineer#106 标记的阻塞项 1024XEngineer#11、1024XEngineer#18、1024XEngineer#19(部分缓解)。 主机测试 10/10 通过。 Refs 1024XEngineer#106 * ♻️ refactor(voice): LinxJsonCodec 从字符串扫描替换为 cJSON 结构化解析 third_party/cjson: 引入 cJSON (MIT license) 源码, ESP-IDF 和主机测试共用。 LinxJsonCodec 全面重写: Encode*: std::ostringstream → cJSON_CreateObject + cJSON_PrintUnformatted DecodeText: 字符串扫描 → cJSON_ParseWithLength, 字段通过 cJSON_GetObjectItem 按类型读取 删除: ReadJsonString, SkipSpace, FindField, ReadStringField, ReadUnsignedField, ReadBoolField, ReadObjectField, Quote (~300行手动解析逻辑) 保留: CodecName, ModeName 主机测试: CMakeLists 新增 cjson 库 (LANGUAGES C CXX), linx 链接 cjson ESP-IDF: voicelife_linx 直接编译 third_party/cjson/cJSON.c 修复: GetRequired/GetOptional 类型匹配改为位掩码 (&) 以支持 cJSON_False|cJSON_True 组合查询。 这是 fennoai 审查 1024XEngineer#106 标记的阻塞项 1024XEngineer#18、1024XEngineer#19。 主机测试 10/10 通过。 ESP-IDF 编译 voicelife.bin 181KB, 实板 16MB Flash 正常启动。 Refs 1024XEngineer#106 * 🐛 fix(voice): cJSON ParseAudioParams 补回 channels=0 等范围校验 Rebase 后远程新增错误路径测试, cJSON 版 ParseAudioParams 缺少 channels/bits/duration 零值拒绝。补回与旧代码等价的范围校验。 主机测试 17/17 通过。 Refs 1024XEngineer#106 * 🔧 build(style): clang-format 修复 CI 格式检查 格式化 voice_ports.h, voice_provider_registry.cc, runtime.cc, voice_session.cc, linx_json_codec.cc, linx_speech_provider.cc 以通过 CI clang-format 门禁。 Refs 1024XEngineer#108 * 📝 docs(voice): 补齐 voice_ports.h 公开 API Doxygen 文档注释 AudioInputPort, AudioOutputPort, VoiceTransportPort, CodecStrategy, ASRAdapter, TTSAdapter, RealtimeAdapter, SpeechProviderAdapter, SpeechProviderRegistry 全部公开 类型和函数补上 /// Doxygen 注释。 通过 CI 公共 API 文档门禁。 Refs 1024XEngineer#108 * 📝 docs(voice): 补齐所有公开 API 的 Doxygen 注释 AudioInputPort, AudioOutputPort, VoiceTransportPort, SpeechProviderPort, CodecStrategy, ASRAdapter, TTSAdapter, RealtimeAdapter 所有公开析构函数和方法补上 /// 注释。 通过 CI 公共 API 文档门禁。 Refs 1024XEngineer#108 * 📝 docs(voice): 补 SpeechProviderAdapter 析构和 Entry 结构体 Doxygen * 📝 docs(voice): 重写 voice_ports.h 为中文 Doxygen 格式 全部 33 个公开头文件通过 check_public_api_docs.py 校验。 Refs 1024XEngineer#108 * 📝 docs(voice): rebase 后补齐头文件 Doxygen 与清理 CMakeLists 修复与 main 合并后丢失的注释: - voice_types.h, voice_session.h, audio_frame_queue.h 补中文 Doxygen - linx_types.h, websocket_fragment_assembler.h 恢复 Doxygen 版本 - CapabilityProfile::Has 改为 std::find, 避免公共 API 检查器误报 tests/host/CMakeLists.txt 清理重复的 linx_esp 定义。 主机测试 25/25 通过, Doxygen 33 头文件 PASS。 Refs 1024XEngineer#108 * ✨ feat(voice): 建立 ESP32-S3 PCM/I2S 板级探针 将板级 GPIO、I2C 地址、设备 PCM 格式和 DMA 预算收敛到独立 Profile,并把探针编排放回 Runtime 组装根。探针只验证 I2C ACK、I2S 通道生命周期和有限静音读写,不把 Codec 录放或云端闭环伪装成已完成。 主机契约测试 11/11、Python 测试 4/4、架构检查和 ESP-IDF 6.0.2 的 ESP32-S3 构建均通过。实板以 115200 只写非活动 ota_1,最终镜像 222320 B 回读逐字节一致;当前连接板为 SKU=voicelife-pcb 的 NoAudioCodec 纯 I2S 板,I2S smoke 通过但 ES8311/ES7210/PCA9557 未 ACK,已恢复 otadata 并确认原固件和 SQLite 数据正常。后续必须为纯 I2S 板与 Lichuang Codec 板分别建立 Profile。 Refs 1024XEngineer#109 * 📝 docs(audio): 补齐 audio_esp 头文件中文 Doxygen 注释 audio_board_profile.h, esp32s3_audio_probe.h 公开 API 补齐 /** @brief @PARAM @return */ 注释。 Doxygen 35 头文件 PASS。 Refs 1024XEngineer#109 * 🏗️ refactor(voice): 接入 voicelife-pcb 纯 I2S PCM Profile 当前实板原固件报告 SKU=voicelife-pcb/NoAudioCodec,不能复用 Lichuang Codec Profile。将 AudioBoardProfile 扩展为 external-codec-duplex 与 direct-i2s-simplex,独立描述 RX/TX controller、GPIO、采样率、wire slot 和 PCM 对齐,并把探针结果接入硬件/PCM 信号失败路径。 迁移旧 voicelife-pcb-native-mvp 的 I2S0 TX + I2S1 RX 拓扑和有界回放边界;同一块板对照 >>12 与 >>14,削波从 79791 ppm 降到 208 ppm,当前 Profile 采用 >>14。主机 11/11、Python 测试、Profile validate、架构检查和 ESP-IDF 6.0.2 构建通过;最终镜像 229488 B,ota_1 回读逐字节一致,恢复 otadata 后原固件从 ota_0 启动且 SQLite 仍加载 7 events/8 reminders/0 notes。 仍未宣称 Codec、AFE、AEC、WakeNet、Opus、WSS、ASR、TTS 或声学播放闭环。 Upstream: 78/xiaozhi-esp32@dd99da0 Refs 1024XEngineer#111 * 📝 docs(audio): 补 esp32s3_audio_probe.h Impl Doxygen Refs 1024XEngineer#111 * 🐛 fix(audio): 同步 audio_esp 源文件与新版头文件字段 audio_board_profile.cc, esp32s3_audio_probe.cc 使用 capture_i2s/ playback_i2s 字段; audio_board_profile.h, esp32s3_audio_probe.h 补齐中文 Doxygen 注释。 主机测试 26/26 通过, Doxygen PASS。 Refs 1024XEngineer#109 * 🔧 build(style): clang-format 修复 audio_esp 与测试文件 runtime.cc, audio_board_profile_contract_test.cc, voice_session_contract_test.cc 等通过 clang-format-18。 Refs 1024XEngineer#109
jing-gou
pushed a commit
that referenced
this pull request
Aug 7, 2026
…#112) * ✨ feat(voice): 接入 ESP32-S3 Linx WSS 传输骨架 建立平台无关的 WebSocket 分片重组与 generation 隔离,增加 ESP-IDF 6.0.2 的 WSS/TLS Transport、凭据解析边界、固定事件队列和 Linx hello 超时。同步主机 TDD、架构边界和语音模块文档,明确真实板闭环仍待 Issue 1024XEngineer#107 验收。 * 🐛 fix(voice): 固化 MVP 音频经验并保护会话代次 * ✨ feat(voice): 完善 Linx 双向协商与 ESP32-S3 回退验证 Linx 服务端可能在重连 hello 中返回不同的下行采样率或帧时长;如果继续沿用旧的播放配置,会把协议已变更的音频静默送进错误的 AudioOutput。物理链路在 hello 失败但尚未完成清理时也不能被重复 Connect。 本次分离 capture/playback 音频格式,Provider 保存已协商格式并拒绝重连时的格式变化;Connect 同时检查 Provider 和底层 Transport 状态。补充断线、重连 hello、旧 generation、TTS abort 和格式变化的主机契约测试,并把受控 ota_1 启动、otadata 恢复和 otatool 回退流程写入文档。 验证:./scripts/run_checks.sh(主机 9/9、架构、Profile、Python 4/4);ESP-IDF 6.0.2 / ESP32-S3 构建通过,voicelife.bin 172480 bytes;真实板以 115200 只写 ota_1@0x410000 后启动成功,并恢复 ota_0 与原 SQLite 数据。 风险:真实 Linx WSS、ASR、TTS、I2S/AFE/Opus 仍未通过;重连格式变化需要 Stop 后重新 Start,当前不做静默 AudioOutput 重配置。 Upstream: 78/xiaozhi-esp32@dd99da0 Refs 1024XEngineer#107 Refs 1024XEngineer#91 * 🐛 fix(test): 显式初始化语音事件测试夹具 GitHub GCC 13 在 -Wmissing-field-initializers -Werror 下拒绝 VoiceEvent 聚合初始化,导致主机测试 job 无法编译。 为 TTS start/stop、断线和重连 fixture 显式补齐 text 与 aborted 字段,保持测试意图不变。 验证:voice_session_contract_test 通过;等待远端完整 CI 复跑。 Refs 1024XEngineer#107 Refs 1024XEngineer#91 * ✨ feat(voice): 固化有界音频队列与迁移边界 把旧 MVP 中的队列满载策略、generation 清理和水位统计落成平台无关契约,新增主机 TDD 覆盖。同步 Linx、ESP32-S3、小智 AFE 与 SQLite 控制面研究证据,明确其他板卡仅按 Profile 准入。\n\nRefs 1024XEngineer#105\nRefs 1024XEngineer#91 * ✨ feat(voice): 接入会话采集音频回调契约 参考旧 voicelife-pcb-native-mvp 的采集任务边界,让 AudioInputPort 只提交格式和负载,由 VoiceSession 统一补齐 generation 与 sequence。补充 TDD 覆盖迟到帧拒绝、回调清理和格式校验,并同步语音架构与研究决策文档。 Refs 1024XEngineer#107 Refs 1024XEngineer#105 Refs 1024XEngineer#91 * 📝 docs(voice): 刷新官方接入与跨板能力边界 记录 Linx MQTT 公开资料缺口、ESP-IDF 6.0.2 I2S/PDM 约束、跨板能力矩阵和立创板迁移输入。\n\n明确当前只验证研究与契约,不把旧 MVP 的 Codec 引脚或芯片能力冒充新工程实板录放通过。\n\nRefs 1024XEngineer#107\nRefs 1024XEngineer#105\nRefs 1024XEngineer#91 * 🐛 fix(docs): 移除失效的 Zephyr 音频来源 跨板矩阵事实仍以 ESP-IDF 6.0.2 I2S 官方文档为主,Zephyr 仅保留已核对的文件系统抽象参考。\n\nRefs 1024XEngineer#107\nRefs 1024XEngineer#105\nRefs 1024XEngineer#91 * 👷 ci(quality): 统一 Linx ESP 传输格式 补齐新增语音端口的 clang-format-18 结果,保持 WebSocket 分片和会话逻辑不变。 * ✅ test(voice): 补充 Linx Codec 与 Provider Registry 错误路径测试 - 覆盖 hello 无效音频、空 abort、非 JSON、未知 tts 状态等拒绝路径 - 覆盖 \u 转义拒绝、opus/wav 音频参数解析边界 - 覆盖 Registry 空 ID、重复注册、未找到与缺能力路径 * 📝 docs(process): 将研究资料迁移到 Issue 归档 * 🔧 build(config): 修正 Flash 配置为 16MB 并启用双 OTA 分区表 sdkconfig: CONFIG_ESPTOOLPY_FLASHSIZE 2MB → 16MB sdkconfig.defaults: SINGLE_APP_LARGE → CUSTOM + 16MB 新增 partitions_voicelife_16mb.csv: nvs 24KB + otadata 8KB + ota_0 4MB + ota_1 4MB + voicelife 2MB 实板验证: ESP32-S3 16MB Flash, 之前配成 2MB 导致启动时 spi_flash 警告并浪费 14MB 空间。 Refs 1024XEngineer#106 * ♻️ refactor(runtime): 从 Scaffold 硬编码切换为 Provider Registry 驱动 将 Runtime 从旧 VoiceSessionCoordinator + ScaffoldAdapters 重构为 新 VoiceSession + SpeechProviderRegistry 架构: - 新增 ScaffoldAudioInput/ScaffoldAudioOutput/ScaffoldSpeechProvider 实现新端口接口 (AudioInputPort/AudioOutputPort/SpeechProviderAdapter) - Runtime 构造时注册 scaffold Provider 到 SpeechProviderRegistry - Start() 从 Registry 创建 Provider 并构造 VoiceSession - 移除旧 AudioDevicePort/SpeechProviderPort/硬编码 scaffold 这是 fennoai 审查 1024XEngineer#106 标记的阻塞项 #1: 之前新语音架构在启动路径 上是死代码, Runtime 从未使用 VoiceSession 或 SpeechProviderRegistry。 现在架构主干真正串联, Scaffold Provider 可被真实 Adapter 替换。 主机测试 10/10 通过。 Refs 1024XEngineer#106 * 🐛 fix(voice): 修复 Interrupt 和 EndCapture 的状态机缺陷 Interrupt: generation 递增从 Abort/Flush 之后移到之前。 旧顺序下迟到的音频帧在 Abort→Flush 窗口内仍可进入会话; 新顺序先失效旧代次, Provider/Output 拒绝后续的旧帧。 EndCapture: provider_.StopCapture() 失败时不再静默保留 kCapturing 状态(此时本地输入已停止, 状态分裂)。 改为显式转 kFailed 并上报 capture_stop_failed 证据。 这是 fennoai 审查 1024XEngineer#106 标记的阻塞项 #2 和 1024XEngineer#3。 主机测试 10/10 通过。 Refs 1024XEngineer#106 * 🐛 fix(voice): 去掉虚假 opus 声明,添加上下行帧大小上限 能力声明: DefaultCapabilities 移除未实现的 opus, 只保留 pcm。 之前 Registry 会按虚假能力选到 Linx Provider, 运行时在音频链路炸掉。 帧校验: AudioFrame 新增 kMaxPayloadBytes=16384。 AcceptFrameLocked、HandleInputAudio、HandleAudio 三层都拒绝超大 payload, 防止畸形帧或攻击性 payload 压爆嵌入式内存。 这是 fennoai 审查 1024XEngineer#106 标记的阻塞项 1024XEngineer#7、1024XEngineer#8、1024XEngineer#16、1024XEngineer#22。 主机测试 10/10 通过。 Refs 1024XEngineer#106 * 🐛 fix(voice): 修复 BeginCapture 回滚、session_id 校验和 JSON 编解码缺陷 BeginCapture: input 启动失败后不再忽略 provider_.StopCapture() 返回值, 回滚失败时 emit evidence 并优先返回回滚错误, 避免云端残留半开采集状态。 Linx OnText: 解码后校验 session_id 与当前会话匹配, 拒绝同 WebSocket 上的旧会话或误路由消息污染当前状态。 EncodeHello: sample_format/frame_size/play_buffer_duration 仅 PCM 时输出, 不再把 PCM 专用字段硬编码进 Opus hello。 Quote: 补全 \b \f 转义, 其余 ASCII 控制字符 (0x00-0x1F) 统一 输出 \u00XX 六位 hex 序列, 避免产出非法 JSON。 这是 fennoai 审查 1024XEngineer#106 标记的阻塞项 1024XEngineer#5、1024XEngineer#14、1024XEngineer#17、1024XEngineer#20。 主机测试 10/10 通过。 Refs 1024XEngineer#106 * 🐛 fix(voice): 修复 Registry 上限、hello 缺少 audio_params 和硬编码缓冲时长 Registry: kMaxProviders 8→16, 满表错误含上限值和诊断提示。 Linx hello: 服务端 hello 不含 audio_params 时拒绝而非静默使用默认格式, 避免协商结果被错误信任。 play_buffer_duration: 从硬编码 1000 改为 frame_duration_ms * 50, 格式变化时自动缩放缓冲时长。 Connect() 已阻塞等待 hello_cv_, hello_timeout_ms 已驱动 wait_for。 这是 fennoai 审查 1024XEngineer#106 标记的阻塞项 1024XEngineer#12、1024XEngineer#15、1024XEngineer#21。 主机测试 10/10 通过。 Refs 1024XEngineer#106 * 🐛 fix(voice): JSON codec 支持 \uXXXX Unicode 转义并加固字段边界 UnescapeJsonString: 新增 \uXXXX 四字节 hex 解码为 UTF-8。 支持 BMP 范围 (U+0000-U+FFFF), 拒绝代理对 (\uD800-\uDFFF)。 解决了此前中文 ASR/TTS 文本因 \uXXXX 转义导致解析失败的问题。 FindField: 匹配字段名前先验证前驱字符为 { 或 ,, 避免把值里的同名字符串误判为对象键。 Registry: 文档化线程安全约束 -- Register() 必须在调度启动前完成。 这是 fennoai 审查 1024XEngineer#106 标记的阻塞项 1024XEngineer#11、1024XEngineer#18、1024XEngineer#19(部分缓解)。 主机测试 10/10 通过。 Refs 1024XEngineer#106 * ♻️ refactor(voice): LinxJsonCodec 从字符串扫描替换为 cJSON 结构化解析 third_party/cjson: 引入 cJSON (MIT license) 源码, ESP-IDF 和主机测试共用。 LinxJsonCodec 全面重写: Encode*: std::ostringstream → cJSON_CreateObject + cJSON_PrintUnformatted DecodeText: 字符串扫描 → cJSON_ParseWithLength, 字段通过 cJSON_GetObjectItem 按类型读取 删除: ReadJsonString, SkipSpace, FindField, ReadStringField, ReadUnsignedField, ReadBoolField, ReadObjectField, Quote (~300行手动解析逻辑) 保留: CodecName, ModeName 主机测试: CMakeLists 新增 cjson 库 (LANGUAGES C CXX), linx 链接 cjson ESP-IDF: voicelife_linx 直接编译 third_party/cjson/cJSON.c 修复: GetRequired/GetOptional 类型匹配改为位掩码 (&) 以支持 cJSON_False|cJSON_True 组合查询。 这是 fennoai 审查 1024XEngineer#106 标记的阻塞项 1024XEngineer#18、1024XEngineer#19。 主机测试 10/10 通过。 ESP-IDF 编译 voicelife.bin 181KB, 实板 16MB Flash 正常启动。 Refs 1024XEngineer#106 * 🐛 fix(voice): cJSON ParseAudioParams 补回 channels=0 等范围校验 Rebase 后远程新增错误路径测试, cJSON 版 ParseAudioParams 缺少 channels/bits/duration 零值拒绝。补回与旧代码等价的范围校验。 主机测试 17/17 通过。 Refs 1024XEngineer#106 * 🔧 build(style): clang-format 修复 CI 格式检查 格式化 voice_ports.h, voice_provider_registry.cc, runtime.cc, voice_session.cc, linx_json_codec.cc, linx_speech_provider.cc 以通过 CI clang-format 门禁。 Refs 1024XEngineer#108 * 📝 docs(voice): 补齐 voice_ports.h 公开 API Doxygen 文档注释 AudioInputPort, AudioOutputPort, VoiceTransportPort, CodecStrategy, ASRAdapter, TTSAdapter, RealtimeAdapter, SpeechProviderAdapter, SpeechProviderRegistry 全部公开 类型和函数补上 /// Doxygen 注释。 通过 CI 公共 API 文档门禁。 Refs 1024XEngineer#108 * 📝 docs(voice): 补齐所有公开 API 的 Doxygen 注释 AudioInputPort, AudioOutputPort, VoiceTransportPort, SpeechProviderPort, CodecStrategy, ASRAdapter, TTSAdapter, RealtimeAdapter 所有公开析构函数和方法补上 /// 注释。 通过 CI 公共 API 文档门禁。 Refs 1024XEngineer#108 * 📝 docs(voice): 补 SpeechProviderAdapter 析构和 Entry 结构体 Doxygen * 📝 docs(voice): 重写 voice_ports.h 为中文 Doxygen 格式 全部 33 个公开头文件通过 check_public_api_docs.py 校验。 Refs 1024XEngineer#108 * 📝 docs(voice): rebase 后补齐头文件 Doxygen 与清理 CMakeLists 修复与 main 合并后丢失的注释: - voice_types.h, voice_session.h, audio_frame_queue.h 补中文 Doxygen - linx_types.h, websocket_fragment_assembler.h 恢复 Doxygen 版本 - CapabilityProfile::Has 改为 std::find, 避免公共 API 检查器误报 tests/host/CMakeLists.txt 清理重复的 linx_esp 定义。 主机测试 25/25 通过, Doxygen 33 头文件 PASS。 Refs 1024XEngineer#108 * ✨ feat(voice): 建立 ESP32-S3 PCM/I2S 板级探针 将板级 GPIO、I2C 地址、设备 PCM 格式和 DMA 预算收敛到独立 Profile,并把探针编排放回 Runtime 组装根。探针只验证 I2C ACK、I2S 通道生命周期和有限静音读写,不把 Codec 录放或云端闭环伪装成已完成。 主机契约测试 11/11、Python 测试 4/4、架构检查和 ESP-IDF 6.0.2 的 ESP32-S3 构建均通过。实板以 115200 只写非活动 ota_1,最终镜像 222320 B 回读逐字节一致;当前连接板为 SKU=voicelife-pcb 的 NoAudioCodec 纯 I2S 板,I2S smoke 通过但 ES8311/ES7210/PCA9557 未 ACK,已恢复 otadata 并确认原固件和 SQLite 数据正常。后续必须为纯 I2S 板与 Lichuang Codec 板分别建立 Profile。 Refs 1024XEngineer#109 * 📝 docs(audio): 补齐 audio_esp 头文件中文 Doxygen 注释 audio_board_profile.h, esp32s3_audio_probe.h 公开 API 补齐 /** @brief @PARAM @return */ 注释。 Doxygen 35 头文件 PASS。 Refs 1024XEngineer#109 * 🏗️ refactor(voice): 接入 voicelife-pcb 纯 I2S PCM Profile 当前实板原固件报告 SKU=voicelife-pcb/NoAudioCodec,不能复用 Lichuang Codec Profile。将 AudioBoardProfile 扩展为 external-codec-duplex 与 direct-i2s-simplex,独立描述 RX/TX controller、GPIO、采样率、wire slot 和 PCM 对齐,并把探针结果接入硬件/PCM 信号失败路径。 迁移旧 voicelife-pcb-native-mvp 的 I2S0 TX + I2S1 RX 拓扑和有界回放边界;同一块板对照 >>12 与 >>14,削波从 79791 ppm 降到 208 ppm,当前 Profile 采用 >>14。主机 11/11、Python 测试、Profile validate、架构检查和 ESP-IDF 6.0.2 构建通过;最终镜像 229488 B,ota_1 回读逐字节一致,恢复 otadata 后原固件从 ota_0 启动且 SQLite 仍加载 7 events/8 reminders/0 notes。 仍未宣称 Codec、AFE、AEC、WakeNet、Opus、WSS、ASR、TTS 或声学播放闭环。 Upstream: 78/xiaozhi-esp32@dd99da0 Refs 1024XEngineer#111 * 📝 docs(audio): 补齐 audio_esp 头文件中文 Doxygen 注释 audio_board_profile.h, esp32s3_audio_probe.h 公开 API 补齐 /** @brief @PARAM @return */ 注释与 Impl 说明。 Doxygen 35 头文件 PASS。 Refs 1024XEngineer#111 * 🔧 build(style): clang-format 修复 audio_esp 与测试文件 Refs 1024XEngineer#111
jing-gou
pushed a commit
that referenced
this pull request
Aug 7, 2026
) * ✨ feat(voice): 接入 ESP32-S3 Linx WSS 传输骨架 建立平台无关的 WebSocket 分片重组与 generation 隔离,增加 ESP-IDF 6.0.2 的 WSS/TLS Transport、凭据解析边界、固定事件队列和 Linx hello 超时。同步主机 TDD、架构边界和语音模块文档,明确真实板闭环仍待 Issue 1024XEngineer#107 验收。 * 🐛 fix(voice): 固化 MVP 音频经验并保护会话代次 * ✨ feat(voice): 完善 Linx 双向协商与 ESP32-S3 回退验证 Linx 服务端可能在重连 hello 中返回不同的下行采样率或帧时长;如果继续沿用旧的播放配置,会把协议已变更的音频静默送进错误的 AudioOutput。物理链路在 hello 失败但尚未完成清理时也不能被重复 Connect。 本次分离 capture/playback 音频格式,Provider 保存已协商格式并拒绝重连时的格式变化;Connect 同时检查 Provider 和底层 Transport 状态。补充断线、重连 hello、旧 generation、TTS abort 和格式变化的主机契约测试,并把受控 ota_1 启动、otadata 恢复和 otatool 回退流程写入文档。 验证:./scripts/run_checks.sh(主机 9/9、架构、Profile、Python 4/4);ESP-IDF 6.0.2 / ESP32-S3 构建通过,voicelife.bin 172480 bytes;真实板以 115200 只写 ota_1@0x410000 后启动成功,并恢复 ota_0 与原 SQLite 数据。 风险:真实 Linx WSS、ASR、TTS、I2S/AFE/Opus 仍未通过;重连格式变化需要 Stop 后重新 Start,当前不做静默 AudioOutput 重配置。 Upstream: 78/xiaozhi-esp32@dd99da0 Refs 1024XEngineer#107 Refs 1024XEngineer#91 * 🐛 fix(test): 显式初始化语音事件测试夹具 GitHub GCC 13 在 -Wmissing-field-initializers -Werror 下拒绝 VoiceEvent 聚合初始化,导致主机测试 job 无法编译。 为 TTS start/stop、断线和重连 fixture 显式补齐 text 与 aborted 字段,保持测试意图不变。 验证:voice_session_contract_test 通过;等待远端完整 CI 复跑。 Refs 1024XEngineer#107 Refs 1024XEngineer#91 * ✨ feat(voice): 固化有界音频队列与迁移边界 把旧 MVP 中的队列满载策略、generation 清理和水位统计落成平台无关契约,新增主机 TDD 覆盖。同步 Linx、ESP32-S3、小智 AFE 与 SQLite 控制面研究证据,明确其他板卡仅按 Profile 准入。\n\nRefs 1024XEngineer#105\nRefs 1024XEngineer#91 * ✨ feat(voice): 接入会话采集音频回调契约 参考旧 voicelife-pcb-native-mvp 的采集任务边界,让 AudioInputPort 只提交格式和负载,由 VoiceSession 统一补齐 generation 与 sequence。补充 TDD 覆盖迟到帧拒绝、回调清理和格式校验,并同步语音架构与研究决策文档。 Refs 1024XEngineer#107 Refs 1024XEngineer#105 Refs 1024XEngineer#91 * 📝 docs(voice): 刷新官方接入与跨板能力边界 记录 Linx MQTT 公开资料缺口、ESP-IDF 6.0.2 I2S/PDM 约束、跨板能力矩阵和立创板迁移输入。\n\n明确当前只验证研究与契约,不把旧 MVP 的 Codec 引脚或芯片能力冒充新工程实板录放通过。\n\nRefs 1024XEngineer#107\nRefs 1024XEngineer#105\nRefs 1024XEngineer#91 * 🐛 fix(docs): 移除失效的 Zephyr 音频来源 跨板矩阵事实仍以 ESP-IDF 6.0.2 I2S 官方文档为主,Zephyr 仅保留已核对的文件系统抽象参考。\n\nRefs 1024XEngineer#107\nRefs 1024XEngineer#105\nRefs 1024XEngineer#91 * 👷 ci(quality): 统一 Linx ESP 传输格式 补齐新增语音端口的 clang-format-18 结果,保持 WebSocket 分片和会话逻辑不变。 * ✅ test(voice): 补充 Linx Codec 与 Provider Registry 错误路径测试 - 覆盖 hello 无效音频、空 abort、非 JSON、未知 tts 状态等拒绝路径 - 覆盖 \u 转义拒绝、opus/wav 音频参数解析边界 - 覆盖 Registry 空 ID、重复注册、未找到与缺能力路径 * 📝 docs(process): 将研究资料迁移到 Issue 归档 * 🔧 build(config): 修正 Flash 配置为 16MB 并启用双 OTA 分区表 sdkconfig: CONFIG_ESPTOOLPY_FLASHSIZE 2MB → 16MB sdkconfig.defaults: SINGLE_APP_LARGE → CUSTOM + 16MB 新增 partitions_voicelife_16mb.csv: nvs 24KB + otadata 8KB + ota_0 4MB + ota_1 4MB + voicelife 2MB 实板验证: ESP32-S3 16MB Flash, 之前配成 2MB 导致启动时 spi_flash 警告并浪费 14MB 空间。 Refs 1024XEngineer#106 * ♻️ refactor(runtime): 从 Scaffold 硬编码切换为 Provider Registry 驱动 将 Runtime 从旧 VoiceSessionCoordinator + ScaffoldAdapters 重构为 新 VoiceSession + SpeechProviderRegistry 架构: - 新增 ScaffoldAudioInput/ScaffoldAudioOutput/ScaffoldSpeechProvider 实现新端口接口 (AudioInputPort/AudioOutputPort/SpeechProviderAdapter) - Runtime 构造时注册 scaffold Provider 到 SpeechProviderRegistry - Start() 从 Registry 创建 Provider 并构造 VoiceSession - 移除旧 AudioDevicePort/SpeechProviderPort/硬编码 scaffold 这是 fennoai 审查 1024XEngineer#106 标记的阻塞项 #1: 之前新语音架构在启动路径 上是死代码, Runtime 从未使用 VoiceSession 或 SpeechProviderRegistry。 现在架构主干真正串联, Scaffold Provider 可被真实 Adapter 替换。 主机测试 10/10 通过。 Refs 1024XEngineer#106 * 🐛 fix(voice): 修复 Interrupt 和 EndCapture 的状态机缺陷 Interrupt: generation 递增从 Abort/Flush 之后移到之前。 旧顺序下迟到的音频帧在 Abort→Flush 窗口内仍可进入会话; 新顺序先失效旧代次, Provider/Output 拒绝后续的旧帧。 EndCapture: provider_.StopCapture() 失败时不再静默保留 kCapturing 状态(此时本地输入已停止, 状态分裂)。 改为显式转 kFailed 并上报 capture_stop_failed 证据。 这是 fennoai 审查 1024XEngineer#106 标记的阻塞项 #2 和 1024XEngineer#3。 主机测试 10/10 通过。 Refs 1024XEngineer#106 * 🐛 fix(voice): 去掉虚假 opus 声明,添加上下行帧大小上限 能力声明: DefaultCapabilities 移除未实现的 opus, 只保留 pcm。 之前 Registry 会按虚假能力选到 Linx Provider, 运行时在音频链路炸掉。 帧校验: AudioFrame 新增 kMaxPayloadBytes=16384。 AcceptFrameLocked、HandleInputAudio、HandleAudio 三层都拒绝超大 payload, 防止畸形帧或攻击性 payload 压爆嵌入式内存。 这是 fennoai 审查 1024XEngineer#106 标记的阻塞项 1024XEngineer#7、1024XEngineer#8、1024XEngineer#16、1024XEngineer#22。 主机测试 10/10 通过。 Refs 1024XEngineer#106 * 🐛 fix(voice): 修复 BeginCapture 回滚、session_id 校验和 JSON 编解码缺陷 BeginCapture: input 启动失败后不再忽略 provider_.StopCapture() 返回值, 回滚失败时 emit evidence 并优先返回回滚错误, 避免云端残留半开采集状态。 Linx OnText: 解码后校验 session_id 与当前会话匹配, 拒绝同 WebSocket 上的旧会话或误路由消息污染当前状态。 EncodeHello: sample_format/frame_size/play_buffer_duration 仅 PCM 时输出, 不再把 PCM 专用字段硬编码进 Opus hello。 Quote: 补全 \b \f 转义, 其余 ASCII 控制字符 (0x00-0x1F) 统一 输出 \u00XX 六位 hex 序列, 避免产出非法 JSON。 这是 fennoai 审查 1024XEngineer#106 标记的阻塞项 1024XEngineer#5、1024XEngineer#14、1024XEngineer#17、1024XEngineer#20。 主机测试 10/10 通过。 Refs 1024XEngineer#106 * 🐛 fix(voice): 修复 Registry 上限、hello 缺少 audio_params 和硬编码缓冲时长 Registry: kMaxProviders 8→16, 满表错误含上限值和诊断提示。 Linx hello: 服务端 hello 不含 audio_params 时拒绝而非静默使用默认格式, 避免协商结果被错误信任。 play_buffer_duration: 从硬编码 1000 改为 frame_duration_ms * 50, 格式变化时自动缩放缓冲时长。 Connect() 已阻塞等待 hello_cv_, hello_timeout_ms 已驱动 wait_for。 这是 fennoai 审查 1024XEngineer#106 标记的阻塞项 1024XEngineer#12、1024XEngineer#15、1024XEngineer#21。 主机测试 10/10 通过。 Refs 1024XEngineer#106 * 🐛 fix(voice): JSON codec 支持 \uXXXX Unicode 转义并加固字段边界 UnescapeJsonString: 新增 \uXXXX 四字节 hex 解码为 UTF-8。 支持 BMP 范围 (U+0000-U+FFFF), 拒绝代理对 (\uD800-\uDFFF)。 解决了此前中文 ASR/TTS 文本因 \uXXXX 转义导致解析失败的问题。 FindField: 匹配字段名前先验证前驱字符为 { 或 ,, 避免把值里的同名字符串误判为对象键。 Registry: 文档化线程安全约束 -- Register() 必须在调度启动前完成。 这是 fennoai 审查 1024XEngineer#106 标记的阻塞项 1024XEngineer#11、1024XEngineer#18、1024XEngineer#19(部分缓解)。 主机测试 10/10 通过。 Refs 1024XEngineer#106 * ♻️ refactor(voice): LinxJsonCodec 从字符串扫描替换为 cJSON 结构化解析 third_party/cjson: 引入 cJSON (MIT license) 源码, ESP-IDF 和主机测试共用。 LinxJsonCodec 全面重写: Encode*: std::ostringstream → cJSON_CreateObject + cJSON_PrintUnformatted DecodeText: 字符串扫描 → cJSON_ParseWithLength, 字段通过 cJSON_GetObjectItem 按类型读取 删除: ReadJsonString, SkipSpace, FindField, ReadStringField, ReadUnsignedField, ReadBoolField, ReadObjectField, Quote (~300行手动解析逻辑) 保留: CodecName, ModeName 主机测试: CMakeLists 新增 cjson 库 (LANGUAGES C CXX), linx 链接 cjson ESP-IDF: voicelife_linx 直接编译 third_party/cjson/cJSON.c 修复: GetRequired/GetOptional 类型匹配改为位掩码 (&) 以支持 cJSON_False|cJSON_True 组合查询。 这是 fennoai 审查 1024XEngineer#106 标记的阻塞项 1024XEngineer#18、1024XEngineer#19。 主机测试 10/10 通过。 ESP-IDF 编译 voicelife.bin 181KB, 实板 16MB Flash 正常启动。 Refs 1024XEngineer#106 * 🐛 fix(voice): cJSON ParseAudioParams 补回 channels=0 等范围校验 Rebase 后远程新增错误路径测试, cJSON 版 ParseAudioParams 缺少 channels/bits/duration 零值拒绝。补回与旧代码等价的范围校验。 主机测试 17/17 通过。 Refs 1024XEngineer#106 * 🔧 build(style): clang-format 修复 CI 格式检查 格式化 voice_ports.h, voice_provider_registry.cc, runtime.cc, voice_session.cc, linx_json_codec.cc, linx_speech_provider.cc 以通过 CI clang-format 门禁。 Refs 1024XEngineer#108 * 📝 docs(voice): 补齐 voice_ports.h 公开 API Doxygen 文档注释 AudioInputPort, AudioOutputPort, VoiceTransportPort, CodecStrategy, ASRAdapter, TTSAdapter, RealtimeAdapter, SpeechProviderAdapter, SpeechProviderRegistry 全部公开 类型和函数补上 /// Doxygen 注释。 通过 CI 公共 API 文档门禁。 Refs 1024XEngineer#108 * 📝 docs(voice): 补齐所有公开 API 的 Doxygen 注释 AudioInputPort, AudioOutputPort, VoiceTransportPort, SpeechProviderPort, CodecStrategy, ASRAdapter, TTSAdapter, RealtimeAdapter 所有公开析构函数和方法补上 /// 注释。 通过 CI 公共 API 文档门禁。 Refs 1024XEngineer#108 * 📝 docs(voice): 补 SpeechProviderAdapter 析构和 Entry 结构体 Doxygen * 📝 docs(voice): 重写 voice_ports.h 为中文 Doxygen 格式 全部 33 个公开头文件通过 check_public_api_docs.py 校验。 Refs 1024XEngineer#108 * 📝 docs(voice): rebase 后补齐头文件 Doxygen 与清理 CMakeLists 修复与 main 合并后丢失的注释: - voice_types.h, voice_session.h, audio_frame_queue.h 补中文 Doxygen - linx_types.h, websocket_fragment_assembler.h 恢复 Doxygen 版本 - CapabilityProfile::Has 改为 std::find, 避免公共 API 检查器误报 tests/host/CMakeLists.txt 清理重复的 linx_esp 定义。 主机测试 25/25 通过, Doxygen 33 头文件 PASS。 Refs 1024XEngineer#108 * ✨ feat(voice): 建立 ESP32-S3 PCM/I2S 板级探针 将板级 GPIO、I2C 地址、设备 PCM 格式和 DMA 预算收敛到独立 Profile,并把探针编排放回 Runtime 组装根。探针只验证 I2C ACK、I2S 通道生命周期和有限静音读写,不把 Codec 录放或云端闭环伪装成已完成。 主机契约测试 11/11、Python 测试 4/4、架构检查和 ESP-IDF 6.0.2 的 ESP32-S3 构建均通过。实板以 115200 只写非活动 ota_1,最终镜像 222320 B 回读逐字节一致;当前连接板为 SKU=voicelife-pcb 的 NoAudioCodec 纯 I2S 板,I2S smoke 通过但 ES8311/ES7210/PCA9557 未 ACK,已恢复 otadata 并确认原固件和 SQLite 数据正常。后续必须为纯 I2S 板与 Lichuang Codec 板分别建立 Profile。 Refs 1024XEngineer#109 * 📝 docs(audio): 补齐 audio_esp 头文件中文 Doxygen 注释 audio_board_profile.h, esp32s3_audio_probe.h 公开 API 补齐 /** @brief @PARAM @return */ 注释。 Doxygen 35 头文件 PASS。 Refs 1024XEngineer#109 * 🏗️ refactor(voice): 接入 voicelife-pcb 纯 I2S PCM Profile 当前实板原固件报告 SKU=voicelife-pcb/NoAudioCodec,不能复用 Lichuang Codec Profile。将 AudioBoardProfile 扩展为 external-codec-duplex 与 direct-i2s-simplex,独立描述 RX/TX controller、GPIO、采样率、wire slot 和 PCM 对齐,并把探针结果接入硬件/PCM 信号失败路径。 迁移旧 voicelife-pcb-native-mvp 的 I2S0 TX + I2S1 RX 拓扑和有界回放边界;同一块板对照 >>12 与 >>14,削波从 79791 ppm 降到 208 ppm,当前 Profile 采用 >>14。主机 11/11、Python 测试、Profile validate、架构检查和 ESP-IDF 6.0.2 构建通过;最终镜像 229488 B,ota_1 回读逐字节一致,恢复 otadata 后原固件从 ota_0 启动且 SQLite 仍加载 7 events/8 reminders/0 notes。 仍未宣称 Codec、AFE、AEC、WakeNet、Opus、WSS、ASR、TTS 或声学播放闭环。 Upstream: 78/xiaozhi-esp32@dd99da0 Refs 1024XEngineer#111 * 📝 docs(audio): 补齐 audio_esp 头文件中文 Doxygen 注释 audio_board_profile.h, esp32s3_audio_probe.h 公开 API 补齐 /** @brief @PARAM @return */ 注释与 Impl 说明。 Doxygen 35 头文件 PASS。 Refs 1024XEngineer#111 * ✨ feat(voice): 接入 ESP32-S3 PCM Audio Port 新增硬件 period 到传输帧的组装器、独立采集/投递/播放任务和有界队列,并将 Profile 接入 Runtime。补充主机 TDD、ESP-IDF 构建与真实 voicelife-pcb 实板回退证据;物理声学与 Linx 云端闭环继续留在后续 Issue。 Refs 1024XEngineer#113 Refs 1024XEngineer#91 * 🐛 fix(voice): 收紧 Linx 传输生命周期与会话失败回滚 * 🏗️ refactor(voice): 拆分超大语音源文件以通过规模门禁 - esp32s3_pcm_audio_port.cc 670 行拆分为平台无关入口 + esp32s3_pcm_i2s_runtime.cc(I2S 通道与任务循环) - linx_json_codec.cc 拆出 linx_json_reader.{h,cc} - esp_websocket_transport.cc 拆出 impl 与事件处理 - 同步 host 测试 CMakeLists 补齐新源文件 * 🐛 fix(voice): 恢复 Connect/BeginCapture/Stop 失败回滚并补 Doxygen 基于新 1024XEngineer#112 重建 PR 1024XEngineer#114: - voice_session.cc 恢复 Connect 失败 Disconnect 回滚、 BeginCapture 双重失败转 kFailed、Stop 断开失败不伪装 kStopped - pcm_frame_assembler.h, esp32s3_pcm_audio_port.h 补中文 Doxygen 主机测试 27/27 通过, Doxygen PASS, 代码规模 PASS。 Refs 1024XEngineer#113 * 🔧 build(style): clang-format 修复 audio_esp 与测试文件 Refs 1024XEngineer#113 * 🔧 chore(build): 移除误提交的 ESP-IDF 构建产物 * 🔧 fix(timing): 链接合并后的主机服务实现
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
…ing tool