Files
management/docs/adr/0018-ki-geraeuschunterdrueckung-clientseitig-opt-in.md
T
Thore CimbalandClaude Opus 4.8 914eb59575 docs(adr): ADR-0018 — client-side noise suppression, opt-in and self-hosted
sorb's decision after the prototype: integrate DeepFilterNet3 as a LiveKit track
processor, off by default, assets fetched only when the user enables it, checkbox
plus slider, 35 percent default.

Opt-in is what makes the 23.3 MB affordable — only those who benefit pay for it.
Three of the source specification's assumptions did not survive measurement and are
recorded as rejected alternatives: the dry/wet mixer (the model limits attenuation
natively, and mixing raw signal back would return the keystrokes), the Rust/wasm
build (a maintained package makes it unnecessary), and loading assets from the
vendor CDN (every participant's IP to a third party at call start).

Mobile stays untested by choice; since the filter is opt-in it simply stays off on
weak devices, so that is a follow-up rather than a blocker.

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

98 lines
5.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
type: adr
id: "0018"
status: accepted
date: 2026-08-15
supersedes: null
superseded_by: null
related:
- "docs/issues/0054-ki-geraeuschunterdrueckung-element-call.md"
---
# 0018 — KI-Geräuschunterdrückung in threadnet-call: client-seitig, opt-in, selbst ausgeliefert
**Status:** akzeptiert · **Datum:** 2026-08-15 · **Entscheider:** sorb
## Kontext
Der WebRTC-Standardfilter (`noiseSuppression`) schätzt ein laufendes Rauschprofil und ist
damit auf **stationäre** Störungen ausgelegt. **Transiente** Geräusche — Tastaturanschläge —
erkennt er nicht als Störung; sie werden mitübertragen. Betroffen sind ausdrücklich auch
leise Chiclet-Tastaturen, nicht nur mechanische. Push-to-Talk als Ausweg wurde verworfen
(„inakzeptabel", sorb).
`threadnet-call:docs/axion1337-fork.md` §5 hatte ML-Rauschunterdrückung bereits einmal
verworfen — allerdings **server-seitig** (LiveKit Agents), weil es dort keinen unterstützten
Weg gibt, bereinigtes Audio an andere Teilnehmer weiterzureichen. Client-seitig greift dieser
Einwand nicht; diese ADR widerspricht der damaligen Entscheidung also nicht, sondern setzt sie
fort.
Eine externe Architekturspezifikation empfahl DeepFilterNet3 via WebAssembly. Statt sie zu
übernehmen, wurde ein **Wegwerf-Prototyp** gebaut und gemessen (#0054). Das hat drei ihrer
Kernannahmen korrigiert und die Entscheidungsgrundlage von Schätzung auf Messung gestellt.
## Entscheidung
**DeepFilterNet3 wird client-seitig integriert — opt-in, nachgeladen, selbst ausgeliefert.**
1. **Modell:** DeepFilterNet3 über `deepfilternet3-noise-filter` (Apache-2.0 ODER MIT) als
LiveKit-`TrackProcessor`. Nicht RNNoise: es ist zwar zehnmal kleiner, aber bei Transienten
deutlich schwächer — genau dem Fall, um den es hier geht.
2. **Opt-in mit Nachladen.** Standard **AUS**. Die Assets (23,3 MB) werden **erst beim
Einschalten** geladen. Damit zahlt nur, wer profitiert — das Größenargument entfällt für
alle anderen.
3. **Bedienung:** Checkbox zum Aktivieren **plus Regler** für die Stärke.
4. **Standardwert 35 %**, nicht 100 %. Gemessen reicht gut ein Drittel für „Tastatur weg und
Stimme natürlich"; weniger Dämpfung heißt weniger Artefaktrisiko.
5. **Regelung über den Modellparameter** (`setSuppressionLevel` / `atten_lim`), **nicht** über
einen Dry/Wet-Mix.
6. **Assets werden selbst ausgeliefert.** Der Default-Pfad des Pakets lädt sie von
`cdn.mezon.ai`; `assetConfig.cdnUrl` wird auf das eigene Deployment gezeigt.
7. **`getUserMedia`:** `noiseSuppression: false` (sonst arbeiten Browser-Filter und Modell
gegeneinander), `echoCancellation: true`.
## Gemessene Grundlage (Prototyp 2026-08-15)
| | |
|---|---|
| Wirkung | Tastatur weg, Stimme natürlich — **bei 35 %** |
| Download je Client | **23,27 MB** (15,66 MB wasm + 7,61 MB Modell) |
| Vergleich RNNoise | 2,04,6 MB, also ~ein Zehntel |
| Paketpflege | 20 Versionen, 4 Maintainer, ~20k Downloads/Monat |
| CDN-freier Betrieb | im Prototyp nachgewiesen |
## Konsequenzen
- **Kein Rust/wasm-Build.** Die Spezifikation hielt ihn für nötig; das fertige Paket macht ihn
entbehrlich. Die Fork-Anpassung bleibt dadurch klein: Abhängigkeit, `TrackProcessor`,
Bedienelement, Assets in der Auslieferung.
- **Kein Dry/Wet-Mixer, kein Delay-Node.** Da über den Modellparameter geregelt wird, gibt es
keinen zweiten Signalpfad — und damit weder Phasenauslöschung noch Latenzkompensation.
- **23,3 MB gehören ins Deployment.** Sie müssen mit ausgeliefert werden (Image/Ingress) und
wachsen bei einem Modell-Update mit.
- **Dauerhafte Fork-Anpassung.** Sie muss jeden Upstream-Rebase überleben und gehört in
`axion1337-fork.md` samt Portier-Hinweis.
- ⚠️ **Mobil ist ungeprüft.** Der Telefontest wurde bewusst ausgesetzt. Weil der Filter opt-in
ist, ist das vertretbar: Auf schwachen Geräten bleibt er schlicht aus. Zeigt sich später,
dass er dort unbrauchbar ist, ist das ein Folge-Issue, kein Widerruf dieser Entscheidung.
- **Neue Lieferkette.** Ein npm-Paket eines Drittanbieters liefert Code, der WebAssembly lädt.
Die **Assets** kontrollieren wir (selbst gehostet); der 23-KB-Wrapper bleibt Fremdcode.
Vendoring wäre möglich und wurde bewusst nicht gewählt — dann müssten wir Updates selbst
nachziehen.
## Verworfene Alternativen
- **Push-to-Talk / bewusstes Stummschalten dokumentieren:** billigste Lösung, aber ein
Rückschritt gegenüber dem, was Konferenzsysteme heute leisten. Ausdrücklich verworfen.
- **RNNoise** statt DFN3: ein Zehntel der Größe, aber konzeptionell schwach bei genau den
transienten Geräuschen, die das Problem sind.
- **Server-seitige ML-Filterung:** bereits in `axion1337-fork.md` §5 verworfen — LiveKit
bietet keinen unterstützten Weg, bereinigtes Audio an andere Teilnehmer weiterzugeben.
- **Standardmäßig AN:** beste Wirkung ohne Zutun, aber jeder Client lädt 23 MB — auch auf
Telefonen, die ungetestet sind.
- **Dry/Wet-Mix als Regler** (Vorschlag der Spezifikation): mischt ungefiltertes Signal
zurück, **inklusive der Tastaturanschläge**, und braucht eine Latenzkompensation, die im
Spec-Entwurf fehlte. Der native Modellparameter leistet dasselbe ohne diese Nachteile.
- **Assets vom Anbieter-CDN laden:** würde bei jedem Call-Start die IP jedes Teilnehmers an
einen Dritten melden und die Verfügbarkeit an fremde Infrastruktur hängen.