added packages back in, replaced var w/ let
CI / Build & Quality Checks (pull_request) Canceled after 2m46s
CI / Trigger Desktop Build (pull_request) Canceled after 0s

This commit is contained in:
2026-08-02 20:24:50 -04:00
parent 35501b4e19
commit 782e056687
4 changed files with 219 additions and 45 deletions
+42 -40
View File
@@ -19,10 +19,11 @@
*
* Any failure falls back to the unprocessed mic so calls never break.
*/
// TODO: MAKE THIS A TS FILE
(function () {
'use strict';
var params;
let params;
try {
params = new URLSearchParams(window.location.search);
if (params.get('lotusDenoise') !== 'ml') return;
@@ -33,31 +34,31 @@
// Derive the parent origin for postMessage targetOrigin from the parentUrl
// widget param (a full URL) so denoise-status messages aren't broadcast with
// '*'. Fall back to this frame's own origin if parentUrl is missing/malformed.
var targetOrigin;
let targetOrigin;
try {
var parentUrl = params.get('parentUrl');
let parentUrl = params.get('parentUrl');
targetOrigin = parentUrl ? new URL(parentUrl).origin : window.location.origin;
} catch (e) {
targetOrigin = window.location.origin;
}
var md = navigator.mediaDevices;
let md = navigator.mediaDevices;
if (!md || typeof md.getUserMedia !== 'function') return;
if (typeof AudioWorkletNode === 'undefined' || typeof AudioContext === 'undefined') return;
var ASSET_BASE = './denoise/';
let ASSET_BASE = './denoise/';
var MODEL = params.get('lotusModel') || 'rnnoise';
let MODEL = params.get('lotusModel') || 'rnnoise';
// DTLN (@workadventure) targets 16 kHz and does not resample internally, so
// its whole graph runs in a 16 kHz context; RNNoise/Speex (sapphi) and
// DeepFilterNet 3 are 48 kHz fullband. The processed MediaStreamTrack is
// published to LiveKit either way (WebRTC/Opus resamples as needed).
var SAMPLE_RATE = MODEL === 'dtln' ? 16000 : 48000;
var USE_NATIVE_NS = params.get('lotusNativeNS') === 'true';
var USE_GATE = params.get('lotusGate') === 'true';
var GATE_THRESHOLD = parseFloat(params.get('lotusGateThreshold') || '-45');
let SAMPLE_RATE = MODEL === 'dtln' ? 16000 : 48000;
let USE_NATIVE_NS = params.get('lotusNativeNS') === 'true';
let USE_GATE = params.get('lotusGate') === 'true';
let GATE_THRESHOLD = parseFloat(params.get('lotusGateThreshold') || '-45');
var PROCESSORS = {
let PROCESSORS = {
rnnoise: {
name: '@sapphi-red/web-noise-suppressor/rnnoise',
script: 'rnnoiseWorklet.js',
@@ -91,9 +92,9 @@
},
};
var origGetUserMedia = md.getUserMedia.bind(md);
var wasmPromises = {};
var ctxPromise = null;
let origGetUserMedia = md.getUserMedia.bind(md);
let wasmPromises = {};
let ctxPromise = null;
function checkSimd() {
try {
@@ -112,12 +113,12 @@
function loadWasm(modelId) {
if (wasmPromises[modelId]) return wasmPromises[modelId];
var p = PROCESSORS[modelId];
let p = PROCESSORS[modelId];
if (!p || !p.wasm) return Promise.resolve(null);
wasmPromises[modelId] = (modelId === 'rnnoise' ? checkSimd() : Promise.resolve(false)).then(
function (simd) {
var file = simd && p.simdWasm ? p.simdWasm : p.wasm;
let file = simd && p.simdWasm ? p.simdWasm : p.wasm;
return fetch(ASSET_BASE + file).then(function (r) {
if (!r.ok) {
if (simd && p.simdWasm)
@@ -137,7 +138,7 @@
function getContext() {
if (!ctxPromise) {
ctxPromise = (function () {
var ctx = new AudioContext({ sampleRate: SAMPLE_RATE });
let ctx = new AudioContext({ sampleRate: SAMPLE_RATE });
if (ctx.sampleRate !== SAMPLE_RATE) {
try {
ctx.close();
@@ -146,7 +147,7 @@
}
// Load worklet modules. DTLN registers its own processor via the
// dynamic-imported helper (see buildMlNode), so it needs nothing here.
var scripts = [];
let scripts = [];
if (MODEL === 'rnnoise' || MODEL === 'speex') scripts.push(PROCESSORS[MODEL].script);
if (USE_GATE) scripts.push(PROCESSORS.gate.script);
@@ -169,7 +170,7 @@
return ctxPromise;
}
var hasNotifiedActive = false;
let hasNotifiedActive = false;
// Build the ML denoise AudioWorkletNode. RNNoise/Speex are flat sapphi
// worklets we instantiate directly with the fetched WASM binary. DTLN comes
@@ -187,9 +188,9 @@
if (MODEL === 'deepfilternet') {
// Resolve an absolute self-hosted base so the package's cdnUrl override
// fetches our vendored df_bg.wasm + ONNX model (never the upstream CDN).
var dfnBase = new URL(ASSET_BASE + 'deepfilternet', window.location.href).href;
let dfnBase = new URL(ASSET_BASE + 'deepfilternet', window.location.href).href;
return import(ASSET_BASE + PROCESSORS.deepfilternet.esm).then(function (mod) {
var core = new mod.DeepFilterNet3Core({
let core = new mod.DeepFilterNet3Core({
sampleRate: SAMPLE_RATE,
noiseReductionLevel: 80,
assetConfig: { cdnUrl: dfnBase },
@@ -212,7 +213,8 @@
});
});
}
var node = new AudioWorkletNode(ctx, PROCESSORS[MODEL].name, {
let node = new AudioWorkletNode(ctx, PROCESSORS[MODEL].name, {
channelCount: 1,
numberOfInputs: 1,
numberOfOutputs: 1,
@@ -230,21 +232,21 @@
}
function processStream(stream) {
var audioTracks = stream.getAudioTracks();
let audioTracks = stream.getAudioTracks();
if (audioTracks.length === 0) return Promise.resolve(stream);
return Promise.all([loadWasm(MODEL), getContext()])
.then(function (res) {
var wasmBinary = res[0];
var ctx = res[1];
let wasmBinary = res[0];
let ctx = res[1];
var source = ctx.createMediaStreamSource(stream);
var dest = ctx.createMediaStreamDestination();
var head = source;
let source = ctx.createMediaStreamSource(stream);
let dest = ctx.createMediaStreamDestination();
let head = source;
// 1. Optional Noise Gate
if (USE_GATE) {
var gateNode = new AudioWorkletNode(ctx, PROCESSORS.gate.name, {
let gateNode = new AudioWorkletNode(ctx, PROCESSORS.gate.name, {
processorOptions: {
openThreshold: GATE_THRESHOLD,
closeThreshold: GATE_THRESHOLD - 5,
@@ -258,7 +260,7 @@
// 2. ML Processor
return buildMlNode(ctx, wasmBinary).then(function (ml) {
var mlNode = ml.node;
let mlNode = ml.node;
head.connect(mlNode);
mlNode.connect(dest);
@@ -266,15 +268,15 @@
// the track handoff — audio flows via bypassUntilReady meanwhile.
if (ml.ready && typeof ml.ready.then === 'function') {
ml.ready.catch(function (err) {
var m = err instanceof Error ? err.message : String(err);
let m = err instanceof Error ? err.message : String(err);
console.error('[lotus-denoise] ' + MODEL + ' init failed:', m);
});
}
var origTrack = audioTracks[0];
var processedTrack = dest.stream.getAudioTracks()[0];
let origTrack = audioTracks[0];
let processedTrack = dest.stream.getAudioTracks()[0];
var torndown = false;
let torndown = false;
function cleanup() {
if (torndown) return;
torndown = true;
@@ -293,7 +295,7 @@
} catch (e) {}
}
var rawStop = processedTrack.stop.bind(processedTrack);
let rawStop = processedTrack.stop.bind(processedTrack);
processedTrack.stop = function () {
cleanup();
rawStop();
@@ -319,7 +321,7 @@
);
}
var out = new MediaStream();
let out = new MediaStream();
out.addTrack(processedTrack);
stream.getVideoTracks().forEach(function (t) {
out.addTrack(t);
@@ -328,7 +330,7 @@
});
})
.catch(function (e) {
var msg = e instanceof Error ? e.message : String(e);
let msg = e instanceof Error ? e.message : String(e);
console.error('[lotus-denoise] Setup failed:', msg);
window.parent.postMessage(
{ type: 'lotus-denoise-status', active: false, error: msg },
@@ -339,10 +341,10 @@
}
navigator.mediaDevices.getUserMedia = function (constraints) {
var wantsAudio = !!(constraints && constraints.audio);
var effective = constraints;
let wantsAudio = !!(constraints && constraints.audio);
let effective = constraints;
if (wantsAudio) {
var audioC =
let audioC =
typeof constraints.audio === 'object' ? Object.assign({}, constraints.audio) : {};
audioC.noiseSuppression = USE_NATIVE_NS;
audioC.channelCount = 1;