
    (HJji                        d Z ddlZddlZddlZddlZddlZddlZddlZddlZddl	m
Z
 ddlmZmZmZmZmZmZmZmZ ddlmZ ddlmZ ddlmZ ddlmZmZmZmZm Z m!Z!m"Z"m#Z#  ed      Z$dee$   d	e%d
e$fdZ&g dZ'de(d
e)fdZ*	 	 	 dBde(dee(   de)deee(      d
e
f
dZ+dee(e
f   d
e%fdZ,de
d
e%fdZ-	 dCdej\                  de%de%dee/   d
df
dZ0de(dee(   de(de%d
ef
d Z1	 	 dDdee(e
f   de(de%d!e)d"e)d
ej\                  fd#Z2da3da4da5da6da7d$ Z8d% Z9d& Z:d' Z;d( Z<dEd)e=fd*Z>d+e?fd,Z@	 dFd-eej                  d.f   d/e?d0e?d1e?fd2ZB	 	 	 dGd-eej                  d.f   d3e=d4e?d5e?fd6ZC	 	 	 	 dHd-ee(ej                  f   d0e?d+ee?   d7e)d8ee?   d
ej                  fd9ZDg d:ZEd;e(d
e)fd<ZFd=e(d
e)fd>ZGde(dee(   d
ee(   fd?ZHdee(e
f   d
ee(   fd@ZIde(fdAZJy)IzUtility functions for mlx_audio.

This module provides a unified interface for loading TTS, STT, and STS models,
with lazy imports to avoid loading unnecessary dependencies.
    N)Path)ListOptionalTupleTypeTypeVarUnion
get_originget_type_hints)snapshot_download)STR_TO_WINDOW_FNbartlettblackmanhamminghanningistftmel_filtersstftT
data_classdatareturnc                    t        j                  |       st        |  d      t        |       }i }t        j                  |       D ]  }|j
                  }||vr||   }||   }t        |      }|t        u r.|j                  D 	cg c]  }	|	t        d       us|	 }
}	|
r|
d   }t        j                  |      rt        |t              rt        ||      }|||<     | di |S c c}	w )Nz is not a dataclassr    )dataclassesis_dataclass	TypeErrorr   fieldsnamer
   r	   __args__type
isinstancedict	from_dict)r   r   field_typeskwargsfield
field_namevalue
field_typeoriginaargss              Y/Users/ahmed/devFolder/claude-voice/.venv/lib/python3.12/site-packages/mlx_audio/utils.pyr$   r$   -   s    ##J/:,&9:;; ,KF##J/ZZ
T!Z  ,
 J'U?)22J2!atDz6IA2DJ!!W
 ##J/Jud4Kj%0E"z) 0,  Ks   C%C%)z*.json*.safetensorsz*.pyz*.modelz
*.tiktokenz*.txtz*.jinjaz*.jsonlz*.yaml*.npzz*.pthpathc                     | j                  d      xs> | j                  d      xs+ | j                  d      xs t        |       dkD  xr | d   dk(  S )z5Check if the path looks like a local filesystem path../~   :)
startswithlen)r1   s    r.   _is_local_pathr:   b   sV     	 	.??3	.??3	. IM,d1gn	    path_or_hf_reporevisionforce_downloadallow_patternsc                     t        |       j                         }|j                         r|S t        |       rt	        d|        |t
        }t        t        | |||            }|S )aP  
    Ensures the model is available locally. If the path does not exist locally,
    it is downloaded from the Hugging Face Hub.

    Args:
        path_or_hf_repo: The local path or Hugging Face repository ID of the model.
        revision: A revision id which can be a branch name, a tag, or a commit hash.
        force_download: Force re-download even if cached.
        allow_patterns: File patterns to download. Defaults to common model files.

    Returns:
        Path: The path to the model.

    Raises:
        FileNotFoundError: If a local path is provided but doesn't exist.
    zLocal path not found: )r=   r?   r>   )r   
