Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
c703adb
Add Noise Reduction settings
fkwp Mar 26, 2026
b6cc810
implement noise reduction based on DeepFilterNet3
fkwp Mar 26, 2026
c9be83b
bundle deepfilternet assets within element call bundle
fkwp Mar 26, 2026
239480e
prettier
fkwp Mar 26, 2026
234fafa
linting
fkwp Mar 26, 2026
1c3c480
linting and cleanup
fkwp Mar 26, 2026
8befa8e
prettier
fkwp Mar 26, 2026
300e747
i18next-parser
fkwp Mar 26, 2026
f2bb9e7
tests for DeepFilterNet Noise reduction
fkwp Apr 7, 2026
3dad3de
add tests for noise reduction
fkwp Apr 7, 2026
95f36fe
Mock localStorage for testing in vitest setup (required as of Node.js…
fkwp Apr 7, 2026
d52916d
Add tests for the SettingsModal, including mocking dependencies and v…
fkwp Apr 7, 2026
161c6f7
Add @types/jest and update test mocks for noise suppression components
fkwp Apr 9, 2026
108a5f0
Merge branch 'livekit' into fkwp/feature_deepfilternet
fkwp Apr 9, 2026
f8fa9d1
prettier
fkwp Apr 9, 2026
03237b7
removed unused global-jsdom
fkwp Apr 9, 2026
b2cc178
fix tests
fkwp Apr 9, 2026
286dd29
add stream tiles in grid view
TomOdellSheetMusic Aug 9, 2026
e3d10a3
add fullscreen button for screen shares
TomOdellSheetMusic Aug 9, 2026
a2b5421
fix: screen shares occupying user grid slots
TomOdellSheetMusic Aug 9, 2026
86f53bc
add stop watching screen shares button
TomOdellSheetMusic Aug 9, 2026
d3cd81c
fix: frozen frame overlay uses renamed streamOverlay class
TomOdellSheetMusic Aug 9, 2026
5157157
Stop streaming data when not watching screen shares
TomOdellSheetMusic Aug 10, 2026
1a83608
adds per user voice activity support related to https://github.com/Sa…
TomOdellSheetMusic Aug 12, 2026
307c7ce
Merge pull request #1 from TomOdellSheetMusic/grid-view-screen-shares
TomOdellSheetMusic Aug 15, 2026
93053aa
Merge pull request #2 from TomOdellSheetMusic/per-user-voice-activity
TomOdellSheetMusic Aug 15, 2026
68de02e
lower voice activity confirmation to 0 and heighten threshold
TomOdellSheetMusic Aug 15, 2026
512d77f
add a setting to hide avatar tiles in calls
TomOdellSheetMusic Aug 15, 2026
6dae0f8
Merge pull request #3 from TomOdellSheetMusic/hide-avatars-in-calls
TomOdellSheetMusic Aug 15, 2026
ac62a33
fix key
TomOdellSheetMusic Aug 16, 2026
0838f09
Merge pull request #4 from TomOdellSheetMusic/hide-avatars-in-calls
TomOdellSheetMusic Aug 16, 2026
8a045c8
adjust debounce setting
TomOdellSheetMusic Aug 16, 2026
430de00
extend slider for 200% volume boost
TomOdellSheetMusic Aug 16, 2026
985fa84
easier to understand
TomOdellSheetMusic Aug 16, 2026
2fa55e6
Merge pull request #5 from TomOdellSheetMusic/200-volume-boost
TomOdellSheetMusic Aug 16, 2026
762ebfb
fix tests threshold
TomOdellSheetMusic Aug 17, 2026
f8696bc
add vine boom moai reaction
TomOdellSheetMusic Aug 17, 2026
425a4c3
Merge pull request #6 from TomOdellSheetMusic/add-moai-reaction
TomOdellSheetMusic Aug 17, 2026
4fe9e8c
fix boosting using default audio device instead of selected one
TomOdellSheetMusic Aug 17, 2026
d398b6a
Merge pull request #7 from TomOdellSheetMusic/200-volume-boost
TomOdellSheetMusic Aug 17, 2026
165f2ec
add support for mute indicator https://github.com/TomOdellSheetMusic/…
TomOdellSheetMusic Aug 17, 2026
9a331a0
Merge pull request #8 from TomOdellSheetMusic/add-support-for-mute-in…
TomOdellSheetMusic Aug 17, 2026
86200f0
fix formatting
TomOdellSheetMusic Aug 17, 2026
f2c8f33
Merge branch 'fkwp/feature_deepfilternet' into livekit
TomOdellSheetMusic Aug 19, 2026
59090a8
Revert "Merge branch 'fkwp/feature_deepfilternet' into livekit"
TomOdellSheetMusic Aug 19, 2026
ee5f6c9
add deepfilternet noise supression
TomOdellSheetMusic Aug 19, 2026
783fc6e
fix formatting
TomOdellSheetMusic Aug 19, 2026
5b205a4
add logger to dependency array
TomOdellSheetMusic Aug 19, 2026
f66ce56
fix threshold type
TomOdellSheetMusic Aug 19, 2026
a9d1342
add setup assets to package
TomOdellSheetMusic Aug 19, 2026
dd1af7d
fix setup and guard against truncated downloads
TomOdellSheetMusic Aug 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ dist-ssr
*.bkp
.idea/
public/config.json
# DeepFilterNet3 WASM/model assets are downloaded by `pnpm setup:assets`
public/assets/deepfilternet3
backend/synapse_tmp/*
backend/synapse_tmp_othersite/*
/coverage
Expand Down
4 changes: 3 additions & 1 deletion locales/de/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,8 @@
"muted_for_me": "Für mich stumm geschaltet",
"screen_share_volume": "Lautstärke der Bildschirmfreigabe",
"volume": "Lautstärke",
"waiting_for_media": "Warten auf Medien..."
"waiting_for_media": "Warten auf Medien...",
"stop_watching": "Nicht mehr zuschauen",
"watch_stream": "Stream zuschauen"
}
}
12 changes: 11 additions & 1 deletion locales/en/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,12 @@
"audio_processing_description": "Changes apply on next call join.",
"audio_processing_header": "Audio processing",
"audio_tab": {
"deepfilternet_description": "Uses a deep-learning model to remove background noise such as keyboard, traffic, and other non-stationary sounds.",
"deepfilternet_header": "AI noise suppression",
"deepfilternet_label": "Enable AI noise suppression (DeepFilterNet)",
"deepfilternet_level_description": "Higher levels remove more noise but can make your voice sound more processed.",
"deepfilternet_level_label": "Noise reduction level",
"deepfilternet_not_supported": "(AI noise suppression is not supported by this browser.)",
"effect_volume_description": "Adjust the volume at which reactions and hand raised effects play.",
"effect_volume_label": "Sound effect volume",
"mic_cutoff_description": "Sound quieter than the cutoff volume will not be sent to other participants.",
Expand Down Expand Up @@ -255,6 +261,8 @@
"preferences_tab": {
"developer_mode_label": "Developer mode",
"developer_mode_label_description": "Enable developer mode and show developer settings tab.",
"hide_avatars_when_camera_off_description": "Hide avatar tiles of participants whose camera is off.",
"hide_avatars_when_camera_off_label": "Hide avatar tiles when camera is off",
"introduction": "Here you can configure extra options for an improved experience.",
"reactions_play_sound_description": "Play a sound effect when anyone sends a reaction into a call.",
"reactions_play_sound_label": "Play reaction sounds",
Expand Down Expand Up @@ -290,7 +298,9 @@
"mute_for_me": "Mute for me",
"muted_for_me": "Muted for me",
"screen_share_volume": "Screen share volume",
"stop_watching": "Stop watching",
"volume": "Volume",
"waiting_for_media": "Waiting for media..."
"waiting_for_media": "Waiting for media...",
"watch_stream": "Watch stream"
}
}
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"dev:full": "vite",
"dev:embedded": "vite --config vite-embedded.config.js",
"build": "pnpm build:full",
"build:full": "vite build",
"build:full": "pnpm setup:assets && vite build",
"build:full:production": "pnpm build:full",
"build:full:development": "pnpm build:full --mode development",
"build:embedded": "pnpm build:full --config vite-embedded.config.ts",
Expand All @@ -16,6 +16,7 @@
"build:sdk:development": "pnpm build:sdk --mode development",
"build:sdk": "pnpm build:full --config vite-sdk.config.js",
"build:sdk:production": "pnpm build:sdk",
"setup:assets": "node scripts/setup-noise-suppression-assets.js",
"serve": "vite preview",
"format": "oxfmt",
"format:check": "oxfmt --check; rc=$?; [[ $rc -ne 0 ]] && printf '\\033[46;30m INFO \\033[0m To fix, run: pnpm format\\n' >&2; exit $rc",
Expand Down Expand Up @@ -124,6 +125,7 @@
"vite-plugin-html": "^3.2.2",
"vite-plugin-node-polyfills": "^0.28.0",
"vite-plugin-node-stdlib-browser": "^0.2.1",
"vite-plugin-static-copy": "^4.1.1",
"vite-plugin-svgr": "^4.0.0",
"vite-plugin-wasm": "^3.6.0",
"vitest": "^4.1.5",
Expand All @@ -132,6 +134,7 @@
"packageManager": "pnpm@11.6.0+sha512.9a36518224080c6fe5165afdcfe79bfa118c29be703f3f462b1e32efe1e98e47e8750b148e08286250aad4113cc7993ca413c4e2cd447752708c2ee5751bc95f",
"dependencies": {
"@jitsi/rnnoise-wasm": "0.2.1",
"@phosphor-icons/react": "^2.1.10"
"@phosphor-icons/react": "^2.1.10",
"deepfilternet3-noise-filter": "^1.3.0"
}
}
36 changes: 36 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

172 changes: 172 additions & 0 deletions scripts/setup-noise-suppression-assets.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
#!/usr/bin/env node

/**
* Setup script to download DeepFilterNet3 assets for local bundling.
* This downloads the WASM binary and AI model from Mezon's CDN
* and places them in public/assets/deepfilternet3/ for bundling.
*
* Usage:
* node scripts/setup-noise-suppression-assets.js
*
* Environment variables:
* DEEPFILTERNET3_CDN_URL: Override the default CDN URL (optional)
*/

import fs from "fs";
import path from "path";
import https from "https";
import { fileURLToPath } from "url";

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const projectRoot = path.join(__dirname, "..");

const CDN_URL =
process.env.DEEPFILTERNET3_CDN_URL ||
"https://cdn.mezon.ai/AI/models/datas/noise_suppression/deepfilternet3";

const ASSETS_DIR = path.join(projectRoot, "public", "assets", "deepfilternet3");
// The deepfilternet3-noise-filter package (>= 1.3.0) requests assets from the
// `v3/` subdirectory. Keep these paths in sync with the installed package's
// AssetLoader (see node_modules/deepfilternet3-noise-filter/dist/index.esm.js).
const VERSION_DIR = path.join(ASSETS_DIR, "v3");
const PKG_DIR = path.join(VERSION_DIR, "pkg");
const MODELS_DIR = path.join(VERSION_DIR, "models");

const FILES_TO_DOWNLOAD = [
{
url: `${CDN_URL}/v3/pkg/df_bg.wasm`,
path: path.join(PKG_DIR, "df_bg.wasm"),
description: "WASM binary",
},
{
url: `${CDN_URL}/v3/pkg/df_bg.wasm.d.ts`,
path: path.join(PKG_DIR, "df_bg.wasm.d.ts"),
description: "WASM TypeScript definitions",
optional: true,
},
{
url: `${CDN_URL}/v3/models/DeepFilterNet3_onnx.tar.gz`,
path: path.join(MODELS_DIR, "DeepFilterNet3_onnx.tar.gz"),
description: "AI Model (ONNX format)",
},
];

function ensureDir(dir) {
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
console.log(`✓ Created directory: ${dir}`);
}
}

