Thanks to visit codestin.com
Credit goes to github.com

Skip to content

eclipse005/FireRedVad

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

16 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

English | 简体中文

FireRedVAD

原生 Rust 实现的工业级 VAD / AED / Stream-VAD,权重全部编译进二进制,单文件可执行,零运行时依赖。也可作为 Rust crate 嵌入。底层 API 与官方 Python FireRedVad / FireRedAed / FireRedStreamVad 1:1 对齐(方法名、参数、JSON schema 全部一致),输出在 event.wav 上与官方逐帧匹配。

三种模式

模式 输入 输出 用途
VAD 任意 wav [{start,end}] 语音段 切长音频给 ASR 喂
AED 任意 wav {speech:[..], singing:[..], music:[..]} 内容分类、过滤非语音
Stream-VAD wav 或 stdin 实时 {dur, timestamps, [frames]} 实时字幕、直播

性能

Intel Core Ultra 7 265K(20 核)上,7835 秒音频推理耗时 3.96 秒RTFx ≈ 1978×。相比原版 Python(PyTorch)快 10–100 倍,且无需 GPU。

测试方法:5 次连续推理取中位,脚本见 tmp/bench_rtfx.ps1

准确度

与上游同源(FireRedTeam/FireRedVAD),测试集 FLEURS-VAD-102:

Metric FireRedVAD Silero TEN FunASR WebRTC
AUC-ROC ↑ 99.60 97.99 97.81 - -
F1 ↑ 97.57 95.95 95.19 90.91 52.30
误报率 ↓ 2.69 9.41 15.47 44.03 2.83

特性

  • 单文件可执行 — 三个模型(VAD + AED + Stream-VAD)权重全部编译进二进制,复制到任何 Windows x64 机器双击就跑
  • CPU 加速gemm 运行期探测 AVX-512/AVX2/AVX/SSE/NEON,自动选最快微内核
  • 并行推理rayon 切块并行,多核线性扩展
  • 格式宽容 — 多通道自动混音,任意采样率重采样到 16kHz,PCM16/float32 都吃
  • 动态 VAD — 可选的动态静音阈值,避免把完整句子切碎
  • API 1:1 对齐 Python — 改用 Rust 不用改业务逻辑;from_pretrained / detect / set_mode / results_to_timestamps 全在

快速开始

下载二进制(从 Releases):

.\fireredvad.exe vad D:\path\to\audio.wav
.\fireredvad.exe aed D:\path\to\audio.wav
.\fireredvad.exe stream-vad D:\path\to\audio.wav

从源码构建:

cargo build --release
# → target/release/fireredvad.exe

CLI 用法

三个子命令,互不影响,按需选用:

fireredvad vad <wav> — 二值 VAD

输出 [start, end] 语音段。

.\fireredvad.exe vad audio.wav
{
  "dur": 22.016,
  "timestamps": [[0.09, 18.48], [18.49, 22.016]],
  "wav_path": "audio.wav",
  "sample_rate_in": 16000,
  "sample_rate_model": 16000
}

fireredvad aed <wav> — 三类 AED

输出 speech / singing / music 三类时间戳。

.\fireredvad.exe aed audio.wav
{
  "dur": 22.016,
  "event2timestamps": {
    "speech":  [[0.40, 3.56], [3.66, 9.08], [9.27, 9.77], [10.78, 21.76]],
    "singing": [[1.79, 19.96], [19.97, 22.016]],
    "music":   [[0.09, 12.32], [12.33, 22.016]]
  },
  "event2ratio": {"speech": 0.848, "singing": 0.905, "music": 0.991},
  "wav_path": "audio.wav"
}

fireredvad stream-vad <wav> — 流式 VAD(整文件模拟)

输出同 vad,但内部按帧串行处理,模拟"逐帧推理"的实时场景。

.\fireredvad.exe stream-vad audio.wav

fireredvad stream-vad --stdin — 真·实时流式

stdin 持续吃 16kHz int16 LE PCM 数据(任意 chunk 大小),stdout 每帧立刻吐一行 JSON(NDJSON),最后一行是汇总:

