Files
management/docs/issues/0054-ki-geraeuschunterdrueckung-element-call.md
T
Thore CimbalandClaude Opus 4.8 a328dacc50 docs: supersede ADR-0006, open #0054 on client-side AI noise suppression
ADR-0006 (Docusaurus as the shared reading surface) is superseded by ADR-0014,
which ADR-0014 had only recorded for ADR-0007. The schema has no 'deprecated', so
superseded with a pointer is the fitting lifecycle state, same shape as ADR-0007.

#0054 evaluates an external architecture spec for filtering keyboard noise with a
WebAssembly model in the client. It holds up on diagnosis, placement and the
awkward parts (128-vs-480 sample buffering, the Chromium worklet leak, SIMD), and
it does not contradict the fork's earlier rejection of ML denoising — that one was
about the server side, for a reason that does not apply here.

It does not hold up on: a missing delay node, which would make the dry/wet mix comb
filter audibly; the premise behind dry/wet at all, since DeepFilterNet can limit
attenuation natively and mixing raw signal back in returns the very keystrokes we
want gone; PESQ figures compared across different test sets; unmeasured bundle size;
throwaway npm packages; and no mention of the standing cost of carrying this through
every upstream rebase.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-15 12:00:00 +00:00

5.5 KiB
Raw Blame History

type, id, status, created, milestone, priority, area, related
type id status created milestone priority area related
issue 0054 open 2026-08-15 M4 medium element
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.54.34": die untere DFN3-Grenze läge unter RNNoise. Werte aus verschiedenen Testsets, nicht vergleichbar.
  4. Bundle-Größe geschätzt, nicht gemessen („1525 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.