fix(asr/nemotron): native-Swift mel front-end to fix iPadOS cold-start zero output (#739)#744
fix(asr/nemotron): native-Swift mel front-end to fix iPadOS cold-start zero output (#739)#744Alex-Wengg wants to merge 2 commits into
Conversation
…t zero output (#739) Replace the CoreML preprocessor in both Nemotron streaming managers (English + multilingual) with a native-Swift log-mel front-end (NemotronMelExtractor). The CoreML preprocessor's flexible RangeDim audio input made CoreML build the ANE default_function against a 1-sample lower bound, raising "ios17.slice_by_index: zero shape error" and skipping the ANE `main` entry point -- the warning behind the iPadOS cold-start empty-transcript failure in #739. NemotronMelExtractor reproduces NeMo's AudioToMelSpectrogramPreprocessor exactly (n_fft=512, win=400, hop=160, 128 mels, symmetric Hann, preemph=0.97, normalize: NA -- raw log-mel). Parity vs NeMo PyTorch: max |delta| ~= 9e-3. The multilingual manager uses two extractor instances so the on-actor path and the concurrent triple-stage prefetch task never share the extractor's non-thread-safe FFT scratch buffers; the CoreML preprocessor is dropped from SharedNemotronMultilingualModels (multi-stream shares MLModel handles, each manager builds its own extractors). Add a load-time encoder health probe that throws ASRError.encoderInstantiationFailed if the encoder returns all-zero output, so the iPad failure fails loudly instead of emitting empty transcripts. Benchmarks (full sets, native-Swift mel): - English LibriSpeech test-clean (2620 files): 560ms 2.71% / 1120ms 2.58% / 2240ms 2.64% WER - Multilingual FLEURS (7 langs, full splits): AVG 8.62 / 8.33 / 8.30% at 560/1120/2240ms Docs: add Sources/.../Streaming/Nemotron/benchmark.md (English); refresh Documentation/ASR/NemotronMultilingual.md to current tiers (560/1120/2240).
…md; fix swift-format - Move Sources/.../Streaming/Nemotron/benchmark.md content into Documentation/Benchmarks.md as a "Nemotron Speech Streaming 0.6B (English)" section (mirrors the Parakeet Unified section); delete the Sources file. - Reformat KMeansClustering.swift `??` wrapping to satisfy the CI swift-format (Swift 6.1.3) check — pre-existing main drift surfaced because the format job reformats the whole tree.
Sortformer High-Latency Benchmark ResultsES2004a Performance (30.4s latency config)
Sortformer High-Latency • ES2004a • Runtime: 6m 10s • 2026-06-28T21:19:12.613Z |
Supertonic3 Smoke Test ✅
Runtime: 0m44s 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. |
PocketTTS Smoke Test ✅
Runtime: 0m5s 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: 1m22s • 06/28/2026, 04:58 PM EST RTFx = Real-Time Factor (higher is better) • Processing includes: Model inference, audio preprocessing, state management, and file I/O |
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: 12m0s • 06/28/2026, 05:08 PM 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 |
✅ Nemotron Multilingual Benchmark — FLEURSFLEURS
Logs (tail) |
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 • 134.4s processing • Test runtime: 2m 20s • 06/28/2026, 05:15 PM EST |
✅ Nemotron Multilingual Benchmark — FLEURSFLEURS
Logs (tail) |
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 • 35.1s diarization time • Test runtime: 2m 9s • 06/28/2026, 05:17 PM EST |
Summary
Fixes #739 —
StreamingNemotronAsrManagerproduces zero output on iPadOS cold start (empty transcript, works on macOS).Root cause (verified, and it differs from the issue's stated theory): the
ios17.slice_by_index: zero shape error/ "Skipped adding default_function to entry point: main" warning comes from the CoreMLpreprocessormodel's flexibleRangeDimaudio input, not the encoder. CoreML builds the ANEdefault_functionagainst the range's 1-sample lower bound → mel extraction slices to zero frames → the preprocessor's ANEmainentry point is never built. (Confirmed by loading each model onCPU_AND_NE: only the preprocessor warns; the encoder graph is clean. The issue's proposed encodercache_lenre-trace was a misdiagnosis —cache_lenonly feeds boolean masks, not a data-dependent slice.)Fix: remove the CoreML preprocessor from both Nemotron streaming managers and compute log-mel natively in Swift (
NemotronMelExtractor). No model re-conversion / HF re-upload needed.Changes
NemotronMelExtractor— wraps the sharedAudioMelSpectrogram, reproducing NeMo'sAudioToMelSpectrogramPreprocessorexactly (n_fft=512,win=400,hop=160, 128 mels, symmetric Hann,preemph=0.97,normalize: NA→ raw log-mel, no per-feature normalization — the one difference fromUnifiedMelExtractor).StreamingNemotronAsrManager++Pipeline): swappreprocessorMLModel for the extractor.StreamingNemotronMultilingualAsrManager++Pipeline/+Buffers/+Shared): same swap, with two extractor instances (on-actor + concurrent triple-stage prefetch) so the extractor's non-thread-safe FFT buffers are never shared across the prefetch boundary;preprocessordropped fromSharedNemotronMultilingualModels.ASRError.encoderInstantiationFailedif the encoder returns all-zero output (instead of silently emitting empty transcripts).Verification
[56,0]numbers on all published languages.swift buildpasses; new unit tests for the extractor (shape/determinism/not-normalized) and the new error case. (Note:swift testneeds full Xcode for XCTest — runs in CI; built/validated locally + via CLI benchmarks.)Docs
Sources/FluidAudio/ASR/Parakeet/Streaming/Nemotron/benchmark.md(English).Documentation/ASR/NemotronMultilingual.mdto current tiers (560/1120/2240; dropped obsolete 320ms) with freshly measured numbers.Not covered
ANEProgramProcessRequestDirect status=0x12confirmation requires a physical iPad — not verifiable from a Mac. The mel-parity root-cause case (removing the RangeDim preprocessor) is solid; the probe guarantees the failure can no longer be silent.