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>
98 lines
5.3 KiB
Markdown
98 lines
5.3 KiB
Markdown
---
|
||
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,0–4,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.
|