
    @i                     &   d Z ddlZddlZddlmZ ddlmZmZ ddlZ	ddl
mZ ddlmZ  ee      j                         j                   d   dz  Zd	efd
Zdeded	efdZ	 ddedededed	ef
dZdededededed	eee   ee   f   fdZded	efdZy)u   说话人匹配服务。

职责概览：
- 对单段 PCM 音频执行 speaker 匹配/注册并返回 rl 与相似度。
- 提供 AudioSegment 到 PCM 原始字节的转换方法，供流水线调用。
    N)Path)OptionalTuple)AudioSegment   )SpeakerRegistryzgzzm_config.jsonreturnc                      	 t         j                  dd      5 } t        j                  |       cd d d        S # 1 sw Y   y xY w# t        $ r i cY S w xY w)Nrzutf-8)encoding)_config_pathopenjsonload	Exception)_fs    %/app/gzzm/services/speaker_matcher.py_load_gzzm_configr      sF    sW599R= 655 	s*   A 8	A AA A AAvaluedefaultc                     t        | t              r| S t        | t              r(| j                         j	                         }|dv ry|dv ryt        | t
        t        f      rt        |       S |S )N)1trueyesonT)0falsenooffF)
isinstanceboolstrstriplowerintfloat)r   r   vs      r   _to_boolr(      sd    %%KKM!**++%#u&E{N    	pcm_bytessample_ratechannelstarget_secondsc           
         | s| S |dk  s|dk  r| S t        dt        |            }t        t        |t        |      z              }t	        j
                  | t        j                        }|j                  |z  |z  }|dk  r| S ||j                  k7  r|d| }||z  }||k  r|j                         S |j                  d|      j                  t        j                        j                  d      dz  }t        dt        t        t        |      d	z                    }	|j                  |	z  }
|
d
k  r|j                         S |d|
|	z   }|j                  |
|	      }t	        j                  t	        j                  t	        j                  |      d      dz         }t        t	        j                  |d            }t        t	        j                  |d            }t        d|dt        d||z
        z  z         }||k\  j                  t        j                        }t        d||	z        }||
k\  r|j                         S t	        j                   dgt	        j"                  |      f      }t	        j                   dgt	        j"                  |      f      }||d |d|  z
  t        |      z  }||d |d|  z
  t        |      z  }t	        j$                  |t        |d      z  dd      }d|z  d|z  z   }t        t	        j&                  |            }||	z  }t)        |||z         }||z  }||z  }||k  r|j                         S ||| j                         S )ub   从完整 PCM 中选择信息量更高的窗口（优先配置时长，不足则返回全段）。r   g?)dtypeN   )axisg      @g{Gz?r   g-q=   _   g-C6?g?g        g:0yE>g      ?gffffff?g333333?)maxr&   r%   roundnp
