
    (HJjWV              5       :   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	 d dl
mZ d dlmZ d dlZd dlmZ d dlmZ d dlmZ ddlmZ dd	lmZ 	 	 	 d;d
ej8                  dededededeeef   fdZ	 	 d<d
ej8                  dedededej8                  f
dZ dedefdZ!de"de#dede"ddf
dZ$dede#e   fdZ%de#e   defdZ&dejN                  de(fdZ)dejN                  de(fdZ*	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d=d e"dee	e"ejN                  f      d!ee   d"e"d#ee"   d$ee"   d%ed&e"d'ee   d(ee   d)ee   d*ee	e"e#e"   f      d+ee	e"e#e"   f      d,ee	e"ejN                  f      d-ee"   d.e"de"d/e(d0e(d1e(d2ed3e(d4ed5e(d6e(ddf4d7Z+d8 Z,d9 Z-e.d:k(  r e-        yy)>    N)PathLike)AnyOptionalTupleUnion)sliding_window_view)write)
load_audio   )AudioPlayer)
load_modelwavsample_ratewindow_durationenergy_thresholdmargin_factorreturnc           
         t        ||z        }||z  }|dz  }t        | |      dd|   }t        j                  t        j                  |dz  d            }	|	|k\  }
t        j
                  |
      st        d      t        dt        j                  |
      |z  |z
        }t        t        |       t        |
      dz
  t        j                  |
ddd         z
  |z  |z         }||fS )	a,  Detect the start and end points of speech in an audio signal using RMS energy.

    Args:
        wav: Input audio signal array with values in [-1, 1]
        sample_rate: Audio sample rate in Hz
        window_duration: Duration of detection window in seconds
        energy_threshold: RMS energy threshold for speech detection
        margin_factor: Factor to determine extra margin around detected boundaries

    Returns:
        tuple: (start_index, end_index) of speech segment

    Raises:
        ValueError: If the audio contains only silence
    
   N   r   axisz*No speech detected in audio (only silence)r   )intr   npsqrtmeanany
