Tone.js/test/helper/compare/Compare.ts
Yotam Mann aaf880c925
Using web-test-runner for tests, updating import paths (#1242)
* WIP moving tests to web-test-runner

* updating thresholds

* Adding file extensions

* Testing integrations

* linting

* fixing dep

* moving back to root dir

* prettier all of the files

* updating eslint rules to use with prettier

* remove import package

* moving tsignore around

* removing unneeded ignores

* all tests run on puppeteer, no need for testing guards

* linting

* import type syntax

* cleaning up

* Update package.json
2024-05-03 14:31:14 -04:00

147 lines
3.4 KiB
TypeScript

import { OfflineRender } from "./OfflineRender.js";
import { analyze } from "./Spectrum.js";
import { TestAudioBuffer } from "./TestAudioBuffer.js";
export function compareSpectra(
bufferA: TestAudioBuffer,
bufferB: TestAudioBuffer
): number {
if (bufferA.length !== bufferB.length) {
throw new Error("buffers must be the same length to compare");
}
const analysisA = analyze(bufferA, 1024, 64);
const analysisB = analyze(bufferB, 1024, 64);
let diff = 0;
analysisA.forEach((columnA, columnNum) => {
const columnB = analysisB[columnNum];
columnA.forEach((valA, index) => {
const valB = columnB[index];
diff += Math.pow(valA - valB, 2);
});
});
return Math.sqrt(diff / analysisA.length);
}
export function compareSignals(
bufferA: TestAudioBuffer,
bufferB: TestAudioBuffer
): number {
const arrayA = bufferA.toArray();
const arrayB = bufferB.toArray();
const diffs = arrayA.map((channelA, channelNum) => {
let diff = 0;
const channelB = arrayB[channelNum];
channelA.forEach((valA, index) => {
const valB = channelB[index];
diff += Math.pow(valA - valB, 2);
});
return Math.sqrt(diff / channelA.length);
});
// average across the channels
return diffs.reduce((t, v) => t + v, 0) / diffs.length;
}
interface BufferResponse {
bufferA: TestAudioBuffer;
bufferB: TestAudioBuffer;
}
type BufferResponseType = BufferResponse | void;
async function getBuffersToCompare(
callback: (context: OfflineAudioContext) => Promise<void> | void,
filename: string,
duration = 0.5,
channels = 1,
sampleRate = 11025,
forceRender = false
): Promise<BufferResponseType> {
if (forceRender) {
const buffer = await OfflineRender(
callback,
duration,
channels,
sampleRate
);
buffer.downloadWav(filename);
return Promise.resolve();
} else {
const bufferB = await fetch(filename)
.then((response) => response.arrayBuffer())
.then((buffer) => {
const context = new OfflineAudioContext(
channels,
1,
sampleRate
);
return context.decodeAudioData(buffer);
})
.then((audioBuffer) => new TestAudioBuffer(audioBuffer));
const bufferA = await OfflineRender(
callback,
bufferB.duration,
bufferB.numberOfChannels,
bufferB.sampleRate
);
// const [bufferA, bufferB] = await Promise.all([bufferAPromise, bufferBPromise]);
return {
bufferA,
bufferB,
};
}
}
export async function toFile(
callback: (context: OfflineAudioContext) => Promise<void> | void,
filename: string,
threshold = 0.1,
forceRender = false,
duration = 0.1,
channels = 1,
sampleRate = 11025
) {
const response = await getBuffersToCompare(
callback,
filename,
duration,
channels,
sampleRate,
forceRender
);
if (response) {
const { bufferA, bufferB } = response;
const error = compareSpectra(bufferA, bufferB);
if (error > threshold) {
throw new Error(
`Error ${error} greater than threshold ${threshold}`
);
}
}
}
export async function toFileSignal(
callback: (context: OfflineAudioContext) => Promise<void> | void,
filename: string,
threshold = 0.1,
forceRender = false,
duration = 0.1,
channels = 1,
sampleRate = 11025
) {
const response = await getBuffersToCompare(
callback,
filename,
duration,
channels,
sampleRate,
forceRender
);
if (response) {
const { bufferA, bufferB } = response;
if (compareSignals(bufferA, bufferB) > threshold) {
throw new Error(`generated buffer does not match file ${filename}`);
}
}
}