--- type: issue id: "0054" status: open created: 2026-08-15 milestone: M4 priority: medium area: element related: - "docs/issues/0029-ui-harmonisieren-gleiche-farben-und-formen.md" --- # Tastaturgeräusche in Calls: quelloffene KI-Geräuschunterdrückung im Client prüfen ## Problem Der WebRTC-Standardfilter (`noiseSuppression`) ist auf **stationäres** Rauschen ausgelegt (Lüfter, Netzbrummen). **Transiente** Geräusche — Tastaturanschläge — rutschen durch: sie haben eine sehr schnelle Anstiegszeit und ein unvorhersehbares Spektrum, sodass die laufende Rauschprofil-Schätzung sie nicht als Störung erkennt. Betroffen sind ausdrücklich **auch leise Chiclet-Tastaturen** (MacBook), nicht nur mechanische. Grundlage ist eine externe Architekturspezifikation (Gemini Deep Research, 2026-07-29): client-seitige KI-Filterung via WebAssembly, eingehängt über das LiveKit-`TrackProcessor`- Interface, mit Intensitätsregler in den Audio-Einstellungen. ## Bewertung der Spezifikation ### Trägt - **Diagnose stimmt.** Stationär vs. transient ist die richtige Erklärung dafür, warum die vorhandenen Toggles nicht helfen. - **Client-seitig ist der richtige Ort — und kein Widerspruch zur bisherigen Linie.** `threadnet-call:docs/axion1337-fork.md` §5 verwirft ML-Rauschunterdrückung **server-seitig** (LiveKit Agents), weil es dort keinen unterstützten Weg gibt, bereinigtes Audio an andere Teilnehmer weiterzureichen. Genau dieser Einwand greift client-seitig **nicht**. Die Spezifikation setzt die alte Entscheidung fort, statt ihr zu widersprechen. - **AudioWorklet statt ScriptProcessor**, eigener hochpriorer Audio-Thread: richtig und nicht verhandelbar. - **Der 128-vs-480-Sample-Mismatch** (Web Audio liefert 128er-Blöcke, die Modelle brauchen 480) und der nötige Ringpuffer sind sauber benannt — daran scheitern naive Umsetzungen. - **Chromium-AudioWorklet-Leak** und die Gegenmaßnahme (eigener `AudioContext`, hart schließen) sind real und richtig adressiert. - **Wasm SIMD** ist tatsächlich Voraussetzung, nicht Optimierung. ### Trägt nicht 1. ⚠️ **Konkreter Fehler: die Latenzkompensation fehlt.** Der finale Dry/Wet-Code mischt das **unverzögerte** Original mit dem ~40 ms verzögerten KI-Signal. Das erzeugt Kammfilter und Phasenauslöschung — hörbar als blechernes Echo, also genau das Gegenteil des Ziels. Ein früherer Entwurf im selben Gespräch hatte dafür einen `DelayNode`; in der Endfassung ist er verschwunden. Das ist kein Detail. 2. **Der Dry/Wet-Ansatz ist konzeptionell fragwürdig.** Die Begründung („neuronale Netze kennen nur An/Aus") ist für DeepFilterNet **falsch** — es hat einen nativen Parameter zur **Begrenzung der Dämpfung**. „Weniger aggressiv" heißt richtig: das Modell weniger dämpfen lassen. Dry/Wet mischt stattdessen ungefiltertes Signal zurück — **inklusive der Tastaturanschläge**, die man loswerden wollte. 3. **Die Zahlen taugen nicht als Entscheidungsgrundlage.** PESQ „RNNoise ~3.88" gegen „DFN3 3.5–4.34": die untere DFN3-Grenze läge unter RNNoise. Werte aus verschiedenen Testsets, nicht vergleichbar. 4. **Bundle-Größe geschätzt, nicht gemessen** („15–25 MB"). Für eine Browser-App, die beim Call-Start lädt, ist das der kritische Wert überhaupt — muss gemessen werden. 5. **Die genannten NPM-Pakete sind Experimente** (`deepfilternet3-worker-test`, `…-noise-filter-trong`). Die Spec empfiehlt selbst, aus dem Rust-Quellcode zu bauen — dann gehört ehrlich dazu: wir übernehmen eine **Rust/wasm-Toolchain in die Build-Kette**. 6. **Mobil fehlt.** Element Call läuft auf Telefonen; DFN3 auf einem Mittelklasse-Android ist offen und wird mit „läuft auf modernen Prozessoren" abgetan. 7. **`getUserMedia`-Constraints fehlen im Code.** Wer selbst filtert, muss die Browser-eigene `noiseSuppression` **abschalten** (sonst arbeiten zwei Filter gegeneinander) und `echoCancellation` erhalten. Im Gespräch erwähnt, im finalen Code verschwunden. 8. **Erzwungene 48 kHz** ohne Fallback — Geräte mit 44,1 kHz brauchen einen Pfad. 9. ⚠️ **Der größte Posten fehlt ganz: Fork-Wartung.** Das wäre eine erhebliche Eigenentwicklung in `threadnet-call`, die bei **jedem** Upstream-Rebase mitgeschleppt und in `axion1337-fork.md` gepflegt werden muss. Die Spec erwähnt das mit keinem Wort. 10. **Lizenz nur behauptet.** DeepFilterNet-Code ist MIT/Apache-2.0 — die **Modellgewichte** sind separat zu prüfen, bevor „null Lizenzkosten" behauptet wird. ## Vorgeschlagenes Vorgehen (vor jeder Zeile Produktivcode) 1. **Messen statt annehmen.** Reproduzierbarer A/B-Test mit mechanischer *und* Chiclet-Tastatur gegen die heutigen Toggles — belegt das Problem und liefert die Referenz für „besser". 2. **Wegwerf-Prototyp außerhalb des Forks.** DFN3 als Wasm auf einer eigenen Testseite: **Bundle-Größe, CPU und Latenz auf echten Geräten messen** (inkl. Telefon). Erst diese Zahlen entscheiden über Modell und Machbarkeit. 3. **Regler über den Modellparameter**, nicht über Dry/Wet. Falls doch Dry/Wet: Delay-Node zur Latenzkompensation ist Pflicht. 4. **Dann erst** Integration als `TrackProcessor` und Eintrag in `axion1337-fork.md`. ## Offen (Entscheidung sorb) Ob der Aufwand lohnt. Die Plattform hat derzeit einen sehr kleinen Nutzerkreis; dem steht eine dauerhaft zu pflegende Fork-Anpassung mit Rust/wasm-Build gegenüber. Die Alternative — Tastaturgeräusche als hinnehmbar erklären und stattdessen Push-to-Talk bzw. bewusstes Stummschalten dokumentieren — ist billiger und sollte bewusst verworfen werden, nicht übersehen.