Skip to content

funasr_onnx: Fsmn_vad returns the segments of all input files in one list #3830

Closed

Description

@tanishqbaweja

The funasr_onnx README documents str, np.ndarray, List[str] input for Fsmn_vad, but for a list of files the result holds one segment list for all of them. Fsmn_vad.__call__ (runtime/python/onnxruntime/funasr_onnx/vad_bin.py) starts from segments = [[]] * self.batch_size and adds the segments of each batch to segments[batch_num] for batch_num in range(0, self.batch_size). With the default batch_size=1, each file is its own batch, so the segments of every file end up in segments[0]:

  • model([a, b]) returns [a_segments + b_segments] instead of [a_segments, b_segments], and b's times start again from 0 after a's last segment.
  • model([silence, b]) returns [b_segments]: b's segments are reported for the silent file, and there is no entry for b.

Paraformer and SenseVoiceSmall in the same package return one result per input.

batch_size > 1 does not work either:

  • One file: E2EVadModel returns one segment list per waveform, but the loop reads segments_part[batch_num] for every batch_num below batch_size. With batch_size=2, a file with speech raises IndexError: list index out of range, and a file without speech returns [[], []].
  • Two or more files in a batch fail before that (numpy 1.26.4): np.array(waveform) raises ValueError for files of different lengths, and int(feats_len) raises TypeError for files of equal length. E2EVadModel also takes one waveform per call (assert len(self.scores) == 1, "only support batch_size = 1 now").

Repro on main (f122f6c) without downloading a model. Only the ONNX session is replaced, by a stub that scores loud frames as speech; WavFrontend (with the frontend_conf of funasr/models/fsmn_vad_streaming/template.yaml, no CMVN) and E2EVadModel are the real ones:

import numpy as np
import soundfile
from funasr_onnx.utils.frontend import WavFrontend
from funasr_onnx.vad_bin import Fsmn_vad


def fake_infer(inputs):  # loud frames are speech
    sil = np.where(inputs[0][0].mean(-1) > 5.0, 0.05, 0.95)
    return np.stack((sil, 1.0 - sil), axis=-1)[None].astype(np.float32), inputs[1:]


model = Fsmn_vad.__new__(Fsmn_vad)
model.frontend = WavFrontend(
    cmvn_file=None, fs=16000, window="hamming", n_mels=80, frame_length=25,
    frame_shift=10, dither=0.0, lfr_m=5, lfr_n=1,
)
model.batch_size, model.vad_scorer_config, model.max_end_sil = 1, {}, 800
model.encoder_conf = {"fsmn_layers": 1, "proj_dim": 2, "lorder": 2}
model.infer = fake_infer

# a.wav: 3 s, noise at 0.5-1.5 s. b.wav: 4.5 s, noise at 1-2 s and 3-3.5 s.
for name, length, noise in (
    ("a.wav", 48000, [(8000, 24000)]),
    ("b.wav", 72000, [(16000, 32000), (48000, 56000)]),
):
    rng = np.random.default_rng(0)
    audio = np.zeros(length, dtype=np.float32)
    for start, end in noise:
        audio[start:end] = rng.uniform(-0.3, 0.3, end - start)
    soundfile.write(name, audio, 16000)

print(model("a.wav"))             # [[[220, 1660]]]
print(model("b.wav"))             # [[[720, 2160], [2720, 3660]]]
print(model(["a.wav", "b.wav"]))  # [[[220, 1660], [720, 2160], [2720, 3660]]]

model.batch_size = 2
for audio_in in ("a.wav", ["a.wav", "b.wav"], ["a.wav", "a.wav"]):
    try:
        print(model(audio_in))
    except Exception as e:
        print(f"{type(e).__name__}: {e}")

With batch_size=2, the loop prints:

IndexError: list index out of range
ValueError: setting an array element with a sequence. The requested array has an inhomogeneous shape after 1 dimensions. The detected shape was (2,) + inhomogeneous part.
TypeError: only length-1 arrays can be converted to Python scalars

Expected: [[[220, 1660]], [[720, 2160], [2720, 3660]]] for the list, and with batch_size=2 the same results as with batch_size=1: [[[220, 1660]]], [[[220, 1660]], [[720, 2160], [2720, 3660]]] and [[[220, 1660]], [[220, 1660]]].

Environment: Windows 11, Python 3.11.17 and 3.12.15, numpy 1.26.4, onnxruntime 1.31.1, kaldi-native-fbank 1.22.5, librosa 0.11.0, soundfile 0.14.0, funasr-onnx 0.4.5 built from runtime/python/onnxruntime.

Activity

  1. LauraGPT commented on Nov 2, 2026

    @LauraGPT
    Collaborator

    Fixed on main by #3831, merged as a1aa2846839480effb4f079771cb0af8809342c2. Each input now has its own result list and is scored sequentially, matching the backend's single-waveform contract. Silent inputs keep their own empty entry; input order and single-input behavior are preserved.

    The contributor's regression reproduced on the old main. Validation covered 74 selected CPU/package cases and 42 real L20Z/CUDA contract cases, followed by exact-head and merged-commit CI artifact/log acceptance. The CUDA graph was a synthetic scoring/cache fixture, not pretrained VAD quality evaluation; this is not a full-suite or Windows/NPU claim.

    Important packaging distinction: this fix is in source/main, not in the already-published funasr-onnx 0.4.5. That release is immutable. A subsequent package release needs a new version and its own artifact acceptance. The issue was closed automatically when its linked PR merged; no separate manual close was performed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions