{"version":3,"sources":["../../src/inference/vad.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2026 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Voice Activity Detection backed by `@livekit/local-inference`.\n *\n * Provides the same streaming VAD shape as `plugins/silero` but routes\n * inference through the bundled native model so a default instance can be\n * auto-provisioned by `AgentSession` without an explicit plugin import.\n */\nimport { AudioFrame, AudioResampler, AudioResamplerQuality } from '@livekit/rtc-node';\nimport { log } from '../log.js';\nimport { VAD as BaseVAD, VADStream as BaseVADStream, VADEventType } from '../vad.js';\nimport { _getLocalInferenceModule } from './_warmup.js';\n\nconst SLOW_INFERENCE_THRESHOLD_MS = 200;\nconst MODEL_SAMPLE_RATE = 16000;\n\nexport type VADModels = 'silero';\n\nexport interface VADOptions {\n  /** Minimum speech duration (ms) before reporting START_OF_SPEECH. */\n  minSpeechDuration: number;\n  /** Trailing silence (ms) before reporting END_OF_SPEECH. */\n  minSilenceDuration: number;\n  /** Pre-roll (ms) included in the speech buffer ahead of START_OF_SPEECH. */\n  prefixPaddingDuration: number;\n  /** Maximum (ms) of buffered speech per utterance. */\n  maxBufferedSpeech: number;\n  /** Sigmoid probability threshold for activation. */\n  activationThreshold: number;\n  /** Sigmoid probability threshold for deactivation (defaults to\n   * `max(activationThreshold - 0.15, 0.01)`). */\n  deactivationThreshold: number;\n}\n\nconst defaultVADOptions: VADOptions = {\n  minSpeechDuration: 50,\n  // 250ms (= MIN_SILENCE_DURATION_MS + 50) so the default satisfies the audio\n  // end-of-turn detector's silence-window requirement out of the box.\n  minSilenceDuration: 250,\n  prefixPaddingDuration: 500,\n  maxBufferedSpeech: 60_000,\n  activationThreshold: 0.5,\n  deactivationThreshold: 0.35,\n};\n\nexport class VAD extends BaseVAD {\n  protected _opts: VADOptions;\n  protected _model: VADModels;\n  label = 'inference.VAD';\n  // Live streams, tracked weakly so they don't outlive their consumers. JS\n  // `WeakSet` isn't iterable, so we hold `WeakRef`s in a `Set` and prune dead\n  // entries on iteration.\n  #streams = new Set<WeakRef<InferenceVADStream>>();\n\n  constructor(opts: Partial<VADOptions> & { model?: VADModels } = {}) {\n    super({ updateInterval: 32 });\n    const model: VADModels = opts.model ?? 'silero';\n    if (model !== 'silero') {\n      throw new Error(`Unknown VAD model: ${String(model)}. Supported: 'silero'.`);\n    }\n    if (opts.deactivationThreshold !== undefined && opts.deactivationThreshold <= 0) {\n      throw new Error('deactivationThreshold must be greater than 0');\n    }\n    this._model = model;\n    const activation = opts.activationThreshold ?? defaultVADOptions.activationThreshold;\n    this._opts = {\n      ...defaultVADOptions,\n      ...opts,\n      activationThreshold: activation,\n      deactivationThreshold: opts.deactivationThreshold ?? Math.max(activation - 0.15, 0.01),\n    };\n  }\n\n  get model(): string {\n    return this._model;\n  }\n\n  get provider(): string {\n    return 'livekit-local-inference';\n  }\n\n  override get minSilenceDuration(): number {\n    return this._opts.minSilenceDuration;\n  }\n\n  /** Update one or more knobs at runtime, propagating to live streams. */\n  updateOptions(opts: Partial<VADOptions>): void {\n    this._opts = { ...this._opts, ...opts };\n    for (const ref of this.#streams) {\n      const stream = ref.deref();\n      if (stream === undefined) {\n        this.#streams.delete(ref);\n        continue;\n      }\n      stream.updateOptions(opts);\n    }\n  }\n\n  stream(): BaseVADStream {\n    // Each stream owns its own options snapshot so its `updateOptions` can read\n    // the prior `maxBufferedSpeech` before this VAD's copy is mutated.\n    const stream = new InferenceVADStream(this, { ...this._opts });\n    this.#streams.add(new WeakRef(stream));\n    return stream;\n  }\n}\n\nclass InferenceVADStream extends BaseVADStream {\n  private _opts: VADOptions;\n  private _logger = log();\n  private _nativeVad:\n    | ReturnType<NonNullable<ReturnType<typeof _getLocalInferenceModule>>['createVad']>\n    | undefined;\n  private _windowSamples: number;\n  private _inputSampleRate = 0;\n  private _resampler: AudioResampler | undefined;\n  private _speechBuffer: Int16Array | null = null;\n  private _speechBufferMaxReached = false;\n  private _prefixPaddingSamples = 0;\n  private _pumpTask: Promise<void>;\n\n  constructor(parent: VAD, opts: VADOptions) {\n    super(parent);\n    this._opts = opts;\n    const mod = _getLocalInferenceModule();\n    if (mod === undefined) {\n      this._logger.warn(\n        'inference.VAD created without @livekit/local-inference; stream will be a no-op',\n      );\n      this._windowSamples = 512;\n    } else {\n      this._nativeVad = mod.createVad();\n      this._windowSamples = mod.VAD_WINDOW_SAMPLES;\n    }\n    this._pumpTask = this._pump().catch((err) => {\n      this._logger.error(\n        { err: err instanceof Error ? err.message : String(err) },\n        'VAD pump failed',\n      );\n    });\n  }\n\n  /**\n   * Apply updated options to this live stream. Once the input sample rate is\n   * known, recomputes the prefix-padding pre-roll and resizes the speech\n   * buffer in place, preserving any audio already accumulated.\n   */\n  updateOptions(opts: Partial<VADOptions>): void {\n    const oldMaxBufferedSpeech = this._opts.maxBufferedSpeech;\n    this._opts = { ...this._opts, ...opts };\n\n    if (this._inputSampleRate && this._speechBuffer !== null) {\n      this._prefixPaddingSamples = Math.trunc(\n        (this._opts.prefixPaddingDuration * this._inputSampleRate) / 1000,\n      );\n      const bufferSize =\n        Math.trunc((this._opts.maxBufferedSpeech * this._inputSampleRate) / 1000) +\n        this._prefixPaddingSamples;\n      const resized = new Int16Array(bufferSize);\n      resized.set(this._speechBuffer.subarray(0, Math.min(this._speechBuffer.length, bufferSize)));\n      this._speechBuffer = resized;\n\n      if (this._opts.maxBufferedSpeech > oldMaxBufferedSpeech) {\n        this._speechBufferMaxReached = false;\n      }\n    }\n  }\n\n  private async _pump(): Promise<void> {\n    let pubSpeaking = false;\n    let pubSpeechDurationMs = 0;\n    let pubSilenceDurationMs = 0;\n    let pubCurrentSample = 0;\n    let pubTimestampMs = 0;\n    let speechThresholdDurationMs = 0;\n    let silenceThresholdDurationMs = 0;\n    let inputFrames: AudioFrame[] = [];\n    let inferenceFrames: AudioFrame[] = [];\n    let inputCopyRemainingFrac = 0;\n    let extraInferenceTime = 0;\n    // Write cursor into `_speechBuffer`. The buffer holds:\n    //   [ ...prefix-padding (sliding pre-roll) ..., ...active speech... ]\n    // and is reset on END_OF_SPEECH (and on silence while idle) so the next\n    // turn starts from a fresh pre-roll window.\n    let speechBufferIndex = 0;\n\n    const resetWriteCursor = () => {\n      if (this._speechBuffer === null) return;\n      if (speechBufferIndex <= this._prefixPaddingSamples) return;\n      // Slide the most-recent `prefixPaddingSamples` samples to the head\n      // of the buffer so the next utterance has continuous pre-roll\n      // context (the audio that immediately preceded START_OF_SPEECH).\n      const paddingData = this._speechBuffer.subarray(\n        speechBufferIndex - this._prefixPaddingSamples,\n        speechBufferIndex,\n      );\n      this._speechBuffer.set(paddingData, 0);\n      speechBufferIndex = this._prefixPaddingSamples;\n      this._speechBufferMaxReached = false;\n    };\n\n    const resetState = () => {\n      this._nativeVad?.reset();\n\n      speechBufferIndex = 0;\n      this._speechBufferMaxReached = false;\n      this._speechBuffer?.fill(0);\n\n      pubSpeaking = false;\n      pubSpeechDurationMs = 0;\n      pubSilenceDurationMs = 0;\n      pubCurrentSample = 0;\n      pubTimestampMs = 0;\n      speechThresholdDurationMs = 0;\n      silenceThresholdDurationMs = 0;\n\n      inputFrames = [];\n      inferenceFrames = [];\n      inputCopyRemainingFrac = 0;\n      extraInferenceTime = 0;\n\n      this._resampler?.close?.();\n      if (this._inputSampleRate && this._inputSampleRate !== MODEL_SAMPLE_RATE) {\n        this._resampler = new AudioResampler(\n          this._inputSampleRate,\n          MODEL_SAMPLE_RATE,\n          1,\n          AudioResamplerQuality.QUICK,\n        );\n      } else {\n        this._resampler = undefined;\n      }\n    };\n\n    const copySpeechBuffer = (): AudioFrame => {\n      if (this._speechBuffer === null) {\n        return new AudioFrame(new Int16Array(0), this._inputSampleRate, 1, 0);\n      }\n      return new AudioFrame(\n        this._speechBuffer.subarray(0, speechBufferIndex),\n        this._inputSampleRate,\n        1,\n        speechBufferIndex,\n      );\n    };\n\n    while (!this.closed) {\n      const { done, value: frame } = await this.inputReader.read();\n      if (done) break;\n      if (typeof frame === 'symbol') {\n        resetState();\n        continue;\n      }\n\n      if (!this._inputSampleRate) {\n        this._inputSampleRate = frame.sampleRate;\n        this._prefixPaddingSamples = Math.trunc(\n          (this._opts.prefixPaddingDuration * this._inputSampleRate) / 1000,\n        );\n        const bufferSize =\n          Math.trunc((this._opts.maxBufferedSpeech * this._inputSampleRate) / 1000) +\n          this._prefixPaddingSamples;\n        this._speechBuffer = new Int16Array(bufferSize);\n        if (this._inputSampleRate !== MODEL_SAMPLE_RATE) {\n          this._resampler = new AudioResampler(\n            this._inputSampleRate,\n            MODEL_SAMPLE_RATE,\n            1,\n            AudioResamplerQuality.QUICK,\n          );\n        }\n      } else if (frame.sampleRate !== this._inputSampleRate) {\n        this._logger.error('a frame with a different sample rate was already pushed');\n        continue;\n      }\n\n      if (this._speechBuffer === null) continue;\n\n      inputFrames.push(frame);\n      if (this._resampler !== undefined) {\n        inferenceFrames.push(...this._resampler.push(frame));\n      } else {\n        inferenceFrames.push(frame);\n      }\n\n      while (!this.closed) {\n        const startTime = performance.now();\n        const availableInferenceSamples = inferenceFrames.reduce(\n          (acc, f) => acc + f.samplesPerChannel,\n          0,\n        );\n        if (availableInferenceSamples < this._windowSamples) break;\n\n        const inputFrame = mergeFrames(inputFrames);\n        const inferenceFrame = mergeFrames(inferenceFrames);\n        const inferenceWindow = inferenceFrame.data.subarray(0, this._windowSamples);\n\n        let p = 0.0;\n        if (this._nativeVad !== undefined) {\n          p = await this._nativeVad.predict(inferenceWindow);\n        }\n\n        const windowDurationMs = (this._windowSamples / MODEL_SAMPLE_RATE) * 1000;\n        pubCurrentSample += this._windowSamples;\n        pubTimestampMs += windowDurationMs;\n        const resamplingRatio = this._inputSampleRate / MODEL_SAMPLE_RATE;\n        const toCopy = this._windowSamples * resamplingRatio + inputCopyRemainingFrac;\n        const toCopyInt = Math.trunc(toCopy);\n        inputCopyRemainingFrac = toCopy - toCopyInt;\n\n        // Append the input-rate samples we just consumed into the\n        // speech buffer so START_OF_SPEECH / END_OF_SPEECH events can\n        // hand downstream consumers (STT, transcription) the prefix-\n        // padded audio they need.\n        const availableSpace = this._speechBuffer.length - speechBufferIndex;\n        const toCopyBuffer = Math.min(toCopyInt, availableSpace);\n        if (toCopyBuffer > 0) {\n          this._speechBuffer.set(inputFrame.data.subarray(0, toCopyBuffer), speechBufferIndex);\n          speechBufferIndex += toCopyBuffer;\n        } else if (!this._speechBufferMaxReached) {\n          this._speechBufferMaxReached = true;\n          this._logger.warn(\n            'maxBufferedSpeech reached, ignoring further data for the current speech input',\n          );\n        }\n\n        const inferenceDuration = performance.now() - startTime;\n        extraInferenceTime = Math.max(0, extraInferenceTime + inferenceDuration - windowDurationMs);\n        // Guard on the per-window inference duration (not the accumulated\n        // slack); the accumulated value is still surfaced as the delay.\n        if (inferenceDuration > SLOW_INFERENCE_THRESHOLD_MS) {\n          this._logger.warn(\n            { extraInferenceTimeMs: extraInferenceTime },\n            'VAD inference is slower than realtime',\n          );\n        }\n\n        if (pubSpeaking) pubSpeechDurationMs += windowDurationMs;\n        else pubSilenceDurationMs += windowDurationMs;\n\n        this.sendVADEvent({\n          type: VADEventType.INFERENCE_DONE,\n          samplesIndex: pubCurrentSample,\n          timestamp: pubTimestampMs,\n          silenceDuration: pubSilenceDurationMs,\n          speechDuration: pubSpeechDurationMs,\n          probability: p,\n          inferenceDuration,\n          frames: [\n            new AudioFrame(\n              inputFrame.data.subarray(0, toCopyInt),\n              this._inputSampleRate,\n              1,\n              toCopyInt,\n            ),\n          ],\n          speaking: pubSpeaking,\n          rawAccumulatedSilence: silenceThresholdDurationMs,\n          rawAccumulatedSpeech: speechThresholdDurationMs,\n        });\n\n        if (\n          p >= this._opts.activationThreshold ||\n          (pubSpeaking && p > this._opts.deactivationThreshold)\n        ) {\n          speechThresholdDurationMs += windowDurationMs;\n          silenceThresholdDurationMs = 0;\n          if (!pubSpeaking && speechThresholdDurationMs >= this._opts.minSpeechDuration) {\n            pubSpeaking = true;\n            pubSilenceDurationMs = 0;\n            pubSpeechDurationMs = speechThresholdDurationMs;\n            this.sendVADEvent({\n              type: VADEventType.START_OF_SPEECH,\n              samplesIndex: pubCurrentSample,\n              timestamp: pubTimestampMs,\n              silenceDuration: pubSilenceDurationMs,\n              speechDuration: pubSpeechDurationMs,\n              probability: p,\n              inferenceDuration,\n              frames: [copySpeechBuffer()],\n              speaking: true,\n              rawAccumulatedSilence: 0,\n              rawAccumulatedSpeech: 0,\n            });\n          }\n        } else {\n          silenceThresholdDurationMs += windowDurationMs;\n          speechThresholdDurationMs = 0;\n          // Keep a sliding pre-roll window while we're not in active\n          // speech — without this the buffer would fill with idle\n          // silence and the next START_OF_SPEECH would lose its\n          // prefix-padding context.\n          if (!pubSpeaking) resetWriteCursor();\n          if (pubSpeaking && silenceThresholdDurationMs >= this._opts.minSilenceDuration) {\n            pubSpeaking = false;\n            pubSilenceDurationMs = silenceThresholdDurationMs;\n            this.sendVADEvent({\n              type: VADEventType.END_OF_SPEECH,\n              samplesIndex: pubCurrentSample,\n              timestamp: pubTimestampMs,\n              silenceDuration: pubSilenceDurationMs,\n              speechDuration: Math.max(0, pubSpeechDurationMs - silenceThresholdDurationMs),\n              probability: p,\n              inferenceDuration,\n              frames: [copySpeechBuffer()],\n              speaking: false,\n              rawAccumulatedSilence: 0,\n              rawAccumulatedSpeech: 0,\n            });\n            pubSpeechDurationMs = 0;\n            resetWriteCursor();\n          }\n        }\n\n        inputFrames = [];\n        inferenceFrames = [];\n        if (inputFrame.data.length > toCopyInt) {\n          const data = inputFrame.data.subarray(toCopyInt);\n          inputFrames.push(new AudioFrame(data, this._inputSampleRate, 1, Math.trunc(data.length)));\n        }\n        if (inferenceFrame.data.length > this._windowSamples) {\n          const data = inferenceFrame.data.subarray(this._windowSamples);\n          inferenceFrames.push(new AudioFrame(data, MODEL_SAMPLE_RATE, 1, Math.trunc(data.length)));\n        }\n      }\n    }\n    this._resampler?.close?.();\n  }\n}\n\n/** Minimal frame-merging helper. The silero plugin uses `mergeFrames` from\n * the agents package — for the inference VAD we keep a local copy to avoid\n * an import cycle through `index.ts`. */\nfunction mergeFrames(frames: AudioFrame[]): AudioFrame {\n  if (frames.length === 1) return frames[0]!;\n  const sampleRate = frames[0]!.sampleRate;\n  const channels = frames[0]!.channels;\n  let total = 0;\n  for (const f of frames) total += f.samplesPerChannel;\n  const buf = new Int16Array(total * channels);\n  let offset = 0;\n  for (const f of frames) {\n    buf.set(f.data, offset);\n    offset += f.samplesPerChannel * channels;\n  }\n  return new AudioFrame(buf, sampleRate, channels, total);\n}\n"],"mappings":"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","names":["BaseVAD","BaseVADStream","_a","_b"]}