Files
micarrsim/packages/acoustics-core
..
2026-08-28 10:46:33 +09:00

Acoustics Core

두 앱이 공유하는 무의존성 수치 기준 구현입니다.

  • 결정적 tone/white/pink noise 및 오디오 asset resampling
  • 자유음장 직접음과 선택적 저비용 shoebox multipath
  • zero-padded FFT 전달함수 기반 fractional sample delay와 마이크 주파수 응답
  • radix-2 FFT, Hann STFT와 선택 bin 추출
  • scan grid 거리 cache 및 conventional frequency-domain DAS

실측 다채널 스트림 위치 추정

realtime-localizer.js는 Scene·음원 합성 없이 이미 sampling된 동기식 마이크 채널을 직접 받습니다. 외부 package 의존성이 없는 ES module이며, 다른 프로젝트에는 acoustics-core 디렉터리를 그대로 복사해 사용할 수 있습니다. 채널 순서는 array.microphones 순서와 같아야 하고 각 chunk는 동일 길이의 Float32Array 목록이어야 합니다.

import { createStreamingLocalizer } from "./packages/acoustics-core/realtime-localizer.js";

const localizer = createStreamingLocalizer({
  sampleRate: 48_000,
  array,
  scan: { xMin: -1, xMax: 1, yMin: -1, yMax: 1, z: 2, columns: 50, rows: 50 },
  bands: [{ id: "machine", minHz: 900, maxHz: 1100, color: "#65f2bf" }],
  stft: { fftSize: 512, hopSize: 256 },
  analysisWindowSamples: 4_800,
  analysisHopSamples: 4_800,
  maximumBufferedSamples: 19_200,
  maximumResultsPerCall: 1,
});

// Capture callback: 복사 후 즉시 반환하며, 오래 밀린 sample은 oldest-first로 버립니다.
localizer.append(channels);

// Analysis loop: 호출마다 설정된 상한 이하의 map만 반환합니다.
const results = await localizer.analyzeAvailable({ signal: abortController.signal });
for (const result of results) {
  const { position, rawPower } = result.locations[0];
  drawMarker(position, rawPower, result.centerTime);
}

한 블록만 처리할 때는 createLocalizationPlan()과 localizeMultichannel()을 사용합니다. getState()은 absolute sample origin, buffered/dropped sample, 대기 window 수와 고정 input buffer byte 수를 돌려주므로 capture adapter가 지연을 감시할 수 있습니다. 실제 장치의 clock, channel framing과 transport는 하드웨어별 adapter 책임이며 이 모듈은 그 다음 단계부터 시작합니다.

반복 CPU 분석 계획은 거리 행렬, bin calibration, 제한 내의 Float64 steering matrix와 임시 bin map을 재사용합니다. 예를 들어 192채널·50×50·2 bins를 10회 분석하면 거리 계산은 4,800,000회에서 480,000회로, steering point/microphone pair는 9,600,000회에서 960,000회로 줄어듭니다 (sin/cos 호출 17,280,000회 회피). 이 경우 약 16.5 MiB를 유지하며, 기본 64 MiB steering 한도를 넘는 설정은 steering cache만 끕니다. 자동 테스트는 cache 전후 raw/display Float32 map의 전 요소 bit-exact 일치를 고정합니다.

이동 음원과 분석 시간창

sourcePositionAtTime은 scene 시작 기준의 절대 시각에서 trajectory keyframe 사이를 선형 보간하며, 경로 범위 밖에서는 가장 가까운 끝점 위치를 유지합니다. sampleQuasiStaticWindow는 요청 경계를 sample 경계로 올림 정렬하고, 시간창 중앙의 위치에서 이동 음원을 정지한 것으로 근사합니다.

시간창은 활성 source만 잘라 짧은 Scene으로 만들지만 signalOffsets에 원래 scene/source의 절대 sample offset을 보존합니다. 이를 synthesizeMicrophoneSignals에 전달하면 각 창을 따로 합성해도 tone phase, seed 기반 noise index, audio asset 재생 위치와 ambient가 전체 timeline 합성과 연속됩니다.

Beam spread 의미

