diff --git a/src/torchaudio/functional/filtering.py b/src/torchaudio/functional/filtering.py index d4b9f19e46..d1d252aa46 100644 --- a/src/torchaudio/functional/filtering.py +++ b/src/torchaudio/functional/filtering.py @@ -1694,6 +1694,19 @@ def vad( flushedLen_ns = (measures_len - num_measures_to_flush) * measure_period_ns break # end for window + if not has_triggered and not bool(torch.isfinite(waveform).all()): + # Nothing triggered. Either the signal really is silent, or the running trigger + # measure was poisoned: one non-finite sample makes it non-finite for the rest of + # the waveform, so every later comparison against trigger_level is false and the + # whole input is discarded as silence. Only the second case is an error, and the + # scan sits in this branch so a waveform that triggers never pays for it. + raise ValueError( + "waveform must be finite everywhere, but it contains NaN or infinite values. " + "Vad tracks a running measure of the signal, and a single non-finite sample " + "makes every later comparison against trigger_level false, so the whole input " + "would be discarded as silence." + ) + if not has_triggered and shape[-1] >= fixed_pre_trigger_len_ns: return waveform[..., :fixed_pre_trigger_len_ns].view(shape[:-1] + torch.Size([fixed_pre_trigger_len_ns])) diff --git a/test/torchaudio_unittest/transforms/transforms_test_impl.py b/test/torchaudio_unittest/transforms/transforms_test_impl.py index 0120c997c0..1b5da0b6c2 100644 --- a/test/torchaudio_unittest/transforms/transforms_test_impl.py +++ b/test/torchaudio_unittest/transforms/transforms_test_impl.py @@ -498,3 +498,18 @@ def test_vad_on_zero_audio(self, input_shape, output_shape, sample_rate: int, pr expected_output = torch.zeros(output_shape, dtype=self.dtype, device=self.device) result = T.Vad(sample_rate, pre_trigger_time=pre_trigger_time)(inpt) self.assertEqual(result, expected_output) + + @parameterized.expand([(float("nan"),), (float("inf"),), (float("-inf"),)]) + def test_vad_rejects_non_finite_audio(self, bad_value: float): + """VAD should raise on non-finite input rather than report the signal as silence. + + The trigger measure is a running mean, so one non-finite sample poisons it and + every later comparison against trigger_level is false. Nothing triggers and the + whole waveform is discarded, returning an empty Tensor. See + https://github.com/pytorch/audio/issues/4216. + """ + sample_rate = 16000 + waveform = torch.zeros(sample_rate, dtype=self.dtype, device=self.device) + waveform[100] = bad_value + with self.assertRaisesRegex(ValueError, "finite"): + T.Vad(sample_rate)(waveform)