428 lines
27 KiB
Markdown
428 lines
27 KiB
Markdown
# 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 및 DAS·DAS-PHAT·GCC-PHAT·MUSIC 위치 추정
|
||
|
||
## 단일 채널 활동 검출 API
|
||
|
||
전 대역 후보 검출은 위치추정기와 분리된 무의존성 ES module입니다.
|
||
<code>analyzeCfarActivity()</code>는 한 채널의 모든 complete Hann frame에 CA-CFAR 또는
|
||
OS-CFAR를 적용하고, <code>computeDixonSpectralFlux()</code>는 Dixon 2006의 Hamming,
|
||
linear-magnitude, half-wave, full-DFT L1 onset novelty를 계산합니다.
|
||
|
||
~~~js
|
||
import {
|
||
analyzeCfarActivity,
|
||
computeDixonSpectralFlux,
|
||
pickCausalSpectralFluxPeaks,
|
||
} from "./packages/acoustics-core/index.js";
|
||
|
||
const cfar = await analyzeCfarActivity({
|
||
samples: onePhysicalAdcChannel,
|
||
sampleRate: 48_000,
|
||
fftSize: 512,
|
||
hopSize: 256,
|
||
window: "hann",
|
||
channelId: "mic-001",
|
||
detector: "os-cfar",
|
||
trainingCellsPerSide: 8,
|
||
guardCellsPerSide: 2,
|
||
rank: 12,
|
||
nominalFalseAlarmProbability: 0.001,
|
||
});
|
||
|
||
const flux = computeDixonSpectralFlux({
|
||
samples: onePhysicalAdcChannel,
|
||
sampleRate: 48_000,
|
||
fftSize: 512,
|
||
hopSize: 256,
|
||
channelId: "mic-001",
|
||
});
|
||
~~~
|
||
|
||
두 함수는 여러 마이크, frame 또는 detector를 합치지 않으며 위치·음원 수·아날로그 alias order를
|
||
추론하지 않습니다. CFAR 결과는 DC와 Nyquist를 포함한 모든 ADC bin record를 보존하고,
|
||
full symmetric reference가 없는 edge를 <code>untested</code>로 남깁니다. Pfa는 IID
|
||
exponential 모델의 tested CUT 하나당 명목값입니다. Spectral flux는 모든 frame×full-DFT-bin
|
||
양의 차이와 raw ODF를 보존하며 peak picker는 별도 명시 호출입니다.
|
||
|
||
<code>estimateCfarActivityWorkload()</code>와
|
||
<code>estimateDixonSpectralFluxWorkload()</code>로 할당 전에 frame/bin/FFT 연산과 raw output
|
||
byte를 검사할 수 있습니다. 기본 상한을 넘으면 일부 frame, top-K bin 또는 축소 결과를
|
||
만들지 않고 실패합니다. 상세 수식과 문헌은
|
||
[전 대역 활동 검출 모델](../../docs/activity-detection.md)에 있습니다.
|
||
|
||
## Nyquist 초과 입력
|
||
|
||
`sampling.js`는 anti-alias filter 없는 이상적 real sampler의 signed alias와 band-to-FFT-bin
|
||
mapping을 제공한다. tone 전파는 먼저 원 analog 주파수에서 path delay와 microphone response를
|
||
적용하고 그 뒤 ADC 시각에서 sampling한다. 따라서 시간축에는 alias가 나타나지만 센서 간 위상은
|
||
원 파장의 물리를 유지한다.
|
||
|
||
Nyquist 위 band는 모든 등가 ADC FFT bin center로 접어 관찰한다. 연속 fold segment와 이산
|
||
bin-center membership은 별도 metadata다. DAS 계열은 양의 ADC bin 주파수로 steering하고
|
||
GCC-PHAT는 선택 bin의 lag correlation을 사용하며, 원 carrier를 몰래 추론하거나 peak를
|
||
고르지 않는다. 단일 channel 시간축에서 같은 sequence로 접혀도 다채널 공간 위상에는 원 파장의
|
||
증거가 남을 수 있다. 현재 localizer는 carrier hypothesis를 열거하지 않으므로 Result에
|
||
`aliasOrderResolution: not-attempted`로 남긴다. asset/noise 입력은 이미 sampling된 baseband로
|
||
취급한다.
|
||
|
||
Tone과 band의 검증 범위는 0–1 MHz다. 이는 Nyquist cutoff가 아니라 현재 IEEE-754 위상 계산,
|
||
마이크 응답과 희소 경로 모델의 명시적 정밀도 범위다. public sampling helper는 비정상적으로
|
||
작은 sample rate에서도 최대 513개 fold segment를 넘기 전에 거부한다.
|
||
|
||
## 수신 빔 패턴 API
|
||
|
||
`beam-pattern.js`의 `computeReceiveBeamPattern()`은 Scene과 ADC 없이 microphone geometry의
|
||
narrowband far-field conventional delay-and-sum receive array factor를 계산합니다.
|
||
|
||
~~~js
|
||
import { computeReceiveBeamPattern } from "./packages/acoustics-core/beam-pattern.js";
|
||
|
||
const pattern = computeReceiveBeamPattern({
|
||
array,
|
||
frequencyHz: 49_125,
|
||
speedOfSound: 343,
|
||
cut: "xz",
|
||
steeringAngleDeg: 0,
|
||
sampleCount: "auto",
|
||
dbFloor: -60,
|
||
});
|
||
~~~
|
||
|
||
파수는 `2π frequencyHz / speedOfSound`이며 `frequencyHz`를 temporal ADC alias로 접지 않습니다.
|
||
결과는 `1/M` weight의 raw normalized complex·power·dB와 별도의 display-floor buffer를 보존합니다.
|
||
이상적으로 보정된 무지향성 point microphone의 geometry-only 응답이므로
|
||
`sensitivityDb`, `phaseOffsetDeg`, `frequencyResponse`, element directivity, 방과 near-field는
|
||
포함하지 않습니다.
|
||
|
||
XZ는 0°=+Z/90°=+X, YZ는 0°=+Z/90°=+Y, XY는 0°=+X/90°=+Y인 great-circle 단면입니다.
|
||
투영 maximum baseline `D`와 angle 수 `N`에서
|
||
`2 k D sin(π/N) ≤ π/8` phase-step criterion을 검사합니다. 이는 모든 lobe 검출 보장이 아니라
|
||
metric sampling 정책입니다. 미충족 시 raw samples만 반환하고 HPBW와 sampled peak 해석을
|
||
`resolution-unresolved`로 남깁니다. HPBW도 sampled half-power bracket이 있을 때만 raw analytic
|
||
response를 48회 이분하며, 비상수 all-above grid를 360° 폭으로 만들지 않습니다.
|
||
|
||
마이크 수·baseline pair·angular sample·phasor evaluation은 큰 반복과 할당 전에 상한을 검사합니다.
|
||
portable 결과 계약과 상태별 의미는 [수신 빔 패턴 문서](../../docs/beam-pattern.md)를 참고하세요.
|
||
|
||
## 위치 추정 알고리즘
|
||
|
||
`localization-algorithms.js`의 registry와 `localization.js`의 generic API가 다음 네 경로를
|
||
공통 interface로 제공합니다.
|
||
|
||
| `algorithm` | 계산 | raw scale | backend |
|
||
| --- | --- | --- | --- |
|
||
| `das` | conventional frequency-domain delay-and-sum | `power` | CPU, WebGPU |
|
||
| `das-phat` | 채널 spectrum을 phase-only로 만든 DAS | `score` | CPU, WebGPU |
|
||
| `gcc-phat` | 하나의 reference mic와 나머지 mic의 GCC-PHAT 지연 scan | `score` | CPU only |
|
||
| `music` | Schmidt narrowband MUSIC의 근거리 spherical noise-subspace projection | `noise-projection` | CPU only |
|
||
|
||
Portable API에서는 top-level `algorithm`으로 선택합니다. 생략하면 기존과 같은 `das`이며, 낮은
|
||
수준의 generic API는 `experiment.beamforming.algorithm`을 사용합니다.
|
||
|
||
```js
|
||
import {
|
||
createLocalizationPlan,
|
||
localizeMultichannel,
|
||
} from "./packages/acoustics-core/realtime-localizer.js";
|
||
|
||
const plan = createLocalizationPlan({
|
||
...commonConfig, // sampleRate, array, scan, bands, stft
|
||
algorithm: "gcc-phat", // "das" | "das-phat" | "gcc-phat" | "music"
|
||
phatFloorRatio: 1e-6,
|
||
});
|
||
|
||
// 반복 분석에서도 같은 plan을 재사용합니다.
|
||
const result = await localizeMultichannel({ plan, channels });
|
||
```
|
||
|
||
`das-phat`의 `phatRelativeFloor`와 `gcc-phat`의 `phatFloorRatio`는 모두 0~1의 상대
|
||
크기 floor이며 기본값은 `1e-6`입니다. GCC-PHAT는 `phatRelativeFloor`도 호환 alias로
|
||
받지만 새 코드에서는 알고리즘별 이름을 권장합니다. PHAT 경로는 mic gain을 위치 score에
|
||
사용하지 않고 알려진 mic·frequency-response phase만 보정합니다.
|
||
|
||
GCC-PHAT는 단일 tone보다 여러 FFT bin에 에너지가 있는 broadband 입력을 권장합니다. 하나의
|
||
band에 선택된 bin이 8개 미만이면 코어는 계산을 허용하지만 주기적 모호성과 ghost peak를 직접
|
||
검증해야 합니다. 또한 reference pair의 scan 전체 최대 절대 기하 지연이 `fftSize / 2` sample을
|
||
넘으면 circular correlation alias를 피하기 위해 plan 생성을 거절합니다. 이때는 `fftSize`를
|
||
늘리거나 배열·scan 기하를 조정합니다.
|
||
|
||
각 band의 `rawScale`은 `power` 또는 `score`입니다. `rawValue`가 scale에 무관한 표준 peak
|
||
값이며, `rawPower`는 `power`, `rawScore`는 `score`일 때만 쓰는 편의 alias입니다. `score`는
|
||
확률·신뢰도·SPL이 아니고 동일 알고리즘·설정 안에서 상대 peak를 비교하는 비교정 값입니다.
|
||
`power`도 입력과 calibration이 절대 단위로 교정되지 않았다면 물리적 W 또는 Pa²로 해석하지 않습니다.
|
||
GCC-PHAT score는 pair 수, band coefficient 수와 IFFT 크기로 정규화한 0–1 양의 상관 응답이라
|
||
scan 범위와 마이크 수에 의한 단순 재척도화는 피하지만, 여전히 확률로 보정된 confidence는 아닙니다.
|
||
|
||
### Narrowband MUSIC
|
||
|
||
`music`은 [Schmidt MUSIC](https://doi.org/10.1109/TAP.1986.1143830)을 sample covariance와
|
||
근거리 구면파 direct-path manifold에 적용합니다. 근거리 배열 처리의 문헌 배경은
|
||
[Huang–Barkat](https://doi.org/10.1109/8.86917)을 참고하세요. raw map은 reciprocal을 취하지
|
||
않은 Float64 noise projection
|
||
|
||
`q(r)=Σ_(j=K)^(M-1)|v_jᴴa(r)|²/(a(r)ᴴa(r))`
|
||
|
||
이고 objective는 minimum입니다. 표준 pseudospectrum `P=1/q`와 표시용 `P/Pmax`는 위치
|
||
확률·likelihood·confidence·신뢰구간이 아닙니다. raw `q`는 peak normalization이나 epsilon
|
||
floor로 바꾸지 않습니다.
|
||
|
||
각 Experiment band는 FFT spacing에 놓인 **아날로그 carrier 가설 하나**만 포함해야 합니다.
|
||
관측 ADC bin이 같아도 alias order가 다른 아날로그 가설은 별도 band입니다. Nyquist 위 값은
|
||
허용하며 signed alias가 음수이면 positive-bin STFT, mic calibration과 전체 complex manifold를
|
||
켤레 방향으로 사용합니다. 공간 위상은 `exp[-j sign(f_signed) 2π f_a Δr/c]`로 analog carrier
|
||
크기를 유지하고 signed ADC alias는 orientation만 정합니다. 이 carrier는 Experiment에서 온 가설이고 Scene source truth는
|
||
localizer에 전달되지 않습니다. 정확한 DC/Nyquist bin은 quadrature-degenerate라 거부합니다.
|
||
|
||
source 수는 `musicSourceCountMethod: "fixed"`와 `musicSourceCount: K` 또는
|
||
[Wax–Kailath](https://doi.org/10.1109/TASSP.1985.1164557) `"aic"`/`"mdl"`로 정합니다.
|
||
AIC/MDL은 `hopSize === fftSize`, 완전한 frame만 사용, `T>M`, roundoff보다 큰 양의 noise
|
||
eigenvalue가 필요합니다. IID complex Gaussian snapshot, spatially white noise와 일정한 source
|
||
수를 가정하지만 실제 데이터에서 이를 검증하지 않으며 score는 posterior가 아닙니다. 조건이
|
||
깨지거나 signal/noise eigengap이 수치 오차보다 작으면 `MusicUnavailableError`
|
||
(`code: "MUSIC_UNAVAILABLE"`)를 반환합니다.
|
||
|
||
동일 주파수의 coherent source는 covariance rank를 잃을 수 있습니다. grating lobe, 동률과 flat
|
||
objective를 고르기 좋게 깨지 않으며, diagonal loading, eigenvalue floor, spatial smoothing,
|
||
band 간 association/fusion을 적용하지 않습니다. [Wax–Shan–Kailath broadband 방법](https://doi.org/10.1109/TASSP.1984.1164400)은
|
||
문헌상 별도 알고리즘이며 현재 구현하지 않았습니다.
|
||
|
||
빠른 portable 예:
|
||
|
||
```js
|
||
const plan = createLocalizationPlan({
|
||
sampleRate: 48_000,
|
||
array,
|
||
scan,
|
||
bands: [{ id: "1k", minHz: 950, maxHz: 1_050, color: "#65f2bf" }],
|
||
stft: { fftSize: 512, hopSize: 256 },
|
||
algorithm: "music",
|
||
normalization: "none",
|
||
musicSourceCountMethod: "fixed",
|
||
musicSourceCount: 1,
|
||
});
|
||
const result = await localizeMultichannel({ plan, channels });
|
||
```
|
||
|
||
band는 정확히 한 FFT-grid center를 포함해야 하므로 `minHz/maxHz`는 사용 중인 `Fs/N`에서
|
||
하나의 center만 감싸야 합니다. CPU plan/working-set, covariance accumulation, projection MAC,
|
||
Jacobi pair-update와 FFT butterfly 상한은 할당·계산 전에 검사합니다. 기본 상한을 넘으면 부분
|
||
eigensystem이나 축소 map을 반환하지 않습니다.
|
||
|
||
## 실측 다채널 스트림 위치 추정
|
||
|
||
`realtime-localizer.js`는 Scene·음원 합성 없이 이미 sampling된 동기식 마이크 채널을 직접
|
||
받습니다. 외부 package 의존성이 없는 ES module이며, 다른 프로젝트에는 `acoustics-core`
|
||
디렉터리를 그대로 복사해 사용할 수 있습니다. 채널 순서는 `array.microphones` 순서와 같아야 하고
|
||
각 chunk는 동일 길이의 `Float32Array` 목록이어야 합니다.
|
||
|
||
`StreamingLocalizer`는 알고리즘 중립 class 이름입니다. 기존 `StreamingDasLocalizer`와 같은 class의
|
||
export alias이므로 오래된 import도 그대로 동작하며, `createStreamingLocalizer()` factory도 네
|
||
알고리즘을 모두 지원합니다. MUSIC plan은 `plan.musicPlan` alias로도 접근할 수 있습니다.
|
||
|
||
```js
|
||
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 },
|
||
algorithm: "das", // 호환 기본값. "das-phat" 또는 "gcc-phat"도 선택할 수 있습니다.
|
||
maxCompensationDb: 24,
|
||
peakDetection: { thresholdRatio: 0.5, maximumDetections: 8 },
|
||
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, rawValue, rawScale } = result.locations[0];
|
||
drawMarker(position, rawValue, rawScale, result.centerTime);
|
||
if (!result.deadlineMet) reportOverload(result.processingTimeMs, result.deadlineMs);
|
||
}
|
||
```
|
||
|
||
`locations`는 band 순서, 같은 band 안에서는 `rawValue` 내림차순의 평평한 목록입니다. 각 항목은
|
||
`{ bandId, rank, index, position, rawValue, rawScale }`이며 같은 band의 여러 local peak를 포함할
|
||
수 있습니다. 기존 DAS 결과의 `rawPower` 별칭은 그대로 유지됩니다. PHAT score는 `rawValue`와
|
||
`rawScale`을 기준으로 읽고, score peak 객체에 포함된 경우 `rawScore`를 같은 값의 별칭으로 씁니다.
|
||
`peakDetection.thresholdRatio` 기본값은 최강 peak의 0.5배,
|
||
`minimumPeakDistanceM` 기본값은 scan cell 대각선, `maximumDetections` 기본값은 8입니다.
|
||
기존처럼 `locations[0]`만 읽으면 최강 후보를 그대로 얻습니다.
|
||
|
||
Streaming 계산이 중단되거나 실패하면 해당 분석창과 sequence를 commit하지 않고 다음 호출에서 같은
|
||
창을 재시도합니다. 성공한 뒤에만 hop·sequence·`completedAnalyses`를 commit하며, 그 뒤 호출되는
|
||
`onResult` callback의 예외는 `failedAnalyses`에 포함하지 않습니다. 계산 중 capture overflow가 나면
|
||
ring의 오래된 sample은 평소와 같이 drop telemetry에 기록됩니다.
|
||
|
||
한 블록 결과의 `processingTimeMs`는 선택한 위치 추정 알고리즘과 local-peak 검출에 걸린 실제 시간을
|
||
기록합니다. Streaming
|
||
결과는 `deadlineMs = analysisHopSamples / sampleRate × 1000`과 `deadlineMet`을 더하며,
|
||
`getState()`는 최근/최대 처리 시간과 `deadlineMisses` 누계를 제공합니다. `deadlineMisses` 또는
|
||
`droppedSamples`가 증가하면 grid·대역 bin·분석창·update rate를 장치 성능에 맞게 줄여야 합니다.
|
||
이 deadline 비교는 실시간 성능 보장이 아니라 완료된 창의 측정값입니다. 목표 장치에서 warm-up을 거친
|
||
지속 부하를 직접 측정하고, capture·adapter·출력을 포함한 end-to-end deadline은 별도로 검증해야 합니다.
|
||
|
||
깊은 응답 notch의 역필터가 sensor noise까지 폭증시키지 않도록 inverse response의 양의 boost는 기본
|
||
24 dB로 제한합니다. Experiment의 `beamforming.maxCompensationDb` 또는 portable plan의 top-level
|
||
`maxCompensationDb`를 0~120 dB로 조절할 수 있습니다. 이 값은 boost만 제한하며 attenuation과 phase
|
||
보정은 유지합니다. 선택 bin 전체의 gain/phase calibration byte도 plan 생성 전에 산정하므로 lazy
|
||
cache가 나중에 `maximumPlanBytes`를 넘지 않습니다.
|
||
|
||
한 블록만 처리할 때는 `createLocalizationPlan()`과 `localizeMultichannel()`을 사용합니다.
|
||
`getState()`은 absolute sample origin, buffered/dropped sample, 대기 window 수와 고정 input
|
||
buffer byte 수를 돌려주므로 capture adapter가 지연을 감시할 수 있습니다. 실제 장치의 clock,
|
||
channel framing과 transport는 하드웨어별 adapter 책임이며 이 모듈은 그 다음 단계부터 시작합니다.
|
||
|
||
반복 CPU 분석 계획은 공통 거리 행렬과 STFT workspace를 재사용합니다. DAS 계열은 bin calibration,
|
||
Hann window, 임시 bin map과 예산 내의 Float64 steering matrix를, GCC-PHAT는 reference 선택과
|
||
correlation workspace를 추가로 유지합니다. 같은 plan을 동시에 두 분석에 쓰지 말고 설정이나 알고리즘이
|
||
바뀌면 새 plan을 만듭니다. 예를 들어 DAS로 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보다 전체 wide-band steering matrix가 크더라도 cache 전체를 끄지 않습니다. 선택 bin
|
||
앞쪽부터 예산에 들어가는 만큼만 Float64로 유지하고 나머지는 고정 microphone workspace에서 계산합니다.
|
||
`analysisCache`는 알고리즘별 실제 작업을 제공합니다. DAS 계열은 cached bin capacity,
|
||
uncached steering pair, STFT transform·FFT butterfly·complex MAC을, GCC-PHAT는 STFT/IFFT,
|
||
2-pass cross-spectrum operation, scan lookup과 correlation workspace reuse를 기록합니다.
|
||
|
||
`P`를 scan point 수, `M`을 mic 수, `T`를 STFT frame 수, `F`를 선택한 고유 bin 수,
|
||
`B`를 band 수, `N`을 `fftSize`라 하면 DAS의 주 scan 연산은 대략
|
||
`P × M × T × F` complex MAC입니다. GCC-PHAT는 forward STFT에 더해 reference pair `M-1`개마다
|
||
band bin을 두 번 순회하는 cross spectrum, `(M-1) × B`회의 길이 `N` IFFT,
|
||
`P × (M-1) × B`회의 fractional correlation lookup을 수행합니다. band별 bin 수를 `F_b`라 하면
|
||
cross-spectrum 항목 수는 구현의 두 pass를 포함해 `2 × (M-1) × T × ΣF_b`입니다. 따라서 GCC가
|
||
scan MAC을 줄여도 작은 grid에서는 forward STFT와 pair별 IFFT가 전체 시간을 지배할 수 있으므로
|
||
두 알고리즘 모두 실제 목표 장치에서 측정해야 합니다.
|
||
|
||
## 이동 음원과 분석 시간창
|
||
|
||
`sourcePositionAtTime`은 scene 시작 기준의 절대 시각에서 trajectory keyframe 사이를 선형 보간하며,
|
||
경로 범위 밖에서는 가장 가까운 끝점 위치를 유지합니다. `sampleQuasiStaticWindow`는 요청 경계를
|
||
sample 경계로 올림 정렬하고, 시간창 중앙의 위치에서 이동 음원을 정지한 것으로 근사합니다.
|
||
|
||
시간창은 요청 출력 시작보다 앞선 sampled noise·asset을 해당 source들의 최대 경로 지연만큼
|
||
pre-roll합니다. analytic tone은 과거 sample buffer 없이 절대시간에서 직접 계산하므로 tone의 더 긴
|
||
경로는 render buffer를 늘리지 않습니다. 그래도 창이 시작되기 전에 발음이 끝난 tone을
|
||
direct·reflection·scattering·diffraction·diffuse 도착 구간으로 선별해 제거하지 않습니다.
|
||
`signalOffsets`는 원래 scene/source의 절대 sample
|
||
offset과 출력 crop을 함께 보존합니다. analytic tone은 pre-roll 길이의 마이크 channel을 만들지 않고
|
||
요청 crop에 절대 sample 위상으로 바로 더합니다. sampled noise·asset은 지연에 필요한 pre-roll
|
||
방사 buffer와 zero-padded FFT는 유지하되 IFFT 결과의 crop 구간만 crop 크기 channel에 더합니다.
|
||
따라서 tone phase, seed 기반 noise index, audio asset 재생 위치와 ambient가 전체 timeline과 같은
|
||
절대 시간축을 사용하면서 `마이크 수 × pre-roll` 출력 메모리와 tone sample-add를 만들지 않습니다.
|
||
|
||
이 보장은 현재 희소 경로 모델 안에서의 **전파 지연 tail**에 관한 것입니다. quasi-static 창은 pre-roll
|
||
구간까지 포함해 source를 출력 창 중앙의 한 위치에 고정하며, retarded-time trajectory, 창 안의 연속
|
||
이동과 Doppler는 계산하지 않습니다. 합성 결과의 `windowing` metadata는 적용한 pre-roll, 출력 절대
|
||
sample, `source-position-fixed-at-window-midpoint` geometry와 `dopplerMode: not-modeled`를 기록합니다.
|
||
`sourceArrivals`는 source별 방사·도착 sample 경계와 출력에 겹친 path kind를,
|
||
`arrivingSourceIds`는 적어도 한 실제 path의 half-open 도착 구간이 출력과 겹친 source를 기록합니다.
|
||
이는 사후 ground-truth 평가에만 쓰이며 위치 추정 입력에는 전달하지 않습니다.
|
||
loop하지 않는 asset의 방사 종료는 선언 duration과 decoded channel duration 중 빠른 쪽이며, PCM
|
||
내용의 leading/trailing zero까지 분석해 truth를 줄이지는 않습니다.
|
||
sampled asset/noise의 fractional delay와 microphone response는 기존 zero-padded block FFT 모델을
|
||
그대로 사용하므로 임의 길이 analog microphone impulse response의 streaming state를 재현하는 모델은
|
||
아닙니다.
|
||
|
||
## Beam spread 의미
|
||
|
||
Peak의 `beamSpread`는 raw map의 peak 주변 연결 성분을 가중한 공간 covariance(`covarianceM2`, m²)와
|
||
그 RMS 주축 ellipse로 요약한 폭입니다. `power` map은 half-power/power-weighted,
|
||
`score` map은 half-maximum/score-weighted 규칙을 사용합니다. 어느 쪽도 가우시안 확률 모델,
|
||
위치 확률 또는 신뢰구간으로 보정된 값이 아니며 `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에 편차로 적용합니다. Conventional DAS는
|
||
알려진 calibration의 역 gain/phase를 모두 보정하고, DAS-PHAT와 GCC-PHAT는 amplitude gain을 무시한
|
||
채 알려진 phase만 보정합니다. raw `power`/`score` map과 표시용 normalized map은 서로 다른 buffer로
|
||
보존합니다. bin spectrum은 제한된 batch로 처리합니다. cooperative yield는 지원 환경에서
|
||
`scheduler.yield()`, 그 외에는 `MessageChannel`을 사용하고 timer task에도 주기적으로 기회를 주므로
|
||
진행 callback과 `AbortSignal` 취소를 유지하면서 연속 `setTimeout(0)` clamp를 피합니다. portable
|
||
plan의 기본 point batch는 DAS 계열 256개, GCC-PHAT 4,096개이고 STFT transform batch는
|
||
128개입니다. GCC-PHAT의 두 cross-spectrum pass도 16,384 operation마다 양보합니다.
|
||
`pointBatchSize`, `stftTransformBatchSize`, `crossSpectrumBatchSize`를
|
||
`computeOptions`로 조정할 수 있습니다.
|
||
|
||
마이크별 `frequencyResponse`는 2~64개의 `{ frequencyHz, gainDb, phaseDeg? }` 점으로 정의합니다.
|
||
`interpolateFrequencyResponse`는 dB와 wrapped phase를 구간 선형 보간하고 측정 범위 밖에서는 가장
|
||
가까운 끝점을 사용합니다. 합성 시 기존 sensitivity/phase 편차에 이 응답을 추가하며, CPU 위치 추정은
|
||
선택 FFT bin의 알고리즘별 역응답을 calibration에 포함합니다. WebGPU DAS 계열 경로는 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에 넣어 결과를 비교해야 합니다.
|
||
|
||
합성기는 source별로 경로를 한 receiver 묶음씩 만들고, sampled source도 하나씩
|
||
`render → spectrum → microphone 적용 → 해제`합니다. render 길이를 `N`, 출력 crop을 `C`,
|
||
마이크 수를 `M`, sampled source `i`의 자체 최대 경로 지연을 포함한 FFT 길이를 `F_i`라 하면
|
||
명시적으로 살아 있는 typed-array peak는 다음입니다.
|
||
|
||
```text
|
||
4 M C + max_i(4 N + 32 F_i)
|
||
```
|
||
|
||
`32 F_i`는 Float64 spectrum 두 개와 재사용 workspace 두 개입니다. sampled ambient는
|
||
zero-delay `F_0 = nextPow2(N + 2)` 후보 하나로 같은 max에 들어가며, analytic tone이나 다른
|
||
sampled source의 수를 transient byte에 더하지 않습니다. 이 논리적 typed-array working set을
|
||
기본 512 MiB로 할당 전에 검사하고, Experiment validator도 동일 estimator와 실제
|
||
reflection·scattering·diffraction·diffuse 지연을 사용합니다. 외부 decoded asset buffer와 JS
|
||
engine의 객체 heap/RSS까지 512 MiB라고 주장하는 값은 아닙니다.
|
||
|
||
라이브 preflight가 만든 경로 count·source별 최대 지연 summary는 geometry, source/microphone id와
|
||
좌표, acoustics/seed, 거리 설정 전체의 서명이 맞을 때만 합성 preflight에 재사용합니다. summary는
|
||
불변이고 모듈 내부에서 인증되므로 복제·변조·다른 geometry의 cache는 자동 재계산합니다. 동일
|
||
Scene·Array 객체에서 band/scan/backend만 바뀐 반복 validation은 이 exact summary를 재사용합니다. 실제
|
||
전달함수 경로는 receiver별로 한 번 다시 만들며, sampled source의 IFFT는 아직 source와 microphone
|
||
조합마다 한 번입니다. 다수 non-tone source의 transform batching은 수학 동치와 메모리 예산을 함께
|
||
검증해야 하는 후속 최적화입니다.
|
||
|
||
실측·장시간 입력은 위 streaming localizer의 bounded ring buffer로 전체 녹음을 메모리에 보관하지
|
||
않고 처리할 수 있습니다. CPU analysis plan도 기본 512 MiB
|
||
hard cap을 할당 전에 검사합니다. DAS steering cache는 별도의 64 MiB 안에서 가능한 bin만 유지하고,
|
||
GCC-PHAT는 거리·STFT·correlation workspace 전체를 plan 한도 안에서 검사합니다. Plan 없는 단발
|
||
GCC 분석도 거리 행렬을 만들기 전에 spectrum·calibration·output을 포함한 working set을 preflight합니다.
|
||
|
||
192채널·50×50 fixture는 기존 DAS 호환 경로를 실제 합성부터 peak 검출까지 고정하며, 별도의 결정적
|
||
회귀 fixture가 DAS-PHAT와 GCC-PHAT의 score scale·lag 부호·plan 재사용을 검사합니다.
|