tests for DeepFilterNet Noise reduction

This commit is contained in:
fkwp
2026-04-07 12:10:02 +02:00
parent 300e7476a3
commit f2bb9e7d73
2 changed files with 223 additions and 0 deletions

View File

@@ -0,0 +1,103 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
vi.mock("deepfilternet3-noise-filter", () => {
const setEnabled = vi.fn();
const setSuppressionLevel = vi.fn();
const destroy = vi.fn();
function DeepFilterNoiseFilterProcessor(this: any, options: any) {
Object.assign(this, options);
this.setEnabled = setEnabled;
this.setSuppressionLevel = setSuppressionLevel;
this.destroy = destroy;
}
return {
__esModule: true,
DeepFilterNoiseFilterProcessor: vi.fn().mockImplementation(DeepFilterNoiseFilterProcessor),
__setEnabledSpy: setEnabled,
__setSuppressionLevelSpy: setSuppressionLevel,
__destroySpy: destroy,
};
});
import { NoiseSuppressionTransformer } from "./NoiseSuppressionTransformer";
import {
DeepFilterNoiseFilterProcessor,
__setEnabledSpy as mockSetEnabled,
__setSuppressionLevelSpy as mockSetSuppressionLevel,
__destroySpy as mockDestroy,
} from "deepfilternet3-noise-filter";
const mockDeepFilterNoiseFilterProcessor = vi.mocked(DeepFilterNoiseFilterProcessor);
describe("NoiseSuppressionTransformer", () => {
beforeEach(() => {
mockSetEnabled.mockClear();
mockSetSuppressionLevel.mockClear();
mockDestroy.mockClear();
mockDeepFilterNoiseFilterProcessor.mockClear();
});
it("initializes the underlying processor with the expected configuration", () => {
const transformer = new NoiseSuppressionTransformer();
transformer.initialize(0.5, false);
expect(mockDeepFilterNoiseFilterProcessor).toHaveBeenCalledTimes(1);
expect(mockDeepFilterNoiseFilterProcessor).toHaveBeenCalledWith(
expect.objectContaining({
sampleRate: 48000,
noiseReductionLevel: 50,
enabled: false,
assetConfig: expect.objectContaining({
cdnUrl: expect.any(String),
}),
}),
);
expect(transformer.getProcessor()).not.toBeNull();
});
it("does not initialize twice", () => {
const transformer = new NoiseSuppressionTransformer();
transformer.initialize(0.3, true);
transformer.initialize(0.7, false);
expect(mockDeepFilterNoiseFilterProcessor).toHaveBeenCalledTimes(1);
expect(transformer.getProcessor()).not.toBeNull();
});
it("forwards suppression level changes and clamps out-of-range values", () => {
const transformer = new NoiseSuppressionTransformer();
transformer.initialize(0.2, true);
transformer.setSuppressionLevel(1.5);
transformer.setSuppressionLevel(-0.2);
expect(mockSetSuppressionLevel).toHaveBeenNthCalledWith(1, 100);
expect(mockSetSuppressionLevel).toHaveBeenNthCalledWith(2, 0);
});
it("forwards enabled state changes to the underlying processor", () => {
const transformer = new NoiseSuppressionTransformer();
transformer.initialize(0.4, true);
transformer.setEnabled(false);
transformer.setEnabled(true);
expect(mockSetEnabled).toHaveBeenNthCalledWith(1, false);
expect(mockSetEnabled).toHaveBeenNthCalledWith(2, true);
});
it("destroys the processor and resets internal state", () => {
const transformer = new NoiseSuppressionTransformer();
transformer.initialize(0.6, true);
transformer.destroy();
expect(mockDestroy).toHaveBeenCalledTimes(1);
expect(transformer.getProcessor()).toBeNull();
});
});

View File

