From 44885fafc0b66123d454d7112c8df211e8a9a053 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20S=C5=82uszniak?= Date: Thu, 28 May 2026 15:30:57 +0200 Subject: [PATCH] refactor(vad): store duration constants in ms instead of hops kMinSpeechDuration, kMinSilenceDuration and kSpeechPad were stored as hop counts with trailing comments hinting the ms equivalent. The unit implicitly depended on kHopLengthMs, and the comments would rot if kHopLengthMs ever changed. Rename to *Ms and convert to hops at the use site by dividing by kHopLengthMs, matching the kWindowSizeMs / kHopLengthMs convention used elsewhere in the file. Closes #1172 --- .../models/voice_activity_detection/Constants.h | 6 +++--- .../VoiceActivityDetection.cpp | 11 ++++++----- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/packages/react-native-executorch/common/rnexecutorch/models/voice_activity_detection/Constants.h b/packages/react-native-executorch/common/rnexecutorch/models/voice_activity_detection/Constants.h index fccc719eaa..a46947572f 100644 --- a/packages/react-native-executorch/common/rnexecutorch/models/voice_activity_detection/Constants.h +++ b/packages/react-native-executorch/common/rnexecutorch/models/voice_activity_detection/Constants.h @@ -21,9 +21,9 @@ inline constexpr auto kPaddedWindowSize = std::bit_ceil(kWindowSize); // 512 inline constexpr size_t kModelInputMin = 100; inline constexpr size_t kModelInputMax = 1000; inline constexpr auto kSpeechThreshold = 0.6f; -inline constexpr size_t kMinSpeechDuration = 25; // 250 ms -inline constexpr size_t kMinSilenceDuration = 10; // 100 ms -inline constexpr size_t kSpeechPad = 3; // 30 ms +inline constexpr size_t kMinSpeechDurationMs = 250; +inline constexpr size_t kMinSilenceDurationMs = 100; +inline constexpr size_t kSpeechPadMs = 30; inline constexpr size_t kStreamBufferMaxSize = 10 * kSampleRate; // 10s inline constexpr size_t kStreamBufferMinReserve = 1 * kSampleRate; // 1s of audio diff --git a/packages/react-native-executorch/common/rnexecutorch/models/voice_activity_detection/VoiceActivityDetection.cpp b/packages/react-native-executorch/common/rnexecutorch/models/voice_activity_detection/VoiceActivityDetection.cpp index ff1d228f2f..77c3a16cc8 100644 --- a/packages/react-native-executorch/common/rnexecutorch/models/voice_activity_detection/VoiceActivityDetection.cpp +++ b/packages/react-native-executorch/common/rnexecutorch/models/voice_activity_detection/VoiceActivityDetection.cpp @@ -209,7 +209,7 @@ VoiceActivityDetection::postprocess(const std::vector &scores, if (score >= threshold) { if (potentialStart == -1) { potentialStart = i; - } else if (i - potentialStart >= kMinSpeechDuration) { + } else if (i - potentialStart >= kMinSpeechDurationMs / kHopLengthMs) { triggered = true; startSegment = potentialStart; potentialStart = -1; @@ -221,7 +221,7 @@ VoiceActivityDetection::postprocess(const std::vector &scores, if (score < threshold) { if (potentialEnd == -1) { potentialEnd = i; - } else if (i - potentialEnd >= kMinSilenceDuration) { + } else if (i - potentialEnd >= kMinSilenceDurationMs / kHopLengthMs) { triggered = false; endSegment = potentialEnd; speechSegments.emplace_back(startSegment, endSegment); @@ -237,11 +237,12 @@ VoiceActivityDetection::postprocess(const std::vector &scores, speechSegments.emplace_back(startSegment, endSegment); } + constexpr size_t kSpeechPadHops = kSpeechPadMs / kHopLengthMs; for (auto &[start, end] : speechSegments) { - // std::max(start-kSpeedchPad, 0) might be underflow that is why we use ? + // std::max(start-kSpeechPadHops, 0) might be underflow that is why we use ? // operator. - start = (start > kSpeechPad ? start - kSpeechPad : 0) * kHopLength; - end = std::min(end + kSpeechPad, scores.size()) * kHopLength; + start = (start > kSpeechPadHops ? start - kSpeechPadHops : 0) * kHopLength; + end = std::min(end + kSpeechPadHops, scores.size()) * kHopLength; } // Merge tightly placed segments according to the max allowed gap parameter.