Peak의 beamSpread는 half-power 연결 성분을 power 가중한 공간 covariance(covarianceM2, m²)와 그 RMS 주축 ellipse로 요약한 빔 폭입니다. 가우시안 확률 모델, 위치 확률 또는 신뢰구간으로 보정된 값이 아니며 calibrated: false를 유지합니다.

FFT는 X[k] = Σ x[n] exp(-j2πkn/N) 규약을 사용합니다. 마이크 신호의 전파 phase exp(-j2πfr/c)를 정렬하기 위해 DAS steering은 exp(+j2πfr/c)를 곱합니다. 단일 tone의 ground-truth 위치에서 peak가 생기는 회귀 테스트로 이 부호를 고정합니다.

마이크 sensitivityDb와 phaseOffsetDeg는 수신 channel에 편차로 적용하고, DAS에서는 알려진 calibration 값의 역 gain/phase로 보정합니다. raw power map과 표시용 normalized map은 서로 다른 buffer로 보존합니다. bin spectrum은 제한된 batch로 처리하며 point batch 사이에서 event loop를 양보해 진행 callback과 AbortSignal 취소가 동작합니다.

마이크별 frequencyResponse는 2~64개의 { frequencyHz, gainDb, phaseDeg? } 점으로 정의합니다. interpolateFrequencyResponse는 dB와 wrapped phase를 구간 선형 보간하고 측정 범위 밖에서는 가장 가까운 끝점을 사용합니다. 합성 시 기존 sensitivity/phase 편차에 이 응답을 추가하며, CPU DAS는 선택 FFT bin의 역응답을 calibration/steering cache에 포함합니다. WebGPU 경로는 GPU 전송 직전의 packed STFT에 같은 역응답을 적용합니다. beamforming.compensateFrequencyResponse: false는 sensitivity/phase 보정은 유지한 채 곡선 보정만 꺼 robustness 비교를 재현합니다.

응답 곡선이 없는 배열은 기존 계산 경로와 결과를 유지합니다. 곡선이 있으면 합성 FFT마다 bin당 보간·복소 곱이 추가되고, WebGPU 준비 단계에는 선택 bin × 마이크 calibration과 STFT 복소 곱이 추가되지만 GPU buffer layout이나 dispatch 크기는 늘어나지 않습니다.

저비용 환경 근사

Scene의 acoustics를 생략하거나 { "model": "free-field" }로 두면 기존 직접음 delay = r/c, gain = 1/r만 사용합니다. model: "shoebox"는 다음 경로를 선택적으로 추가합니다.

  • 직육면체 room의 여섯 면에 대한 1차 image-source 반사
  • 반사 에너지 일부를 deterministic 지연 경로로 나누는 scattering 근사
  • 축 정렬 box occluders를 통과하는 직접음 감쇠와 가장 짧은 corner detour 회절 경로
  • RT60·gain·tap 수로 제한한 deterministic diffuse-reverb tail

모든 경로의 위상과 gain은 마이크마다 하나의 frequency-domain 전달함수로 합친 뒤 IFFT를 한 번만 수행합니다. 경로 위상은 recurrence로 갱신하고 1,024 bin마다 재고정하므로 각 bin·path마다 삼각함수를 다시 호출하지 않습니다. UI preset도 수신점당 sparse path 상한을 표시합니다.

이 모델은 배열 배치와 위치추정 robustness를 빠르게 비교하기 위한 근사입니다. 파동 방정식, 고차 반사, 재질별 주파수 의존 흡음, 복잡한 mesh 회절, Doppler, 음원·마이크 지향성, 압력/SPL, 센서 self-noise, ADC quantization·clock jitter는 계산하지 않습니다. 그런 효과가 중요한 검증에서는 실측 다채널 데이터를 아래 streaming API에 넣어 결과를 비교해야 합니다.

snapshot 합성은 전체 signal과 가장 늦은 sparse path를 포함하는 delay FFT working set을 512 MiB로 제한합니다. 실측·장시간 입력은 위 streaming localizer의 bounded ring buffer로 전체 녹음을 메모리에 보관하지 않고 처리할 수 있습니다. CPU analysis plan도 기본 512 MiB hard cap을 할당 전에 검사하며 steering cache는 별도의 64 MiB 한도를 넘으면 자동으로 끕니다.

192채널·50×50 fixture는 브라우저 통합 테스트에서 실제 합성부터 DAS peak 검출까지 실행합니다.