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:
1 parent
6d64987d64
commit
bd921b1ac5
15 files changed
+869
-18
No files matched your search
+19
-9
@@ -15,6 +15,7 @@
|
||||
#include "appcli.h"
|
||||
#include "audio.h"
|
||||
#include "livepipeline.h"
|
||||
#include "iasr.h"
|
||||
#include "qwen3asr.h"
|
||||
#include "streamingasr.h"
|
||||
#include "transcript.h"
|
||||
@@ -25,7 +26,7 @@ std::atomic<bool> g_stop{false};
|
||||
|
||||
extern "C" void handleSigInt(int) { g_stop = true; }
|
||||
|
||||
int runFile(lc::Qwen3Asr & asr, const QString & path)
|
||||
int runFile(lc::IAsr & asr, const QString & path)
|
||||
{
|
||||
std::vector<float> pcm;
|
||||
std::string err;
|
||||
@@ -54,7 +55,7 @@ int runFile(lc::Qwen3Asr & asr, const QString & path)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int runStreamFile(lc::Qwen3Asr & asr, const QString & path, const lc::StreamOptions & sop)
|
||||
int runStreamFile(lc::IAsr & asr, const QString & path, const lc::StreamOptions & sop)
|
||||
{
|
||||
std::vector<float> pcm;
|
||||
std::string err;
|
||||
@@ -94,7 +95,7 @@ int runStreamFile(lc::Qwen3Asr & asr, const QString & path, const lc::StreamOpti
|
||||
return 0;
|
||||
}
|
||||
|
||||
int runCliLive(lc::Qwen3Asr & asr, const lc::StreamOptions & sop)
|
||||
int runCliLive(lc::IAsr & asr, const lc::StreamOptions & sop)
|
||||
{
|
||||
lc::LivePipelineOptions popt;
|
||||
popt.stream = sop;
|
||||
@@ -135,19 +136,28 @@ int main(int argc, char ** argv)
|
||||
parser.addHelpOption();
|
||||
lc::app::addModelOptions(parser);
|
||||
lc::app::addStreamOptions(parser);
|
||||
lc::app::addBackendOptions(parser);
|
||||
parser.addOption(QCommandLineOption("file", "转写一个 wav 文件后退出", "path"));
|
||||
parser.addOption(QCommandLineOption("stream-file", "以流式模式转写 wav 文件后退出", "path"));
|
||||
parser.addOption(QCommandLineOption("cli-live", "实时转写字幕,只用命令行输出(不开窗口)"));
|
||||
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;
|
||||
}
|
||||
|
||||
if (parser.isSet("file")) return runFile(asr, parser.value("file"));
|
||||
if (parser.isSet("stream-file")) return runStreamFile(asr, parser.value("stream-file"), sop);
|
||||
if (parser.isSet("file")) return runFile(*asr, parser.value("file"));
|
||||
if (parser.isSet("stream-file")) return runStreamFile(*asr, parser.value("stream-file"), sop);
|
||||
|
||||
return runCliLive(asr, sop);
|
||||
return runCliLive(*asr, sop);
|
||||
}
|
||||
Reference in new issue
Block a user