279 lines
14 KiB
Markdown
279 lines
14 KiB
Markdown
# 음향 환경과 마이크 응답 모델
|
||
|
||
micarrsim의 음향 합성은 마이크 어레이 배치와 위치추정 알고리즘의 상대 성능을 빠르게 비교하기
|
||
위한 모델이다. ADC나 마이크 전자회로까지 재현하는 하드웨어 시뮬레이터가 아니며, 자유음장과
|
||
계산량이 제한된 실내 희소 경로 근사 중 하나를 선택한다.
|
||
|
||
## 신호 처리 범위
|
||
|
||
Snapshot 한 번의 처리 순서는 다음과 같다.
|
||
|
||
1. tone, noise 또는 audio asset으로 음원 신호를 만든다.
|
||
2. 음원과 각 마이크 사이의 직접음 또는 희소 multipath 전달함수를 만든다.
|
||
3. 마이크별 `sensitivityDb`, `phaseOffsetDeg`와 `frequencyResponse`를 수신 신호에 적용한다.
|
||
4. 합성된 동기식 다채널 신호를 STFT하고 `beamforming.algorithm`으로 선택한 위치 추정
|
||
알고리즘의 scan map을 계산한다.
|
||
5. 알고리즘에 맞게 알려진 마이크 응답을 보정하고 map의 peak와 평가 지표를 구한다.
|
||
|
||
Scene의 `acoustics`를 생략하거나 `model: "free-field"`로 지정하면 기존 직접음 결과를
|
||
유지한다. `model: "shoebox"`는 직육면체 방 안에서 경로 수가 제한된 근사를 추가한다.
|
||
|
||
## 자유음장
|
||
|
||
음원과 마이크 사이 거리 `r`, 음속 `c`에 대해 직접음의 지연은 `r / c`, 진폭은
|
||
최소 거리 clamp를 적용한 `1 / r`이다. 분수 sample 지연은 zero-padded FFT의 주파수 영역
|
||
위상 이동으로 계산한다.
|
||
|
||
이 모델은 반사가 거의 없는 조건의 기준선과 배열 자체의 aperture·형상·대역 효과를 비교할 때
|
||
적합하다.
|
||
|
||
## Shoebox 희소 경로 근사
|
||
|
||
`shoebox` 모델은 source–microphone 쌍마다 다음 경로만 만든다.
|
||
|
||
- 직접음 1개. 축 정렬 occluder와 교차하면 `transmissionDb`만큼 감쇠한다.
|
||
- 방의 여섯 면마다 최대 1개의 1차 image-source 반사.
|
||
- 반사 에너지의 일부를 0.2–2 ms의 결정적 지연으로 나누는 scattering 경로.
|
||
- 직접음을 막는 occluder마다 여덟 corner 중 최단 우회 거리로 만든 회절 경로 1개.
|
||
- `tapCount`로 수를 제한하고 RT60에 걸쳐 -60 dB로 감쇠하는 결정적 diffuse tail.
|
||
|
||
같은 Scene seed와 source/microphone id에서는 scattering과 tail이 재현된다. 모든 희소 경로는
|
||
source–microphone 쌍의 주파수 전달함수 하나로 먼저 합쳐지고 IFFT는 한 번만 수행된다. 경로 위상은
|
||
recurrence로 갱신하고 1,024 bin마다 다시 고정해 bin마다 삼각함수를 반복하지 않는다.
|
||
|
||
Scene JSON의 사용자 설정 예는 다음과 같다.
|
||
|
||
```json
|
||
{
|
||
"acoustics": {
|
||
"model": "shoebox",
|
||
"room": {
|
||
"min": [-3.0, -2.5, -0.5],
|
||
"max": [3.0, 2.5, 4.0]
|
||
},
|
||
"reflections": {
|
||
"order": 1,
|
||
"wallGainDb": -8,
|
||
"perWallDb": {
|
||
"zMin": -14
|
||
},
|
||
"scattering": 0.25
|
||
},
|
||
"diffuseReverb": {
|
||
"enabled": true,
|
||
"rt60Seconds": 0.4,
|
||
"gainDb": -20,
|
||
"tapCount": 6
|
||
},
|
||
"occluders": [
|
||
{
|
||
"id": "screen",
|
||
"min": [-0.4, -0.1, 0.6],
|
||
"max": [0.4, 0.1, 1.8],
|
||
"transmissionDb": -18,
|
||
"diffractionGainDb": -12
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
`room`은 shoebox에서 필수다. 정적 source, trajectory keyframe과 occluder는 room 안에 있어야
|
||
하며 occluder id는 중복될 수 없다. 사용자 설정에서도 마이크 좌표가 room 안에 놓이도록 구성해야
|
||
1차 반사가 생성된다.
|
||
|
||
## Camera 환경 preset과 비용
|
||
|
||
Camera의 **고급 설정 → 음향 환경**은 현재 geometry 전체가 들어가도록 room을 자동 추론하고 다음
|
||
preset을 적용한다.
|
||
|
||
| 선택 | 벽 gain | scattering | RT60 / tail gain / taps | 수신점당 최대 경로 |
|
||
| --- | ---: | ---: | ---: | ---: |
|
||
| 자유음장 | — | — | — | 1 |
|
||
| 흡음된 실내 | -14 dB | 0.15 | 0.2 s / -26 dB / 4 | 17 |
|
||
| 일반 실내 | -8 dB | 0.25 | 0.4 s / -20 dB / 6 | 19 |
|
||
| 반사 강한 실내 | -4 dB | 0.40 | 0.8 s / -14 dB / 10 | 23 |
|
||
|
||
표의 상한은 직접음 1개, 반사 6개, scattering 6개와 tail tap 수를 합한 값이다. 사용자 occluder는
|
||
각 source–microphone 직접 경로를 실제로 막을 때 하나씩 추가된다. 대략적인 합성 경로 예산은
|
||
`음원 수 × 마이크 수 × 수신점당 최대 경로`다. Camera는 실행 전에 이 예산을 표시한다.
|
||
|
||
반사·tail을 늘리면 신호 합성의 전달함수 누산도 늘어난다. DAS 계열의 scan 누산은 대략
|
||
`O(scan points × microphones × selected bins × STFT frames)`이고, GCC-PHAT는 pair별 상호
|
||
상관을 만든 뒤 `O(scan points × (microphones - 1) × bands)` lookup을 수행한다. 어느 방식이든
|
||
STFT와 거리 cache 비용은 별도로 필요하다. 처음에는 50×50 grid와 자유음장으로 기준선을 만들고,
|
||
같은 입력에서 환경과 알고리즘을 하나씩 바꾸어 비교하는 것이 좋다.
|
||
|
||
## 위치 추정 알고리즘
|
||
|
||
마이크 `m`, STFT frame `t`, 주파수 `f`의 복소 spectrum을 `X_m(t, f)`, scan point `p`와
|
||
마이크 사이 거리를 `r_m(p)`, 파수를 `k = 2πf/c`라고 하자. `φ_m(f)`는 알려진 마이크의 정적
|
||
phase offset과, 주파수 응답 보정을 켠 경우 response phase를 합한 값이다. 모든 map은 scan point
|
||
마다 하나의 비음수 상대값을 만들지만, 그 값은 위치의 확률이나 posterior가 아니다.
|
||
|
||
### DAS (`das`)
|
||
|
||
Conventional frequency-domain delay-and-sum은 각 후보점에서 구면파 전파 위상을 되돌려 채널을
|
||
coherent sum한다.
|
||
|
||
```text
|
||
B_DAS(p,t,f) = (1/M) Σ_m A_m⁻¹(f) X_m(t,f) exp(j[k r_m(p) - φ_m(f)])
|
||
P_DAS(p) = mean_(t,f) |B_DAS(p,t,f)|²
|
||
```
|
||
|
||
`A_m⁻¹(f)`에는 sensitivity의 역 gain과, 설정한 경우 frequency-response gain의 제한된 역보정이
|
||
들어간다. 구현은 frame별 제곱 크기를 먼저 평균하고 선택 band의 bin들을 다시 평균한다. 따라서
|
||
Result의 `rawMap.scale`은 `power`지만, 절대 SPL로 calibration한 power가 아니라 같은 입력·설정
|
||
안에서 비교하는 선형 상대 power다. CPU와 WebGPU를 지원한다.
|
||
|
||
### DAS-PHAT (`das-phat`)
|
||
|
||
DAS-PHAT는 steering 전에 각 frame·bin·마이크 spectrum을 위상만 남기도록 정규화한다.
|
||
|
||
```text
|
||
U_m(t,f) = X_m(t,f) / |X_m(t,f)|
|
||
S_DAS-PHAT(p) = mean_(t,f) |(1/M) Σ_m U_m(t,f) exp(j[k r_m(p) - φ_m(f)])|²
|
||
```
|
||
|
||
한 frame·bin에서 최강 마이크 크기의 기본 `10⁻⁶`배인 상대 floor 이하의 값은 `0`으로 두어
|
||
무음이나 극소값의 불안정한 위상을 제외한다. 입력과 calibration의 amplitude gain은 coherent sum에 사용하지 않고,
|
||
알려진 phase만 보정한다. 그 결과 source spectrum이나 채널 gain 차이에 덜 끌릴 수 있지만 유용한
|
||
amplitude 정보도 버리며, 약한 bin의 위상과 multipath에 민감할 수 있다. `rawMap.scale`은 무차원
|
||
`score`이고 CPU와 WebGPU를 지원한다.
|
||
|
||
### GCC-PHAT (`gcc-phat`)
|
||
|
||
현재 GCC-PHAT는 배열 중심에서 가장 먼 마이크 하나를 결정적인 reference로 고르고, reference와
|
||
나머지 `M - 1`개 마이크의 pair만 사용한다. 각 band와 pair에 대해 다음 PHAT cross spectrum을
|
||
STFT frame에 걸쳐 평균한다.
|
||
|
||
```text
|
||
C_mr(t,f) = X_m(t,f) conj(X_r(t,f))
|
||
G_mr(f) = mean_t [C_mr(t,f) / max(|C_mr(t,f)|, floor)]
|
||
× exp(j[φ_r(f) - φ_m(f)])
|
||
```
|
||
|
||
기본 `floor`는 해당 band·pair의 모든 선택 bin과 frame에서 얻은 최대 cross-spectrum 크기의
|
||
`10⁻⁶`배다. 이 floor는 amplitude를 score 가중치로 쓰기 위한 것이 아니라 극소 cross spectrum의
|
||
불안정한 위상 정규화를 제한한다.
|
||
|
||
선택 band 밖의 bin은 `0`으로 두고 켤레 대칭 spectrum을 IFFT하여 원형 상호상관
|
||
`R_mr,b(ℓ)`을 만든다. scan point마다 far-field 각도 근사가 아니라 정확한 거리 차이로 near-field
|
||
예측 lag를 계산한다.
|
||
|
||
```text
|
||
ℓ_mr(p) = [r_m(p) - r_r(p)] sampleRate / c
|
||
Q_b(p) = Σ_(m≠r) R_mr,b(ℓ_mr(p))
|
||
K_b = I(0 ∈ b) + I(N/2 ∈ b) + 2 × #(나머지 positive bins)
|
||
S_b(p) = clamp[N Q_b(p) / ((M-1) K_b), 0, 1]
|
||
```
|
||
|
||
분수 sample lag는 IFFT 원형 상호상관의 이웃 두 sample을 선형 보간한다. Hermitian coefficient
|
||
수 `K_b`, IFFT의 `1/N`, reference pair 수로 나눈 뒤 음의 상관은 0으로 두어 score를 0–1로
|
||
제한한다. 따라서 같은 위치 가설의 raw GCC score는 scan 범위나 마이크 수 자체에 의해 재척도화되지
|
||
않는다. 이 값도 확률이나 calibration된 confidence는 아니다. `rawMap.scale`은 `score`다.
|
||
PHAT가 amplitude를 제거하므로 sensitivity와 frequency-response gain은 역보정하지 않고 알려진
|
||
phase만 보정한다. 현재 CPU만 지원하고 두 개 이상의 마이크가 필요하다.
|
||
|
||
GCC-PHAT는 broadband 신호에서 상호상관 peak가 뚜렷할수록 유리하다. 단일 tone이나 지나치게 좁은
|
||
band는 주기적으로 같은 phase를 만드는 lag가 많아 여러 위치가 비슷한 score를 얻을 수 있고,
|
||
반사음이나 여러 동시 음원은 pair 상관 peak를 섞는다. IFFT 길이가 `N = fftSize`이면 표현 가능한
|
||
비중첩 원형 lag는 절대값 `N/2` sample까지다. 현재 구현은 scan 전체의 최대 기하학적 lag가 이를
|
||
넘으면 실행을 거절하므로 FFT를 키우거나 array aperture 또는 scan 범위를 줄여야 한다. 이 검사는
|
||
원형 lag alias를 막지만 좁은 대역의 주기적 모호성, 제한된 bandwidth, scan grid 양자화까지
|
||
해결하지는 않는다. 단일 reference 방식은 모든 마이크 쌍을 쓰는 GCC보다 계산량이 작지만 pair
|
||
중복성과 고장 마이크에 대한 강건성도 작다.
|
||
|
||
## 마이크 주파수 응답
|
||
|
||
각 마이크에는 2–64개의 측정점을 지정할 수 있다. `frequencyHz`는 0–1,000,000 Hz에서
|
||
중복 없이 오름차순이어야 하며 `gainDb` 범위는 -120–120 dB, 선택적 `phaseDeg` 범위는
|
||
-180–180°다.
|
||
|
||
```json
|
||
{
|
||
"id": "mic-001",
|
||
"position": [-0.225, 0.225, 0],
|
||
"sensitivityDb": 0.6,
|
||
"phaseOffsetDeg": -1.5,
|
||
"frequencyResponse": [
|
||
{ "frequencyHz": 100, "gainDb": -8.0, "phaseDeg": -12 },
|
||
{ "frequencyHz": 1000, "gainDb": -0.7, "phaseDeg": -2 },
|
||
{ "frequencyHz": 8000, "gainDb": 2.1, "phaseDeg": 7 },
|
||
{ "frequencyHz": 20000, "gainDb": -5.0, "phaseDeg": 20 }
|
||
]
|
||
}
|
||
```
|
||
|
||
측정점 사이 gain dB와 phase는 선형 보간한다. phase는 ±180° 경계에서 가장 짧은 방향으로
|
||
보간하고 측정 범위 밖에서는 가장 가까운 끝점 값을 유지한다. 합성기는 이 curve를 실제 마이크
|
||
채널에 적용한다.
|
||
|
||
합성기는 알고리즘과 무관하게 sensitivity, 정적 phase offset과 frequency-response curve를 수신
|
||
channel에 적용한다. 위치 추정에서의 역보정은 알고리즘별로 다르다.
|
||
|
||
- DAS는 sensitivity 역 gain과 정적 phase를 항상 보정한다. `compensateFrequencyResponse`를
|
||
생략하거나 `true`로 두면 response의 역 gain과 phase도 보정한다. 응답 notch의 무제한 gain
|
||
boost는 noise까지 키울 수 있으므로 `maxCompensationDb`로 제한하며 기본값은 24 dB, 허용 범위는
|
||
0–120 dB다. 응답 peak를 줄이는 attenuation과 phase 보상에는 이 boost 상한을 적용하지 않는다.
|
||
- DAS-PHAT와 GCC-PHAT는 amplitude를 정규화하므로 sensitivity와 response gain의 역보정을
|
||
사용하지 않는다. 정적 phase는 항상 보정하고, `compensateFrequencyResponse`가 `true`이면 알려진
|
||
response phase도 보정한다. 따라서 `maxCompensationDb`는 두 PHAT 알고리즘의 score에는 영향을
|
||
주지 않는다.
|
||
|
||
```json
|
||
{
|
||
"beamforming": {
|
||
"algorithm": "das",
|
||
"backend": "auto",
|
||
"minimumDistance": 0.01,
|
||
"normalization": "peak",
|
||
"dynamicRangeDb": 45,
|
||
"compensateFrequencyResponse": true,
|
||
"maxCompensationDb": 24
|
||
}
|
||
}
|
||
```
|
||
|
||
Camera의 **마이크 주파수 응답 처리**에서 **측정 curve 적용 + 보정하지 않음**을 고르면 합성에는
|
||
curve가 남고 위치 추정의 response gain/phase 역보정만 꺼진다. DAS-PHAT와 GCC-PHAT는 여전히
|
||
amplitude를 버리지만, curve가 신호 대 잡음과 relative floor 통과 여부에 준 영향까지 되돌리지는
|
||
못한다. 같은 배열에서 알려진 response 보정 유무의 robustness를 비교할 때 사용한다. curve가 없는
|
||
마이크는 0 dB, 0°로 취급한다.
|
||
|
||
## 결과에서 확인할 값
|
||
|
||
Result `signalStats`에는 다음 선택 필드가 들어간다.
|
||
|
||
- `acousticModel`: `free-field` 또는 `shoebox`
|
||
- `acousticPathCount`: 실제 합성에 사용한 전체 경로 수
|
||
- `acousticPathCounts`: `direct`, `reflection`, `scattering`,
|
||
`diffraction`, `diffuse` 종류별 수
|
||
|
||
같은 입력과 seed에서 환경 preset만 바꿨다면 이 값과 위치 오차·precision/recall·PSR을 함께
|
||
비교할 수 있다.
|
||
|
||
Result 최상위 `algorithm`은 실제 실행한 `das`, `das-phat` 또는 `gcc-phat`를 기록한다. band의
|
||
`rawMap.scale`은 DAS에서 `power`, 두 PHAT 방식에서 `score`다. peak 정규화된 `displayMap`은
|
||
시각 비교용일 뿐 어느 scale도 확률분포나 신뢰구간으로 바꾸지 않는다.
|
||
|
||
## 정확도 범위와 제외 사항
|
||
|
||
이 구현은 FDTD/FEM/BEM이나 room impulse response 측정치를 대신하지 않는다. 다음 항목은
|
||
계산하지 않는다.
|
||
|
||
- 파동 방정식과 고차 반사
|
||
- 재질별 주파수 의존 흡음과 복잡한 mesh 산란
|
||
- edge 형상·파장에 따른 물리 회절
|
||
- 음원과 마이크 지향성, 바람, Doppler
|
||
- 절대 음압/SPL, 마이크 self-noise와 비선형 왜곡
|
||
- preamp, anti-alias filter, ADC quantization, channel clock jitter·drift
|
||
- 실제 장착 구조와 마이크 위치 공차
|
||
|
||
따라서 preset의 RT60이나 wall gain은 특정 방을 인증하는 값이 아니라 배열과 위치 추정 알고리즘이
|
||
multipath에 얼마나 민감한지 비교하는 조건이다. 실제 장치 성능을 평가할 때는 같은
|
||
Array/scan/band 설정으로
|
||
동기식 실측 채널을 [실시간 위치추정 코어](realtime-porting.md)에 입력하고 합성 결과와 나란히
|
||
비교해야 한다. 주파수 응답 curve만으로 noise floor, distortion, ADC와 clock 오차까지 모사되지는
|
||
않는다.
|