+
    vj+                        R t ^ RIt^ RIt^ RIt^ RItRt]3R ltR8R ltR t	R9R lt
R:R ltR	 tR
 tR tR;R lt]R8X  Ed   ]P"                  ! 4       t]P'                  RRRR7       ]P'                  RRRR7       ]P'                  RRRR7       ]P'                  RRRR7       ]P'                  RRRR7       ]P)                  4       tR]P,                  R]P.                  /t]! ]P.                  4      ]R&   ]! ]P,                  ]P.                  ]P2                  R 7      ]R!&   ]! ]P,                  4      ]! ]P.                  4      utt]! ]4      ]R"&   ]! ]4      ]R#&   ]P8                  '       Ed{   ]! ]R,          R$,          4      tR%]! ]]! ]],          4      R&7      R']! ]]! ]],          4      R&7      /t R(]P8                  3R)]PB                  33 EF  w  t"t#]#'       g   K  ]$;QJ d#    . R* ]#PK                  R+4       4       F  NK  	  5M! R* ]#PK                  R+4       4       4      t&]! ]P.                  ]&4      w  t't(t)R,]&R-]! ])4      /t*] PW                  4        F}  w  t,t-R.]'^ 3R/](^33 Fi  w  t.t/t0]1! ]2! ]/4      ]2! ]-4      ]0,
          4      t3]
! ]/R]3 ]-]0]3]0,            ^R07      w  t4t5t6R1]4R2]5R3]7! ]5;'       g    ^ ],          ^4      R4]6/]*]. R5], 2&   Kk  	  K  	  ]*]R6]" 2&   EK  	  ]8! ]Pr                  ! ]R^R77      4       R# R# )<u  H3 ⑤ 오디오 구동 립싱크 — 공급 오디오 대비 산출물을 관측값으로 뽑는다.

이 발주 전체의 전제는 **DoD 2 (오디오 동일성)** 다 — "립싱크가 잘 되더라" 는 인상은 모델이
공급 오디오를 무시하고 그럴듯한 음성을 새로 만들었을 때도 똑같이 나온다. 그래서 축 3개를 잰다:

  ⓐ **오디오 동일성** — 공급 트랙 ↔ 산출 트랙의 정규화 상호상관(lag 탐색).
     ④ 에서 pass-through(Wan)=0.995 / 재생성(H3)=0.023 이 나온 그 지표다. 판정선이 아니라
     **두 모드가 2자릿수 갈리는** 지표라서 쓴다.
  ⓑ **립싱크 정합** — 입 영역 모션 에너지 ↔ 오디오 RMS 포락선의 상호상관(lag=프레임).
     포즈/랜드마크 추정이 아니라 "입 근처가 언제 움직였나" 대리지표다. 얼굴 위치는
     --mouth-box 로 명시(자동 검출 없음 — 틀린 자동검출이 조용히 결론을 만든다).
  ⓒ **발화 구간** — 산출 오디오의 유성 구간과 중앙 f0(자기상관·옥타브 보정). 화자 귀속용.

★계측이 못 보는 축은 못 본다고 적는다 — 음소 뭉개짐·가사 정확도는 여기서 안 보인다(청취/STT 몫).
Ni>  c                    \         P                  ! RRRRV RRR\        V4      RR	R
.RR7      p\        P                  ! VP
                  \        P                  R7      P                  4       # )uH   mono float32 로 디코드. 오디오 스트림이 없으면 빈 배열.ffmpeg-verror-iz-ac1z-ar-ff32le-T)capture_outputdtype)