# 从麦克风管道送入
mic --format=s16le --rate=16000 --channels=1 | fireredvad.exe stream-vad --stdin

NDJSON 格式(每帧一行 + 最后一行 _summary: true):

{"frame_idx":1,"is_speech":true,"raw_prob":0.86,"smoothed_prob":0.81,"is_speech_start":true,...}
{"frame_idx":2,"is_speech":true,"raw_prob":0.91,"smoothed_prob":0.86,...}
...
{"_summary":true,"dur":22.016,"timestamps":[[0.09,20.13],[20.14,21.99]],"n_frames":2200}

100Hz 帧率(每 10ms 一帧),与 stream-vad <wav> 离线模拟逐帧一致。

作为 Rust 库使用

[dependencies]
fireredvad = "0.1"

底层 API 与官方 Python FireRedVad / FireRedAed / FireRedStreamVad 1:1 对齐

use fireredvad::{Vad, VadConfig, Aed, AedConfig, StreamVad, StreamVadConfig};

// ---- VAD: binary speech/silence ----
let vad = Vad::from_pretrained()?;       // Python: FireRedVad.from_pretrained(model_dir, config)
let out = vad.detect_wav("audio.wav", &VadConfig::default())?;
println!("{} segments", out.timestamps.len());

// ---- AED: speech / singing / music ----
let aed = Aed::from_pretrained()?;       // Python: FireRedAed.from_pretrained(model_dir, config)
let out = aed.detect_wav("audio.wav")?;
for (ev, segs) in &out.event2timestamps {
    println!("{ev}: {} segs", segs.len());
}

// ---- Stream-VAD: real-time streaming ----
let mut sv = StreamVad::from_pretrained()?;
sv.set_mode(0);                           // Python: FireRedStreamVad.set_mode(0..3)
// Per-frame (one frame = 400 samples at 16 kHz = 25 ms):
let r = sv.detect_frame(&[0.0f32; 400])?; // Python: detect_frame(audio_frame)
if r.is_speech_start { /* speech begins at r.speech_start_frame */ }
// Per-chunk (variable length; processed at 100 Hz = 10 ms shift):
let r = sv.detect_chunk(&pcm_chunk)?;     // Python: detect_chunk(audio_chunk)
// Whole file (offline streaming simulation):
let (frames, out) = sv.detect_full("audio.wav")?;  // Python: detect_full(audio)
// Convert per-frame results to (start, end) in seconds:
let timestamps = StreamVad::results_to_timestamps(&frames);

API 速查(与 Python 对应)

Python (fireredvad 原版) Rust crate
FireRedVad.from_pretrained(model_dir, config) Vad::from_pretrained() / Vad::new()
vad.detect(audio) vad.detect_wav(path) / vad.detect_pcm(samples)
FireRedAed.from_pretrained(model_dir, config) Aed::from_pretrained() / Aed::new() / Aed::with_config(cfg)
aed.detect(audio) aed.detect_wav(path) / aed.detect_pcm(samples)
FireRedStreamVad.from_pretrained(model_dir, config) StreamVad::from_pretrained() / StreamVad::new() / StreamVad::with_config(cfg)
sv.reset() sv.reset()
sv.set_mode(0..3) sv.set_mode(mode: usize)
sv.detect_frame(audio_frame) sv.detect_frame(samples: &[f32])
sv.detect_chunk(audio_chunk) sv.detect_chunk(samples: &[f32])
sv.detect_full(audio) sv.detect_full(path) (returns (Vec<FrameResult>, Output))
FireRedStreamVad.results_to_timestamps(results) StreamVad::results_to_timestamps(results)

from_pretrained 当前不接受 path 参数——权重全部编译进二进制,无外部模型依赖。如果未来需要外部加载,会加一个 from_path(model_dir) 不破坏 API。

CLI 参数

vad

参数 默认值 说明
<wav> (必填) 输入 WAV 路径
--speech-threshold 0.4 语音概率阈值
--smooth-window-size 5 滑动窗口平滑(帧)
--min-speech-frame 20 确认语音的最少连续帧
--max-speech-frame 2000 单段最大长度,超过强制切分
--min-silence-frame 20 确认静音的最少连续帧
--merge-silence-frame 0 短于该帧数的静音缝合并
--extend-speech-frame 0 语音段前后延展帧数
--chunk-max-frame 30000 并行分块的最大帧数
--dynamic-vad off 启用动态静音阈值

