Metadata-Version: 2.4
Name: fasr-vad-firered
Version: 0.5.8
Summary: FireRedVAD for fasr (bundled fireredvad inference)
Author-email: fasr <wangmengdi06@58.com>
Requires-Python: <3.13,>=3.10
Description-Content-Type: text/markdown
Requires-Dist: fasr
Requires-Dist: kaldiio>=2.18.0
Requires-Dist: kaldi-native-fbank>=1.19.0
Requires-Dist: numpy>=1.24
Requires-Dist: soundfile>=0.12.0
Requires-Dist: textgrid
Requires-Dist: torch>=2.0.0

# fasr-vad-firered

英文文档地址: [README_EN.md](README_EN.md)

FireRedVAD 语音活动检测插件。插件内置 FireRedVAD 的 Python 推理代码，加载官方 FireRedVAD checkpoint 后输出 fasr 的 `AudioSpan` 或流式 `AudioChunk`。

## 安装

```bash
pip install fasr-vad-firered
```

开发环境中可以在仓库根目录使用：

```bash
uv sync --group dev
```

## 注册模型

| 注册名 | 类 | 能力 | 适用场景 |
|---|---|---|---|
| `firered` | `FireRedForVAD` | batch/offline | 离线文件切分、transcribe pipeline |
| `firered_online` | `FireRedStreamForVAD` | streaming + batch | realtime 服务；输出语义对齐官方 Stream-VAD |

默认 checkpoint 是 `FireRedTeam/FireRedVAD`，默认使用 ModelScope 下载。官方模型目录通常包含 `VAD/` 和 `Stream-VAD/` 两个子目录；插件会按注册名自动选择对应子目录。

## 配置示例

离线 VAD：

```toml
[vad_model]
@vad_models = "firered"
use_gpu = false
speech_threshold = 0.4
min_speech_frame = 20
min_silence_frame = 50
```

流式 VAD：

```toml
[service.vad_model]
@vad_models = "firered_online"
use_gpu = false
chunk_size_ms = 100
speech_threshold = 0.5
pad_start_frame = 5
min_speech_frame = 8
min_silence_frame = 50
```

realtime 快速启动示例：

```bash
fasr-service-realtime serve --vad firered_online --stream-asr paraformer_online
```

## Python 使用

离线检测：

```python
from fasr.config import registry
from fasr.data import AudioSpan, Waveform

model = registry.vad_models.get("firered")(speech_threshold=0.4)
segments = model.detect(
    AudioSpan(start_ms=0, waveform=Waveform.from_file("example.wav"))
)
for segment in segments:
    print(segment.start_ms, segment.end_ms)
```

流式检测：

```python
from fasr.config import registry
from fasr.data import AudioChunk

vad = registry.vad_models.get("firered_online")(chunk_size_ms=100)
for audio_chunk in realtime_chunks:
    for emitted in vad.push_chunk(audio_chunk):
        print(emitted.vad_state, emitted.start_ms, emitted.end_ms)
```

`firered_online` 也实现了 `detect()`。它直接调用官方 Stream-VAD 的
`detect_full()`，因此 batch 输出会和官方 Stream-VAD 完整音频模式一致。
实时 `push_chunk()` 也按官方 chunkwise 方式切块：当 `chunk_size_ms = 100`
时，内部实际送入模型的是 `115ms` 窗口、`100ms` 步长，相邻窗口保留
`15ms` 重叠，以避免 fbank 边界帧丢失。

## 参数

FireRedVAD 的帧移约为 `10ms`，所以 `*_frame` 参数都可以粗略理解为 `帧数 * 10ms`。例如 `min_silence_frame = 50` 约等于需要连续 `500ms` 静音才判定说话结束。

离线和流式共有参数：

