feat(enhancement): LocalVQE AEC with safe streaming and benchmark validation (beta) - #930
Alex-Wengg wants to merge 14 commits into
Conversation
Adds LocalVqeManager / LocalVqeStream, a Core ML port of LocalVQE (localai-org/LocalVQE, Apache-2.0): joint acoustic echo cancellation, noise suppression and dereverberation for 16 kHz speech, requested in #49 for hands-free calls where the mic picks up the loudspeaker. The models are fp32 streaming exports with explicit recurrent state (33 in_*/out_* tensors). LocalVqeStream discovers the state tensors from the model description, passes each call's outputs back as the next call's inputs, buffers arbitrary input sizes into whole calls, drops the leading hop (the t<0 region) and flushes one hop of zeros at the end so whole-clip output is sample-aligned and length-preserving. Two chunk exports per checkpoint: 256 ms (files, 36x RTFx for v1.3 on M5 Pro CPU) and 16 ms (live capture, 1.2 ms per call). Verified against the upstream PyTorch reference (74 dB, 16-bit WAV limited) and the upstream GGML CLI (80 dB) on the upstream double-talk demo clip; 100/256/1000/4096-sample streaming buffers and whole-clip processing agree to 1e-5. CPU is the default compute unit: the graph is too small for ANE dispatch to pay off and fp16 was rejected for parity (102 -> 5 dB). Upstream's GGUF-only v1.4-AEC / GTCRN line depends on a C++ adaptive-filter front-end and is not ported. CLI: fluidaudiocli enhance mic.wav --reference speaker.wav --output clean.wav (--streaming reports per-call latency; --model-dir loads local bundles). Model tests skip in CI and when the bundle is absent locally (FLUIDAUDIO_LOCALVQE_MODEL_DIR overrides the cache location). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015S4u7dmbe4skFjUNMpodud
PocketTTS Smoke Test ✅
Runtime: 0m8s Note: PocketTTS uses CoreML MLState (macOS 15) KV cache + Mimi streaming state. CI VM lacks physical GPU — audio quality and performance may differ from Apple Silicon. |
Parakeet EOU Benchmark Results ✅Status: Benchmark passed Performance Metrics
Streaming Metrics
Test runtime: 1m41s • 09/20/2026, 01:36 AM EST RTFx = Real-Time Factor (higher is better) • Processing includes: Model inference, audio preprocessing, state management, and file I/O |
Offline VBx Pipeline ResultsSpeaker Diarization Performance (VBx Batch Mode)Optimal clustering with Hungarian algorithm for maximum accuracy
Offline VBx Pipeline Timing BreakdownTime spent in each stage of batch diarization
Speaker Diarization Research ComparisonOffline VBx achieves competitive accuracy with batch processing
Pipeline Details:
🎯 Offline VBx Test • AMI Corpus ES2004a • 1049.0s meeting audio • 155.5s processing • Test runtime: 2m 39s • 09/20/2026, 01:46 AM EST |
Sortformer High-Latency Benchmark ResultsES2004a Performance (30.4s latency config)
Sortformer High-Latency • ES2004a • Runtime: 2m 24s • 2026-09-20T05:40:14.453Z |
… for LocalVQE Scores an echo canceller with the in-repo Parakeet TDT v3 ASR on the Microsoft AEC-Challenge synthetic set (mic + loopback + clean near-end triples, 200-example subset at FluidInference/aec-challenge-synthetic-mini, auto-downloaded). The clean-near-end transcript is the reference, the loopback transcript gives the far-end words; reports recall (1 - (D+S)/N), WER, and far-end word leakage per condition and per SER bucket, plus enhancement RTFx. --no-reference adds a silent-far-end condition to show what the model does without the loopback. Results (200 files, SER -10..+10 dB, M5 Pro, 256 ms chunk, CPU): unprocessed recall 39.5% / leakage 33.8%; v1.3 87.5% / 1.8% (36x RTFx); v1.2 86.3% / 1.9% (62x); v1.3 with silent reference 45.3% / 24.4%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015S4u7dmbe4skFjUNMpodud
VAD Benchmark ResultsPerformance Comparison
Dataset Details
✅: Average F1-Score above 70% |
ASR Benchmark Results ✅Status: All benchmarks passed Parakeet v3 (multilingual)
Parakeet v2 (English-optimized)
Streaming (v3)
Streaming (v2)
Streaming tests use 5 files with 0.5s chunks to simulate real-time audio streaming 25 files per dataset • Test runtime: 8m11s • 09/20/2026, 01:47 AM EST RTFx = Real-Time Factor (higher is better) • Calculated as: Total audio duration ÷ Total processing time Expected RTFx Performance on Physical M1 Hardware:• M1 Mac: ~28x (clean), ~25x (other) Testing methodology follows HuggingFace Open ASR Leaderboard |
Supertonic3 Smoke Test ✅
Runtime: 0m18s Note: CI VMs lack a physical Neural Engine; the ANE-bucketed VectorEstimator falls back to CPU here. This validates download + variant resolution + synthesis, not ANE residency/perf. |
Speaker Diarization Benchmark ResultsSpeaker Diarization PerformanceEvaluating "who spoke when" detection accuracy
Diarization Pipeline Timing BreakdownTime spent in each stage of speaker diarization
Speaker Diarization Research ComparisonResearch baselines typically achieve 18-30% DER on standard datasets
Note: RTFx shown above is from GitHub Actions runner. On Apple Silicon with ANE:
🎯 Speaker Diarization Test • AMI Corpus ES2004a • 1049.0s meeting audio • 57.5s diarization time • Test runtime: 3m 0s • 09/20/2026, 01:37 AM EST |
…GML fidelity result AECMOS / ERLE / DNSMOS over the 800-clip ICASSP 2022 blind set for the Swift port (v1.3, v1.2, unprocessed), and the aligned same-clip control against the upstream GGML engine (identical per-scenario means, per-clip echo delta mean -0.0001). Notes the two places the upstream README table cannot be reproduced from the published weights. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015S4u7dmbe4skFjUNMpodud
…-help, doc wording - LocalVqeStream: use MLModel's async prediction API. Apple documents the synchronous API as not thread-safe (WWDC23 10049); streams created from one manager share its MLModel and could previously call it concurrently. enhance()/flush() become async. - enhance / enhance-benchmark: print usage via stdout so --help shows in release builds (the logger is silent there). - Docs: numerical equivalence rather than bit-match; single-talk agreement with the 38.2 vs 40.6 dB exception; double-talk mismatch stated as an unresolved upstream protocol discrepancy; note concurrent streams. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015S4u7dmbe4skFjUNMpodud
…ct enhance-benchmark recall editDistance(hyp, ref) labelled a reference word missing from the hypothesis as an insertion and an extra hypothesis word as a deletion. WER was unaffected (it sums all three) but the breakdown was wrong everywhere it is read: enhance-benchmark's recall (N - D - S)/N awarded 100% to an empty transcript, and canary-transcribe / tts-asr-verify printed S/D/I swapped. Corrected recall on the AEC-Challenge synthetic subset (167 scored, the 33 examples with an empty clean-near-end transcript are now excluded and reported): unprocessed 44.1% -> v1.3 77.6% / v1.2 73.0%; previously published as 39.5% -> 87.5% / 86.3%. Leakage 34.0% -> 1.1%. Docs relabel the subset as exploratory (first 200 of a training shard, machine-transcript references). Adds WERCalculatorTests. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015S4u7dmbe4skFjUNMpodud
… 'not from published weights' claim The HF model-card protocol (legacy AECMOS model, first 20 s; rated-segment DNSMOS; technical-report gated ERLE) reproduces its unprocessed baseline exactly and every doubletalk / near-end cell within 0.02. Reference table stays on the challenge protocol; v1.2 far-end echo rows remain unexplained and are reported as such. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015S4u7dmbe4skFjUNMpodud
v1.2 far-end differs on echo, gated ERLE and OVRL, not only echo MOS; v1.3 numbers stated with their actual bounds. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015S4u7dmbe4skFjUNMpodud
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015S4u7dmbe4skFjUNMpodud
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015S4u7dmbe4skFjUNMpodud
The single macOS job hit the 60-minute limit on de4c0c0 and showed no progress because stdout was block-buffered through `tee`. The same 200-example run takes 6.6 min on an M5 Pro; the 3-core runner is simply far slower. - enhance-benchmark: `--shard i/n` scores contiguous shard i of the numeric-fileid selection (after --max-files) and records it in the report; progress lines flush stdout. - verify_localvqe_benchmark.py: accepts every shard report of one run, checks index/count coverage and shared configuration, merges them in shard order (`--merged out.json`), then runs the existing full-report verification. Lone shard reports are rejected. - Workflow: 5 parallel shard jobs (60 min each) + ubuntu verify job that merges and applies the 200-file coverage and improvement gates; scorer/dataset regressions run in their own job. - Tests: shard slicing (Swift); merge ordering, mismatch/missing-shard errors, and a real-report split/merge round trip (Python).
Why is this change needed?
Adds LocalVQE acoustic echo cancellation, noise suppression and dereverberation for 16 kHz speech, addressing the LocalVQE request in #49. Includes the Core ML consumer, CLI, benchmark corrections and safe streaming lifecycle. Beta: live capture/playback integration on target devices remains unvalidated.
Companion conversion and evaluation PR: mobius #96. Models: FluidInference/localvqe-coreml.
Implementation
LocalVqeManagerfor files/whole clips andLocalVqeStreamfor arbitrary buffer sizes; v1.3/v1.2, fp32, 16 ms or 256 ms model chunks, CPU default.enhanceCLI plus the exploratoryenhance-benchmark; fixes the shared WER scorer's swapped insertion/deletion labels and adds regression tests.Benchmark findings and fixes
The main evaluation covers 800 real AEC-Challenge recordings. Direct reference comparisons support port fidelity, but the complete Hugging Face table is not reproduced. The legacy protocol reproduces the unprocessed baseline exactly; v1.2 far-end echo remains unresolved. The model card declares plain whole-signal ERLE, while its reported values resemble a separately reconstructed gated metric, so the two are now reported separately instead of treating gated ERLE as the confirmed upstream protocol.
The companion PR now includes a standard-library verifier that checks all 13 saved result-file hashes, exact 800-clip coverage, scenario membership, metric availability and every stored aggregate without rerunning inference. The published model-card targets and source revision are pinned alongside it.
The separate training-shard ASR study remains exploratory: 167/200 examples scored after excluding 33 empty reference transcripts. Corrected v1.3 recall is 77.6% versus 44.1% unprocessed; leakage is 1.1% versus 34.0%. Its dataset revision, archive and metadata hashes are now pinned and verified. Malformed metadata, duplicate IDs and missing audio fail loudly; JSON output records exact selected/excluded IDs, machine transcripts and raw metric numerators/denominators.
Benchmark data and investigation retain all tested configurations, the historical 300-recording follow-up, evidence verifier and unresolved differences.
Automated LocalVQE benchmark
The new
LocalVQE Benchmarkworkflow runs both variants over the pinned 200-example study on relevant PR changes, split into 5 contiguous shards (enhance-benchmark --shard i/5) on parallel macOS runners because a single runner exceeded the 60-minute job limit; a final job merges the shard reports in order and verifies them as one 200-example report. Protocollocalvqe-asr-v2fixes Parakeet v3 int8 scoring to CPU, records model/audio SHA256 fingerprints, per-file S/D/I counts and timing, and rejects incomplete or invalid runs. An independent Python verifier recomputes every score and aggregate and checks all selected waveforms against the archive-derived 600-file manifest. Both variants must improve recall and reduce leakage over the same-run unprocessed baseline.The workflow uploads the JSON report, logs and environment and publishes a job summary. The first cloud run is here (still running as of 2026-09-20 03:19 UTC). All 14 other PR checks passed, including the full macOS test suite.
The full local 200-example real-model run passed on revision
de4c0c03: 167 examples scored, 33 empty-reference transcripts excluded. The independent verifier passed coverage, audio/model fingerprints, recomputed scores and improvement gates; all 12 verifier tests passed on the full report, including deliberate corruption of report coverage, counts and fingerprints.These use protocol
localvqe-asr-v2, Parakeet v3 int8, CPU-only and 256ms enhancement exports. They remain exploratory training-shard results with machine transcripts, not a reproduction of the upstream 800-recording table. Historical exploratory figures above are not a frozen baseline for the new explicit CPU protocol.The macOS suite's cold-cache Sortformer download race is addressed by running speaker-enrollment tests serially before the remaining parallel tests; assertions are retained.
Validation
FLUIDAUDIO_LOCALVQE_MODEL_DIRenables real-model tests in CI and missing supplied bundles fail.Streaming validation report records coverage and timing limits. Target-device reference timing, route changes, audio callbacks and sustained live-call performance still need application-level validation.