|
| 1 | +/** |
| 2 | + * WebCaptureAdapter 纯函数测试(PWA 麦克风应急通道,M4 Task12 补测) |
| 3 | + * |
| 4 | + * @ai-context: 覆盖 resampleToMono(48k→16k 降采样 + 多声道混单)与 |
| 5 | + * computeRms(能量计算)。MediaRecorder/getUserMedia 依赖真实浏览器, |
| 6 | + * jsdom 无法实例化,故仅测纯函数层。 |
| 7 | + * @ai-context EN: unit tests for resampleToMono (48k→16k downsampling + |
| 8 | + * channel mixing) and computeRms. MediaRecorder/getUserMedia need a real |
| 9 | + * browser, so only pure functions are covered under jsdom. |
| 10 | + */ |
| 11 | +import { describe, it, expect } from 'vitest'; |
| 12 | +import { resampleToMono, computeRms } from './WebCaptureAdapter'; |
| 13 | + |
| 14 | +/** 构造最小 AudioBuffer 假对象(仅覆盖 resampleToMono 用到的字段) */ |
| 15 | +function fakeAudioBuffer(sampleRate: number, channelData: Float32Array[]): AudioBuffer { |
| 16 | + return { |
| 17 | + sampleRate, |
| 18 | + numberOfChannels: channelData.length, |
| 19 | + length: channelData[0]?.length ?? 0, |
| 20 | + duration: 0, |
| 21 | + getChannelData: (c: number) => channelData[c] ?? new Float32Array(0), |
| 22 | + } as unknown as AudioBuffer; |
| 23 | +} |
| 24 | + |
| 25 | +describe('resampleToMono 重采样混单', () => { |
| 26 | + it('48kHz 单声道降采样到 16kHz,长度约为 1/3', () => { |
| 27 | + const src = new Float32Array(48000); |
| 28 | + src.fill(0.5); |
| 29 | + const out = resampleToMono(fakeAudioBuffer(48000, [src]), 16000); |
| 30 | + expect(out.length).toBe(Math.floor(48000 / 3)); |
| 31 | + expect(out[0]).toBeCloseTo(0.5, 3); |
| 32 | + }); |
| 33 | + |
| 34 | + it('双声道取平均', () => { |
| 35 | + const left = new Float32Array(16000).fill(0.4); |
| 36 | + const right = new Float32Array(16000).fill(0.8); |
| 37 | + const out = resampleToMono(fakeAudioBuffer(16000, [left, right]), 16000); |
| 38 | + expect(out.length).toBe(16000); |
| 39 | + expect(out[0]).toBeCloseTo(0.6, 3); |
| 40 | + }); |
| 41 | + |
| 42 | + it('空缓冲返回空数组', () => { |
| 43 | + const out = resampleToMono(fakeAudioBuffer(48000, [new Float32Array(0)]), 16000); |
| 44 | + expect(out.length).toBe(0); |
| 45 | + }); |
| 46 | +}); |
| 47 | + |
| 48 | +describe('computeRms 能量计算', () => { |
| 49 | + it('全零样本能量为 0', () => { |
| 50 | + expect(computeRms(new Float32Array(100))).toBe(0); |
| 51 | + }); |
| 52 | + |
| 53 | + it('满幅样本能量约为 1', () => { |
| 54 | + const pcm = new Float32Array(100).fill(1.0); |
| 55 | + expect(computeRms(pcm)).toBeCloseTo(1, 5); |
| 56 | + }); |
| 57 | + |
| 58 | + it('空数组能量为 0', () => { |
| 59 | + expect(computeRms(new Float32Array(0))).toBe(0); |
| 60 | + }); |
| 61 | +}); |
0 commit comments