blumia 85b206f1cd feat(stream): add append-only streaming mode with flat cost
Add an append-only streaming path that feeds each audio chunk as a new
conversation turn, so nothing is ever recomputed: the KV grows by
append, no prefix is re-decoded and no audio is re-encoded. Per-step
cost becomes flat instead of growing with the segment (RTF 1.06 -> 0.26
on 62.6s of continuous speech), but accuracy requires ~8s chunks because
the audio encoder needs its 8s attention window, so text only appears
every 8s. The incremental mode stays the default.

新增追加式流式:每块音频作为一个新的对话轮追加,前缀不重算、旧音频不重
编码,单步成本不再随段落增长(连续 62.6s 语音 RTF 从 1.06 降到 0.26)。
但准确率要求 chunk≈8s,否则会被截断,所以出字粒度是 8s;增量式仍为默认。
2026-10-12 14:22:29 +08:00

live-caption

Linux 桌面实时字幕:采集系统正在播放的声音,实时转写成字幕显示(类似 Chrome 实时字幕)。 技术栈:C++ / Qt6 / CMake / Qwen3-ASR(llama.cpp,纯 C++,无 Python 依赖)。

当前进度

阶段 内容 状态
Phase 0 可行性验证(采集 / 模型 / CPU 实时性) ✅ 见 docs/phase0-verification.md
Phase 1 命令行原型(进程内推理 + monitor 采集 + 滑窗 + 稳定前缀) ✅ 见 experiments/phase1/README.md
Phase 2 原生流式解码(8s 切片 + 增量 KV 复用 + 前缀回滚) ✅ 见 docs/phase2-streaming.md
Phase 3 Qt 悬浮字幕窗(折叠/展开、语言标签、置顶、实时链路) ✅ 见 docs/phase3-caption-window.md
Phase 4 设置界面(模型切换、语言、字体、位置) 待做
Phase 5 Wayland 支持、打包、模型分发 待做

关键结论(Phase 0 实测)

  • 系统播放音频可从默认输出的 monitor 源采集(PipeWire + pipewire-pulse,走 libpulse,兼容 PulseAudio)。
  • Qwen3-ASR-0.6B 在本机(i5-13420H,无独显)纯推理 RTF ≈ 0.19,实时有充足余量。
  • 模型与推理运行时必须锁版本:只有 ggml-org 官方 GGUF(general.architecture = qwen3vl)与系统 llama.cpp 兼容;社区的 Q4 量化用的是 qwen3asr / qwen3_asr,装不进去。
  • 转写原始输出为 language <LANG><asr_text><正文>,语言名正好用于 UI 的语言标签。

快速开始

# 1. 准备模型(只需一次;网络可达 hf-mirror.com)
B=https://hf-mirror.com/ggml-org/Qwen3-ASR-0.6B-GGUF/resolve/main
aria2c -x16 -s16 -k4M -o Qwen3-ASR-0.6B-Q8_0.gguf        "$B/Qwen3-ASR-0.6B-Q8_0.gguf"
aria2c -x16 -s16 -k4M -o mmproj-Qwen3-ASR-0.6B-Q8_0.gguf "$B/mmproj-Qwen3-ASR-0.6B-Q8_0.gguf"

# 2. 构建
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j

# 3. 悬浮字幕窗
./build/live-caption \
  --model Qwen3-ASR-0.6B-Q8_0.gguf \
  --mmproj mmproj-Qwen3-ASR-0.6B-Q8_0.gguf

# 4. 命令行:单文件转写 / 流式转写 / 无窗口实时
./build/live-caption-cli --model M --mmproj MM --file some_16k_mono.wav
./build/live-caption-cli --model M --mmproj MM --stream-file some_16k_mono.wav
./build/live-caption-cli --model M --mmproj MM --cli-live

# 5. 单元测试(不需要模型,也不需要显示环境)
ctest --test-dir build --output-on-failure

代码结构

src/core/            → liblivecaption_core(静态库,**不依赖 Qt**)
  iasr.h                推理后端抽象(便于用假实现测流式策略)
  transcript.{h,cpp}    输出解析、语言名映射
  audio.{h,cpp}         monitor 采集 / WAV 读取 / 重采样
  qwen3asr.{h,cpp}      进程内推理(libllama + libmtmd)
  streamingasr.{h,cpp}  流式策略(8s 切片复用、前缀回滚、分段)
  vad.h                 能量 VAD
  livepipeline.{h,cpp}  实时管线:采集→VAD→分段→流式转写
src/ui/              → liblivecaption_ui(静态库,Qt6::Widgets)
  captionwindow.{h,cpp} 悬浮字幕窗
  livetranscriber.{h,cpp} LivePipeline 的 Qt 适配器(只转信号)
apps/gui/            → live-caption      (悬浮窗程序)
apps/cli/            → live-caption-cli  (命令行程序,只链 Qt6::Core)
apps/common/         两个程序共用的命令行选项
tests/               → 单元测试(QtTest,只链 core)
docs/                各阶段说明与验证报告
experiments/         Phase 0/1 的验证脚本

拆分的意义:业务逻辑全在 core 且不依赖 Qt,所以流式策略、VAD、输出解析这些最容易出错的部分可以脱离真模型和显示环境做单元测试;GUI 与 CLI 各自独立演进,CLI 也不再需要链接 Qt Widgets。

S
Description
No description provided
Readme
371 KiB
0 Stars 1 Watchers 0 Forks
Languages
C++ 89%
CMake 6.6%
Shell 3.6%
Python 0.8%