Files
live-caption/src/streamingasr.h
T

82 lines
3.4 KiB
C++
Raw Normal View History

#pragma once
#include <cstddef>
#include <functional>
#include <string>
#include <vector>
namespace lc {
class Qwen3Asr;
// 官方流式解码的参数(见 docs/phase2-streaming.md)
struct StreamOptions {
double chunkSec = 2.0; // 累积多少秒音频跑一步
double windowSec = 16.0; // 仅 window 模式使用:滑窗上限,<=0 表示不滑窗
int unfixedTokenNum = 5; // 前缀回滚:丢掉最后 N 个 token
int unfixedChunkNum = 2; // 前 N 个 chunk 不带前缀(冷启动)
int maxTokensPerStep = 256;
// true = 增量 KV 复用:只编码新音频,已解码音频留在 KV(推荐)
// false = 每次重编码整个窗口(Phase 2 早期的做法,仅用于对比)
bool incremental = true;
// 单段最长时长(秒):超过就提交并重置上下文(0 = 不限)。
// 真实场景应由静音检测决定分段;这里是一个兜底上界,
// 因为解码器对 KV 的注意力随总长增长,段太长单步会超预算。
double maxUtteranceSec = 30.0;
};
// 把「累积音频 → 带前缀继续生成 → 前缀回滚」这套策略封装起来。
// 输出分两部分:
// committed —— 已稳定文本(就是下一步要回喂的前缀)
// full —— 含不稳定尾部(最后 unfixedTokenNum 个 token)
class StreamingAsr {
public:
struct Step {
int chunkIndex = 0;
std::string language;
std::string committed; // 稳定部分
std::string full; // 含不稳定尾部
std::string rawFull; // 未 trim 的完整文本(含 language 头)
double windowSeconds = 0.0;
double encodeSeconds = 0.0;
double inferSeconds = 0.0;
int audioTokens = 0;
bool ok = true;
std::string error;
};
StreamingAsr(Qwen3Asr & asr, StreamOptions opt = {});
// 喂入 16k 单声道音频;每凑够 chunkSec 就跑一步并回调
void feed(const float * pcm, size_t n, const std::function<void(const Step &)> & onStep);
// 处理不足一块的残留音频,并作为最后一步提交全部文本
void flush(const std::function<void(const Step &)> & onStep);
// 结束当前这段(例如检测到静音):清空 KV 与状态,下一段重新开始
void reset();
// 重置全部状态(含历史文本)
void resetAll();
// 全部已提交文本(含此前各段,已剥离 language 头)
std::string committedText() const;
const std::string & language() const { return language_; }
int chunkCount() const { return chunkIndex_; }
private:
void runStep(const std::vector<float> & audio, bool finalStep,
const std::function<void(const Step &)> & onStep);
Qwen3Asr & asr_;
StreamOptions opt_;
std::vector<float> buffer_; // window 模式:当前窗口音频
std::vector<float> pending_; // 尚未凑够一块的音频
double totalSeconds_ = 0.0;
std::string committedRaw_; // 当前段的原始已提交文本(含 language 头,用于回喂)
std::string historyText_; // 此前各段已提交的正文(已剥离 language 头)
std::string language_;
int chunkIndex_ = 0;
};
} // namespace lc