aethersdr / aethersdr/AetherSDR

Smart Squelch via libfvad — voice-aware discriminator layered on adaptive squelch

Open
#3,041 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

aetherclaude-eligible audio claude-active enhancement maintainer-review priority: low
Dominant language
C++
Stars
221
Forks
117
Avg merge
2d 7h
Merged PRs (30d)
299

Description

Background

Phase 2 of the client-side DSP roadmap. Tracks a focused implementation of voice-aware squelch as an opt-in layer above the existing power-based adaptive squelch.

The discriminator gap

Current state (SpectrumWidget.cpp:2683-2689) ships a power-based adaptive squelch: two-pass trimmed-mean noise-floor estimation + EWMA α=0.1 smoothing + configurable margin in dB. It correctly tracks moving band noise. It does not discriminate signal content — anything above the noise floor opens it.

Failure modes from #85 Phase 2 that the current squelch doesn't address:

Trigger Adaptive squelch (now) Voice-aware squelch (proposed)
Steady carrier (CW unkey, AM dead air) ❌ opens (above threshold) ✅ stays closed
TX key-up noise on another channel ❌ opens ✅ stays closed
Data signals (FT8/RTTY mid-tone) ❌ opens (treats as signal) ✅ stays closed
Speech signal ✅ opens ✅ opens

For SSB monitoring this is the kind of squelch behavior operators actually want — open on speech, stay closed on the rest.

Library choice — libfvad

The roadmap listed libfvad (BSD-3) and SpeexDSP (BSD-3) as candidates. Picking libfvad:

  • Purpose-built standalone VAD extracted from WebRTC (Google's continuously-maintained codebase).
  • WebRTC's VAD has been studied extensively and outperforms older Speex VAD on speech-detection accuracy.
  • Small dependency footprint vs. SpeexDSP which bundles AEC + denoise + resampler + jitter buffer (none of which we need).
  • Speex's VAD is from a codec largely superseded by Opus; less actively maintained.
  • Same BSD-3 license as SpeexDSP, no licensing differential.

Repo: https://github.com/dpirch/libfvad

Frame sizes: 10, 20, or 30 ms. Sample rates: 8/16/32/48 kHz. Four aggressiveness modes (0-3 → least to most aggressive about declaring non-speech).

Implementation sketch

  1. Vendor libfvad under third_party/libfvad/ (matches the project's vendor pattern — see third_party/rnnoise/, third_party/r8brain/, etc.). CMake target via subdirectory add_subdirectory(third_party/libfvad).

  2. Wrap in src/core/VoiceActivityDetector.{h,cpp} — thin RAII wrapper around Fvad* handle. Process 10 ms frames at 48 kHz. Returns voice/no-voice + per-decision confidence (libfvad gives binary; we can smooth with hangover timer for hysteresis).

  3. Hook into the existing audio path in AudioEngine after demod, before the squelch gate. The decision feeds an additional gate that's AND'd with the existing power-based squelch.

  4. UX shape: extend the current squelch mode selector to Off / Adaptive / Adaptive + Voice. The voice layer is purely additive — when enabled, gate stays closed unless both the power-based gate AND the VAD agree.

  5. Hangover timer (e.g., 300-500 ms hold-open after last VAD=true frame) — prevents the gate from chopping during inter-word silences in speech.

  6. Aggressiveness setting exposed in Radio Setup → DSP tab (slider 0-3, with descriptive labels: "Permissive / Balanced / Aggressive / Strict").

Open questions for implementation PR

  1. Per-slice VAD instances vs. one global VAD?
    → Per-slice is correct (different slices may have different speech vs. noise content), but the cost is N × Fvad state. libfvad state is tiny so per-slice is fine.

  2. Where exactly does this fit in the DSP chain order? Before/after NR2 + RN2?
    → After NR2/RN2/NR4, just before the final squelch gate. NR cleans the audio; VAD then sees a cleaner discriminator signal.

  3. Behavior on non-SSB modes (CW, AM, FM, FT8/data)?
    → Auto-disable the voice layer when slice mode is anything other than USB/LSB/AM. CW and data should not be gated by a speech VAD.

  4. Confidence smoothing: libfvad returns binary; pure binary will jitter at speech onsets. Hangover timer (option 5 above) is one approach; integrating decisions over a sliding window (e.g., 5-of-7 frames must say voice to open) is another. Start with hangover; evaluate during dogfood.

Acceptance criteria

  • Squelch mode selector adds "Adaptive + Voice" option
  • When enabled on an SSB slice with a steady tuned-up CW carrier, the squelch stays closed (current squelch opens)
  • When enabled on an SSB slice with normal voice traffic, no audible chopping during words or short pauses
  • Aggressiveness control exposed and persisted
  • Auto-disable on CW/data/FM modes — voice layer becomes a no-op when slice mode isn't speech-bearing
  • No new flat-key AppSettings (Principle V — settings nest under a VoiceSquelch JSON block)
  • Cross-platform: libfvad builds on Linux/Windows/macOS via the existing third_party CMake pattern

Out of scope

  • Replacing the existing adaptive squelch — the voice layer is purely additive
  • PA3FWM spectral-tilt VAD — different approach, would be a separate issue if libfvad turns out to be inadequate in dogfood
  • Acoustic echo cancellation (the SpeexDSP angle) — no current need

Parent

Tracks Phase 2 of #85.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading src/gui/SpectrumWidget.cpp around lines 2683-2689 and tracing the AudioEngine audio path and existing third_party CMake patterns. Review the proposed VoiceActivityDetector wrapper, squelch selector, and Radio Setup DSP settings before resolving the listed DSP-order and VAD-state questions. Done means the acceptance criteria pass across supported platforms and non-speech modes.

Written by the indexing model from the issue text.

Assessment

Tech stack
cmake, cpp
Domain
audio-video-rtc, desktop
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.