feat: add physically explicit narrowband MUSIC

This commit is contained in:
2026-08-29 12:40:48 +09:00
parent 044be3cf92
commit 11fee76280
40 changed files with 6473 additions and 275 deletions
+49 -5
View File
@@ -1,7 +1,10 @@
import { synthesizeMicrophoneSignals } from "/packages/acoustics-core/propagation.js";
import { sampleQuasiStaticWindow } from "/packages/acoustics-core/trajectory.js";
import { createLocalizationAnalysisPlan } from "/packages/acoustics-core/localization.js";
import { localizationAlgorithmSupportsBackend } from "/packages/acoustics-core/localization-algorithms.js";
import { computeLocalizationWithBackend } from "/packages/webgpu-kernels/compute.js";
import { isMusicUnavailableError } from "/packages/acoustics-core/music.js";
import { getCompleteFrameCount } from "/packages/acoustics-core/stft.js";
function post(type, jobId, payload = {}, transfer = []) {
self.postMessage({ type, jobId, ...payload }, transfer);
@@ -35,8 +38,47 @@ function signalStats(synthesis) {
}
function collectBandTransfers(run, transfer) {
for (const band of run.bands) {
transfer.push(band.rawMap.buffer, band.displayMap.buffer);
const knownBuffers = new Set(transfer);
for (const band of run.bands ?? []) {
for (const map of [band.rawMap, band.displayMap]) {
const buffer = map?.buffer;
if (buffer instanceof ArrayBuffer && !knownBuffers.has(buffer)) {
knownBuffers.add(buffer);
transfer.push(buffer);
}
}
}
}
async function computeLocalizationOrUnavailable(options) {
try {
return await computeLocalizationWithBackend(options);
} catch (error) {
if (!isMusicUnavailableError(error)) throw error;
return {
algorithm: "music",
backend: "cpu",
status: "unavailable",
unavailable: {
code: error.code,
reason: error.reason,
message: error.message,
details: { ...error.details },
},
frameCount: getCompleteFrameCount(
options.channels[0]?.length ?? 0,
options.experiment.stft.fftSize,
options.experiment.stft.hopSize,
),
microphoneCount:
options.arrayDefinition.microphones.length,
pointCount:
options.experiment.scan.rows *
options.experiment.scan.columns,
objectiveDirection: "minimum",
rawScale: "noise-projection",
bands: [],
};
}
}
@@ -54,7 +96,9 @@ async function runTimeline({ jobId, scene, arrayDefinition, experiment, assetBuf
const localizationScene = { speedOfSound: scene.speedOfSound };
const algorithm = experiment.beamforming.algorithm ?? "das";
const requestedBackend = experiment.beamforming.backend ?? "auto";
const reuseCpuPlan = algorithm === "gcc-phat" || requestedBackend === "cpu";
const reuseCpuPlan =
!localizationAlgorithmSupportsBackend(algorithm, "webgpu") ||
requestedBackend === "cpu";
let localizationExperiment = experiment;
let analysisPlan = reuseCpuPlan
? createLocalizationAnalysisPlan({
@@ -105,7 +149,7 @@ async function runTimeline({ jobId, scene, arrayDefinition, experiment, assetBuf
signalGeneration += frameSignalGeneration;
const localizationStartedAt = performance.now();
const localization = await computeLocalizationWithBackend({
const localization = await computeLocalizationOrUnavailable({
channels: synthesis.channels,
sampleRate: synthesis.sampleRate,
arrayDefinition,
@@ -234,7 +278,7 @@ self.addEventListener("message", async (event) => {
});
const localizationStartedAt = performance.now();
const localization = await computeLocalizationWithBackend({
const localization = await computeLocalizationOrUnavailable({
channels: synthesis.channels,
sampleRate: synthesis.sampleRate,
arrayDefinition,