
    Nj{                       d Z ddlmZ ddlZddlmZ ddlZ ee      j                         j                  j                  dz  Zedz  ZdZdZ eej                   j#                  d	d
            ZdZdZdZdZddZ G d d      Zy)u  Speaker verification: the assistant only answers Ahmed.

Engine: sherpa-onnx + NVIDIA TitaNet-small (192-dim embeddings, ~14ms
per 3s utterance on the M4; statically-linked onnxruntime, no torch).
Chosen after benchmarking 7 models on this machine — best speed/margin.

Lifecycle:
  ENROLLING  no profile yet: the first ENROLL_N good utterances (>= MIN_S
             net audio) build the profile. The assistant answers normally
             during enrollment (it's presumed to be the owner talking).
  LOCKED     each utterance is scored (cosine vs profile). Accept at
             T_ACCEPT; short utterances use a lenient threshold instead
             of hard-failing (embeddings are unreliable under ~2s).
             CONTINUOUS LEARNING: utterances scoring >= T_ADAPT are
             folded into the profile with a small anchored-EMA step —
             anchored to the original enrollment so absorbed mistakes
             can't destroy the profile (verified experimentally: a
             plain-averaged impostor collapses the margin; an anchored
             EMA one costs ~0.08 and recovers).

Env: VOICE_LOCK=0 disable · SPEAKER_THRESHOLD (0.50) ·
     SPEAKER_PROFILE (models/speaker_profile.npz) · SPEAKER_RESET=1
    )annotationsN)Pathmodelsztitanet_small.onnx   g       @SPEAKER_THRESHOLDz0.50gffffff?g?g?c                l    | t        t        t        j                  j	                  |             d      z  S )Ng&.>)maxfloatnplinalgnorm)vs    >/Users/ahmed/devFolder/Ultron/claude-voice/voice/speaker_id.py_normr   ,   s&    s5*+T222    c                  P    e Zd ZddZed	d       Zed
d       ZddZddZddZ	y)SpeakerGatec           	     f   dd l }|j                  t        t              dd      }|j	                         sJ d       |j                  |      | _        t        t        j                  j                  dt        t        dz                    | _        g | _        d | _        d | _        t        j                  j                  d      d	k(  r| j                  j!                  d
       | j                  j#                         r4t%        j&                  | j                        }|d   | _        |d   | _        y y )Nr      cpu)modelnum_threadsproviderzspeaker model missing/invalidSPEAKER_PROFILEzspeaker_profile.npzSPEAKER_RESET1T)
missing_okanchorprofile)sherpa_onnxSpeakerEmbeddingExtractorConfigstr
MODEL_PATHvalidateSpeakerEmbeddingExtractor_exr   osenvironget_MODELS_DIRprofile_path_enroll_embs_anchor_profileunlinkis_filer   load)selfr    cfgdatas       r   __init__zSpeakerGate.__init__1   s   99j/q5 : 
 ||~>>>~88= JJNN,{-BBCE
 /1*.+/::>>/*c1$$$5$$&774,,-D>DL ODM 'r   c                    | j                   d uS N)r.   r2   s    r   enrolledzSpeakerGate.enrolledI   s    }}D((r   c                8    t        | j                        t        fS r7   )lenr,   ENROLL_Nr8   s    r   enroll_progresszSpeakerGate.enroll_progressM   s    4$$%x//r   c                   | j                   j                         }|j                  d|       |j                          t	        j
                  | j                   j                  |      t        j                        }t        |      S )Ni>  )sample_ratewaveform)dtype)	r&   create_streamaccept_waveforminput_finishedr   asarraycomputefloat32r   )r2   audiosembs       r   _embedzSpeakerGate._embedS   s`    HH""$	ee<	jj))!,BJJ?Szr   c                p    t        j                  | j                  | j                  | j                         y )N)r   r   )r   savezr+   r-   r.   r8   s    r   _savezSpeakerGate._saveZ   s     
""4<<Or   c                <   | j                  |      }| j                  s|t        k\  r| j                  j	                  |       t        | j                        t        k\  rrt        t        j                  t        j                  | j                        d            | _        | j                  j                         | _        | j                          yyt        t        j                   || j                              }|t        k  r|t"        k\  ||t"        k\  rdfS dfS |t$        k  rd|dfS |t&        k\  rgt        dt(        z
  | j                  z  t(        |z  z         }t        t*        | j                  z  dt*        z
  |z  z         | _        | j                          d	|d
fS )zGate one utterance. Returns (accept, score, state).

        state: 'enrolling' | 'enrolled' | 'ok' | 'rejected' | 'short-ok'
        r   )axis)T      ?r9   )TrQ   	enrollingzshort-okrejectedFr   Tok)rK   r9   MIN_Sr,   appendr;   r<   r   r   meanstackr-   copyr.   rN   r
   dotT_ACCEPT_SHORTT_ACCEPTT_ADAPT	EMA_ALPHAANCHOR_LAMBDA)r2   rH   speech_srJ   scoresteppeds         r   checkzSpeakerGate.check]   s`   
 kk% }}5 !!((-t(()X5#():): ;!D$FDL$(LL$5$5$7DMJJL0)bffS$--01eN*E#~5
G G;EG G8%++GQ]dmm;i#oMNG!,M0AW/LLNDMJJLUD  r   N)returnNone)rd   bool)rd   ztuple[int, int])rH   
np.ndarrayrd   rg   )rH   rg   r`   r
   rd   ztuple[bool, float, str])
__name__
__module____qualname__r5   propertyr9   r=   rK   rN   rc    r   r   r   r   0   s?    ,0 ) ) 0 0
P!r   r   )r   rg   rd   rg   )__doc__
__future__r   r'   pathlibr   numpyr   __file__resolveparentr*   r#   r<   rU   r
   r(   r)   r\   r[   r]   r^   r_   r   r   rl   r   r   <module>rt      s   0 # 	  8n$$&--44x?//
 3V<=
	3K! K!r   