fix: 修复流式播放无声音问题(SSE 行缓冲 + 解析层级)

This commit is contained in:
2026-05-09 04:06:47 +08:00
parent ceeb6a3c31
commit d34ebd147e
15 changed files with 679 additions and 458 deletions

View File

@@ -555,4 +555,59 @@ mimo-tts daemon --port 9876 stop
- 播放功能复用现有的 `play_audio()` 函数
- style 参数按官方文档转换为 `<style>...</style>` 标签
## 2026-04-24 - 第十轮修改
## 2026-05-09 - 代码质量提升
### 用户指令
开始修复之前评价中指出的问题。
### 修复清单
1. ✅ HTTP /synthesize 接口缺少实际 TTS 调用 — 通过 `tokio::runtime::Handle::current()` + `block_on()` 桥接
2. ✅ 语气替换长匹配优先问题 — 调整 `insert_mid_tone()``...` 优先于 `.`
3. ✅ show_voices() 显示完整 9 音色 — 更新为完整列表含语言/性别/说明
4. ✅ 9 个编译器警告清理 — `#[allow(dead_code)]` + 移除未使用变量/导入
5. ✅ write_log 线程安全问题 — 添加 `Mutex<()>` 静态锁
6. ✅ changelog 版本结构整理 — 合并重复条目为 0.1.0→0.2.0→0.3.0→0.3.1
### 最终状态
- 版本0.3.1
- 构建0 warnings, 0 errors
- 测试9/9 passed
---
## 2026-05-09 - 第十四轮修改:流式播放修复
### 背景
守护进程 `send --stream` 和 CLI `--stream --play` 均无声音输出,但非流式播放正常。
### 诊断过程
1. **第一阶段:假设 RunLoop 问题**
- 认为 `rodio::OutputStream(!Send)``std::thread::spawn` 中因为 CoreAudio RunLoop 不可用导致无声
- 将 daemon 主循环改为 `LocalSet` + `spawn_local`,消除 `std::thread::spawn`
- 结果:仍无声 → 假设错误
2. **第二阶段:定位 SSE 解析问题**
- `--stream`(不带 `--play`)测试:能输出 PCM16 数据(走 JSON 响应,不走 SSE
- 用 curl 直接查看 API 原始 SSE 响应 → 发现音频数据在 `choices[0].delta.audio.data`
-`SseEvent` 只解析顶层 `audio` 字段 → 永远 None → 通道为空 → 静音
- 修复1创建 `SseChunk`/`SseChoice`/`SseDelta`/`SseAudio` 嵌套结构体
- 结果:仍无声
3. **第三阶段:定位行缓冲问题**
- 添加调试日志发现SSE `data:` 行因音频 base64 过长,被 HTTP chunk 分割
- 行被切碎 → serde_json 解析失败 → 永远收不到数据
- 修复2添加 `line_buf` 跨 chunk 累积完整行再解析
- 结果69KB PCM16 数据成功进入 channelsink 正常播放 ✅
### 技术细节
- `reqwest::Response::bytes_stream()` 的 chunk 边界不一定对齐 SSE `\n`
-`data:` 行会跨 chunk 分割
- SSE 行缓冲器:按 `\n` 切割,不完整行等下一个 chunk
- `extract_audio_from_sse()``choices[0].delta.audio.data`
### 最终状态
- CLI `--stream --play` 正常出声 ✅
- Daemon `send --stream` 正常出声 ✅
- Daemon 非流式 `send` 仍正常 ✅
- 版本0.3.2(流式播放修复)