FireRedASR2 Node.js 服务
本文档面向希望理解、运行或二次开发本仓库的读者,说明项目目标、架构、数据流与关键配置。
1. 项目是什么
这是一个在 Node.js 上运行的 FireRedASR2-AED 语音识别服务:
- 离线/整段:通过 HTTP 上传 WAV,返回整段转写文本。
- 流式:通过 WebSocket 持续接收 PCM(16 kHz、单声道、16 bit 小端),按滑动窗口做中间结果(
partial)与句末结果(final)。
数据管线(概念上):
音频 (WAV/PCM) → FBank 特征(Kaldi 风格 povey 窗 + am.mvn 做 CMVN)→ Encoder ONNX → Decoder ONNX(AED、16 层、带 cross-attention 与 KV-cache 自回归)→ 词表 tokens.txt 拼成可读文本。
2. 技术栈与依赖
| 依赖 | 作用 |
|---|---|
onnxruntime-node | 在 CPU 上加载并运行 encoder.int8.onnx / decoder.int8.onnx |
express | HTTP 路由、静态资源(public/index.html 演示页) |
multer | POST /transcribe 上传音频文件到临时目录 |
ws | WebSocket 服务器,路径 /ws |
环境:Node.js ≥ 16(见 package.json 的 engines)。
3. 仓库中主要文件
| 路径 | 职责 |
|---|---|
index.js | 服务入口:模型加载、WAV 解析、ASR 管线、HTTP/WS 协议、StreamSession 流式逻辑 |
fbank.js | 纯 JS 的 FBank + am.mvn CMVN,对齐训练侧配置(见文件头注释) |
public/index.html | 浏览器端流式 Demo:麦克风 → PCM → WebSocket |
uploads/ | multer 临时目录(可自动创建) |
模型目录(默认名见 index.js 内 MODEL_DIR) | 需包含 encoder.int8.onnx、decoder.int8.onnx、tokens.txt、am.mvn 等 |
4. 核心模块说明
4.1 WAV 读取(readWav)
- 仅 HTTP 整段识别使用。
- 要求 RIFF/WAVE、PCM format=1、16-bit;多声道会混成单声道。
- 若采样率不是 16 kHz,会做简单线性插值重采样到 16 kHz。
4.2 特征:fbank.js
- 输出为 对数 FBank(行主序
[numFrames * numMels]),与训练侧conf.yaml等对齐(Povey 窗、80 mel、25 ms 帧长 / 10 ms 步长、FFT 上取 2 的幂等)。 loadAmMvn/applyCmvn:从am.mvn读入 shift/scale,原地做
feat = (feat - shift) * scale(详见fbank.js内注释)。
4.3 ASRPipeline 类
-
load()
加载tokens.txt、am.mvn、encoder.int8.onnx、decoder.int8.onnx(图优化在 encoder 上开启)。 -
transcribePcm(samples)
输入为 Float32Array,表示 -1~1 归一化 的 16 kHz 单声道 PCM。
流程:computeFbank→applyCmvn→encoder.run→_decodeAED→tokensToText。 -
_decodeAED- 从 encoder 输出取各层的
cross_k_*/cross_v_*与mask。 - Decoder 逐步自回归:每步 feed 上一步 token、
step、各层 空的 self KV 后由 decoder 更新为new_self_k_cache_*/new_self_v_cache_*(自注意力侧 KV-cache)。 - 每步对
logits取 argmax;若得到 EOS 则结束。 - 常数如
N_LAYERS=16、D_MODEL=1280、SOS_ID/EOS_ID在index.js顶部定义。
- 从 encoder 输出取各层的
-
特殊词元与 BPE
SPECIAL_TOKEN_MAX以下词元在tokensToText中忽略;WORD_BOUNDARY(SentencePiece 的▁)用于在字/子词间插空格。
4.4 全局推理调度:Semaphore + ASR_CONCURRENCY
- ONNX Runtime 在 CPU 上多任务并发容易争抢线程。
- 通过 信号量 把对
asr.*的调用限制为 最多ASR_CONCURRENCY个并发(默认 1,即串行),兼顾多 WebSocket 连接的公平与 CPU 稳定性。
5. HTTP 接口
| 方法/路径 | 说明 |
|---|---|
GET /health | 服务状态(加载中/就绪/错误)、token 数、WebSocket 连接数等 |
GET /info | 模型与流式相关公开参数(采样率、窗口、解码间隔、并发等) |
POST /transcribe | multipart 上传文件,字段名 audio(见启动日志说明);成功返回 text、duration_sec、elapsed_ms、timing_ms 等 |
| 静态文件 | public/ 与项目根均可能被静态挂载;前端 Demo 一般访问 http://localhost:PORT/ |
上传限制示例:multer 的 fileSize 在代码中配置(如 100 MB)。
端口:默认 3000(index.js 中 PORT)。
6. WebSocket 流式协议(/ws)
6.1 客户端 → 服务端
- 二进制帧:原始 PCM,16 bit little-endian,单声道,16000 Hz(
Int16序列的字节流)。 - JSON 文本帧(节选):
type | 含义 |
|---|---|
start | 清空缓冲,开始新一句(utterance),并回复 started |
end | 对当前缓冲做 final(reason: client_end) |
reset | 与 end 相同流程,对当前缓冲做 final(reason: client_reset);runFinal 结束后会内部 reset 清空缓冲。源码注释中的「等价 end+start」指收尾并准备接下一段输入,并非再发一条 start 消息 |
config | 可带 sample_rate,仅校验为 16000 |
6.2 服务端 → 客户端
type | 含义 |
|---|---|
ready | 握手成功,可带 sample_rate、window_sec |
partial | 滑动窗口上的中间识别结果 |
final | 一句定稿(可带 reason) |
error | 错误信息 |
busy | 模型未就绪,连接会被关闭 |
started | 响应 start |
6.3 StreamSession 流式策略(调参前必读)
- 缓冲:用
Float32Array累加整段;超过STREAM_WINDOW_SEC(如 12 s) 只保留尾部窗口,避免无限增长。 - 首次解码:缓冲时长 ≥
STREAM_MIN_AUDIO_SEC才可能触发第一次 partial。 - 节流:两次解码间隔 ≥
STREAM_MIN_DECODE_INTERVAL_MS,减轻 CPU 压力。 - 静音/能量:用末尾约 120 ms 的 RMS 估计连续静音;整段
bufferRms低于STREAM_MIN_BUFFER_RMS时跳过推理,减少无意义输出。 - 收尾
final触发条件(在代码中shouldFinalize):- 末尾静音持续 ≥
STREAM_SILENCE_TAIL_SEC;或 - 从
utteranceStartedAt起算时长 ≥STREAM_MAX_UTTERANCE_SEC。
- 末尾静音持续 ≥
以上常量均在 index.js 顶部,修改后需自行回归测试流式体验与延迟。
7. 运行步骤(概要)
- 安装依赖:
npm install - 将 FireRedASR2 ONNX 模型目录 放到本仓库默认路径,或改
index.js的MODEL_DIR/ 使用上级目录的 fallback 路径(见resolveModelDir) - 启动:
npm start或npm run dev(--watch下文件变更会重启 Node) - 浏览器打开首页测试流式;或用
POST /transcribe测整段
若模型缺失,进程会在 main() 中打印检查项并 process.exit(1)。
8. 学习路径建议
- 先读
index.js顶部注释与配置常量,建立「整段 vs 流式」的边界。 - 跟踪
transcribePcm→encoder→_decodeAED→tokensToText这一条完整链路。 - 对照
fbank.js文件头理解特征是否与论文/工程侧「Kaldi 风格」一致。 - 阅读
StreamSession与ws.on('message'),理解 partial/final 的触发条件。 - 打开
public/index.html看前端如何getUserMedia→ AudioContext → 重采样/整形 → WebSocket`
9. 扩展与注意点
- GPU:当前
InferenceSession使用cpu;若需 CUDA 等,需改executionProviders并保证本机 ORT 与驱动支持。 - 并发与延迟:
ASR_CONCURRENCY、流式窗口与间隔会直接影响延迟、吞吐与公平性;生产环境应结合压测与业务 SLA 调参。 - 长音频整段:HTTP 路径用完整 WAV 解码,不受「滑动窗口」截断,与 WS 流式行为不同。
想要拿源码的联系作者 wx:18404966472 或者私信