diff --git a/src/app/features/settings/general/General.tsx b/src/app/features/settings/general/General.tsx index 624fdf17f..ca2242204 100644 --- a/src/app/features/settings/general/General.tsx +++ b/src/app/features/settings/general/General.tsx @@ -2037,7 +2037,7 @@ export function Calls() { Model 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() { - Transients + Typing & clicks diff --git a/src/app/state/settings.ts b/src/app/state/settings.ts index 6f6631140..b512d5725 100644 --- a/src/app/state/settings.ts +++ b/src/app/state/settings.ts @@ -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, diff --git a/src/app/utils/lotusDenoiseUtils.test.ts b/src/app/utils/lotusDenoiseUtils.test.ts index 201ab2947..472d7e72f 100644 --- a/src/app/utils/lotusDenoiseUtils.test.ts +++ b/src/app/utils/lotusDenoiseUtils.test.ts @@ -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'], ); }); diff --git a/src/app/utils/lotusDenoiseUtils.ts b/src/app/utils/lotusDenoiseUtils.ts index 0f3d5327e..3f1d9a430 100644 --- a/src/app/utils/lotusDenoiseUtils.ts +++ b/src/app/utils/lotusDenoiseUtils.ts @@ -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',