@@ -0,0 +1,120 @@
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { BehaviorSubject } from "rxjs";
const localStorageMock = {
getItem: vi.fn(() => null),
setItem: vi.fn(() => {}),
removeItem: vi.fn(() => {}),
clear: vi.fn(() => {}),
};
Object.defineProperty(globalThis, "localStorage", {
value: localStorageMock,
configurable: true,
writable: true,
});
vi.mock("deepfilternet3-noise-filter", () => {
const setEnabled = vi.fn();
const setSuppressionLevel = vi.fn();
const destroy = vi.fn();
function DeepFilterNoiseFilterProcessor(this: any, options: any) {
Object.assign(this, options);
this.setEnabled = setEnabled;
this.setSuppressionLevel = setSuppressionLevel;
this.destroy = destroy;
}
return {
__esModule: true,
DeepFilterNoiseFilterProcessor: vi.fn().mockImplementation(DeepFilterNoiseFilterProcessor),
__setEnabledSpy: setEnabled,
__setSuppressionLevelSpy: setSuppressionLevel,
__destroySpy: destroy,
};
});
import { ObservableScope } from "../state/ObservableScope";
import type { LocalAudioTrack } from "livekit-client";
import type { Behavior } from "../state/Behavior";
import {
__setEnabledSpy as mockSetEnabled,
__setSuppressionLevelSpy as mockSetSuppressionLevel,
__destroySpy as mockDestroy,
} from "deepfilternet3-noise-filter";
let audioTrackNoiseSuppressionSync: typeof import("./audioTrackNoiseSuppressionSync").audioTrackNoiseSuppressionSync;
let noiseSuppressionEnabled: typeof import("../settings/settings").noiseSuppressionEnabled;
let noiseSuppressionLevel: typeof import("../settings/settings").noiseSuppressionLevel;
class MockLocalAudioTrack {
private processor: unknown = undefined;
public readonly setProcessor = vi.fn(async (processor: unknown) => {
this.processor = processor;
});
public readonly getProcessor = vi.fn(() => this.processor);
public readonly stopProcessor = vi.fn(async () => {
this.processor = undefined;
});
}
describe("audioTrackNoiseSuppressionSync", () => {
let scope: ObservableScope;
let audioTrack$: Behavior<LocalAudioTrack | null>;
let track: MockLocalAudioTrack;
beforeEach(async () => {
mockSetEnabled.mockClear();
mockSetSuppressionLevel.mockClear();
mockDestroy.mockClear();
track = new MockLocalAudioTrack();
audioTrack$ = new BehaviorSubject<LocalAudioTrack | null>(track as unknown as LocalAudioTrack);
const settingsModule = await import("../settings/settings");
noiseSuppressionEnabled = settingsModule.noiseSuppressionEnabled;
noiseSuppressionLevel = settingsModule.noiseSuppressionLevel;
const syncModule = await import("./audioTrackNoiseSuppressionSync");
audioTrackNoiseSuppressionSync = syncModule.audioTrackNoiseSuppressionSync;
noiseSuppressionEnabled.setValue(true);
noiseSuppressionLevel.setValue(0.75);
scope = new ObservableScope();
});
afterEach(async () => {
scope.end();
await Promise.resolve();
});
it("sets the processor on the audio track and updates the processor settings", async () => {
audioTrackNoiseSuppressionSync(scope, audioTrack$);
await Promise.resolve();
expect(track.setProcessor).toHaveBeenCalledTimes(1);
expect(track.getProcessor()).not.toBeUndefined();
expect(mockSetEnabled).toHaveBeenCalledWith(false);
expect(mockSetSuppressionLevel).toHaveBeenCalledWith(75);
});
it("reapplies processor when audio track becomes available", async () => {
audioTrack$ = new BehaviorSubject<LocalAudioTrack | null>(null);
audioTrackNoiseSuppressionSync(scope, audioTrack$);
await Promise.resolve();
expect(track.setProcessor).toHaveBeenCalledTimes(0);
audioTrack$.next(track as unknown as LocalAudioTrack);
await Promise.resolve();
expect(track.setProcessor).toHaveBeenCalledTimes(1);
});
it("destroys the transformer when the scope ends", async () => {
audioTrackNoiseSuppressionSync(scope, audioTrack$);
await Promise.resolve();
scope.end();
await Promise.resolve();
expect(mockDestroy).toHaveBeenCalledTimes(1);
});
});