feat: add varied microphone array presets
This commit is contained in:
@@ -196,6 +196,31 @@ test("Acoustic Camera가 8×8 CPU 실행·결과 저장·stale·재실행 흐름
|
||||
!documentObject.querySelector("#run-button").disabled,
|
||||
"original comparison configuration restore",
|
||||
);
|
||||
|
||||
const comparisonPresets = [
|
||||
["rectangular-2x2-450mm", "4 ch", "0.450 × 0.450 m"],
|
||||
["rectangular-4x3-450mm", "12 ch", "0.450 × 0.450 m"],
|
||||
["rectangular-8x6-450mm", "48 ch", "0.450 × 0.450 m"],
|
||||
["rectangular-24x16-450mm", "384 ch", "0.450 × 0.450 m"],
|
||||
["rectangular-16x12-compact", "192 ch", "0.120 × 0.120 m"],
|
||||
["rectangular-16x12-large", "192 ch", "1.200 × 1.200 m"],
|
||||
];
|
||||
assertEqual(
|
||||
documentObject.querySelectorAll("#array-preset option").length,
|
||||
9,
|
||||
"all built-in and custom array choices are visible",
|
||||
);
|
||||
for (const [presetId, microphoneCount, aperture] of comparisonPresets) {
|
||||
dispatchValue(windowObject, documentObject.querySelector("#array-preset"), presetId);
|
||||
await waitFor(
|
||||
() => documentObject.querySelector("#array-preset").value === presetId &&
|
||||
documentObject.querySelector("#microphone-count").textContent === microphoneCount &&
|
||||
documentObject.querySelector("#array-meta").textContent.includes(aperture) &&
|
||||
documentObject.documentElement.dataset.cameraValid === "true",
|
||||
`${presetId} preset`,
|
||||
);
|
||||
}
|
||||
|
||||
dispatchValue(
|
||||
windowObject,
|
||||
documentObject.querySelector("#array-preset"),
|
||||
|
||||
@@ -11,6 +11,16 @@ async function loadJson(path) {
|
||||
return response.json();
|
||||
}
|
||||
|
||||
const RECTANGULAR_ARRAY_EXAMPLES = [
|
||||
{ id: "rectangular-2x2-450mm", microphoneCount: 4, columns: 2, rows: 2, width: 0.45, height: 0.45 },
|
||||
{ id: "rectangular-4x3-450mm", microphoneCount: 12, columns: 4, rows: 3, width: 0.45, height: 0.45 },
|
||||
{ id: "rectangular-8x6-450mm", microphoneCount: 48, columns: 8, rows: 6, width: 0.45, height: 0.45 },
|
||||
{ id: "rectangular-16x12", microphoneCount: 192, columns: 16, rows: 12, width: 0.45, height: 0.45 },
|
||||
{ id: "rectangular-24x16-450mm", microphoneCount: 384, columns: 24, rows: 16, width: 0.45, height: 0.45 },
|
||||
{ id: "rectangular-16x12-compact", microphoneCount: 192, columns: 16, rows: 12, width: 0.12, height: 0.12 },
|
||||
{ id: "rectangular-16x12-large", microphoneCount: 192, columns: 16, rows: 12, width: 1.2, height: 1.2 },
|
||||
];
|
||||
|
||||
test("moving-tone sample Scene and Experiment satisfy their linked contracts", async () => {
|
||||
const [arrayDefinition, scene, experiment] = await Promise.all([
|
||||
loadJson("/assets/array-geometries/rectangular-16x12.array.json"),
|
||||
@@ -36,6 +46,92 @@ test("192채널 기준 배열이 계약을 통과한다", async () => {
|
||||
assertEqual((await validateArrayDefinition(arrayDefinition)).length, 0, "array issues");
|
||||
});
|
||||
|
||||
test("rectangular array examples cover low-to-high channel counts and compact-to-wide apertures", async () => {
|
||||
const arrays = await Promise.all(RECTANGULAR_ARRAY_EXAMPLES.map(({ id }) =>
|
||||
loadJson(`/assets/array-geometries/${id}.array.json`)));
|
||||
|
||||
for (let index = 0; index < arrays.length; index += 1) {
|
||||
const arrayDefinition = arrays[index];
|
||||
const expected = RECTANGULAR_ARRAY_EXAMPLES[index];
|
||||
assertEqual(arrayDefinition.id, expected.id, `${expected.id} fixture id`);
|
||||
assertEqual(arrayDefinition.microphones.length, expected.microphoneCount, `${expected.id} channel count`);
|
||||
assertEqual(arrayDefinition.aperture.width, expected.width, `${expected.id} aperture width`);
|
||||
assertEqual(arrayDefinition.aperture.height, expected.height, `${expected.id} aperture height`);
|
||||
assertEqual(
|
||||
(await validateArrayDefinition(arrayDefinition)).length,
|
||||
0,
|
||||
`${expected.id} contract issues`,
|
||||
);
|
||||
assertEqual(
|
||||
new Set(arrayDefinition.microphones.map(({ id }) => id)).size,
|
||||
expected.microphoneCount,
|
||||
`${expected.id} microphone ids are unique`,
|
||||
);
|
||||
assertEqual(
|
||||
new Set(arrayDefinition.microphones.map(({ position }) => position.join(","))).size,
|
||||
expected.microphoneCount,
|
||||
`${expected.id} positions are unique`,
|
||||
);
|
||||
|
||||
const xCoordinates = arrayDefinition.microphones.map(({ position }) => position[0]);
|
||||
const yCoordinates = arrayDefinition.microphones.map(({ position }) => position[1]);
|
||||
assert(
|
||||
arrayDefinition.microphones.every(({ position }) => position[2] === 0),
|
||||
`${expected.id} microphones stay on the z=0 plane`,
|
||||
);
|
||||
assertEqual(new Set(xCoordinates).size, expected.columns, `${expected.id} column count`);
|
||||
assertEqual(new Set(yCoordinates).size, expected.rows, `${expected.id} row count`);
|
||||
const uniqueX = [...new Set(xCoordinates)].sort((left, right) => left - right);
|
||||
const uniqueY = [...new Set(yCoordinates)].sort((left, right) => left - right);
|
||||
const expectedXPitch = expected.width / (expected.columns - 1);
|
||||
const expectedYPitch = expected.height / (expected.rows - 1);
|
||||
assert(
|
||||
uniqueX.slice(1).every((value, position) =>
|
||||
Math.abs(value - uniqueX[position] - expectedXPitch) < 1.1e-6),
|
||||
`${expected.id} uniform x pitch`,
|
||||
);
|
||||
assert(
|
||||
uniqueY.slice(1).every((value, position) =>
|
||||
Math.abs(value - uniqueY[position] - expectedYPitch) < 1.1e-6),
|
||||
`${expected.id} uniform y pitch`,
|
||||
);
|
||||
assert(Math.abs(Math.min(...xCoordinates) + expected.width / 2) < 1e-6, `${expected.id} x minimum`);
|
||||
assert(Math.abs(Math.max(...xCoordinates) - expected.width / 2) < 1e-6, `${expected.id} x maximum`);
|
||||
assert(Math.abs(Math.min(...yCoordinates) + expected.height / 2) < 1e-6, `${expected.id} y minimum`);
|
||||
assert(Math.abs(Math.max(...yCoordinates) - expected.height / 2) < 1e-6, `${expected.id} y maximum`);
|
||||
}
|
||||
|
||||
const channelSeries = arrays.slice(0, 5);
|
||||
assert(
|
||||
channelSeries.every(({ aperture }) => aperture.width === 0.45 && aperture.height === 0.45),
|
||||
"channel-count series keeps the 0.45 m aperture fixed",
|
||||
);
|
||||
assertEqual(
|
||||
channelSeries.map(({ microphones }) => microphones.length).join(","),
|
||||
"4,12,48,192,384",
|
||||
"channel-count series",
|
||||
);
|
||||
const apertureSeries = [
|
||||
arrays.find(({ id }) => id === "rectangular-16x12-compact"),
|
||||
arrays.find(({ id }) => id === "rectangular-16x12"),
|
||||
arrays.find(({ id }) => id === "rectangular-16x12-large"),
|
||||
];
|
||||
assert(
|
||||
apertureSeries.every(({ microphones }) => microphones.length === 192),
|
||||
"aperture series keeps 192 channels fixed",
|
||||
);
|
||||
assertEqual(
|
||||
apertureSeries.map(({ aperture }) => aperture.width).join(","),
|
||||
"0.12,0.45,1.2",
|
||||
"compact-to-large aperture series",
|
||||
);
|
||||
assertEqual(
|
||||
Math.max(...arrays.map(({ microphones }) => microphones.length)),
|
||||
384,
|
||||
"high-count example",
|
||||
);
|
||||
});
|
||||
|
||||
test("192채널 Fermat spiral 비교 배열이 계약과 원형 aperture를 만족한다", async () => {
|
||||
const arrayDefinition = await loadJson("/assets/array-geometries/fermat-spiral-192.array.json");
|
||||
assertEqual(arrayDefinition.microphones.length, 192, "spiral microphone count");
|
||||
|
||||
Reference in New Issue
Block a user