Skip to content

Add best-guess fallback for low-note detection#30

Merged
user1303836 merged 1 commit intomainfrom
feature/best-guess-fallback
Feb 16, 2026
Merged

Add best-guess fallback for low-note detection#30
user1303836 merged 1 commit intomainfrom
feature/best-guess-fallback

Conversation

@user1303836
Copy link
Owner

Summary

  • When the CMNDF threshold search fails (no dip below 0.15), the YIN detector now falls back to the global CMNDF minimum instead of returning silence. This addresses harmonically complex signals (e.g., distorted guitar on low notes) that never cross the strict threshold but still have a clear pitch estimate in the CMNDF.
  • Widens the analysis window from sampleRate/70 to sampleRate/60 for better low-frequency margin (~5ms additional latency).
  • Adds 3 new tests: harmonically complex fallback detection, silence still returns zero, and threshold path preference for clean signals.

Test plan

  • All 34 tests pass (441,095 assertions)
  • Full plugin builds (VST3)
  • CI passes on macOS and Windows
  • Manual test: play low distorted guitar notes and verify ring modulator engages

When the CMNDF threshold search fails (no dip below 0.15), fall back
to the global CMNDF minimum instead of returning silence. This ensures
harmonically complex signals like distorted guitar still trigger the
ring modulator with a rough pitch estimate.

Also widen analysis window from sampleRate/70 to sampleRate/60 for
better low-frequency coverage (~5ms additional latency).
Copy link
Owner Author

@user1303836 user1303836 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Clean, minimal change that addresses the low-note detection gap identified post-merge.

What this PR does:

  1. Window widening (/70.0 -> /60.0): Extends the analysis window to cover fundamentals down to ~60 Hz (was ~70 Hz). Adds ~5ms latency at 44.1kHz — acceptable tradeoff for better low-register coverage.

  2. Best-guess CMNDF fallback: When the absolute threshold (0.15) finds no candidate, the global CMNDF minimum is used instead. This prevents silence on harmonically complex signals where strong overtones prevent the CMNDF from dipping below threshold.

Observations (non-blocking):

  • The fallback may lock onto subharmonics for complex timbres, but this is explicitly acceptable for a ring modulator (musically related frequencies). The confidence value from a non-threshold hit will naturally be lower, so the PitchSmoother sensitivity gate provides a second line of defense.
  • The silence test correctly verifies that tauEstimate remains 0 when all CMNDF values equal 1.0 (flat signal), since the fallback loop only updates on strict < comparison.
  • The test tolerance for the harmonically complex E2 test is intentionally wide (40-200 Hz) — appropriate for a fallback path where subharmonic ambiguity is expected.

Test coverage is solid: fallback path exercised, silence regression guarded, and threshold-first preference verified for clean signals.

if (cmndf[tau] < minVal)
{
minVal = cmndf[tau];
tauEstimate = tau;
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fallback scans the entire CMNDF for the global minimum, which for harmonically complex signals may land on a subharmonic (e.g. octave below) rather than the fundamental. This is fine for the ring modulator use case since subharmonic detection still produces a musically related modulation frequency, and the confidence value from the CMNDF minimum will naturally be lower, letting the PitchSmoother's sensitivity gate attenuate the effect.

Worth noting: if accuracy requirements tighten in the future, a refinement would be to only accept the fallback when minVal is below some secondary threshold (e.g. 0.5), rejecting truly ambiguous frames. But that's not needed now.

feedHarmonicComplex(yin, 44100.0, 82.4f, 44100, 0.15f);

auto result = yin.getResult();
REQUIRE(result.frequency > 0.0f);
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 40-200 Hz tolerance band is very wide (over 2 octaves) for an 82.4 Hz fundamental. This is intentional given the fallback's subharmonic ambiguity, but it means this test mainly verifies "something was detected in a plausible range" rather than "the fundamental was correctly identified." That's a reasonable stance for a fallback path — just calling it out for clarity.

@user1303836 user1303836 merged commit 77dcf51 into main Feb 16, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant