fix: normalize ambience loudness for v0.2.5
This commit is contained in:
+41
-9
@@ -1,4 +1,4 @@
|
||||
# Find-Word v0.2.3 설계
|
||||
# Find-Word v0.2.5 설계
|
||||
|
||||
## 데이터와 출제
|
||||
|
||||
@@ -92,20 +92,52 @@ Promise를 무시한다. 질문과 보기는 재생 완료 후 DOM에 생성되
|
||||
50ms fade로 루프 접합 클릭을 줄인다. 절차적 잡음 합성 코드는 제거했다.
|
||||
|
||||
`src/audio/engine.ts`는 클릭 이벤트에서 AudioContext 생성·resume을 호출한 뒤
|
||||
같은 출처의 WAV를 로드한다. mono PCM을 decode하고 음성과 배경의 전체 버퍼
|
||||
RMS를 0.03으로 정규화한다. `gain = 10^(dB/20)`으로 목표/배경 비율을 지정한다. 정규화한 세 신호의 최대
|
||||
피크 합을 계산해 모든 경로를 공통 감쇠하므로 합계 peak ≤ 0.8이고 SNR이 유지된다.
|
||||
시간별·주파수별 청감이나 물리적 dB SPL을 보장하는 수치는 아니다.
|
||||
같은 출처의 WAV를 로드한다. `integrity.ts`가 JS 번들에 고정한 109개 URL·bytes·SHA-256과 응답을
|
||||
디코드 전에 대조한다. 런타임 manifest fetch나 외부 요청은 없다. mono·길이·유한 sample·non-silence와
|
||||
장치 rate로 리샘플한 PCM의 경로별 peak 상한도 확인한다. 변조/상한 초과는 재시도 가능한 오류이며
|
||||
파일별 자동 감쇠로 덮지 않는다.
|
||||
|
||||
배경은 새 FFmpeg ebur128 실측에서 구한 double precision 스칼라 volume으로 **−32.5 LUFS**에 맞춘 저장 PCM을 그대로 사용한다.
|
||||
최종 ebur128 목표 ±0.2 LU/spread ≤0.4 LU/true peak ≤−6 dBTP를 강제한다. 생성 내부 ceiling은 −6.2 dBTP다.
|
||||
독립 pyloudnorm 0.2.0도 통과했다. 새 market의 VAD ASR 실패는 불변 진단으로 남기고,
|
||||
릴리스 gate는 자동 empty-ASR OR [exact-byte Oz 청취 승인](../.agent/RELEASE-APPROVAL-v0.2.5.json)을 검사한다.
|
||||
현재는 후자만 통과한다. `scripts/release_approval.py`가 전달 ZIP·manifest·8 WAV·notice 바이트와
|
||||
문구/시각/범위를 검증하며 `legalPrivacyClearance=false`를 강제한다.
|
||||
voice와 intro만 기존 RMS **0.03** 정규화를 유지한다. `g(db)=10^(db/20)`은 목표 음성 상대 gain이며
|
||||
배경마다 RMS가 다르므로 표시 SNR은 기준 gain의 dB 변화다. 실제 파일 RMS 비율/순간 청감/dB SPL은 보장하지 않는다.
|
||||
|
||||
고정 상한 `B=0.55`, `V=0.25`, `I=0.20`, `SNR_MAX=12`, 안내 시작 배율 `A=1.8`, 합성 ceiling `C=0.8`을 사용한다.
|
||||
24kHz 실제 최대값은 배경 0.47195435, RMS 정규화 voice 101개 0.20529779, intro 0.14995423이다.
|
||||
새 자산의 실제 Chrome 24/44.1/48/96kHz 전수 디코드 최대값은 배경 0.48797759,
|
||||
voice 0.20585650, intro 0.14999594다.
|
||||
[review-fixes 브라우저 증거](../.agent/evidence-v0.2.5/review-fixes/browser-summary.json)에 전체 측정을 보존했다.
|
||||
여유를 둔 상한은 이 모든 값보다 크며 재생 때도 검사한다. 실제 GainNode의 Float32 plateau는
|
||||
0.4198884665966034로 환경/SNR에 걸쳐 동일하고 JS 예약값과의 차이는 Float32 반올림이다.
|
||||
|
||||
```text
|
||||
Q = B + V × 10^(12/20) + I × A = 1.905267926383743
|
||||
G = min(1, C/Q) = 0.4198884518664126
|
||||
backgroundGain = G
|
||||
voiceGain(db) = G × 10^(db/20)
|
||||
introGain = G × 1.8 (기존 ramp로 1/15까지 감소)
|
||||
|mix(t)| ≤ master × G × (B + V × 10^(db/20) + I × A) ≤ 0.8
|
||||
```
|
||||
|
||||
삼각부등식이 모든 위상·loop offset·세 경로가 동시에 겹치는 경우까지 덮는다. master 범위는 [0,1],
|
||||
배경 fade는 [0,G], intro ramp도 [0,G×1.8] 이내이므로 상한을 키우지 않는다. intro가 없어도 같은 G를 사용한다.
|
||||
환경·실제 sample peak·현재 SNR을 G 계산에 넣지 않는다. SNR은 voice만 바꾸고 master는 모든 경로를 함께 바꾼다.
|
||||
`mixing-loudness.test.ts`는 101×8×25=20,200개 조합에서 유한 gain·비율·peak 상계를 검사한다.
|
||||
이는 디지털 샘플 안전성이다. DAC의 재구성 필터·기기 음압·임상 청감의 보장은 아니다.
|
||||
|
||||
`src/audio/assets.ts`는 음성 자산 버전과 URL 생성을 한 곳에서 관리한다.
|
||||
intro·목표 100개·현장음 8개 모두 `?v=0.2.3`을 붙여 동일 파일명에 남은 이전 캐시를
|
||||
intro·목표 100개·현장음 8개 모두 `?v=0.2.5`을 붙여 동일 파일명에 남은 이전 캐시를
|
||||
피한다. 파일을 다시 생성하면 제품 버전 유지 여부와 관계없이 자산 버전을
|
||||
갱신한다. 버전 확인을 위한 외부 요청이나 런타임 manifest 요청은 없다.
|
||||
|
||||
```text
|
||||
배경 BufferSource → 배경 Gain (공통 감쇠, 시작/끝 fade) ─┐
|
||||
목표 BufferSource → 음성 Gain (공통 감쇠 × 10^(dB/20)) ─────┼→ master Gain → destination
|
||||
안내 BufferSource → 안내 Gain (공통 감쇠 × 1.8 → 0.12) ─────┘
|
||||
배경 BufferSource → 배경 Gain (고정 G, 시작/끝 fade) ─┐
|
||||
목표 BufferSource → 음성 Gain (고정 G × 10^(dB/20)) ─────┼→ master Gain → destination
|
||||
안내 BufferSource → 안내 Gain (고정 G × 1.8 → 0.12) ─────┘
|
||||
```
|
||||
|
||||
배경 소스는 `loop=true`이며 목표 ended에서 즉시 stop/disconnect한다.
|
||||
|
||||
Reference in New Issue
Block a user