> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ai-coustics.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Migrate to the Dedicated VAD

> Move voice activity detection off the processor and onto a dedicated VAD object backed by a VAD model.

**Before:** the VAD was a side effect of enhancement. It inferred speech from how much energy was left after the enhancement model suppressed non-speech, so its accuracy depended on which model you ran.

**Now:** the VAD is its own object, running a model trained for the task. You create it from a dedicated VAD model and drive it yourself.

<Warning>
  This is not a drop-in replacement. Sensitivity changed from an energy threshold to a probability, so your old value cannot be carried across. See [Retune sensitivity](#retune-sensitivity).
</Warning>

## What changed

<Tabs>
  <Tab title="Python">
    |             | Before                                 | Now                                                                 |
    | ----------- | -------------------------------------- | ------------------------------------------------------------------- |
    | Model       | Any enhancement model                  | A dedicated VAD model only                                          |
    | Create      | `processor.get_vad_context()`          | `aic.Vad(vad_model, license_key, config)`, then `vad.get_context()` |
    | Advance     | Implicit, via `processor.process()`    | `vad.process(audio)`                                                |
    | Sensitivity | `1.0` to `15.0`, an energy threshold   | `0.0` to `1.0`, a probability threshold                             |
    | Reset       | `processor_context.reset()` reset both | `vad_context.reset()` resets only the VAD                           |
    | Delay       | `processor_context.get_output_delay()` | `vad_context.get_prediction_delay()`                                |
    | Async       | Not available separately               | `aic.VadAsync`, `await vad.process_async(audio)`                    |
  </Tab>

  <Tab title="C">
    |             | Before                                    | Now                                                                           |
    | ----------- | ----------------------------------------- | ----------------------------------------------------------------------------- |
    | Model       | Any enhancement model                     | A dedicated VAD model only                                                    |
    | Create      | `aic_vad_context_create(&ctx, processor)` | `aic_vad_create` → `aic_vad_initialize` → `aic_vad_context_create(&ctx, vad)` |
    | Advance     | Implicit, via `aic_processor_process_*`   | `aic_vad_process(vad, audio, block_size)`                                     |
    | Sensitivity | `1.0` to `15.0`, an energy threshold      | `0.0` to `1.0`, a probability threshold                                       |
    | Reset       | `aic_processor_context_reset` reset both  | `aic_vad_context_reset(ctx)`                                                  |
    | Delay       | `aic_processor_context_get_output_delay`  | `aic_vad_context_get_prediction_delay`                                        |
    | Destroy     | `aic_vad_context_destroy`                 | `aic_vad_context_destroy`, then `aic_vad_destroy`                             |
  </Tab>
</Tabs>

Use `vad-2.1-xxs-16khz` for general speech detection, or `vad-vf-2.0-s-16khz` to detect the primary speaker only.

## Migrate the setup

<Tabs>
  <Tab title="Python">
    <CodeGroup>
      ```python Before theme={null}
      # One model, one processor: enhancement and VAD were coupled.
      processor = aic.Processor(model, license_key, config)

      vad_context = processor.get_vad_context()
      vad_context.set_parameter(aic.VadParameter.Sensitivity, 5.0)  # energy threshold

      enhanced = processor.process(audio)  # also advanced the VAD

      print(vad_context.is_speech_detected())
      ```

      ```python Now theme={null}
      # Two models, two objects.
      vad_model = aic.Model.from_file(
          aic.Model.download("vad-2.1-xxs-16khz", "./models")
      )
      # Raises ModelTypeUnsupportedError if the model is not a VAD model.
      vad = aic.Vad(vad_model, license_key, aic.ProcessorConfig.optimal(vad_model))

      vad_context = vad.get_context()
      vad_context.set_parameter(aic.VadParameter.Sensitivity, 0.8)  # probability

      vad.process(audio)  # returns None, does not modify the audio

      print(vad_context.is_speech_detected())
      print(vad_context.raw_vad_probability())
      ```
    </CodeGroup>
  </Tab>

  <Tab title="C">
    <CodeGroup>
      ```c Before theme={null}
      aic_processor_create(&processor, enhancement_model, license, NULL);
      aic_processor_initialize(processor, sample_rate, 1, num_frames, false);

      aic_vad_context_create(&vad_context, processor);
      aic_vad_context_set_parameter(
          vad_context, AIC_VAD_PARAMETER_SENSITIVITY, 5.0f);  // energy threshold

      aic_processor_process_interleaved(processor, audio, 1, num_frames);

      bool is_speech_detected = false;
      aic_vad_context_is_speech_detected(vad_context, &is_speech_detected);
      ```

      ```c Now theme={null}
      aic_model_create_from_file(&vad_model, "path/to/vad-model.aicmodel");

      // Returns AIC_ERROR_CODE_MODEL_TYPE_UNSUPPORTED if the model is not a VAD model.
      aic_vad_create(&vad, vad_model, license, NULL);

      aic_model_get_optimal_sample_rate(vad_model, &sample_rate);
      aic_model_get_optimal_block_size(vad_model, sample_rate, &block_size);
      aic_vad_initialize(vad, sample_rate, block_size, false);

      aic_vad_context_create(&vad_context, vad);
      aic_vad_context_set_parameter(
          vad_context, AIC_VAD_PARAMETER_SENSITIVITY, 0.8f);  // probability

      aic_vad_process(vad, audio, block_size);

      bool is_speech_detected = false;
      aic_vad_context_is_speech_detected(vad_context, &is_speech_detected);

      aic_vad_context_destroy(vad_context);
      aic_vad_destroy(vad);
      aic_model_destroy(vad_model);
      ```
    </CodeGroup>

    See `examples/vad.c` in the SDK distribution for a complete, error-checked version.
  </Tab>
</Tabs>

## Feed the VAD your original audio

If you run enhancement and VAD together, give both the same original input block. Do not chain them.

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    vad.process(audio)                  # reads the block, does not modify it
    enhanced = processor.process(audio)
    ```
  </Tab>

  <Tab title="C">
    ```c theme={null}
    aic_vad_process(vad, input, block_size);             // const float *, leaves the buffer untouched
    aic_processor_process(processor, input, block_size);  // enhances the block in place
    ```
  </Tab>
</Tabs>

Enhancement changes the signal on purpose, so running the VAD on its output means detecting speech in audio the VAD model was not trained on. It also stacks the processor's delay on top of the VAD's own.

<Info>
  The two models can have different optimal block sizes and sample rates. If yours disagree, initialize both to the configuration your stream already uses, or keep separate block sizes and feed each object from your own buffer.
</Info>

## Retune sensitivity

This is the step most likely to change your application's behavior.

|           | Before                                          | Now                                               |
| --------- | ----------------------------------------------- | ------------------------------------------------- |
| Meaning   | An energy threshold, $10^{-\text{sensitivity}}$ | A probability threshold                           |
| Range     | `1.0` to `15.0`                                 | `0.0` to `1.0`                                    |
| Direction | Higher detected speech more aggressively        | Higher requires more confidence, so it fires less |

There is no conversion formula. The old value described leftover energy after enhancement, the new one describes model confidence, and the direction is inverted.

<Warning>
  An old value like `5.0` is out of range and raises a parameter error. One that happens to be in range, like `1.0`, is valid but means "only at maximum confidence", which looks like a VAD that never fires.
</Warning>

Start from the model default and adjust against representative production audio. `raw_vad_probability()` gives the model's probability before thresholding, which is the practical way to pick a threshold from recordings.

`SpeechHoldDuration` and `MinimumSpeechDuration` keep their previous meaning and units.

## Delay queries

The processor delays audio, the VAD does not, so there is no single output delay any more. The VAD's prediction delay tells you how far behind its input the published decision is, so you can align speech decisions with the audio timeline. Fed from the same block, the two are independent.

| Binding | Audio delay                             | Prediction delay                       |
| ------- | --------------------------------------- | -------------------------------------- |
| C       | `aic_processor_context_get_audio_delay` | `aic_vad_context_get_prediction_delay` |
| Python  | `processor_context.get_audio_delay()`   | `vad_context.get_prediction_delay()`   |
| Rust    | `processor_context.audio_delay()`       | `vad_context.prediction_delay()`       |
| Node.js | `processorContext.getOutputDelay()`     | `vadContext.getOutputDelay()`          |

<Note>
  Node.js has not adopted the rename yet. Its `VadContext.getOutputDelay()` returns the prediction delay, so only the name differs.
</Note>

If you previously compensated for VAD timing using the processor's delay, read the VAD's own delay instead.

## Also worth knowing

* **Model types are enforced.** A processor takes enhancement and bypass models, a VAD takes VAD models, an analyzer takes analysis models. A mismatch fails at creation with `AIC_ERROR_CODE_MODEL_TYPE_UNSUPPORTED`, or `ModelTypeUnsupportedError` in Python, instead of silently doing something else.
* **Reset clears published values immediately.** After a reset, the speech decision is false and the raw probability is `0.0`, so queries no longer return stale values from the previous stream.
* **Sessions can be closed explicitly** with `aic_vad_terminate_session`, or `terminate_session()` in Python, without waiting for the object to be destroyed. Useful where deallocation is delayed. The object cannot process more audio afterwards.

## Related migrations

<CardGroup cols={2}>
  <Card title="Multi-channel to mono" href="/reference/deprecated/multi-channel-to-mono">
    The same release removed multi-channel processing and buffering.
  </Card>

  <Card title="Python SDK 2.5 to 3.0" href="/reference/deprecated/python-2-5-to-3-0">
    Every Python-specific rename in this release.
  </Card>
</CardGroup>
