feat(settings): make DTLN the default ML noise-suppression model #482

Merged
jared merged 1 commits from feat/denoise-dtln-default into lotus 2026-10-30 15:42:48 -04:00
4 changed files with 30 additions and 25 deletions
@@ -2037,7 +2037,7 @@ export function Calls() {
<Text size="L400">Model</Text>
<SettingTile
title="ML model"
description="Choose the machine learning model used for noise removal. Heavier models clean more aggressively at a higher CPU cost. CPU figures are the share of one processor core during a call on a mid-range desktop; Task Manager shows that divided by your number of cores, and slower computers use more."
description="Choose the machine learning model used for noise removal. DTLN is the best all-rounder; DeepFilterNet removes the most noise at a higher CPU cost. None of the models remove other people talking in the background. CPU figures are the share of one processor core during a call on a mid-range desktop; Task Manager shows that divided by your number of cores, and slower computers use more."
after={
<SettingsSelect<DenoiseModelId>
value={callDenoiseModel}
@@ -2066,7 +2066,7 @@ export function Calls() {
[
{ label: 'CPU (one core)', value: selectedDenoiseModel.cpuUsage },
{ label: 'Voice quality', value: selectedDenoiseModel.voiceQuality },
{ label: 'Transients', value: selectedDenoiseModel.transients },
{ label: 'Typing & clicks', value: selectedDenoiseModel.transients },
{ label: 'Download', value: selectedDenoiseModel.binarySize },
] as const
).map((stat) => (
@@ -2113,7 +2113,7 @@ export function Calls() {
</Box>
<Box grow="Yes">
<Text size="T200" priority="300">
Transients
Typing & clicks
</Text>
</Box>
</Box>
+4 -2
View File
@@ -377,9 +377,11 @@ const defaultSettings: Settings = {
cameraOnJoin: false,
// Tier default stays browser-native (known-good; best-perceived in testing so
// far). If a user opts into the ML tier, default to the highest-quality model.
// far). If a user opts into the ML tier, default to DTLN: in our measurements
// it keeps voices the most natural, removes fans and typing well, and costs a
// third of DeepFilterNet's CPU.
callNoiseSuppression: 'browser',
callDenoiseModel: 'deepfilternet',
callDenoiseModel: 'dtln',
// "Series suppression" (stack the browser's native NS before the ML model) is
// off by default — best practice is a single NS stage; it's an opt-in test aid.
callDenoiseNativeNS: false,
+2 -2
View File
@@ -5,10 +5,10 @@ import { DENOISE_MODELS, ML_DENOISE_REQUIREMENTS, isMLDenoiseSupported } from '.
// ── Model catalog (data integrity) ──────────────────────────────────────────
test('DENOISE_MODELS lists the four models ordered best-quality (highest CPU) first', () => {
test('DENOISE_MODELS lists the four models most-recommended first', () => {
assert.deepEqual(
DENOISE_MODELS.map((m) => m.id),
['deepfilternet', 'dtln', 'rnnoise', 'speex'],
['dtln', 'deepfilternet', 'rnnoise', 'speex'],
);
});
+21 -18
View File
@@ -1,8 +1,8 @@
/**
* Detection utilities + model catalog for Lotus ML noise suppression
* (DeepFilterNet 3 / DTLN / RNNoise / Speex). The catalog is ordered by
* quality (and, correspondingly, CPU cost) — highest first — and drives the
* order of the model dropdown in settings.
* (DTLN / DeepFilterNet 3 / RNNoise / Speex). The catalog is ordered by
* recommendation — most recommended first — and drives the order of the model
* dropdown in settings.
*/
import { DenoiseModelId } from '../state/settings';
@@ -10,6 +10,9 @@ import { DenoiseModelId } from '../state/settings';
// CPU = share of ONE core while in a call, measured with each model running
// exactly as Lotus Call runs it (Ryzen 7 PRO 4750GE, Chromium); download = bytes
// actually fetched on first use. Re-measure when a model or its settings change.
// Ratings come from scoring each model against the clean voice (PESQ/STOI) on
// real speech mixed with fan, keyboard, street and babble noise at SNR 5 and
// 15 dB; `transients` is how well it removes typing and clicks.
export type DenoiseModel = {
id: DenoiseModelId;
name: string;
@@ -20,25 +23,25 @@ export type DenoiseModel = {
voiceQuality: 'Moderate' | 'High' | 'Very High';
};
// Ordered best-quality (highest CPU) first — this is the dropdown order.
// Ordered most-recommended first — this is the dropdown order.
export const DENOISE_MODELS: DenoiseModel[] = [
{
id: 'deepfilternet',
name: 'DeepFilterNet 3 (beta)',
id: 'dtln',
name: 'DTLN (recommended)',
description:
'Studio-grade deep-learning model (48 kHz fullband, ONNX). Best quality; highest CPU and a larger one-time download.',
cpuUsage: '~30%',
binarySize: '~18 MB',
'Keeps voices the most natural of the four. Removes fans and typing well, but not traffic or people talking in the background.',
cpuUsage: '~9%',
binarySize: '~20 MB',
transients: 'Excellent',
voiceQuality: 'Very High',
},
{
id: 'dtln',
name: 'DTLN (beta)',
id: 'deepfilternet',
name: 'DeepFilterNet 3 (strongest)',
description:
'Dual-signal deep-learning model (16 kHz). Strong on transient noise; moderate CPU.',
cpuUsage: '~9%',
binarySize: '~20 MB',
'Removes the most noise, including loud typing and traffic, but voices can sound slightly muffled. Uses about three times the CPU of DTLN.',
cpuUsage: '~30%',
binarySize: '~18 MB',
transients: 'Excellent',
voiceQuality: 'High',
},
@@ -46,17 +49,17 @@ export const DENOISE_MODELS: DenoiseModel[] = [
id: 'rnnoise',
name: 'RNNoise',
description:
'Lightweight hybrid model (48 kHz). Very low CPU; good for steady noise like fans, but can sound processed at full strength.',
'Very light. Handles steady noise like fans and traffic; does little for typing and can sound slightly processed.',
cpuUsage: '~3%',
binarySize: '< 1 MB',
transients: 'Good',
transients: 'Poor',
voiceQuality: 'Moderate',
},
{
id: 'speex',
name: 'Speex (Legacy)',
name: 'Speex (legacy)',
description:
'Classic DSP noise suppressor. Minimal CPU, gentlest on voice; weakest suppression.',
'Classic suppressor kept for very slow computers. Barely reduces noise in our tests; prefer any other option.',
cpuUsage: '~2%',
binarySize: '< 1 MB',
transients: 'Poor',