aed

参数 默认值 说明
<wav> (必填) 输入 WAV 路径
--speech-threshold 0.4 语音阈值
--singing-threshold 0.5 唱歌阈值
--music-threshold 0.5 音乐阈值
--smooth-window-size 5 同上
--min-event-frame 20 任一事件确认的最少连续帧
--max-event-frame 2000 同上
--min-silence-frame 20 同上
--merge-silence-frame 0 同上
--extend-speech-frame 0 同上
--chunk-max-frame 30000 同上

stream-vad

参数 默认值 说明
<wav> (可选) 整文件模拟(默认)
--stdin off 真·实时流式模式(覆盖 <wav>
--pad-trailing off --stdin 模式下,把尾部不足一帧的部分补零再推理
--speech-threshold 0.3 语音概率阈值
--smooth-window-size 5 滑动窗口
--pad-start-frame 5 语音开始处向前回看帧数
--min-speech-frame 8 确认语音最少帧
--max-speech-frame 2000 单段最大长度
--min-silence-frame 20 确认静音最少帧

Stream-VAD 灵敏度档位(set_mode

mode 名称 threshold min_speech min_silence
0 VERY_PERMISSIVE 0.3 8 20
1 PERMISSIVE 0.5 10 15
2 AGGRESSIVE 0.7 15 10
3 VERY_AGGRESSIVE 0.9 20 5

动态 VAD

vad 子命令的 --dynamic-vad 让切分阈值随当前段的累积时长收紧——段越长,越短的停顿就足以切分(适用于不切碎长句):

当前段已说 需静音超过 才切
≤ 5 s 2000 ms 2 秒
≤ 10 s 1500 ms 1.5 秒
≤ 15 s 1000 ms 1 秒
≤ 30 s 800 ms
≤ 45 s 400 ms
> 45 s 100 ms 一停就切
.\fireredvad.exe vad audio.wav --dynamic-vad
  • --min-silence-frame 互斥,开启后固定阈值被忽略
  • --max-speech-frame 仍作为兜底硬切
  • 表源自 FunASR fsmn_vad_streaming/dynamic_vad.py

VAD vs AED 关系

VAD 是宽松的"speech"(会吞掉 singing),AED 的 speech 类更严。如果要"喂 ASR 的纯净人话",推荐:

// 方案 A:AED 直接替代 VAD
let aed_out = aed.detect_wav("audio.wav")?;
let speech_segments = aed_out.event2timestamps["speech"];

// 方案 B:VAD 粗切 + AED 过滤掉 singing 重叠段(见 examples/pipeline_vad_aed.rs)

工作流程

WAV → 混音 mono → 重采样 16kHz → 80维 fbank → CMVN → DFSMN 编码器 → sigmoid → 状态机后处理 → 段
模块 作用
audio.rs 解码 PCM16/float32 WAV,混音,重采样
fbank.rs 80维 log-Mel 滤波器组 + Povey 窗
cmvn.rs CMVN 均值/方差归一化
model.rs Deep FSMN 编码器(gemm 矩阵乘),含流式 cache
postprocess.rs 平滑、阈值、状态机、切段;含流式变体
aed.rs AED 高层封装(3 路并行后处理)
stream_vad.rs Stream-VAD 高层封装(流式 cache)
cli.rs 三个子命令 + stdin 模式

权重来源

model/ 下三个模型权重源自 FireRedTeam/FireRedASR2Spretrained_models/FireRedVAD/{VAD,AED,Stream-VAD}/,Apache-2.0 协议再分发。模型通过 include_bytes! 在编译期嵌入二进制,不依赖任何外部文件。

许可证

Apache-2.0

About

Self-contained native Rust CLI for non-streaming Voice Activity Detection (VAD) on Windows. Model embedded, zero runtime dependencies.

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages

  • Rust 100.0%