expanduserexistsr:   FileNotFoundErrorDEFAULT_ALLOW_PATTERNSr   )r<   r=   r>   r?   
model_paths        r.   get_model_pathrF   l   sz    , o&113J o&"88I JKK /))		
J r;   rE   c                     t        | t              rt        | fi |} | dz  }|j                         r,t	        |d      5 }t        j                  |      cddd       S t        d|        # 1 sw Y   xY w)a_  Load model configuration from a path or Hugging Face repo.

    Args:
        model_path: Local path or Hugging Face repo ID to load config from
        **kwargs: Additional keyword arguments (revision, force_download)

    Returns:
        dict: Model configuration

    Raises:
        FileNotFoundError: If config.json is not found at the path
    zconfig.jsonzutf-8)encodingNzConfig not found at )r"   strrF   rB   openjsonloadrC   )rE   r&   config_filefs       r.   load_configrO      sp     *c"#J9&9
},K+0A99Q< 10 2:,?
@@ 10s   A,,A5c                    t        j                   t        | dz              }|s!t        j                   t        | dz              }|st        d|        i }|D ]&  }|j                  t	        j
                  |             ( |S )zLoad model weights from safetensors or npz files.

    Args:
        model_path: Path to the model directory

    Returns:
        dict: Dictionary of weight name -> array

    Raises:
        FileNotFoundError: If no weight files found
    r/   r0   z.No weight files (safetensors or npz) found in )globrI   rC   updatemxrL   )rE   weight_filesweightswfs       r.   load_weightsrW      s}     99So!=>?LyyZ'%9!:;<ZLI
 	
 Grwwr{#  Nr;   modelconfigrU   model_quant_predicatec           	          |j                  dd      |j                  dd      yj                  dd      fd}t        j                  | d   j                  dd	      |
       y)ai  Apply quantization to a model if specified in config.

    Args:
        model: The model to quantize
        config: Model config dict (should contain 'quantization' key if quantized)
        weights: Loaded weights dict (used to check which layers have scales)
        model_quant_predicate: Optional model-specific predicate for which layers to quantize
    quantizationNquantization_config
group_size@   c                     t        |d      syt        |d      r |j                  j                  d   z  dk7  ry | |      }t        |t              r|S |sy| v r|    S |  dv S )Nto_quantizedFweightr   z.scales)hasattrrb   shaper"   r#   )pmpred_resultr^   rZ   r\   rU   s      r.   get_class_predicatez/apply_quantization.<locals>.get_class_predicate   s    q.)1hAHHNN2$6$Cq$H ,/15K+t,""?"G}''r;   bitsmodeaffine)r^   rj   rk   class_predicate)getnnquantize)rX   rY   rU   rZ   ri   r^   r\   s     `` @@r.   apply_quantizationrq      s{     ::nd3Lzz"7>!!,3J(( KK&!fh/+r;   
model_type
model_namecategorymodel_remappingc           	         |j                  | d      }t        t              j                  |z  dz  }g }|j	                         rm|j                         r]|j                         D ]J  }|j                         s|j                  j                  d      r0|j                  |j                         L ||| k7  r|D ]  }||v r|} ||v s||   }  n n||} 	 d| d|  }	t        j                  |	      }
|
| fS # t        $ ri}|j                  	k7  r,t        dd       t        d	|  d
| d|j                         |d|  d| d}t        j                  |       t!        |      d}~ww xY w)a  
    Retrieve the model architecture module based on the model type and name.

    Args:
        model_type: The type of model to load (e.g., "whisper", "voxtral").
        model_name: List of model name components for remapping hints.
        category: Either "tts" or "stt".
        model_remapping: Dictionary mapping model names to architecture names.

    Returns:
        Tuple[module, str]: The imported architecture module and resolved model_type.

    Raises:
        ValueError: If the model type is not supported.
    Nmodels__
