sherpa-onnx Flutter 离线标点恢复实战:构建跨 Android/iOS/桌面/Web 的本地标点恢复应用

sherpa-onnx Flutter 离线标点恢复实战:构建跨 Android/iOS/桌面/Web 的本地标点恢复应用 sherpa-onnx Flutter 离线标点恢复实战构建跨 Android/iOS/桌面/Web 的本地标点恢复应用【免费下载链接】sherpa-onnxSpeech-to-text, text-to-speech, speaker diarization, speech enhancement, source separation, and VAD using next-gen Kaldi with onnxruntime without Internet connection. Support embedded systems, Android, iOS, HarmonyOS, Raspberry Pi, RISC-V, RK NPU, Axera NPU, Ascend NPU, x86_64 servers, websocket server/client, support 12 programming languages项目地址: https://gitcode.com/GitHub_Trending/sh/sherpa-onnxsherpa-onnx 的 Flutter 示例 offline-punctuation 演示了如何在 Flutter 应用中集成离线标点恢复Offline Punctuation Restoration能力输入一段不带标点的纯文本中英文混合均可由本地运行的 CT-Transformer 模型自动补全逗号、句号等标点全程无需联网。本篇以该示例为主体完整覆盖模型下载、资产配置、跨平台构建与运行的全部步骤并结合 Flutter 插件源码剖析其原生平台用后台 Isolate、Web 平台用 Web Worker WASM的双轨架构与 FFI/WASM 调用链帮助你在自己的 Flutter 项目中落地同一套离线文本后处理方案。1. 什么是离线标点恢复ASR语音识别输出的文本通常是无标点裸文本。离线标点恢复针对完整文本一次性处理给定一整段字符串模型输出带标点的版本。sherpa-onnx 同时提供在线流式标点接口两者定位不同离线本示例面向完整句子/段落一次调用得到最终结果适合作为 ASR 结果的最终后处理步骤在线面向流式场景边出字边补标点可参考 online-punctuation 示例。本示例使用的模型是sherpa-onnx-punct-ct-transformer-zh-en-vocab272727-2024-04-12-int8基于 CT-Transformer 结构、int8 量化同时支持中文与英文文本。从 插件源码的文档注释 看OfflinePunctuation类明确标注其用途是 complete text strings when you want one-shot punctuation insertion完整文本串的一次性标点插入。2. 示例效果真实输入/输出对照README 中给出的官方示例是一段 61 词的中英文混合文本输入为yesterday afternoon 我去了一家 near my apartment 的 coffee shop 想要 enjoy a cup of hot latte 并 check 一些 important work emails 顺便在 我的 laptop 上写一点 code 因为那里的 atmosphere 总是非常 quiet and comfortable 可以让我更加 focus on coding 和 writing documents 而且 没有任何 distractions 所以 if you also like this kind of relaxing weekend vibe 我们 definitely 应该 plan 一个 time 一起 hang out.模型输出逗号、句号被自动补全yesterday afternoon我去了一家near my apartment的coffee shop想要 enjoy a cup of hot latte并check一些important work emails顺便在 我的laptop上写一点code因为那里的atmosphere总是非常quiet and comfortable可以让我更加focus on coding和writing documents而且 没有任何distractions。所以if you also like this kind of relaxing weekend vibe我们definitely应该plan一个time一起hang out。官方给出的性能数据Web demo单线程Words: 61 | Elapsed: 0.035s即 61 个词约 35 毫秒完成说明 int8 量化模型在浏览器 WASM 环境下也能做到接近实时的延迟。该示例文本硬编码在 UI 层 punct_screen.dart 的_exampleText常量中用户点击 Example 按钮即可一键填充。3. 支持平台与项目结构README 声明该示例支持以下平台AndroidiOSLinuxmacOSarm64 与 x86_64 均支持WindowsWeb示例工程flutter-examples/offline-punctuation的核心 Dart 文件组织如下文件职责lib/main.dart应用入口底部导航切换 Punctuation 主界面与 Info 信息页lib/model_config.dart共享的模型选择配置模型目录、文件名、下载 URL、OfflinePunctuationConfiglib/model.dart原生平台Android/iOS/桌面的模型准备把 assets 拷贝到磁盘并解析绝对路径lib/model_web.dartWeb 平台的模型准备从 Flutter assets 读取模型字节流lib/punct_manager.dart标点引擎生命周期管理原生走 IsolateWeb 走 Web Workerlib/worker_web.dartWeb Worker 封装负责 WASM 与模型文件的加载/通信lib/punct_screen.dart主界面输入框、结果区、状态/耗时日志web/punct-worker.jsWeb 端 Worker 脚本内部运行 WASM 版 sherpa-onnxgenerate-asset-list.py自动生成 assets 清单并写入 pubspec.yaml 的脚本pubspec.yaml 中依赖sherpa_onnx: 1.13.7要求 Dart SDK3.2.0 4.0.0、Flutter2.8.1另依赖path_provider原生平台取应用支持目录、path路径拼接、url_launcherInfo 页打开外部文档链接。4. 模型下载与资产配置4.1 下载模型按 README 的原始步骤进入示例的 assets 目录并下载、解压 int8 模型cd flutter-examples/offline-punctuation/assets wget https://github.com/k2-fsa/sherpa-onnx/releases/download/punctuation-models/sherpa-onnx-punct-ct-transformer-zh-en-vocab272727-2024-04-12-int8.tar.bz2 tar xvf sherpa-onnx-punct-ct-transformer-zh-en-vocab272727-2024-04-12-int8.tar.bz2 rm sherpa-onnx-punct-ct-transformer-zh-en-vocab272727-2024-04-12-int8.tar.bz2 cd .. ./generate-asset-list.pyREADME 注明generate-asset-list.py是软链指向 ../tts/generate-asset-list.py 的脚本。该脚本的功能从 generate-asset-list.py 源码可见是遍历./assets下所有非隐藏文件与子目录生成形如- assets/xxx/的条目并自动写入./pubspec.yaml的flutter: assets:段前提是该文件中最后一行为assets:段。这样新增/更换模型文件后无需手工维护 pubspec 资产清单。4.2 模型配置model_config.dart示例把模型相关常量集中在 lib/model_config.dart原生与 Web 两侧共用换模型只需改这一个文件/// Model directory name. const String modelDir sherpa-onnx-punct-ct-transformer-zh-en-vocab272727-2024-04-12-int8; /// Model file name. const String modelFile model.int8.onnx; /// Offline punctuation config. final OfflinePunctuationConfig punctConfig OfflinePunctuationConfig( model: OfflinePunctuationModelConfig( ctTransformer: $modelDir/$modelFile, numThreads: 1, debug: true, ), );OfflinePunctuationModelConfig的完整字段定义见 offline_punctuation_config.dart各参数及默认值如下参数类型默认值说明ctTransformerString必填CT-Transformer 模型文件.onnx路径示例中为模型目录/model.int8.onnxnumThreadsint1推理线程数示例固定为 1移动端与 Web 常见取值providerStringcpu计算设备CPU示例未显式设置取默认值debugbooltrue是否开启调试日志示例显式设为 true注意ctTransformer在model_config.dart中是相对路径后续在原生与 Web 两条链路上被分别解析成绝对路径或 WASM 虚拟文件系统路径见第 6、7 节。5. 原生平台实现后台 Isolate FFI原生侧的核心在 punct_manager.dartprepareModelConfig()model.dart通过AssetManifest遍历打包进 APK/IPA/桌面包的 assets逐个用rootBundle.load读出字节按存在且大小一致则跳过的策略拷贝到getApplicationSupportDirectory()各平台的应用支持目录再把ctTransformer等相对路径用p.join拼成绝对路径。这解决了移动平台原生推理引擎不能直接读取 assets 内部路径、必须落盘的问题。init()设置状态机uninitialized → initializing → initialized调用sherpa_onnx.initBindings()加载 FFI 绑定然后Isolate.spawn(_workerEntry, ...)启动后台 Isolate通过SendPort/ReceivePort与主 Isolate 通信。_workerEntrypunct_manager.dart 中定义后台 Isolate 收到OfflinePunctuationConfig后创建sherpa_onnx.OfflinePunctuation实例并回发_Ready收到_PunctRequest后调用punct!.addPunct(text)用Stopwatch计时把结果与耗时打包为_PunctDone(result, elapsed)回传收到_DisposeRequest则punct?.free()释放原生资源并关闭端口。dispose()主界面销毁时发送_DisposeRequest并Isolate.kill避免原生内存泄漏。为什么用后台 Isolate从源码结构看标点推理onnxruntime 执行是 CPU 密集任务放在后台 Isolate 可避免阻塞 UI 线程这与移动端常见实践一致主 Isolate 只负责 UI 状态与logStream/resultStream两条广播流的转发。FFI 底层实现在插件的 offline_punctuation.dartOfflinePunctuation工厂构造时用calloc分配SherpaOnnxOfflinePunctuationConfig把ctTransformer、provider路径转为原生 UTF-8 指针调用sherpaOnnxCreateOfflinePunctuation创建句柄L17-L46addPunct(text)L62-L88将文本转为原生指针后调用sherpaOfflinePunctuationAddPunct返回的字符串指针读回 Dart 后立即用sherpaOfflinePunctuationFreeText释放避免 C 侧内存泄漏。free()则调用sherpaOnnxDestroyOfflinePunctuation销毁实例。Web 端无法走 IsolateDart Web 没有多 Isolate 模型插件通过条件导入切换实现import ./model.dart if (dart.library.js_interop) ./model_web.dart as m; import ./worker_web.dart if (dart.library.io) ./worker_stub.dart as worker_lib;即编译到 Web 时用model_web.dart与worker_web.dart编译到原生时用model.dart与worker_stub.dart空壳同一份punct_manager.dart逻辑即可跨平台复用。6. Web 平台实现Web Worker WASMWeb 链路由 worker_web.dart 与 web/punct-worker.js 协作完成目标同样是把 WASM 推理移出主线程Dart 侧准备数据worker_web.dartloadModelFileBytes()model_web.dart从 Flutter assets 中筛出所有含modelDir的资产并读成字节从packages/sherpa_onnx_web/assets/下加载三份核心资产sherpa-onnx-wasm-web.jsEmscripten JS glue、sherpa-onnx-punctuation.js标点辅助 JS、sherpa-onnx-wasm-web.wasm编译好的 WASM 二进制configToJs()model_web.dart把OfflinePunctuationConfig经toJson→jsonEncode→ JSJSON.parse转成 JS 对象创建web.Worker(punct-worker.js)把以上全部打包为init消息 postMessage 过去。Worker 侧初始化punct-worker.jsevalJS glue 与标点辅助 JSSherpaOnnx({ wasmBinary, print, printErr })编译 WASM 模块把 C 端print输出经 postMessage 回传为log消息通过 Emscripten 的FS把模型文件写入 WASM 虚拟文件系统mkdirTreewriteFile保证模型在虚拟 FS 中保持modelDir/...的相对路径结构调用initSherpaOnnxOfflinePunctuationConfig_SherpaOnnxCreateOfflinePunctuation创建实例成功后回发ready。推理主线程发{type: punctuate, text}Worker 内用Module._mallocstringToUTF8写入文本指针调用_SherpaOnnxOfflinePunctuationAddPunctperformance.now()计时结果经UTF8ToString读回后回发{type: result, result, elapsed}并立即_SherpaOnnxOfflinePunctuationFreeText/_free释放指针——这与原生 FFI 侧addPunct的分配—调用—释放模式完全对称。销毁dispose消息触发_SherpaOnnxDestroyOfflinePunctuation并self.close()。punct_manager.dart中 Web 分支_initWeb把 Worker 的ready/result/error回调统一映射为与前文相同的_state状态机和logStream/resultStreamUI 层因此对两条链路完全无感。7. 各平台构建与运行以下命令完整继承自 README在示例目录flutter-examples/offline-punctuation/下执行前提是已完成第 4 节的模型下载与./generate-asset-list.py。Linuxflutter build linuxmacOS构建 universal2arm64 x86_64应用flutter build macos仅构建x86_64export FLUTTER_XCODE_ARCHSx86_64 flutter build macos仅构建arm64export FLUTTER_XCODE_ARCHSarm64 flutter build macosWindowsflutter build windowsAndroidflutter build apk --split-per-abiWeb直接运行调试flutter run -d chrome或构建后用本地静态服务器访问flutter build web cd build/web python3 -m http.server 6006然后在浏览器打开http://localhost:6006。README 给出的 35 毫秒性能数据即来自该 Web demo。iOS连接 iPhone 后flutter devices flutter run -d device-id --release若遇到签名错误README 的建议是在 Xcode 中打开ios/Runner.xcworkspace配置好签名 Team 后重试。8. 小结与复用要点一套配置跨六平台model_config.dart集中管理模型目录、文件名与OfflinePunctuationConfig配合条件导入model.dart/model_web.dart、worker_web.dart/worker_stub.dart同一套业务代码覆盖 Android、iOS、Linux、macOS、Windows、Web。推理永不阻塞 UI原生侧用后台 Isolate 承载 FFI 调用Web 侧用 Web Worker 承载 WASM 执行消息协议ready/result/log/error统一由PunctManager收敛为两条 Dart 流。资产自动化generate-asset-list.py免手工维护 pubspec 资产清单原生侧运行时落盘策略存在且大小一致则跳过拷贝让模型只在首次或更新时写入磁盘。生命周期完整从initBindings创建、addPunct调用的即时释放到free()/dispose销毁实例与终止 Worker/IsolateC 指针与 WASM 指针均有配对释放可直接作为 Flutter 中集成 sherpa-onnx 文本处理模型的工程模板。若要接入流式在线标点可参考同仓库的 online-punctuation 示例更完整的标点模型列表与量化说明可查阅应用内 Info 页给出的官方文档入口见 main.dart 中modelDocUrl常量指向的文档页。【免费下载链接】sherpa-onnxSpeech-to-text, text-to-speech, speaker diarization, speech enhancement, source separation, and VAD using next-gen Kaldi with onnxruntime without Internet connection. Support embedded systems, Android, iOS, HarmonyOS, Raspberry Pi, RISC-V, RK NPU, Axera NPU, Ascend NPU, x86_64 servers, websocket server/client, support 12 programming languages项目地址: https://gitcode.com/GitHub_Trending/sh/sherpa-onnx创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考