feat(asr): add pluggable ONNX streaming backend and model registry

Add a second backend (sherpa-onnx) alongside llama.cpp, so native
streaming models can be selected by ID from a JSON registry. All such
models share one adapter: only the model files and a few options differ.
Models now live under the app data directory and are fetched by
scripts/fetch-models.sh. Adding a model is a data change, not code.

Measured on the same audio: the streaming backend is ~35x cheaper in CPU
than Qwen3-ASR (0.10-0.12 vs 4.27 core-seconds per audio second), shows
the first caption at 0.48-1.44s, and matches its accuracy for zh/en.

新增 sherpa-onnx 流式后端,与 llama.cpp 并存;模型按注册表 id 从应用
数据目录加载。同族模型共用一个适配器,新增模型只改 data/models.json。
实测流式后端 CPU 约为 Qwen3-ASR 的 1/35,首个字幕 0.48~1.44s,中英
准确率持平。注意预编译库是旧 C++ ABI,必须走其 C API。
This commit is contained in:
blumia committed 2026-10-12 16:06:51 +08:00
1 parent 6d64987d64
commit bd921b1ac5
15 files changed
+869 -18

No files matched your search

+14 -5
View File
@@ -10,13 +10,14 @@
#include "appcli.h"
#include "captionwindow.h"
#include "livetranscriber.h"
#include "iasr.h"
#include "qwen3asr.h"
#include "streamingasr.h"
#include "transcript.h"
namespace {
int runGui(lc::Qwen3Asr & asr, const lc::StreamOptions & sop,
int runGui(lc::IAsr & asr, const lc::StreamOptions & sop,
const QString & screenshotPath, int screenshotDelaySec, int opacityPercent)
{
lc::CaptionWindow window;
@@ -84,18 +85,26 @@ int main(int argc, char ** argv)
parser.addHelpOption();
lc::app::addModelOptions(parser);
lc::app::addStreamOptions(parser);
lc::app::addBackendOptions(parser);
parser.addOption(lc::app::makeOption("opacity", "字幕背景不透明度,取值 20-100", "n", "84"));
parser.addOption(lc::app::makeOption("screenshot", "启动后自动截图并退出(验证渲染用)", "path"));
parser.addOption(lc::app::makeOption("screenshot-delay", "截图前等待秒数", "sec", "6"));
parser.process(app);
const lc::AsrOptions asrOpt = lc::app::asrOptions(parser);
const lc::StreamOptions sop = lc::app::streamOptions(parser);
lc::StreamOptions sop = lc::app::streamOptions(parser);
lc::Qwen3Asr asr;
if (!lc::app::loadModel(asr, asrOpt)) return 1;
std::string err;
std::unique_ptr<lc::IAsr> asr = lc::app::makeAsr(parser, asrOpt, &err);
if (!asr) {
std::fprintf(stderr, "%s\n", err.c_str());
return 1;
}
if (asr->kind() == lc::AsrKind::NativeStreaming && !parser.isSet("stream-chunk")) {
sop.chunkSec = 0.32;
}
return runGui(asr, sop,
return runGui(*asr, sop,
parser.value("screenshot"),
parser.value("screenshot-delay").toInt(),
parser.value("opacity").toInt());