mlx_audio..models.
T)flushz"
Missing dependency while loading z: z&
Please install it using: pip install zModel type z not supported for r3   )rn   r   __file__parentrB   is_diriterdirr   r8   append	importlibimport_moduleImportErrorprintloggingerror
ValueError)rr   rs   rt   ru   model_type_mapped
models_diravailable_modelsitempartmodule_patharchemsgs                r.   get_model_classr     s   , (++J= h&&1H<Jz002&&(D{{}TYY%9%9$%? ''		2 ) "3z"AD''!
&,T2
  
	&&
"8*HZLA&&{3   66[ $d#5j\A3 G889xA 
 J<':8*AFcos   C7 7	E) A$E$$E)lazystrictc                    |j                  dd      }|j                  dd      }|j                  dd      }t        | t              rA|t        |       }|j	                  dd      }	|j	                  dd      }
t        | |	|
|      } n5t        | t              r|#t        |       }nt        d	t        |              t        |       }t        |       |d
<   ||j	                  dd      }||j	                  dd      }|||d   j                         nd}|dk(  rd|v rd}t        ||||      \  }}t        |d      r|j                  j                  |      n|}|j                  |      }t!        |       }t        |d      r|j#                  |      }t%        |dd      }t'        ||||       |j!                  t)        |j+                               |       |s#t-        j.                  |j1                                |j/                          t        |j                  d      r|j                  j3                  ||       }|S )a1  
    Base implementation for loading models (shared between TTS, STT, and STS).

    Args:
        model_path: The path or HuggingFace repo to load the model from.
        category: Either "tts" or "stt".
        model_remapping: Dictionary mapping model names to architecture names.
        lazy: If False, evaluate model parameters immediately.
        strict: If True, raise an error if any weights are missing.
        **kwargs: Additional keyword arguments (revision, force_download).

    Returns:
        nn.Module: The loaded and initialized model.
    model_name_partsNrr   r?   r=   r>   F)r=   r>   r?   Invalid model path type: rE   architecturer   llamaacoustic_dimtada)rr   rs   rt   ru   ModelConfigsanitizerZ   )r   post_load_hook)popr"   rI   get_model_name_partsrn   rF   r   r   r!   rO   lowerr   rd   r   r$   ModelrW   r   getattrrq   listitemsrS   eval
parametersr   )rE   rt   ru   r   r   r&   rs   rr   r?   r=   r>   rY   model_classmodel_configrX   rU   rZ   s                    r.   base_load_modelr   A  sD   , .5JL$/JZZ 0$7N*c"-j9J::j$/$4e<#))	

 
J	%-j9J4T*5E4FGHH$Fz?F< ZZd3
ZZ5
.8.DZ]((*$
 W6!9
-'	K ;. 	))&1 
 l+E :&G uj!..) $E+BDIufg/DE	tGMMO,V<
  "#	JJL {  "23!!00
CLr;   c                  *    t         ddlm}  | a t         S )zLazy load STT utils.r   utils)
_stt_utilsmlx_audio.sttr   )	stt_utilss    r.   _get_stt_utilsr          4
r;   c                  *    t         ddlm}  | a t         S )zLazy load TTS utils.r   r   )
_tts_utilsmlx_audio.ttsr   )	tts_utilss    r.   _get_tts_utilsr     r   r;   c                  *    t         ddlm}  | a t         S )zLazy load STS utils.r   r   )
_sts_utilsmlx_audio.stsr   )	sts_utilss    r.   _get_sts_utilsr     r   r;   c                  *    t         ddlm}  | a t         S )zLazy load VAD utils.r   r   )
_vad_utilsmlx_audio.vadr   )	vad_utilss    r.   _get_vad_utilsr     r   r;   c                  *    t         ddlm}  | a t         S )zLazy load LID utils.r   r   )
_lid_utilsmlx_audio.lidr   )	lid_utilss    r.   _get_lid_utilsr     r   r;   coeffc                    ddl }|j                  |j                  |             }|d   dk  rt        |d   d      }| |z  dz  } ||dkD     }|j                  d   }|dk  r| S |j                  |t        d|z        t        d	|z               }| |j                  ||z  dd
      z  } |j                  |j                  |             }|dkD  r| |z  } | S )zNormalize the volume of an audio signal.

    Args:
        audio: Input audio signal array (numpy array).
        coeff: Target coefficient for normalization, default is 0.2.

    Returns:
        numpy array: The volume-normalized audio signal.
    r   Nrc   g?gMbP?g{Gz?
   g?gGz?)a_mina_maxr6   )numpysortabsmaxre   meanintclip)audior   nptempscaling_factorLvolume	max_values           r.   audio_volume_normalizer     s      77266%=!D Bx#~T"Xt,&, tD

1A 	Bw WWT#cAg,TAX78F BGGEFN#RG@@E rvve}%I1}	!Lr;   lengthc           	          ddl }ddl}| j                  d   |k  r-|j                  | dt	        || j                  d   z
        f      } |j                  d| j                  d   |z
        }t	        ||z         }| || S )zGet a random audio segment of given length.

    Args:
        audio: Input audio array (numpy array).
        length: Desired segment length (sample_rate * duration).

    Returns:
        numpy array: Audio segment of specified length.
    r   N)randomr   re   padr   randint)r   r   r   r   start_index	end_indexs         r.   random_select_audio_segmentr     sz     {{1~uq#fu{{1~&=">?@..EKKNV$;<KK&()IY''r;   r   z
np.ndarrayorig_sample_ratesample_rateaxisc                    ddl }ddl}ddlm} ||k(  r| S |j	                  |       }|j                  t        |      t        |            }||z  }	||z  }
t        |	|
      }d\  }}}|j                  d|z  |z  dz   ||z  d|f      }|j                  ||	|
||d	
      j                  |j                  d      }t        | t        j                        rt        j                  |      S |S )a  Resample audio with polyphase filtering.

    Uses a Kaiser-windowed sinc anti-aliasing filter equivalent to ``librosa``'s
    ``kaiser_best`` (resampy parameters) instead of ``resample_poly``'s default
    Kaiser(5.0). The default filter has a wide transition band that leaves
    significant energy near the new Nyquist; the sharper filter band-limits
    cleanly so resampled audio matches the librosa-equivalent featurizers that
    ASR reference pipelines (e.g. NeMo) assume. See issue #24.

    Args:
        audio: Audio array as numpy or MLX.
        orig_sample_rate: Original sample rate.
        sample_rate: Target sample rate.
        axis: Axis containing the time dimension.

    Returns:
        Audio resampled to ``sample_rate``. The return type matches the input type.
    r   N)signal)r_   gbR?gQaTi-@   r6   kaiser)windowedge)r   r   padtypeFcopy)mathr   scipyr   asarraygcdr   r   firwinresample_polyastypefloat32r"   rS   array)r   r   r   r   r   r   r   audio_npr   updownmax_rate	num_zerosrolloffbetafir	resampleds                    r.   resample_audior    s	   0 ;&zz% H
((3'(#k*:
;C		Bs"D
 2t}HIIw
--	I 1$($  C
 $$
 %  fRZZef$  %"xx	""r;   top_dbframe_length
hop_lengthc                 *   ddl }|j                  |       }dt        |      |z
  |z  z   }|dk  r| S |j                  t	        |      D cg c]1  }|j                  |j                  |||z  ||z  |z    dz              3 c}      }d|j                  |j                  |d            z  }	|j                  |	      |z
  }