ValueErrormaxargmaxminlen)r   r   r   r   r   window_sizemargin	step_sizewindowsenergyspeech_maskstartends                `/Users/ahmed/devFolder/claude-voice/.venv/lib/python3.12/site-packages/mlx_audio/tts/generate.pydetect_speech_boundariesr-      s    , o34K[(Fr!I "#{3KiK@G WWRWWWaZa01F,,K66+EFF299[)I5>?E
C	[	A			+dd*; <	<	IFRC
 #:    volume_thresholdc                 .    t        | |||      \  }}| || S )a  Remove silence from both ends of an audio signal.

    Args:
        wav: Input audio signal array
        sample_rate: Audio sample rate in Hz
        window_duration: Duration of detection window in seconds
        volume_threshold: Amplitude threshold for silence detection

    Returns:
        np.ndarray: Audio signal with silence removed from both ends

    Raises:
        ValueError: If the audio contains only silence
    )r-   )r   r   r   r/   r*   r+   s         r,   remove_silence_on_both_endsr1   A   s*    ( *[/+;JE3 uS>r.   pitchc                 B    dt        j                  d| dz  z         z  }|S )z
    Converts a frequency from the Hertz scale to the Mel scale.

    Parameters:
    - pitch: float or ndarray
        Frequency in Hertz.

    Returns:
    - mel: float or ndarray
        Frequency in Mel scale.
    i#
  r   i  )r   log10)r2   mels     r,   hertz_to_melr6   [   s$     !eck/*
*CJr.   	file_nameaudio_chunksaudio_formatc                 |    |sy t        |      dkD  rt        j                  |d      n|d   }t        | |||       y )Nr   r   r   format)r#   mxconcatenateaudio_write)r7   r8   r   r9   audios        r,   write_joined_audiorA   k   sE      |q  	|!,!_ 

 	5+lCr.   valuec                 R    | g S t        | t        t        f      rt        |       S | gS )N)
isinstancelisttuple)rB   s    r,   _as_reference_listrG   |   s+    }	%$'E{7Nr.   valuesc                 2    | sy t        |       dk(  r| d   S | S )Nr   r   )r#   )rH   s    r,   _collapse_reference_listrJ      s#    
6{aayMr.   modelc                     	 dt        j                  | j                        j                  v S # t        t
        f$ r Y yw xY w)Nref_textF)inspect	signaturegenerate
parameters	TypeErrorr   rK   s    r,   _model_accepts_ref_textrT      s>    W..u~~>IIIIz" s   *- ??c                      t        | dd      du S )Npreserve_ref_audio_pathFT)getattrrS   s    r,    _model_preserves_ref_audio_pathsrX      s    53U;tCCr.   text
max_tokensvoicepromptinstructspeed	lang_code	cfg_scale
ddpm_stepssigma	ref_audiorM   	stt_modeloutput_pathfile_prefix
join_audioplayverbosetemperaturestreamstreaming_intervalsaveuse_zero_spk_embc                    	 |xs |}|t        d      t        |t              rt        |      }t	        |      }t	        |      }|r$|r"t        |      t        |      k7  rt        d      t        |      dkD  r|st        d      |rd}t        |d      r|j                  d	k(  rd
}t        |      }|rg }|D ]}  }t        |t        t        f      rTt        j                  |      } t        j                  j                  |       st        d|        |j                  |        m|j                  |        ng }|D ]  }t        |t        t        f      rjt        j                  |      } t        j                  j                  |       st        d|        |j                  t!        | |j"                  |             |j                  |        t%        |      }|rt%        |      }n|rd}nt'        |      r|t        d      t)        d       ddlm}! t        |t              r |!|      }|D "cg c]  }"|j/                  |"      j0                   }#}"~t3        j4                          t%        |#      }t)        d|        nd}n|rt%        |      }|rt7        |j"                        nd}$|r7t        j8                  |d
       t        j                  j;                  ||      }|t)        d|        t)        d|  d| d| d|        |j=                         D %&ci c]  \  }%}&|&	|%|& }'}%}&t?        d?| |||||||||||d|'}(|||(d<   |||(d<   |	|	|(d<   |||(d<   |
|
|(d<    |j.                  d?i |(})|xr |}*g }+g },i }-i }.| d| }/tA        |)      D ]  \  }0}1|r|$jC                  |1jD                         |*ra|r|,j                  |1jD                         n|1jF                  }2|2|-vrg |-|2<   |1j"                  |.|2<   |-|2   j                  |1jD                         nq|r|s|+j                  |1jD                         nQ|sO| d |0d!d| }/tI        |/tK        jL                  |1jD                        |1j"                  |"       t)        d#|/        |st)        d$       t)        d%|1jN                          t)        d&|1jP                  d'   d(       t)        d)|1jR                   d*|1jT                  d+   d(d,       t)        d-|1jP                  d.    d/|1jP                  d'   d(d0       t)        d1|1jV                  d2d3       t)        d4|1jX                  d2d5       t)        d6|1jZ                  d2d7        |*rE|rC|,rA|rt)        d8t        |,       d9       t]        |/|,|j"                  |       t)        d#|/        n|*r`|-r^t_        |-      D ]O  }2| d |2d!d| }/|-|2   }3|.|2   }4|rt)        d8t        |3       d:|2        t]        |/|3|4|       t)        d#|/        Q nH|rF|sD|+rB|rt)        d8t        |+       d;       t]        |/|+|j"                  |       |rt)        d#|/        |r!|$ja                          |$jc                          yyc c}"w c c}&}%w # td        $ r#}5t)        d<|5        t)        d=       Y d}5~5yd}5~5wtf        $ r,}5t)        d>|5        ddl4}6|6jk                          Y d}5~5yd}5~5ww xY w)@a  
    Generates audio from text using a specified TTS model.

    Parameters:
    - text (str): The input text to be converted to speech.
    - model (str): The TTS model to use.
    - voice (str): The voice style to use (also used as speaker for Qwen3-TTS models).
    - instruct (str): Instruction for emotion/style (CustomVoice) or voice description (VoiceDesign).
    - temperature (float): The temperature for the model.
    - speed (float): Playback speed multiplier.
    - lang_code (str): The language code.
    - ref_audio (mx.array): Reference audio you would like to clone the voice from.
    - ref_text (str): Caption for reference audio.
    - stt_model_path (str): A mlx whisper model to use to transcribe.
    - output_path (str): Directory path where audio files will be saved.
    - file_prefix (str): The output file path without extension.
    - audio_format (str): Output audio format (e.g., "wav", "flac").
    - join_audio (bool): Whether to join multiple audio files into one.
    - play (bool): Whether to play the generated audio.
    - verbose (bool): Whether to print status messages.
    - save (bool): Whether to save streamed audio to a file when using stream mode.
    - model (object): A already loaded model.
    - stt_model (object): A already loaded stt model.
    Returns:
    - None: The function writes the generated audio to a file when not streaming, or when streaming with saving enabled.
    Nz.Model path or model instance must be provided.)