subprocessrunstrnp
frombufferstdoutfloat32copy)pathsrps   && 8/Users/inhohur/ai/experiments/h3-r2v-passthru/analyze.pypcmr      s[    $tUCPSTVPWgs,<@	BA==499;;    c                    \        V 4      V,          pV^ 8X  d   \        P                  ! ^ 4      # \        P                  ! V RW!,           P	                  W!4      ^,          P                  ^R7      4      # )u   25ms RMS 포락선.Naxis)lenr   zerossqrtreshapemean)xhopns   && r   enveloper'       sY    A#AAvxx{77AhqwK''/14:::BCCr   c                 f    W P                  4       ,
          V P                  4       R ,           ,          # )&.>)r#   std)vs   &r   zmeanr,   (   s    LQUUWt^,,r   c                   \        \        V 4      \        V4      4      pV^8  d   R# \        \        P                  ! \        V RV 4      \        VRV 4      4      R,          4      pRp\        V) V^,           V4       F  pV^ 8  d   WR VR\        V4      V,
           3MV R\        V 4      V,            W) R 3w  r\        \        V4      \        V	4      4      p
V
\        ^V^,          4      8  d   Kw  \        \        P                  ! \        VRV
 4      \        V	RV
 4      4      R,          4      pW^ ,          8  g   K  W3pK  	  \        V^ ,          ^4      V^,          \        V^4      3# )u  a 기준 b 를 밀며 최대 상관과 그 lag. 반환 = (best_corr, best_lag, corr_at_lag0).

★**겹침 하한 = 짧은 쪽의 50%**. 이걸 안 걸면 lag 를 끝까지 밀었을 때 남는 몇백 ms 짜리
  꼬리끼리 우연히 맞아 `best_corr` 가 신호처럼 솟는다(실측: 무관 트랙 쌍이 겹침 1.9s 에서 0.033,
  공급 트랙 쌍이 겹침 1.81s 에서 0.28 — 둘 다 **판정에 못 쓰는 가장자리 인공물**이었다).
N)NNN       g       r/   )	minr   floatr   corrcoefr,   rangemaxround)abmax_lagstepn0lag0bestlagr$   yr&   cs   &&&&        r   xcorrrB   ,   s@    
SVSV	B	BwU1Sb6]E!CR&M:4@ADDgXw{D1.1Qh$=CFSL)*Q}A=MqQUQVx<XAAs2rQw"++eAbqElE!BQ%L9$?@Aw;8D 2 a!d1guT1~55r   Fc                   \        V 4      \        V4      rC\        V4      ^ 8X  g   \        V4      ^ 8X  d-   R\        \        V4      4      R\        \        V4      4      RR/# V'       Ed   \        V4      p\        V4      V8:  d   \        R4      hRp\	        ^ \        V4      V,
          \
        ^d,          4       FV  p\        \        P                  ! \        V4      \        W7Wu,            4      4      R,          4      pW^ ,          8  g   KS  W3pKX  	  \        ^ V^,          \
        ^d,          ,
          4      p	\	        V	\        \        V4      V,
          V^,          \
        ^d,          ,           4      4       FV  p\        \        P                  ! \        V4      \        W7Wu,            4      4      R,          4      pW^ ,          8  g   KS  W3pKX  	  R\        \        V4      \
        ,          ^4      R\        \        V4      \
        ,          ^4      RR	R
\        V^ ,          ^4      R\        V^,          \
        ,          ^4      /# \        W4\
        ^,          R7      w  rpR\        \        V4      \
        ,          ^4      R\        \        V4      \
        ,          ^4      R
T
R\        T;'       g    ^ \
        ,          ^4      RV/# )uF  공급 ↔ 산출 파형 동일성. ★포락선이 아니라 **샘플 파형**으로 잰다 —
포락선은 서로 다른 음성이 같은 리듬만 가져도 높게 나와 pass-through 와 재생성을 못 가른다.