|j                  |	|
k\        d   }t        |      dk(  r| S t        |d         }t        |d         dz   }||z  }t        ||z  |z   t        |            }||| }t        | t        j                        r0t        j                  |j                  |j                   d	            S |j                  |j                   d	      S c c}w )
zATrim leading/trailing low-energy regions using a simple RMS gate.r   Nr6   r      g|=rc   Fr   )r   r   r9   r   rangesqrtr   log10maximumr   wherer   minr"   rS   r   r   )r   r  r  r  r   r   n_framesirmsrms_db	threshold
non_silentstart_frame	end_framestart_sample
end_sampletrimmeds                    r.   trim_silencer  Z  s    zz% HCML0Z??H1}
((
 8_		
 % GGZ!j.<2OPTUUV %		
C "((2::c5122Fv'I&I-.q1J
:!jm$KJrN#a'I+LY+l:CMJJ|J/G%"xxrzz>??>>"**5>11+	
s   	6Fvolume_normalizesegment_durationc           	         t        | t        j                        r| S t        | t              st	        dt        |              ddl}ddl}ddlm	} |j                  j                  |       st        d|         ||       \  }}	|j                  }
t        |
      dkD  r|j                  d      }||
d   z  }||	k7  rt!        ||	|      }|t#        ||z        }t%        ||      }|rt'        |      }|E|j                  d   |kD  r|d| }n-|j)                  |dt#        ||j                  d   z
        f      }t        j                  |t        j*                        S )	a  Load audio from file path or return mx.array as-is.

    Args:
        audio: Audio input - can be:
            - str: Path to audio file (will be loaded and resampled to sample_rate)
            - mx.array: MLX array (returned as-is)
        sample_rate: Target sample rate (default 24000)
        length: Target length in samples (pad or truncate if specified)
        volume_normalize: Whether to normalize audio volume
        segment_duration: If specified, randomly select a segment of this duration (seconds)

    Returns:
        mx.array: Audio waveform at target sample rate

    Raises:
        FileNotFoundError: If audio file path does not exist
        TypeError: If audio is not str or mx.array
    z#audio must be str or mx.array, got r   N)readzAudio file not found: r6   )r   )dtype)r"   rS   r   rI   r   r!   osr   mlx_audio.audio_ior  r1   rB   rC   re   r9   sumr  r   r   r   r   r   )r   r   r   r  r  r  r   
audio_readsamplesr   re   
seg_lengths               r.   
load_audior$    sW   2 %"eS!=d5k]KLL577>>% "8 @AA *5 1GMME 5zA~++1+%E!H$ && *:KH #'778
-gzB (1 ==f$gv&GffWq#fw}}Q7G.G*H&IJG88G2::..r;   )r   r   r   r   r   r   r   r   r$  r  r  r   r   r$   is_valid_module_nameget_model_categoryr   
load_modelrF   rO   rW   rq   r   r   r   c                 v    t        | t              xr( | j                         xr t        j                  |        S )z0Check if a string is a valid Python module name.)r"   rI   isidentifierkeyword	iskeyword)r   s    r.   r%  r%    s1    dC XT%6%6%8XARARSWAX=XXr;   r   c                     	 t         j                  j                  |       d uS # t        $ r4}|j                  xs d}| |k(  s| j                  | d      rY d }~y d }~ww xY w)N r3   F)r   util	find_specModuleNotFoundErrorr   r8   )r   excmissing_names      r.   _has_model_moduler3    sa    ~~''4D@@ xx~2,&+*@*@L>QRAS*T	s    # 	A )AAA c                    t               }t               }t               }t               }t	               }| g|xs g z   }d|j
                  fd|j
                  fd|j
                  fd|j
                  fd|j
                  fg}|D ]H  \  }	}
|	|vr|D ]9  }|
j                  ||      }t        |      s!d|	 d| }t        |      s5|	c c S  J |D ];  \  }	}
|D ]1  }||
v s|
|   }t        |      sd|	 d| }t        |      s-|	c c S  = |D ]6  \  }	}
|D ],  }||
vst        |      sd|	 d| }t        |      s(|	c c S  8 y)	zMDetermine whether a model belongs to the TTS, STT, STS, LID, or VAD category.ttssttstslidvadry   rz   N)	r   r   r   r   r   MODEL_REMAPPINGrn   r%  r3  )rr   rs   r   r   r   r   r   
candidates
categoriesrt   remaphintr   r   s                 r.   r&  r&    s    I I I I I!1r2J 
	))*		))*		))*		))*		))*J &%:%D99T4(D'-&xj?K -  & &%Du}T{+D1 *8*HTFC$[1#O  & &%D5 %9$%? *8*HTFC$[1#O	  & r;   c                    d }t        | t              r#| j                         j                  d      d   }nvt        | t              rO	 | j
                  j                  d      }| j
                  |dz      j                         j                  d      d   }nt        dt        |              g }t               }|j                  d      D cg c]  }|s|	 }}|D ]  }|r||v r
