fix(calls): wire DTLN ML denoise correctly via @workadventure JS API
CI / Build & Quality Checks (push) Successful in 10m25s
CI / Trigger Desktop Build (push) Successful in 6s

The prior DTLN attempt (89a2321d) broke the build (missing dep, wrong
`cinny/` asset paths) and typecheck (`'dtln'` not in DenoiseModelId), and was
wired against an API the package doesn't expose. @workadventure/noise-
suppression is not a flat AudioWorklet — it's a self-contained ES module whose
processor name is `workadventure-noise-suppression` and which resolves its own
LiteRT WASM + TFLite models via import.meta.url. Driving it by hand-rolled
addModule + processorOptions cannot work.

- Re-add @workadventure/noise-suppression@0.0.4 (package.json + lockfile).
- vite: copy the package's whole dist/ tree intact to
  denoise/workadventure/ (preserving assets/ + vendor/litert) so import.meta
  resolution works at runtime; fail the build if the entry module is missing.
- shim: for the DTLN model, dynamic-import denoise/workadventure/audio-worklet
  .js and use createNoiseSuppressionAudioWorklet(ctx, { bypassUntilReady })
  to build the node; RNNoise/Speex keep their direct flat-worklet path. Async
  init errors are logged + reported and fall back to the raw mic.
- Restore 'dtln' in DenoiseModelId (+ settings coercion), the model chart, and
  the settings dropdown, labelled "(beta)".

DTLN builds and is fully self-hosted, but its in-call audio is UNVERIFIED in
this environment — needs a real-call test. DeepFilterNet stays excluded (CDN
asset loading, incompatible with self-hosting / Tauri CSP).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-16 17:11:45 -04:00
parent 89a2321dd4
commit 86272b6b08
7 changed files with 148 additions and 104 deletions
@@ -1441,7 +1441,7 @@ function Calls() {
options={[
{ value: 'rnnoise', label: 'RNNoise' },
{ value: 'speex', label: 'Speex (Legacy)' },
{ value: 'dtln', label: 'DTLN (Balanced)' },
{ value: 'dtln', label: 'DTLN (beta)' },
]}
/>
}
+7 -5
View File
@@ -14,10 +14,10 @@ export type MessageSpacing = '0' | '100' | '200' | '300' | '400' | '500';
// - 'browser' : WebRTC built-in suppression (Element Call noiseSuppression param)
// - 'ml' : client-side RNNoise ML suppression (Lotus denoise shim)
export type NoiseSuppressionMode = 'off' | 'browser' | 'ml';
// Only self-hostable, build-bundled models are exposed. DTLN/DeepFilterNet were
// evaluated but rely on remote-style asset loading incompatible with our
// self-hosted/Tauri-CSP strategy (see LOTUS_DENOISE_ENGINEERING_REVIEW.md).
export type DenoiseModelId = 'rnnoise' | 'speex';
// Self-hostable, build-bundled ML models. DeepFilterNet remains excluded — it
// loads its runtime/models from external CDNs, which breaks the self-hosted /
// Tauri-CSP strategy (see LOTUS_DENOISE_ENGINEERING_REVIEW.md).
export type DenoiseModelId = 'rnnoise' | 'speex' | 'dtln';
export type ChatBackground =
| 'none'
| 'blueprint'
@@ -263,7 +263,9 @@ export const getSettings = (): Settings => {
// Coerce any retired/unknown persisted model (e.g. 'dtln', 'deepfilternet'
// from earlier beta builds) back to the default working model.
callDenoiseModel:
saved.callDenoiseModel === 'rnnoise' || saved.callDenoiseModel === 'speex'
saved.callDenoiseModel === 'rnnoise' ||
saved.callDenoiseModel === 'speex' ||
saved.callDenoiseModel === 'dtln'
? saved.callDenoiseModel
: defaultSettings.callDenoiseModel,
composerToolbarButtons: {
+9
View File
@@ -31,6 +31,15 @@ export const DENOISE_MODELS: DenoiseModel[] = [
transients: 'Poor',
voiceQuality: 'Moderate',
},
{
id: 'dtln',
name: 'DTLN (beta)',
description: 'Deep-learning model (TFLite). Stronger on transient noise; higher CPU.',
cpuUsage: '10-20%',
binarySize: '~4 MB',
transients: 'Excellent',
voiceQuality: 'High',
},
];
export const isMLDenoiseSupported = (): boolean => {