slide=True: 공급이 산출보다 **훨씬 길 때**(ⓐ 30s ref vs 5.2s 산출) 쓴다. ±0.5s lag 로는
긴 소스의 뒷부분을 아예 안 본다 — 그러면 "안 맞는다" 가 "안 찾아봤다" 와 구분되지 않는다.
산출 전체 길이를 창으로 삼아 공급 전 구간을 10ms 격자로 훑고, 최대 상관과 그 오프셋을 낸다.
src_samplesout_samplesverdictu   오디오 스트림 결손uB   ❌ --slide 는 공급이 산출보다 길 때만 의미가 있다src_duration_sout_duration_smodeu    slide (공급 전 구간 탐색)waveform_best_corrwaveform_best_src_offset_sr:   waveform_best_lag_swaveform_corr_at_lag0r1   r.   )r   r   int
SystemExitr5   SRr3   r   r4   r,   r6   r2   r7   rB   )srcoutslider8   r9   r&   r>   offrA   locorrr?   r=   s   &&&          r   waveform_identityrX   C   s    s8SXq
1v{c!fks3q6{M3s1v;	SoppuFq6Q;abbCFQJc	2Cbkk%(E!.,AB4HIA7{x 3 DGbCi'(SQ!T!WrSy-@ABCbkk%(E!.,AB4HIA7{x C
 eCFRK35EuSQRVVX[Z[G\6 %Q"3(%Q"a*@	
 	
 A"'2ODt%AQ/1A5QRTVWCXd$95#((bRS;T r   c                    \         P                  ! R RRRV RRV RV R2RR	R
.
RRR7      P                  p\        P                  ! V\        P
                  R7      P                  RW!4      P                  \        P                  4      # )r   r   r   r   z-vfzscale=:z,format=grayr   rawvideor
   T)r   checkr   )	r   r   r   r   r   uint8r"   astyper   )r   whraws   &&& r   grayrc   j   s}    
..(D'4uqcQRSTRUUaFb
C1AETSSYSY ==BHH-55b!?FFrzzRRr   c                     \         P                  ! R RRRRRRRRV .
R	R	R
7      P                  p\        P                  ! V4      R,          ^ ,          # )ffprober   r   z-select_streamszv:0z-show_entriesz3stream=width,height,nb_frames,r_frame_rate,durationz-ofjsonT)r   textstreams)r   r   r   rf   loads)r   rS   s   & r   proberj   p   sV    
..)T74EuoOQVX^ 044AAG  ::c?9%a((r   c                f   \        V 4      p\        VR,          4      \        VR,          4      rC\        W^,          V^,          4      pVP                  ^,          VP                  ^,          rvVw  rrVR\        W,          4      \        W,          4      1\        W,          4      \        W,          4      13,          p\        P
                  ! V^4      pW8  P                  RR7      p\        P                  ! \        P                  ! V^ R7      4      P                  RR7      pW\        V4      3# )u  box=(x0,y0,x1,y1) 정규화 좌표. 두 시계열을 낸다.

ⓐ openness — 그 영역의 **어두운 픽셀 비율**(입이 열리면 구강 내부가 어둡다). 프레임과 1:1 대응이라
   "지금 열려 있나" 를 직접 잰다.
ⓑ motion — 프레임간 변화량. 열림/닫힘 **전이**에 반응한다.
두 축이 필요한 이유: 계속 벌린 채 정지한 입은 ⓑ 로는 안 보이고, 천천히 도는 고개는 ⓐ 를 흔든다.
widthheight:NNNr   )r0      )
rj   rO   rc   shaper   
percentiler#   absdiffr   )r   boxr+   WHfra   r`   x0y0x1y1cropthropennessmotions   &&              r   mouth_seriesr   w   s     	dAqz?C(,qT616"A771:qwwqzqNBBQBFCK'RVS[)@@AD
--b
!C
  f -HVVBGGDq)*//V/<FSV##r   c                   \        W4      p^\        P                  ! VR,           4      ,          pWR8  p. ^ rV\        V4      8  dh   Wh,          '       dO   Tp	V	\        V4      8  d   Wi,          '       d   V	^,          p	K)  W,
          V8  d   VP	                  W34       T	pKl  V^,          pKw  . p
V EF  w  rW,          W,          r. pRp\        WV,
          R4       EF  pWW,            p\        P                  ! V^,          P                  4       4      R8  d   K@  VVP                  4       ,
          p\        P                  ! VVR4      V^,
          R p\        \        R,          4      \        \        ^F,          4      ppV\        V4      8  d   K  V\        \        P                  ! VVV 4      4      ,           pVV,          RV^ ,          ,          8  g   K  VP	                  \        V,          4       EK  	  T
P	                  R	\        V\        ,          ^4      R
\        V\        ,          ^4      RV'       d*   \        \        \        P                  ! V4      4      ^4      MRR\        V4      /4       EK  	  V
# )uo   유성 구간 + 구간별 중앙 f0(자기상관). f0 는 화자 동일성 판정용으로는 약한 지표다.r)   i   i   g{Gz?fullN  g333333?start_send_smedian_f0_hzn_f0_frames)r'   r   log10r   appendr5   r!   r#   	correlaterO   rQ   argmaxr7   r3   median)r$   r%   	thresh_dbmin_lenenvdbonsegsijrS   r8   r9   valswinkfracrV   hir   s   &&&&                 r   voiced_segmentsr      s   
1
C	bhhsTz"	"B	B!!
c"g+55Ac"g+"%%QuQF#AFA
Cw1qc'3'AQWBwwa~~'(4/bggiBb"f-cAgh7Bc]CRLBSW}S2b9-..A!usRU{"BF# ( 	

IuQVQ/%B:J"E%		$*@!$DRV!3t9. 	/! & Jr   __main__z--src-audioTu    공급한 오디오 레퍼런스)requiredhelpz--out-videou   산출 영상z--mouth-boxu   입 영역 정규화 좌표 x0,y0,x1,y1 (미지정 시 립싱크 축 생략). ★자동 얼굴검출을 안 쓰는 이유 = 틀린 검출이 조용히 결론을 만든다)defaultr   z--extra-boxu0   2인 구도용 2번째 입 영역(화자 귀속)z--slide
store_trueug   공급이 산출보다 훨씬 길 때 — 공급 전 구간을 훑어 최대 상관 지점을 찾는다)actionr   	src_audio	out_videovideo)rT   audio_identitysrc_voiced_segmentsout_voiced_segmentsr_frame_ratevs_output_audio)r%   vs_src_audiomouthmouth_2c              #   8   "   T F  p\        V4      x  K  	  R # 5i)N)r3   ).0r+   s   & r   	<genexpr>r      s     :/Qa/s   ,rs   framesr}   r~   rL   	best_corrbest_lag_frames
best_lag_scorr_at_lag0_lipsync_)ensure_asciiindent)r   )r0   )F)r   i   ):__doc__argparserf   r   numpyr   rQ   r   r'   r,   rB   rX   rc   rj   r   r   __name__ArgumentParserapadd_argument
parse_argsr8   r   r   resrT   rR   rS   	mouth_boxevalfpsrO   r   	extra_boxnameboxstuplesplitrs   r}   r~   nfrentitemsrefelblseriesrU   r2   r   r&   rW   r?   r=   r7   printdumps r   r   <module>r      s&      
  <D-6.$NS)$*$N z		 	 	"BOOMD7YOZOOMDOGOOM4x  y OOM46hOiOOIl C  D
A[!++
>C%CL-akk1;;aggVC1;;Q[[!1HC!0!5C!0!5C{{{3w</0 !(3CSM"BxR#X?A#Q[[1Iq{{3KLJD$%:$**S/:%%:$**S/::C$0c$B!Hfc#xS2C ))+Q*4h)BXvWXDY(Z$CCKQ#6A&+F2AJ#a#gPQ&ROD#t+6>OQT+7qC?OQR9S+94+AC3%q' )[ & &)C(4&!"! M" 
$**SuQ
78W r   