fix: normalize ambience loudness for v0.2.5

This commit is contained in:
oz
2026-10-14 12:12:13 +00:00
parent 812540c743
commit 91aff66445
390 changed files with 97709 additions and 257 deletions
+93 -5
View File
@@ -1,4 +1,4 @@
# Find-Word v0.2.3 오디오 출처와 재현
# Find-Word v0.2.5 오디오 출처와 재현
## 포함 자산과 생성 설정
@@ -70,7 +70,8 @@ MP3를 24kHz mono signed 16-bit little-endian PCM으로 디코드한다. 파일
생성기와 독립 자산 테스트는 101개 파일의 원문·집합·SHA-256·bytes·duration,
24kHz mono PCM16, 1–20초 길이, RMS > 100/32768, full-scale 미도달,
총 48MiB 미만을 검사한다. 기존 음성 단독 검사는 RMS 0.03과 +6dB에서 peak < 0.8을
요구한다. 0.2.3의 사용자 최대 +12dB 혼합은 세 경로 공통 감쇠로 별도 검증한다. 앱은 Web Audio에서 배경·음성을 RMS 정규화한 뒤 상대 SNR을 적용한다.
요구한다. v0.2.5 최대 +12dB 혼합은 [고정 headroom 수식](ARCHITECTURE.md#오디오)으로 별도 검증한다.
앱은 voice/intro의 RMS 정규화를 유지하고 배경 loudness 정규화 PCM을 그대로 쓴다.
물리적 음압이나 주파수별 청감을 보정한 값은 아니다.
## 도구 라이선스와 서비스 조건
@@ -102,12 +103,99 @@ MP3를 24kHz mono signed 16-bit little-endian PCM으로 디코드한다. 파일
대표 샘플과 게임 내 소음 혼합 상태를 직접 들어 인수 판단한다. 배경은 실제 현장 녹음의 짧은 구간이다. 이 게임의 수치는 임상 검증·치료·청력 판정용이 아니다.
## v0.2.3 현장 녹음 파생본
## v0.2.5 loudness 정규화
**exact 자산 소유자 승인:** loudness 수정은 두 계기를 통과했다. 새 market WAV의 동일 VAD ASR 조건은
여전히 실패하며, 릴리스는 아래 Oz의 새 exact-byte 청취 승인 경로로만 수용한다. 법적/privacy clearance는 없다.
기존 loudnorm input_i의 0.01 LU spread는 같은 파일의 전용 ebur128 **2.0 LU**, 독립 pyloudnorm 0.2.0
**1.925 LU** 결과에 의해 거부되었다. loudnorm input_i를 수용 계기로 인정하지 않는다.
[거부된 당시 RESULT](../.agent/evidence-v0.2.5/review-fixes/rejected-RESULT-v0.2.5.md),
[기존 원문 측정](../.agent/evidence-v0.2.5/independent-loudness.json)과 기존 증거 전체는 불변 역사다.
원본 8개 SHA-256/bytes·creator·license·attribution·선택 구간을 모두 보존했다.
[정규화 계획](../.agent/AMBIENCE-NORMALIZATION-v0.2.5.json)은 불변 source plan의 SHA-256을 포함한다.
도구는 **ffmpeg version 6.1.1-3ubuntu5**이며 일반 회귀 검사는 Python 표준 라이브러리와 FFmpeg만 사용한다.
1. 원본 전체의 hash/bytes를 검증한 뒤 기존 seek/full decode sample trim으로 승인 구간을 추출한다.
2. 24kHz mono float PCM 288,000 samples에 기존 양끝 50ms 선형 fade를 적용한다.
3. 정확한 faded PCM을 새 `ebur128=peak=true` 프로세스로 측정하고 **gain_db=−32.5−measured_i**를 계산한다.
4. **measured_true_peak+gain_db > −6.2 dBTP**면 변환 전에 실패한다. market의 파형을 보존할 수 있도록
공통 목표를 −32.5 LUFS로 선택했다. −6.2 내부 ceiling은 최종 −6 dBTP 조건에 0.2 dB 여유를 둔다.
5. 같은 PCM에 오직 `volume=<gain_db>dB:precision=double`을 적용하고 FFmpeg `pcm_s16le`로 인코딩한다.
표준 `wave` writer는 44-byte WAV 헤더를 쓴다. compressor/limiter/loudnorm은 사용하지 않는다.
6. staged WAV 전체를 새 ebur128 프로세스로 측정한다. 목표 오차가 ±0.2 LU 밖일 때만 원래 faded PCM에서
스칼라를 보정한다. 최대 총 3회, 매 시도 peak 사전 검사를 수행하고 비수렴은 실패한다.
7. 전체 staging의 형식/길이/fade/출처/notice/hash를 검증하고 ebur128을 다시 실행한다.
**목표 ±0.2 LU, spread ≤0.4 LU, true peak ≤−6 dBTP**를 강제한다. 생성 중에는 −6.2도 강제한다.
전부 통과한 8 WAV·manifest·고지를 단일 `renameat2(RENAME_EXCHANGE)`로 교환한다.
source mismatch·NaN·측정/쓰기/교환 실패·목표/peak 위반은 이전 공개 디렉터리를 보존한다.
manifest `normalization`에는 목표/허용오차/spread/두 ceiling/계기/최대 반복 수가 있고,
`toolVersions.ffmpeg`에는 실제 도구 버전이 있다. 각 `conversion.loudness`는 faded PCM hash,
입력 ebur128 통계, 매 iteration의 정확한 gain/filter/예측 peak/출력 통계와 최종 통계를 보존한다.
이번 8개는 모두 **첫 시도에 수렴**하여 보정 반복이 필요하지 않았다.
| 환경 | 원본 구간(초) | 입력 LUFS | gain dB | 최종 LUFS | true peak dBTP | sample peak |
| --- | --- | --- | --- | --- | --- | --- |
| street | 35–47 | -35.7 | 3.2 | -32.5 | -10.2 | 0.31008911 |
| mall | 36–48 | -22.1 | -10.4 | -32.5 | -12.2 | 0.23022461 |
| theater | 45–57 | -25.7 | -6.8 | -32.5 | -16.1 | 0.15737915 |
| department | 90–102 | -24.1 | -8.4 | -32.5 | -16.6 | 0.14721680 |
| market | 24–36 | -26.4 | -6.1 | -32.5 | -6.2 | 0.47195435 |
| subway | 65–77 | -16.5 | -16.0 | -32.5 | -16.2 | 0.15432739 |
| cafe | 0–12 | -19.5 | -13.0 | -32.5 | -18.2 | 0.12213135 |
| rain | 180–192 | -52.2 | 19.7 | -32.5 | -12.1 | 0.24734497 |
최종 전용 ebur128: **−32.5 LUFS 전수, spread 0.0 LU**, 최대 true peak **−6.2 dBTP**.
생성기를 import하지 않는 [독립 회귀 테스트](../tests/python/test_loudness_audio.py)가 이 동등성을 강제한다.
별도 disposable **pyloudnorm 0.2.0 / soundfile 0.13.1**의 exact-WAV 측정은
**−32.6062~−32.5291 LUFS, spread 0.0772 LU**, 최대 목표 오차 **0.1063 LU 미만**으로 통과했다.
[결과/스크립트/고정 의존성과 해시](../.agent/evidence-v0.2.5/review-fixes/README.md)를 별도로 보존한다.
일반 npm test는 이 패키지 설치나 네트워크가 필요 없다. 같은 FFmpeg/입력/플랫폼에서 두 번 다시 생성한
WAV/notice/측정 통계의 재현성도 검사했다. manifest의 생성 시각만 달라진다.
```bash
python3 scripts/ambience_audio.py
python3 -m unittest discover -s tests/python -v
```
[새 exact-WAV ASR 결과](../.agent/evidence-v0.2.5/review-fixes/final-asr.json)는 동일한
faster-whisper **1.2.1**, tiny revision `d90ca5fe260221311c53c58e660288d3deb8d356`, CPU int8,
beam_size=5, best_of=5, 기본 Silero VAD, condition_on_previous_text=true를 사용했다.
**market 1 segment, mall/cafe 0개, requiredEmptyPassed=false**다. market hash는
`7178307c48c192ac396dcd3c880c377bf56b45e829b459b1feca9f986437e632`이다.
반환 구간 10.45–12.45초와 텍스트 ` Let's go, let's go, let's go.`를 수정 없이 보존했다.
텍스트는 검증된 전사가 아니며 환각일 수 있다. ASR 원문 SHA-256
`98acdb16bc5dce57bb3609b705b63125e5511c2d5f7b26bdde54f4b7bd9a5e49`와 모든 설정/segment를 보존한다.
ASR 보고서 자체의 **humanListeningAcceptance=false, legalPrivacyClearance=false**도 변경하지 않는다.
제품 소유자 Oz는 `/home/hermes/voice-memos/find-word-v0.2.5-normalized-ambience-review.zip`의
현재 8 WAV·manifest·notice를 받은 뒤 **“승인합니다. 이대로 진행하세요”**라고
**2026-10-14T11:29:51Z / 2026-10-14 20:29:51 KST**에 회신했다. 사용자가 전달한 이 새 회신은
별도 [RELEASE-APPROVAL](../.agent/RELEASE-APPROVAL-v0.2.5.json)에 기록한다.
ZIP SHA-256은 `bb2e974b652df43db4a1f2a59716836e2c521839c724b34d4b71c1b40c3322e5`,
현재 manifest SHA-256은 `6433bf23af35f24031c6bbe784dd0abf9330f276da6b8705e47c7a1144a285b7`이며
모든 WAV/notice 해시도 기록했다. 이 별도 인수 기록은 **productOwnerReleaseApproval=true,
humanListeningAcceptance=true, legalPrivacyClearance=false**다. 법적/privacy clearance나 ASR의 PASS를 주장하지 않는다.
`python3 scripts/release_approval.py`는 동일 설정의 자동 empty-ASR 조건 **OR** 유효한 exact-byte 소유자
청취 승인을 요구한다. 현재 통과 사유는 후자뿐이다. 승인 문구/범위/시각/소유자/flags와 고정 ZIP hash를
검사하고 ZIP의 manifest·notice·8 WAV를 현재 파일과 바이트 단위로 비교한다. 다른 자산/manifest/ZIP,
필수 정보 누락, 법적 clearance 과장은 거부한다. 원래 실패 테스트·실패 로그는
[release-approval 증거](../.agent/evidence-v0.2.5/release-approval/README.md)에 보존하며
현재 회귀 테스트는 원문 실패의 불변성과 별도 승인 계약을 각각 검사한다. 이전 v0.2.3 승인의 범위는 그대로다.
## v0.2.3 현장 녹음 파생본 — 역사 기록
아래 절의 “현재/최종”은 v0.2.3 당시 상태를 뜻한다. 당시 exact manifest/WAV는
[v0.2.3 snapshot](../.agent/evidence-v0.2.5/baseline/ambience/)에 보존했고 원본 evidence는 수정하지 않았다.
원본 선정 계약은 [.agent/AMBIENCE-SOURCES-v0.2.3.json](../.agent/AMBIENCE-SOURCES-v0.2.3.json),
배포 바이트의 출처·직접 URL·저작자·라이선스·변환·SHA-256·bytes·duration·RMS·peak는
[manifest](../public/ambience/manifest.json), 정확한 attribution과 변경 고지는
[AMBIENCE_NOTICES](../public/ambience/AMBIENCE_NOTICES.md)에 있다. 이 고지는 dist에도 포함되고 UI에서 링크한다.
[manifest](../.agent/evidence-v0.2.5/baseline/ambience/manifest.json), 정확한 attribution과 변경 고지는
[AMBIENCE_NOTICES](../.agent/evidence-v0.2.5/baseline/ambience/AMBIENCE_NOTICES.md)에 있다. 이 고지는 dist에도 포함되고 UI에서 링크한다.
여덟 원본 모두 서로 다른 현장 녹음이며 NC/ND 자산을 사용하지 않는다. 원본은 저장소/빌드에 포함하지 않는다.
| 환경 | 저작자 | 라이선스 | 원본 구간(초) |