|j                  |       |j                  |       d|v r@|j                  d      D ],  }|s||vs|j                  |       |j                  |       . t        j                  d	d
|      }|s||vs|j                  |       |j                  |        t        t!        |            D ]x  }	t        |	dz   t!        |      dz         D ]X  }
||	|
 }dj#                  |      d
j#                  |      fD ],  }|s||vs|j                  |       |j                  |       . Z z |S # t        $ r | j                  j                         }Y w xY wc c}w )Nr4   rc   hubr6   z--r   -_z
[^a-z0-9]+r-  r   )r"   rI   r   splitr   partsindexr   r   r!   setr   addresubr  r9   join)rE   rs   rE  rD  seenr   
dash_partssubpart
normalizedstartendsegmentcombineds                r.   r   r   ,  s   J*c"%%'--c226
	J	%	1$$**51E#))%!)4::<BB4HLJ 4T*5E4FGHHE5D#-#3#3C#8A#84D$#8JAtt|T$;::c?wd2LL)HHW% +
 VVM2t4
*D0LL$HHZ  " s:'C
Oa$78C s+G XXg.0@A 4LL*HHX& B 9 ( LG  	1#..0J	1 Bs   AH I	I	#IIc                 *   t        |       }d}	 t        |       }|j                  dd      }||j                  dd      }|t	               j                  |      }t        ||      }|s||t        d|        t               j                  t               j                  t	               j                  t               j                  t               j                  d}||vrt        d| d       ||   |       S # t        $ r}i }|}Y d}~d}~ww xY w)a$  Load a TTS, STT, STS, LID, or VAD model based on its configuration and name.

    Args:
        model_name (str): Name or path of the model to load

    Returns:
        The loaded model instance

    Raises:
        ValueError: If the model type cannot be determined or is not supported
    Nrr   r   z#Could not determine model type for )r5  r6  r7  r8  r9  zModel type 'z' not supported)r   rO   rC   rn   r   infer_model_type_from_configr&  r   r   r'  r   r   r   )rs   r   
load_errorrY   r1  rr   model_categorymodel_loaderss           r.   r'  r'  Z  s%    ,J7JZ( L$/JZZ5
#%BB6J
'
4DEN!>zlKLL **********M ]*<'7GHH(=(449  
s   C; ;	DDD)NFN)N)FF)g?)rc   )r  i   i   )i]  NFN)K__doc__r   rQ   r   importlib.utilrK   r*  r   rH  pathlibr   typingr   r   r   r   r   r	   r
   r   mlx.corecorerS   mlx.nnro   huggingface_hubr   mlx_audio.dspr   r   r   r   r   r   r   r   r   r#   r$   rD   rI   boolr:   rF   rO   rW   Modulecallablerq   r   r   r   r   r   r   r   r   r   r   r   r   floatr   r   r   r   r  r  r$  __all__r%  r3  r&  r   r'  r   r;   r.   <module>rf     s          	 	 	 	   -	 	 	 CL $q'    !  L    # *.	,,sm, , T#Y'	,
 
,^AE#t), A4 A0T d D 15	/99// / $H-	/
 
/d;;S	; ; 	;
 ;D _c4i __ _ 	_
 _ YY_F 




' 'T(s (4 	:<'(:: : 	:~ 	$2<'($2$2 $2 	$2R  "&*D/bhhD/D/ SMD/ 	D/
 smD/ XXD/N@Ys Yt Y
3 4 23 2DI 2(3- 2j+U39%5 +$s) +\-53 -5r;   