Commit 284029c0 for whisper.cpp

commit 284029c07012d2000941a08d38a31ffb71f4734e
Author: Lucius <37386791+luciusacarr@users.noreply.github.com>
Date:   Wed Oct 28 01:07:30 2026 -0700

    whisper : clear VAD state when VAD is disabled (#4122)

    * whisper : clear VAD state when VAD is disabled

    * test hardening

diff --git a/src/whisper.cpp b/src/whisper.cpp
index 6e0fbf4a..112e7078 100644
--- a/src/whisper.cpp
+++ b/src/whisper.cpp
@@ -6814,6 +6814,12 @@ static void whisper_sequence_score(
     }
 }

+static void whisper_state_clear_vad(struct whisper_state * state) {
+    state->vad_segments.clear();
+    state->vad_mapping_table.clear();
+    state->has_vad_segments = false;
+}
+
 static bool whisper_vad(
         struct whisper_context * ctx,
           struct whisper_state * state,
@@ -6824,9 +6830,8 @@ static bool whisper_vad(
     WHISPER_LOG_INFO("%s: VAD is enabled, processing speech segments only\n", __func__);
     int filtered_n_samples = 0;

-    // Clear any existing mapping table
-    state->vad_mapping_table.clear();
-    state->has_vad_segments = false;
+    // Clear any existing VAD state
+    whisper_state_clear_vad(state);

     if (state->vad_context == nullptr) {
         struct whisper_vad_context_params vad_ctx_params = whisper_vad_default_context_params();
@@ -7968,6 +7973,8 @@ int whisper_full(
         }
         samples = vad_samples.data();
         n_samples = vad_samples.size();
+    } else {
+        whisper_state_clear_vad(ctx->state);
     }
     return whisper_full_with_state(ctx, ctx->state, params, samples, n_samples);
 }
@@ -7995,6 +8002,8 @@ int whisper_full_parallel(
         }
         samples = vad_samples.data();
         n_samples = vad_samples.size();
+    } else {
+        whisper_state_clear_vad(ctx->state);
     }
     int ret = 0;

diff --git a/tests/test-vad-full.cpp b/tests/test-vad-full.cpp
index f033e058..6452b05d 100644
--- a/tests/test-vad-full.cpp
+++ b/tests/test-vad-full.cpp
@@ -81,6 +81,35 @@ int main() {
         vad_prev_end = t1;
     }

+    // non-VAD call after a VAD call on the same context must not use stale VAD state.
+    // ref: https://github.com/ggml-org/whisper.cpp/issues/4113
+    struct whisper_full_params wparams_novad = wparams;
+    wparams_novad.vad = false;
+
+    struct whisper_context * wctx_ref = whisper_init_from_file_with_params(
+            whisper_model_path.c_str(),
+            cparams);
+
+    assert(wctx_ref);
+
+    for (int processors = 1; processors <= 2; ++processors) {
+        assert(whisper_full_parallel(wctx_ref, wparams_novad, pcmf32.data(), pcmf32.size(), processors) == 0);
+
+        assert(whisper_full_parallel(wctx, wparams,       pcmf32.data(), pcmf32.size(), 1)          == 0);
+        assert(whisper_full_parallel(wctx, wparams_novad, pcmf32.data(), pcmf32.size(), processors) == 0);
+        assert(whisper_full_n_vad_segments(wctx) == 0);
+
+        const int n_segments_ref = whisper_full_n_segments(wctx_ref);
+        assert(n_segments_ref > 0);
+        assert(whisper_full_n_segments(wctx) == n_segments_ref);
+
+        for (int i = 0; i < n_segments_ref; ++i) {
+            assert(whisper_full_get_segment_t0(wctx, i) == whisper_full_get_segment_t0(wctx_ref, i));
+            assert(whisper_full_get_segment_t1(wctx, i) == whisper_full_get_segment_t1(wctx_ref, i));
+        }
+    }
+
+    whisper_free(wctx_ref);
     whisper_free(wctx);

     return 0;