function downloadFile(fileUrl, filePath, isOptional = false) {
return new Promise((resolve, reject) => {
const fileName = path.basename(filePath);

// Skip if already exists
if (fs.existsSync(filePath)) {
console.log(`✓ Already exists: ${fileName}`);
resolve();
return;
}

console.log(`⏳ Downloading ${fileName}...`);

https
.get(fileUrl, (response) => {
// Handle redirects
if (
response.statusCode === 301 ||
response.statusCode === 302 ||
response.statusCode === 307
) {
const redirectUrl = response.headers.location;
console.log(` Redirected to: ${redirectUrl}`);
downloadFile(redirectUrl, filePath, isOptional)
.then(resolve)
.catch(reject);
return;
}

if (response.statusCode !== 200) {
const error = new Error(
`Download failed: HTTP ${response.statusCode} for ${fileName}`,
);
if (isOptional) {
console.warn(`⚠ Optional file skipped: ${fileName}`);
resolve();
} else {
reject(error);
}
return;
}

const expectedSize = Number(response.headers["content-length"]);
const fileStream = fs.createWriteStream(filePath);

response.pipe(fileStream);

fileStream.on("finish", () => {
fileStream.close();
const actualSize = fs.statSync(filePath).size;
// Guard against truncated downloads: if the server told us the
// expected size and we received fewer bytes, the file is corrupt.
if (Number.isFinite(expectedSize) && expectedSize > 0 && actualSize !== expectedSize) {
fs.unlinkSync(filePath);
reject(
new Error(
`Download incomplete for ${fileName}: expected ${expectedSize} bytes, got ${actualSize}`,
),
);
return;
}
const sizeMB = (actualSize / 1024 / 1024).toFixed(2);
console.log(`✓ Downloaded: ${fileName} (${sizeMB} MB)`);
resolve();
});

fileStream.on("error", (err) => {
fs.unlink(filePath, () => {}); // Clean up partial file
reject(err);
});
})
.on("error", (err) => {
if (isOptional) {
console.warn(`⚠ Optional file skipped: ${fileName} (${err.message})`);
resolve();
} else {
reject(err);
}
});
});
}

async function main() {
try {
console.log("\n🚀 Setting up DeepFilterNet3 assets for bundling...\n");
console.log(`📦 CDN URL: ${CDN_URL}`);
console.log(`📁 Asset directory: ${ASSETS_DIR}\n`);

// Ensure directories exist
ensureDir(ASSETS_DIR);
ensureDir(VERSION_DIR);
ensureDir(PKG_DIR);
ensureDir(MODELS_DIR);

// Download files
for (const file of FILES_TO_DOWNLOAD) {
await downloadFile(file.url, file.path, file.optional);
}

console.log("\n✅ Asset setup complete!");
console.log(
"\nAssets are ready for bundling. Next build will include them.\n",
);
process.exit(0);
} catch (error) {
console.error("\n❌ Asset setup failed:", error.message);
process.exit(1);
}
}

main();
Loading