마지막 업데이트 2026-07-22
ai_stt_req19 (1).wav (무손실 PCM).
use-ai-audio-noise-guard.ts)가 쓰는 음향 지표(RMS dB · ZCR · CV · FFT)를 오프라인 재현 + 클리핑/스펙트로그램 분석.
| 항목 | 값 | 해석 |
|---|---|---|
| 포맷 | 16kHz mono PCM, 3.33s | AI STT 캡처본, 무손실 |
| peak | 0.0 dBFS | 풀스케일 도달 |
| 클리핑 샘플 | 941개 (1.8%), 0.9s~3.3s 전반 | 하드 클리핑(flat-top) |
| 큰 구간 RMS | -3 ~ -5 dBFS | 정상 음성(-18~-12)보다 훨씬 과도 |
| 스펙트로그램 | 전대역 에너지 번짐 | 클리핑 고조파(왜곡) 전형 |
| CV<0.35 기계음 후보 | 17프레임 (1.25~1.5s, 1.95~2.3s 등) | 클리핑으로 파형이 납작해진 구간과 일치 |
AI STT 캡처 경로(use-guest-page-session.ts:847~854)의 Float32 → Int16 변환:
const toInt16 = (float32) => { for (...) { const s = Math.max(-1, Math.min(1, float32[i])); // ±1.0 초과를 하드 클램프 int16[i] = s < 0 ? s * 0x8000 : s * 0x7fff; } };
Math.min(1, ...) 클램프가 ±1.0을 넘는 샘플을 풀스케일로 깎아낸다. 측정된 flat-top 클리핑이 정확히 이것 → 즉 aiAudioStream의 float 신호가 이미 ±1.0을 초과한 과레벨 상태로 들어오고 있다.
캡처는 aiAudioStream에 붙인 ScriptProcessorNode(16kHz)로 떠가며, AI 발화 종료 시 requestAIAudioSTT()가 트리거(use-guest-page-session.ts:790~791).
LogRocket 로그 분석에서 본 또 다른 지지직 경로와 혼동 주의. "AI 지지직"은 두 가지 별개 경로가 있다.
| 경로 | 원인 | 로그/지표 | 양상 |
|---|---|---|---|
| ① 네트워크 PLC | guest↔OpenAI WebRTC 패킷 손실 → 디코더 보정 아티팩트 | AI_NOISE_GUARD "suppressed during PLC-active window" · concealedSampleRatio | 간헐적 |
| ② 과레벨 클리핑 (본 문서) | aiAudioStream float > ±1.0 → toInt16 하드 클램프 | WAV peak 0dBFS · 941 클리핑 샘플 · RMS -3~-5dBFS | 발화 전반 지속 |
concealedSamples/packetsLost/jitter)는 WebRTC inbound-rtp 통계로, WAV(최종 파형)에는 들어있지 않다 → 파일만으론 ①/② 구분이 어렵고, 로그 타임라인 교차검증이 필요.
aiAudioStream 생성~재생까지 게인 스테이지 추적 — 과레벨이 어디서 들어오는지(OpenAI 출력 vs 프로젝트 게인) 확정.커밋 id-008 (feat: AI 발화 하드 클리핑 고속 감지 경로 추가)가 실제 WAV에서 동작하는지, 정상 발화 오탐은 없는지 독립 재현·검증.
CLIP_CREST_THRESHOLD_DB=6, CLIP_SAMPLE_LEVEL=0.98, CLIP_SAMPLE_RATIO_THRESHOLD=0.01, CLIP_RING_SIZE=4, CLIP_FRAME_THRESHOLD=3, grace 500ms, silence floor -50dB)를 그대로 재현. 라이브 AnalyserNode와 동일하게 48kHz·2048샘플 윈도우·50ms 틱으로 시뮬레이션(16kHz 캡처본은 48kHz 리샘플).
| 파일 | 결과 | 비고 |
|---|---|---|
| 아동330_4회기_지지직 (문제) | 🔴 감지 t=1.15s | 첫 클립프레임 1.00s → 3프레임 누적 후 확정 |
| ai_stt_req18 (정상) | 🟢 미감지 | 클립프레임 0 — 오탐 없음 |
| ai_stt_req19 (정상) | 🟢 미감지 | 클립프레임 0 — 오탐 없음 |
onNoiseDetected(응답 중단 + 이슈 리포트) 트리거.clipRatio ≥ 1% 게이트. 정상 발화는 peak -1.4dBFS라 0.98 초과 샘플이 사실상 없어 clipRatio≈0 → 절대 클립프레임 안 됨. 견고.cancelResponse(공유 핸들러): 지지직은 멈추나 그 발화 나머지가 끊김. 호스트 이슈 라벨은 "AI 기계음 감지"로 클리핑과 구분 안 됨(로그만 Hard clipping ... (fast path) 구분).toInt16 클램프 후(peak=1.0) 파일 기준. 라이브 pre-clamp가 over-unity(>1.0)면 더 잘 감지 → 실제로는 최소 이만큼은 잡힘."안 들리게 막기"는 감지가 아니라 재생 직전 단계에서 레벨을 잡아야 한다. 현재 재생은 <audio>.srcObject = MediaStream 직결(use-ai-session.ts:1683/1706)이라 그래프가 없다.
createMediaStreamSource → Limiter → MediaStreamDestination → <audio> 경유가 유일. AudioContext.destination 직접 출력은 금지(:1681 주석상 AEC 에코 참조 보존 위해 <audio> 재생 유지 필요).