// DeepFilterNet 3 in a module worker, for johnmcm.com's Remove Background // Noise page. Message in: { id, channels: Float32Array[] at 48 kHz, atten } // (atten: the most noise to remove, in dB; 100 is no limit). Messages out: // { id, type: 'progress', done } with done from 0 to 1, then // { id, type: 'done', channels } (same length and alignment as the input), // or { id, type: 'error', message }. The wasm and the model load on the // first message and stay loaded; each channel gets a fresh runtime. import init, { DeepFilter } from './deepfilter.js'; let model = null; function load() { model ??= (async () => { await init(new URL('./deepfilter_bg.wasm', import.meta.url)); const res = await fetch(new URL('./deepfilternet3.bin', import.meta.url)); if (!res.ok) throw new Error(`the noise model could not be downloaded (${res.status})`); return new Uint8Array(await res.arrayBuffer()); })(); model.catch(() => (model = null)); return model; } // About five seconds of audio per call, so progress moves and the page can stop it. const CHUNK_FRAMES = 500; self.onmessage = async (e) => { const { id, channels, atten } = e.data; try { const bytes = await load(); self.postMessage({ id, type: 'loaded' }); const total = channels.reduce((n, c) => n + c.length, 0) || 1; let doneSamples = 0; const out = []; for (const input of channels) { const df = new DeepFilter(bytes, atten); const hop = df.hop; const delay = df.delay; // The output lags by `delay` samples: feed that much silence after the end, then drop it from the start. const frames = Math.ceil((input.length + delay) / hop); const padded = new Float32Array(frames * hop); padded.set(input); const result = new Float32Array(frames * hop); for (let f = 0; f < frames; f += CHUNK_FRAMES) { const n = Math.min(CHUNK_FRAMES, frames - f); result.set(df.process(padded.subarray(f * hop, (f + n) * hop)), f * hop); doneSamples += Math.min(n * hop, Math.max(0, input.length - f * hop)); self.postMessage({ id, type: 'progress', done: doneSamples / total }); } df.free(); out.push(result.slice(delay, delay + input.length)); } self.postMessage({ id, type: 'done', channels: out }, out.map((c) => c.buffer)); } catch (err) { self.postMessage({ id, type: 'error', message: String((err && err.message) || err) }); } };