import { describe, expect, it } from "vitest"; import { ENVIRONMENTS, LEVELS } from "../src/content"; import { dbToGain, normalizeRms, mixingGains } from "../src/audio/dsp"; // 청감이나 실제 음압은 장치마다 다르므로 테스트 대상은 디지털 RMS와 gain // 비율이다. 배경·음성을 같은 기준으로 정규화해야 표기한 상대 dB가 성립한다. const rms = (a: Float32Array) => Math.sqrt(a.reduce((sum, n) => sum + n * n, 0) / a.length); describe("상대 dB와 결정론적 배경 합성", () => { it("20 log10(음성 gain / 배경 gain)이 표시 dB와 일치한다", () => { for (const level of LEVELS) expect(20 * Math.log10(dbToGain(level.db))).toBeCloseTo(level.db, 10); expect(dbToGain(-6)).toBeCloseTo(0.501187, 5); }); it("평균 전력을 맞추고 원본 배열을 변경하지 않으며 무음은 오류로 알린다", () => { const input = new Float32Array([0.4, -0.2, 0.1, -0.3]); const output = normalizeRms(input); expect(rms(output)).toBeCloseTo(0.12, 6); expect(output).not.toBe(input); expect(() => normalizeRms(new Float32Array(4))).toThrow("무음"); }); it("여덟 환경은 고유 same-origin WAV이며 절차적 합성 매개변수를 갖지 않는다", () => { expect(new Set(ENVIRONMENTS.map((e) => e.audioUrl)).size).toBe(8); for (const environment of ENVIRONMENTS) { const url = new URL(environment.audioUrl, "http://localhost"); expect(url.origin).toBe("http://localhost"); expect(url.pathname).toBe(`/ambience/${environment.id}.wav`); expect(environment).not.toHaveProperty("seed"); } }); it("피크가 큰 녹음도 공통 감쇠로 합성 peak 0.8을 넘지 않고 SNR 비율을 보존한다", () => { const voice = normalizeRms( new Float32Array([1, ...Array(999).fill(0)]), 0.03, ); const background = normalizeRms(new Float32Array([0.2, -0.2]), 0.03); for (const db of [-12, -6, 0, 6, 12]) { const gains = mixingGains(voice, background, voice, db); expect(20 * Math.log10(gains.voice / gains.background)).toBeCloseTo( db, 10, ); expect( Math.max(...voice) * (gains.voice + gains.intro) + 0.03 * gains.background, ).toBeLessThanOrEqual(0.800001); } }); });