마지막 업데이트 2026-07-22
핑퐁이(AI) 출력 음성에 섞이는 기계음/버징/정적을 실시간 탐지하는 가드. OpenAI 출력 스트림을 Web Audio analyser로 분석하며, 같은 50ms 루프(analyzeFrame)·같은 buffer 위에서 서로 독립된 두 경로로 판정한다.
| 경로 | 판정 기준 | 확정 지연 |
|---|---|---|
| ① CV(변동계수) 경로 — 주 | 음량(RMS) 변동이 작고 균일(CV)+ZCR. 1차 링버퍼+2차 재검증, PLC 억제 적용 | ~900ms |
| ② 하드 클리핑 고속 경로 — 보조 | crest(peak/RMS)·풀스케일 샘플 비율. 재검증 없이 즉시 확정, PLC 억제 미적용 | ~150ms |
어느 경로든 통과하면 onNoiseDetected() 콜백만 호출한다.
| 지표 | 역할 | 임계 |
|---|---|---|
| CV (변동계수, std/mean) | 주 기준. 정상 음성은 진폭 변동 큼(CV~0.85), 기계음은 균일(CV~0.27) | cv < 0.35 |
| ZCR (영교차율) | 보조. 너무 매끈한 저주파/안정 TTS 구간 오탐 방지 | zcr ≥ 0.04 |
| RMS (dBFS) | 무음 필터 전용(상한 없음 — 기기별 레벨 차 무관) | > -50dB만 판정 |
CV는 최근 8프레임(400ms) linear RMS 슬라이딩 윈도우로 계산. 한 프레임이 "기계음 프레임"이 되려면 윈도우 충분 + CV<0.35 + ZCR≥0.04 모두 충족.
OpenAI Realtime 디코더가 간헐적으로 출력을 풀스케일로 깨뜨리는(하드 클리핑) 경우, CV 경로(~900ms)로는 대응이 느리다. 그래서 같은 analyzeFrame 루프 안에서 CV와 완전히 독립으로 도는 고속 경로를 둔다. 같은 buffer를 crest factor·풀스케일 샘플 비율로 직접 판정한다.
| 지표 | 역할 | 임계 |
|---|---|---|
| crest (peak/RMS, dB) | 정상 음성 12–18dB vs 클리핑 3–6dB. 파형이 천장에 눌리면 crest 급감 | crest < 6dB |
| 풀스케일 샘플 비율 | |sample| ≥ 0.98 인 샘플 비중. 천장에 붙은 샘플이 얼마나 많은지 | ratio ≥ 0.01 (1%) |
return하며, 진행 중이던 CV 재검증 상태(isReverifyingRef)도 강제 초기화한다.analyzeFrame이 같은 2048 샘플 buffer를 각자 다른 방식으로 계산한다. 누적 버퍼만 분리(CV: noiseFrameRing 14 + 재검증 4 / 클리핑: clipRing 4).WebRTC/Chrome이 패킷 손실을 보정(PLC)하면서 만드는 균일 반복 파형은 CV 기준으로 기계음처럼 보인다. 즉 "진짜 OpenAI 기계음"과 "네트워크 손실 보정 파형"이 헷갈린다. 이를 막으려고 inbound 통계로 PLC 활성 구간을 추정해 그 구간의 노이즈 후보를 무효화한다.
getAudioInboundStats()(주입)를 500ms마다 읽어 직전 대비 델타로 판정. 아래 중 하나라도 충족하면 PLC active(TTL 1.5s):
| 지표 | 임계 |
|---|---|
| concealedSampleRatio (보정 샘플 비율) | ≥ 0.03 |
| concealmentEvents 델타 | ≥ 2 |
| packetsLost 델타 | ≥ 3 |
| jitter | ≥ 80ms |
aiAudioStream(OpenAI/릴레이 음성), isPeerTalking, enabled, onNoiseDetected, getAudioInboundStats?.isPeerTalking일 때만 50ms setInterval. 발화 종료 시 정지.{ isNoiseDetected, detectionCount }.getAudioInboundStats 미제공 시 PLC 완화가 꺼져 네트워크 불안정 구간에서 오탐 급증(PLC 오탐 분석).| 파일/심볼 | 역할 |
|---|---|
| use-ai-audio-noise-guard.ts | 가드 본체 |
| analyzeFrame (L321–494) | 50ms 루프 — CV 1차/2차 + 하드 클리핑 고속 경로 동시 처리 |
| 하드 클리핑 판정 (L385–424) | clipRing 누적·즉시 확정(재검증·PLC 억제 없음) |
| calculateClipFeatures (L214–238) | crest(dB)·풀스케일 샘플 비율 계산 |
| samplePlcState (L240–318) | inbound stats로 PLC 활성 추정(CV 경로 전용) |
| calculateCV/ZCR/RmsDb (L167–211) | CV 경로 지표 계산 |
| 상단 상수 (L9–65) | 임계값 일람(CV·클리핑 튜닝 지점) |