| 参数 | 默认值 | 含义 | 调大 / 开启后 | 调小 / 关闭后 | 建议场景 |
|---|---:|---|---|---|---|
| `use_gpu` | `false` | 是否把 FireRedVAD 模型放到 CUDA 上推理。 | 有 CUDA 时通常吞吐更好，长音频或高并发更快，但会占用显存。 | 使用 CPU，部署更简单，不占显存，但长音频处理会更慢。 | GPU 资源宽裕且 VAD 成为瓶颈时开启；显存紧张或只跑轻量实时 VAD 时关闭。 |
| `smooth_window_size` | `5` | 对逐帧语音概率做滑动平均的窗口大小，用来抑制概率抖动。 | 输出更平滑，短促噪声更不容易触发语音，但起止点会更钝，可能多一点延迟。 | 响应更灵敏，语音起止更贴近原始概率，但容易受瞬时噪声影响。 | 背景噪声多、误触发多时调大；想降低端点延迟或保留短促语音时调小。 |
| `speech_threshold` | 离线 `0.4`，流式 `0.5` | 判断一帧是否为语音的概率阈值。 | 判定更严格，噪声、背景人声、弱音乐更不容易被当成语音，但轻声/远场语音可能漏掉。 | 判定更敏感，弱语音更容易保留，但噪声和背景声误检会增加。 | 噪声误检多时调大到 `0.55`/`0.6`；漏检轻声时调小到 `0.35`/`0.3`。 |
| `min_speech_frame` | 离线 `20`，流式 `8` | 连续多少个语音帧后才确认进入语音段。 | 更不容易被短噪声触发，语音开始更稳定，但很短的“嗯/啊/好”可能被过滤。 | 更容易触发语音开始，短词更容易保留，但误触发概率升高。 | 电话客服里短背景音频繁触发时调大；需要捕捉很短应答词时调小。 |
| `max_speech_frame` | `2000` | 单个语音段允许持续的最大帧数，默认约 `20s`。超过后会强制切段。 | 单段可更长，长句不容易被强制切开，但极长语音会推迟最终结果。 | 更早强制切段，最终结果更频繁，但一句长话可能被拆成多段。 | 长会议/长客服陈述可调大；希望长语音定期出最终结果时调小。 |
| `min_silence_frame` | `50` | 连续多少个静音帧后才确认语音结束，默认约 `500ms`。 | 结束判定更保守，不容易被句中停顿截断，但最终结果延迟会增加。 | 结束更快，延迟更低，但短暂停顿更容易把一句话切成多段。 | 句子经常被切碎时调大；追求低延迟实时交互时调小。 |
| `chunk_max_frame` | `30000` | 离线长音频内部推理分块的最大帧数，默认约 `300s`。 | 单次模型调用处理更长，切块边界更少，但峰值内存更高。 | 峰值内存更低，更适合超长音频或小内存机器，但内部循环次数增加。 | 普通音频一般不用改；遇到内存压力或超长音频时调小。 |

离线 batch detect 后处理参数：

| 参数 | 默认值 | 含义 | 调大后 | 调小后 | 建议场景 |
|---|---:|---|---|---|---|
| `merge_silence_frame` | `0` | 语音段之间如果只有很短静音，是否把两段合并。`0` 表示不合并。 | 更倾向把被短停顿分开的语音合成一段，减少碎片，但可能把本应分开的两句话粘在一起。 | 分段更细，停顿会保留下来，但结果可能更碎。 | 识别结果过碎、句中停顿很多时调大；需要精细端点时保持 `0` 或调小。 |
| `extend_speech_frame` | `0` | 在检测到的语音段前后额外扩展多少帧。`0` 表示不扩展。 | 更不容易截掉开头/结尾弱音，但会带入更多前后噪声或静音。 | 片段更紧凑，噪声更少，但开头结尾更容易被切掉。 | ASR 经常丢首字/尾字时调大；噪声很明显或只想要紧凑语音段时保持较小。 |

流式专属参数：

| 参数 | 默认值 | 含义 | 调大后 | 调小后 | 建议场景 |
|---|---:|---|---|---|---|
| `chunk_size_ms` | `100` | 流式 VAD 的官方 chunkwise 步长。内部窗口长度为 `25ms + (chunk_size_ms / 10 - 1) * 10ms`，所以默认 `100ms` 步长对应 `115ms` 窗口。 | 每次处理音频更多，调度开销更低，但单块处理和端点反馈会更粗，实时性略差。 | 反馈更细、更实时，但调用频率更高，Python 调度和模型调用开销会增加。 | 默认 `100ms` 和官方示例一致；低延迟场景可尝试 `40-80ms`，吞吐优先可调到 `150-200ms`。 |
| `pad_start_frame` | `5` | 检测到语音开始时，向前额外保留多少帧，默认约 `50ms`。 | 更不容易丢掉第一个字/起音，但会带入更多语音前噪声。 | 起点更紧，前置噪声更少，但可能切掉开头弱音。 | ASR 经常少首字时调大；前置噪声明显时调小。 |

## realtime 动态更新

`firered_online` 支持 realtime `session.update` 的 `turn_detection` 映射：

| realtime 字段 | FireRed 字段 | 效果 |
|---|---|---|
| `threshold` | `speech_threshold` | 调高更保守，调低更敏感。 |
| `silence_duration_ms` | `min_silence_frame` | 按 10ms/frame 换算，调高延迟更大但更不容易截断。 |
| `prefix_padding_ms` | `pad_start_frame` | 按 10ms/frame 换算，调高会保留更多语音开头前的上下文。 |

## 依赖

- `fasr`
- `torch >= 2.0.0`
- `numpy >= 1.24`
- `soundfile >= 0.12`
- `kaldiio >= 2.18`
- `kaldi-native-fbank >= 1.19`
- `textgrid`
