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>
95 lines
5.5 KiB
Markdown
95 lines
5.5 KiB
Markdown
---
|
||
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.
|