frombufferint16sizetobytesreshapeastypefloat32meansqrtsquare
percentileconcatenatecumsumclipargmaxmin)r*   r+   r,   r-   target_samplespcmusabletotal_samplesmonoframe_samplesframe_counttrimmedframesrmsnoise_floorpeakactivity_thresholdactivitywindow_frames
c_activityc_rmsactive_ratiomean_rmsnorm_energyscore
best_framestart_sample
end_sample	start_i16end_i16s                                 r   #_select_most_informative_pcm_windowrb   +   s     a8q=eN34N~k0BBCDN
--	
2Chh("h.F{'6lh&M&{{};;r8$++BJJ7<<!<DwND3uU;%7$%>?@AM))},KQ{{}0[=01G__[-8F
''"''"))F+!4u<
=Cc2./KsB'(DT;s3{@R7S1S#ST))11"**=H>]:;M#{{} #		((; <=JNNSE299S>23E}~.Om^1LLPUVcPddLmn%o~(>>%BVVH''(St_4c3?K<C+$56ERYYu%&J-L]L>$ABJx'I8#G){{}y!))++r)   registrydevicec                    | syt               }	 t        |j                  dd            }	 t        |j                  dd            }t	        |j                  dd      d      }	 t        |j                         j                  dd	            }	|	d	k(  r|}
d}n|}
|rt        | |||

      }n| }	 |j                  ||||d      }t        |t              st        |      dfS t        |      d	kD  r|d	   nd}t        |      dkD  r|d   nd}|dv ry	 |t        |      nd}t        |      |fS # t        $ r d}Y w xY w# t        $ r d}Y w xY w# t        $ r d	}	Y w xY w# t        $ r Y yt        $ r Y yw xY w# t        $ r d}Y hw xY w)u|  为单段 PCM 音频识别或注册说话人 ID。

    参数:
    - pcm_bytes (bytes): 原始 PCM 音频数据（假定 16-bit、每样本 2 字节、交错声道）。
    - sample_rate (int): 采样率（Hz）。
    - channels (int): 声道数。
    - registry (SpeakerRegistry): 用于匹配/注册说话人的注册器实例。
    - device (str): 设备标识（传递给 registry）。

    返回:
    - Tuple[Optional[str], Optional[float]]: (speaker_id, similarity)
      - speaker_id: 匹配或新注册的说话人 ID（字符串），无法匹配或出错时为 None。
      - similarity: 相似度分数（浮点），如果不可用则为 None。

    实现细节:
    - 若 `pcm_bytes` 为空，函数会直接返回 (None, None)。
    - 时长通过 `len(pcm_bytes) / (sample_rate * channels * 2)` 估算（假定每样本 2 字节）。
    - 可通过配置 `speaker_use_informative_window` 开关控制是否启用“信息量窗口挑选”。
    - 当输入长于配置窗口 `speaker_match_window_seconds` 时，会先自动挑选信息量更高的窗口（尽量避开静音）再做匹配。
    - 当输入短于配置窗口时，会使用实际可用的整段音频做匹配（不会因“最短阈值”跳过）。
    - 调用 `registry.register_or_match(...)` 获取匹配结果；若抛出 ImportError 或其他异常，函数会捕获并返回 (None, None)。
    )NNspeaker_match_window_seconds      @ speaker_initial_baseline_secondsg      $@speaker_use_informative_windowTcountr   )r*   r+   r,   r-   )r+   r,   rd   
return_simNr1   )Nr0   )r   r&   getr   r(   r%   statsrb   register_or_matchImportErrorr    tupler"   len)r*   r+   r,   rc   rd   cfgdefault_match_secondsinitial_baseline_secondsuse_informative	reg_countr-   pcm_for_matchmatchedcand_idcand_simsims                   r   identify_speaker_id_from_pcmr|   m   s   : 

C$ %cgg.Lc&R S(#(1SUY)Z#[  sww'GNPTUO(,,Wa89	 A~1.;#)	
 ",,# - 
 gu%7|T!!L1,gaj$G \A-wqz4H*!)!5eHo4 w<q  $ #$  (#' (  	4     s^   D, D> #)E -E! E: ,D;:D;>EEEE!	E7,E76E7:FFsegmentc                 p    t        j                         }| j                  |d       |j                         S )u$  将 pydub `AudioSegment` 导出为原始 PCM 字节序列（raw 格式）。

    返回的 bytes 为原始 PCM 数据；调用者可通过 `segment.frame_rate`、`segment.channels`、
    `segment.sample_width` 获取对应的音频元信息（采样率、声道数、样本宽度）。
    raw)format)ioBytesIOexportgetvalue)r}   bufs     r   audio_segment_to_pcm_bytesr      s+     **,CNN3uN%<<>r)   )rg   )__doc__r   r   pathlibr   typingr   r   numpyr7   pydubr   utils.speaker_idr   __file__resolveparentsr   dictr   objectr!   r(   bytesr%   r&   rb   r"   r|   r    r)   r   <module>r      s   
   "   .H~%%'//25GG4 F T d (  	?,?,?, ?, 	?,
 ?,D\\\ \ 	\
 \ 8C=(5/)*\~  r)   