model_pathz7ref_audio and ref_text lists must have the same length.r   z;Multiple ref_text values require matching ref_audio values.F
model_typesparkTz Reference audio file not found: )r   volume_normalizezKSTT model path or model instance must be provided when ref_text is missing.z-Ref_text not found. Transcribing ref_audio...r   )loadz[94mRef_text:[0m )r   )exist_okz[94mInstruct:[0m z[94mText:[0m z
[94mVoice:[0m z
[94mSpeed:[0m zx
[94mLanguage:[0m )rY   r[   r^   r_   rc   rM   rj   ri   rk   rl   r]   rn   rZ   r`   ra   r\   rb   ._03dr;   u0   ✅ Audio successfully generated and saving as: z
==========zDuration:              zSamples/sec:           zsamples-per-secz.1fzPrompt:                z	 tokens, ztokens-per-secz tokens-per-seczAudio:                 samplesz
 samples, z samples-per-seczReal-time factor:      z.2fxzProcessing time:       szPeak memory usage:     GBzJoining z streamed audio chunksz# streamed audio chunks for segment z audio fileszImport error: zIThis might be due to incorrect Python path. Check your project structure.zError loading model:  )6r   rD   strr   rG   r#   hasattrrq   rX   r   osfspathpathexistsFileNotFoundErrorappendr
   r   rJ   rT   printmlx_audio.sttrt   rP   rY   r=   clear_cacher   makedirsjoinitemsdict	enumeratequeue_audior@   segment_idxr?   r   arrayaudio_durationaudio_samplestoken_countr\   real_time_factorprocessing_time_secondspeak_memory_usagerA   sortedwait_for_drainstopImportError	Exception	traceback	print_exc)7rY   rK   rZ   r[   r\   r]   r^   r_   r`   ra   rb   rc   rM   rd   re   rf   r9   rg   rh   ri   rj   rk   rl   rm   rn   kwargsref_audio_valuesref_text_values	normalizepreserve_ref_pathsloaded_ref_audioref_audio_itemref_audio_pathload_stt_modelr@   transcribed_ref_textplayerkeyrB   extra_kwargs
gen_kwargsresultssave_streamed_audio
audio_liststreamed_audio_chunksstreamed_segment_audiostreamed_segment_sample_ratesr7   iresultr   r8   r   er   s7                                                          r,   generate_audior      s   pr~v=MNNeS!%0E-i8,X6O$,<(==VWW!#,<M  Iul+0@0@G0K 	!A%!H!#% &6N!.3/B)+>)B!ww~~n="3"B>BR S#  )//?(//? '7 $& &6N!.3/B)+>)B!ww~~n="3"B>BR S#  )//& .,1,=,=1: )//? '7  11ABI3OD#(/$$/  EF@i- .y 9I@P(@PuI&&u-22@P % (  34HI1(<=/@H @D):):; KKd3'',,{K@K-hZ89#D6 *%%*G ,%%*G ,((1{4	
 *0
)7:35;LCJ 	 
  
#1-
 

 !'1J|$ &/J{#!'1J|$#)Jx "'Jw %...:.$o
 "!#(*%"m1\N3	"7+IAv""6<<0")00>"("4"4K"*@@>@.{;EKEWEW5kB*;7>>v||LF!!&,,/*m1QsG1\NC	HHV\\*&&'	 HTUl#/0E0E/FGH-f.B.BCT.UVY-Z[ -f.@.@-A6==YiKjknJoo~ -f.B.B9.M-NjY_YmYmn  ZA  BE  YF  FV  W /0G0G/LANO/0N0Ns/SSTUV/0H0H/MRPQQ ,T :2G%:!; <<RST%!!	 DYKPQ %;%&<=*m1[,=Q|nM	5kB;KH"|,--P&-)
 #  	 HTU!  >" :Z 1>?!!	 HTU!!#KKM o(>
x  
qc"#W	
 	
  %aS)*	sQ   H7Z+ 9"Z CZ+ )
Z%4Z%9E*Z+ $G:Z+  Z+ +	\
4[\
"\\
c                     t        j                  d      } | j                  dt        dd       | j                  dt        d d	       | j                  d
t        d d	       | j                  dt        d d	       | j                  dt        d d	       | j                  dt        d d	       | j                  dt
        dd	       | j                  dt
        d d	       | j                  dt        d d	       | j                  dt
        dd	       | j                  dt
        d d	       | j                  dt
        d d	       | j                  d t        d d!	       | j                  d"t
        d d#	       | j                  d$t        d d%	       | j                  d&d d'(       | j                  d)t        d*d+	       | j                  d,t
        dd-	       | j                  d.t        d/d0	       | j                  d1t        d d2	       | j                  d3t        d4d5	       | j                  d6d7d89       | j                  d:d7d;9       | j                  d<d7d=9       | j                  d>t        d?d@	       | j                  dAt        dBd dCD       | j                  dEt        dBd dFD       | j                  dGt        dHdI	       | j                  dJt
        dKdL	       | j                  dMt
        d dN	       | j                  dOd7dP9       | j                  dQt
        dRdS	       | j                  dTt        dUdV	       | j                  dWt
        dXdY	       | j                  dZd7d[9       | j                  d\t
        d]d^	       | j                  d_d7d`9       | j                         }|j                  r|j                  s| j                  da       |j                  zt        j                  j                         s3t        j                  j                         j                         |_
        |S t!        db       t#        dc      j                         |_
        |S )dNz#Generate audio from text using TTS.)descriptionz--modelTzPath or repo id of the model)typerequiredhelpz--max_tokensz$Maximum number of tokens to generate)r   defaultr   z--textz1Text to generate (leave blank to input via stdin)z--voicez?Voice/speaker name (e.g., Chelsie, Ethan, Vivian for Qwen3-TTS)z--promptz&Optional model-specific prompt prefix.z
--instructzNInstruction for CustomVoice (emotion/style) or VoiceDesign (voice description)z--exaggerationg      ?z!Exaggeration factor for the voicez--cfg_scalezDClassifier-free guidance scale. Defaults to the model configuration.z--ddpm_stepszFOverride diffusion steps. Higher = better quality, slower (try 30-50).z--speed      ?zSpeed of the audioz--gen_durationz7Optional model-specific generation duration in seconds.z--duration_multiplierz6Optional model-specific automatic duration multiplier.z--stepsz.Optional model-specific generation step count.z--stg_scalez6Optional model-specific spatiotemporal guidance scale.z--stg_blockz6Optional model-specific spatiotemporal guidance block.z--rescale_scalez*Optional model-specific CFG rescale value.)r   r   z--gendermalez"Gender of the voice [male, female]z--pitchzPitch of the voicez--lang_codeenzLanguage codez--output_pathzDirectory path for output filesz--file_prefixr@   zOutput file name prefixz	--verbose
store_truezPrint verbose output)actionr   z--join_audiozJoin all audio files into onez--playzPlay the output audioz--audio_formatr   zOutput audio formatz--ref_audior   z8Path to reference audio. Repeat for multiple references.)r   r   r   r   z
--ref_textzBCaption for reference audio. Repeat to match repeated --ref_audio.z--stt_model-mlx-community/whisper-large-v3-turbo-asr-fp16z.STT model to use to transcribe reference audioz--temperatureffffff?zTemperature for the modelz--sigmaz6Optional model-specific sigma value (e.g., Ming Omni).z--use_zero_spk_embzFOptional model-specific zero speaker embedding mode (e.g., Ming Omni).z--top_pg?zTop-p for the modelz--top_k2   zTop-k for the modelz--repetition_penaltyg?z Repetition penalty for the modelz--streamz.Stream the audio as segments during generationz--streaming_interval       @z3The time interval in seconds for streaming segmentsz--savez1Save streamed audio to a file. Requires --stream.z--save requires --streamz"Please enter the text to generate:z> )argparseArgumentParseradd_argumentr~   r   float
parse_argsrm   rk   errorrY   sysstdinisattyreadstripr   input)parserargss     r,   r   r     s   $$1VWF
+	   3	   @	   N	   5	   ]	   0	   S	   U	   	sAUV
F	   E	   =	   E	   E	   9  
 f3W   	sAUV
COT
c46W   c79R   L?UV
|2Q   <ST
sE8M   G   Q   ?=	   eS7R   E	   U  
 	sAVW
	R>ST
/	   =  
 B	   @   Dyy/0yyyy!		(..0DI
 K 67d))+DIKr.   c                  @    t               } t        di t        |        y )Nr}   )r   r   vars)r   s    r,   mainr     s    <D T$Z r.   __main__)皙?{Gz?r   )r   r   )Ni  af_heartNNr   r   NNNNNr   Nr@   r   FFTr   Fr   FF)/r   rN   r   r   r   typingr   r   r   r   mlx.corecorer=   mlx.nnnnnumpyr   numpy.lib.stride_tricksr   mlx_audio.audio_ior	   r?   mlx_audio.utilsr
   audio_playerr   utilsr   ndarrayr   r   r-   r1   r6   r~   rE   rA   rG   rJ   ModuleboolrT   rX   r   r   r   __name__r}   r.   r,   <module>r      si     	 
  . .    7 3 & %  !"*	** * 	*
 * 38_*` !"		  	
 ZZ4 %  DDD D 	D
 
D"c d3i T#Y 3 299  DBII D$ D .2 $ "!% $!1504 	8!% #"7j
jE#ryy.)*j j 	j
 SMj smj j j j j E?j c49n-.j uS$s)^,-j c299nj" ##j$ %j& 'j( )j* +j, -j. /j0 1j2 3j4 5j6 7j: 
;jZ	xv!
 zF r.   