§
    ‰eîia—  ã            	       óÊ  — U 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	Z	ddl
Z
ddlZddlmZmZmZmZ  ej        e¦  «        Zd„ Zdefd„ZddlmZ defd„Zdee         fd	„Zdefd
„Zdefd„Zdefd„Z dZ!dZ"dZ#dZ$dZ%dZ&ej'         (                     ej)        ¦   «         d¦  «        Z*d/de+de,de+ddfd„Z- G d„ d¦  «        Z. G d„ d¦  «        Z/de/e.z  fd„Z0h d£Z1 ej2        d ej3        ¬!¦  «        Z4d"edefd#„Z5d0d$ed%ee         deeef         fd&„Z6da7eej8                 e9d'<    e	j:        ¦   «         Z;d1d(„Z<d)edefd*„Z=deeef         fd+„Z>d2d-e+de+fd.„Z?dS )3aV  Voice Mode -- Push-to-talk audio recording and playback for the CLI.

Provides audio capture via sounddevice, WAV encoding via stdlib wave,
STT dispatch via tools.transcription_tools, and TTS playback via
sounddevice or system audio players.

Dependencies (optional):
    pip install sounddevice numpy
    or: pip install hermes-agent[voice]
é    N)ÚAnyÚDictÚListÚOptionalc                  ó   — ddl } ddl}| |fS )z²Lazy-import sounddevice and numpy.  Returns (sd, np).

    Raises ImportError or OSError if the libraries are not available
    (e.g. PortAudio missing on headless servers).
    r   N)ÚsounddeviceÚnumpy)ÚsdÚnps     ú2/home/div/.hermes/hermes-agent/tools/voice_mode.pyÚ_import_audior       s%   € ð ÐÐÐØÐÐÐØˆrˆ6€Mó    Úreturnc                  óT   — 	 t          ¦   «          dS # t          t          f$ r Y dS w xY w)z/Return True if audio libraries can be imported.TF)r   ÚImportErrorÚOSError© r   r   Ú_audio_availabler   +   s<   € ðÝ‰ŒˆØˆtøÝ�Ð!ð ð ð Øˆuˆuðøøøs   ‚ ’'¦')Ú	is_termuxc                  ó&   — t          ¦   «         rdS dS )NzGpkg install python-numpy portaudio && python -m pip install sounddevicezpip install sounddevice numpy)Ú_is_termux_environmentr   r   r   Ú_voice_capture_install_hintr   7   s   € ÝÑÔð YØXÐXØ*Ð*r   c                  óJ   — t          ¦   «         sd S t          j        d¦  «        S )Nztermux-microphone-record)r   ÚshutilÚwhichr   r   r   Ú_termux_microphone_commandr   =   s&   € Ý!Ñ#Ô#ð ØˆtÝŒ<Ð2Ñ3Ô3Ð3r   c                  ó’   — t          ¦   «         sdS 	 t          j        g d¢dddd¬¦  «        } d| j        pdv S # t          $ r Y dS w xY w)NF)ÚpmÚlistÚpackageszcom.termux.apiTé   ©Úcapture_outputÚtextÚtimeoutÚcheckzpackage:com.termux.apiÚ )r   Ú
subprocessÚrunÚstdoutÚ	Exception)Úresults    r   Ú_termux_api_app_installedr-   D   s{   € Ý!Ñ#Ô#ð Øˆuð
Ý”Ø8Ð8Ð8ØØØØð
ñ 
ô 
ˆð (¨F¬MÐ,?¸RÐ@Ð@øÝð ð ð Øˆuˆuðøøøs   ’%8 ¸
AÁAc                  ó>   — t          ¦   «         d uot          ¦   «         S ©N)r   r-   r   r   r   Ú_termux_voice_capture_availabler0   T   s   € Ý%Ñ'Ô'¨tÐ3ÐSÕ8QÑ8SÔ8SÐSr   c                  óØ  — g } g }t          ¦   «         }t          ¦   «         }t          |o|¦  «        }t          d„ dD ¦   «         ¦  «        r|                      d¦  «         ddlm}  |¦   «         r|                      d¦  «         	 t          dd¦  «        5 }d	|                     ¦   «          	                    ¦   «         v rJt          j                             d
¦  «        r|                     d¦  «         n|                      d¦  «         ddd¦  «         n# 1 swxY w Y   n# t          t          t          f$ r Y nw xY w	 t!          ¦   «         \  }}	 |                     ¦   «         }	|	s-|r|                     d¦  «         n|                      d¦  «         nr# t$          $ re t          j                             d
¦  «        r|                     d¦  «         n-|r|                     d¦  «         n|                      d¦  «         Y nw xY wnÝ# t&          $ rZ |r|                     d¦  «         n?|r|s|                      d¦  «         n%|                      dt)          ¦   «         › d�¦  «         Y nzt          $ rn |r|                     d¦  «         nS|r|s|                      d¦  «         n9t+          ¦   «         r|                      d¦  «         n|                      d¦  «         Y nw xY w|  | |dœS )zøDetect if the current environment supports audio I/O.

    Returns dict with 'available' (bool), 'warnings' (list of hard-fail
    reasons that block voice mode), and 'notices' (list of informational
    messages that do NOT block voice mode).
    c              3   óT   K  — | ]#}t           j                             |¦  «        V — Œ$d S r/   )ÚosÚenvironÚget)Ú.0Úvs     r   ú	<genexpr>z+detect_audio_environment.<locals>.<genexpr>f   s0   è è € Ð
RÐ
R �2Œ:�>Š>˜!ÑÔÐ
RÐ
RÐ
RÐ
RÐ
RÐ
Rr   )Ú
SSH_CLIENTÚSSH_TTYÚSSH_CONNECTIONz.Running over SSH -- no audio devices availabler   )Úis_containerz3Running inside Docker container -- no audio devicesz/proc/versionÚrÚ	microsoftÚPULSE_SERVERz%Running in WSL with PulseAudio bridgezÜRunning in WSL -- audio requires PulseAudio bridge.
  1. Set PULSE_SERVER=unix:/mnt/wslg/PulseServer
  2. Create ~/.asoundrc pointing ALSA at PulseAudio
  3. Verify with: arecord -d 3 /tmp/test.wav && aplay /tmp/test.wavNzMNo PortAudio devices detected, but Termux:API microphone capture is availablez&No audio input/output devices detectedz?Audio device query failed but PULSE_SERVER is set -- continuingzMPortAudio device query failed, but Termux:API microphone capture is availablez6Audio subsystem error (PortAudio cannot query devices)zDTermux:API microphone recording available (sounddevice not required)zkTermux:API Android app is not installed. Install/update the Termux:API app to use termux-microphone-record.zAudio libraries not installed (ú)zBTermux:API microphone recording available (PortAudio not required)zmPortAudio system library not found -- install it first:
  Termux: pkg install portaudio
Then retry /voice on.z›PortAudio system library not found -- install it first:
  Linux:  sudo apt-get install libportaudio2
  macOS:  brew install portaudio
Then retry /voice on.)Ú	availableÚwarningsÚnotices)r   r-   ÚboolÚanyÚappendÚhermes_constantsr<   ÚopenÚreadÚlowerr3   r4   r5   ÚFileNotFoundErrorÚPermissionErrorr   r   Úquery_devicesr+   r   r   r   )
rB   rC   Útermux_mic_cmdÚtermux_app_installedÚtermux_capturer<   Úfr
   Ú_Údevicess
             r   Údetect_audio_environmentrT   X   sã  € ð €HØ€GÝ/Ñ1Ô1€NÝ4Ñ6Ô6ÐÝ˜.ÐAÐ-AÑBÔB€Nõ Ð
RÐ
RÐ&QÐ
RÑ
RÔ
RÑRÔRð JØ�ŠÐHÑIÔIÐIð .Ð-Ð-Ð-Ð-Ð-Ø€|�~„~ð OØ�ŠÐMÑNÔNÐNðÝ�/ 3Ñ'Ô'ð 
	¨1Ø˜aŸfšf™hœhŸnšnÑ.Ô.Ð.Ð.Ý”:—>’> .Ñ1Ô1ð Ø—N’NÐ#JÑKÔKÐKÐKà—O’Oð^ñô ð ð
	ð 
	ð 
	ñ 
	ô 
	ð 
	ð 
	ð 
	ð 
	ð 
	ð 
	øøøð 
	ð 
	ð 
	ð 
	øøõ �µÐ8ð ð ð Øˆðøøøð.Ý‘”‰ˆˆAð	ZØ×&Ò&Ñ(Ô(ˆGØð NØ!ð NØ—N’NÐ#rÑsÔsÐsÐsà—O’OÐ$LÑMÔMÐMøøÝð 	Zð 	Zð 	Zõ Œz�~Š~˜nÑ-Ô-ð ZØ—’Ð`ÑaÔaÐaÐaØð ZØ—’ÐnÑoÔoÐoÐoà—’Ð XÑYÔYÐYøøð	Zøøøøøõ ð `ð `ð `Øð 	`Ø�NŠNÐaÑbÔbÐbÐbØð 	`Ð$8ð 	`Ø�OŠOØ}ñô ð ð ð �OŠOÐ^Õ>YÑ>[Ô>[Ð^Ð^Ð^Ñ_Ô_Ð_øøÝð ð ð Øð 	Ø�NŠNÐ_Ñ`Ô`Ð`Ð`Øð 	Ð$8ð 	Ø�OŠOØ}ñô ð ð õ $Ñ%Ô%ð 	Ø�OŠOð(ñô ð ð ð �OŠOð(ñô ð øøðøøøð, "�\ØØðð ð sw   ÂD! ÂA3DÄ	D! ÄDÄD! ÄDÄD! Ä!D;Ä:D;Ä?H ÅAF ÆH ÆA,HÈH ÈHÈH ÈA!K"É+A4K"Ë!K"i€>  é   Úint16é   éÈ   ç      @Úhermes_voiceép  ç¸…ëQ¸¾?Ú	frequencyÚdurationÚcountc           	      óz  — 	 t          ¦   «         \  }}n# t          t          f$ r Y dS w xY w	 d}t          t          |z  ¦  «        }t          t          |z  ¦  «        }g }t          |¦  «        D �]!}	|                     d||d¬¦  «        }
|                     d|j        z  | z  |
z  ¦  «        }t          t          t          dz  ¦  «        |dz  ¦  «        }|d|…xx         |                     dd	|¦  «        z  cc<   || d…xx         |                     d	d|¦  «        z  cc<   | 
                    |d
z  dz                       |j        ¦  «        ¦  «         |	|d	z
  k     r/| 
                    |                     ||j        ¬¦  «        ¦  «         �Œ#|                     |¦  «        }|                     |t          ¬¦  «         t!          j        ¦   «         dz   }|                     ¦   «         rˆ|                     ¦   «         j        rot!          j        ¦   «         |k     rXt!          j        d¦  «         |                     ¦   «         r0|                     ¦   «         j        rt!          j        ¦   «         |k     °X|                     ¦   «          dS # t,          $ r&}t.                               d|¦  «         Y d}~dS d}~ww xY w)zòPlay a short beep tone using numpy + sounddevice.

    Args:
        frequency: Tone frequency in Hz (default 880 = A5).
        duration: Duration of each beep in seconds.
        count: Number of beeps to play (with short gap between).
    Ng¸…ëQ¸®?r   F)ÚendpointrW   ç{®Gáz„?é   rU   ç333333Ó?iÿ  ©Údtype©Ú
samplerateç       @zBeep playback failed: %s)r   r   r   ÚintÚSAMPLE_RATEÚrangeÚlinspaceÚsinÚpiÚminrF   ÚastyperV   ÚzerosÚconcatenateÚplayÚtimeÚ	monotonicÚ
get_streamÚactiveÚsleepÚstopr+   ÚloggerÚdebug)r]   r^   r_   r
   r   ÚgapÚsamples_per_beepÚsamples_per_gapÚpartsÚiÚtÚtoneÚfade_lenÚaudioÚdeadlineÚes                   r   Ú	play_beeprˆ   É   s¬  € ðÝ‘”‰ˆˆBˆBøÝ�Ð!ð ð ð Øˆˆðøøøð4ØˆÝ�{¨XÑ5Ñ6Ô6ÐÝ�k¨CÑ/Ñ0Ô0ˆàˆÝ�u‘”ð 		Hñ 		HˆAØ—’˜A˜xÐ)9ÀE�ÑJÔJˆAà—6’6˜!˜bœe™) iÑ/°!Ñ3Ñ4Ô4ˆDÝ�3�{¨TÑ1Ñ2Ô2Ð4DÈÑ4IÑJÔJˆHØ��(�ˆOˆOŒO˜rŸ{š{¨1¨a°Ñ:Ô:Ñ:ˆOˆO‰OØ�(���ÐÐÔ §¢¨A¨q°(Ñ ;Ô ;Ñ;ÐÐÑØ�LŠL˜$ ™* uÑ,×4Ò4°R´XÑ>Ô>Ñ?Ô?Ð?Ø�5˜1‘9Š}ˆ}Ø—’˜RŸXšX o¸R¼X˜XÑFÔFÑGÔGÐGùà—’˜uÑ%Ô%ˆØ
�Š�¥+ˆÑ.Ô.Ð.õ ”>Ñ#Ô# cÑ)ˆØ�mŠm‰oŒoð 	 "§-¢-¡/¤/Ô"8ð 	½T¼^Ñ=MÔ=MÐPXÒ=XÐ=XÝŒJ�tÑÔÐð �mŠm‰oŒoð 	 "§-¢-¡/¤/Ô"8ð 	½T¼^Ñ=MÔ=MÐPXÒ=XÐ=Xà
�Š‰	Œ	ˆ	ˆ	ˆ	øÝð 4ð 4ð 4Ý�ŠÐ/°Ñ3Ô3Ð3Ð3Ð3Ð3Ð3Ð3Ð3øøøøð4øøøs$   ‚ ”)¨)­IJ
 Ê

J:ÊJ5Ê5J:c                   ó¬   — e Zd ZdZdZdd„Zedefd„¦   «         Zede	fd„¦   «         Z
edefd„¦   «         Zddd	„Zdd
„Zdee         fd„Zdd„Zdd„ZdS )ÚTermuxAudioRecorderzBRecorder backend that uses Termux:API microphone capture commands.Fr   Nc                 ón   — t          j        ¦   «         | _        d| _        d| _        d | _        d| _        d S )NFç        r   )Ú	threadingÚLockÚ_lockÚ
_recordingÚ_start_timeÚ_recording_pathÚ_current_rms©Úselfs    r   Ú__init__zTermuxAudioRecorder.__init__ú   s6   € Ý”^Ñ%Ô%ˆŒ
ØˆŒØˆÔØ.2ˆÔØˆÔÐÐr   c                 ó   — | j         S r/   ©r�   r”   s    r   Úis_recordingz TermuxAudioRecorder.is_recording  s
   € àŒÐr   c                 óJ   — | j         sdS t          j        ¦   «         | j        z
  S ©NrŒ   ©r�   ru   rv   r‘   r”   s    r   Úelapsed_secondsz#TermuxAudioRecorder.elapsed_seconds  ó'   € àŒð 	Ø�3ÝŒ~ÑÔ $Ô"2Ñ2Ð2r   c                 ó   — | j         S r/   ©r“   r”   s    r   Úcurrent_rmszTermuxAudioRecorder.current_rms  s   € àÔ Ð r   c                 óô  — ~t          ¦   «         }|st          d¦  «        ‚t          ¦   «         st          d¦  «        ‚| j        5  | j        r	 d d d ¦  «         d S t          j        t          d¬¦  «         t          j	        d¦  «        }t
          j
                             t          d|› d�¦  «        | _        d d d ¦  «         n# 1 swxY w Y   |d| j        d	d
dddt          t          ¦  «        dt          t          ¦  «        g}	 t!          j        |dddd¬¦  «         ny# t           j        $ rG}|j        p|j        pt          |¦  «                             ¦   «         }t          d|› �¦  «        |‚d }~wt,          $ r}t          d|› �¦  «        |‚d }~ww xY w| j        5  t          j        ¦   «         | _        d| _        d| _        d d d ¦  «         n# 1 swxY w Y   t4                               d¦  «         d S )Nz’Termux voice capture requires the termux-api package and app.
Install with: pkg install termux-api
Then install/update the Termux:API Android app.zrTermux voice capture requires the Termux:API Android app.
Install/update the Termux:API app, then retry /voice on.T©Úexist_okú%Y%m%d_%H%M%SÚ
recording_z.aacz-fz-lÚ0z-eÚaacz-rz-cé   r"   z Termux microphone start failed: r   zTermux voice recording started)r   ÚRuntimeErrorr-   r�   r�   r3   ÚmakedirsÚ	_TEMP_DIRru   ÚstrftimeÚpathÚjoinr’   Ústrrk   ÚCHANNELSr(   r)   ÚCalledProcessErrorÚstderrr*   Ústripr+   rv   r‘   r“   r{   Úinfo)r•   Úon_silence_stopÚmic_cmdÚ	timestampÚcommandr‡   Údetailss          r   ÚstartzTermuxAudioRecorder.start  sÚ  € ØÝ,Ñ.Ô.ˆØð 	ÝðBñô ð õ
 )Ñ*Ô*ð 	ÝðKñô ð ð
 ŒZð 	Yð 	YØŒð Øð	Yð 	Yð 	Yñ 	Yô 	Yð 	Yð 	Yð 	Yõ ŒK�	¨DÐ1Ñ1Ô1Ð1Ýœ oÑ6Ô6ˆIÝ#%¤7§<¢<µ	Ð;WÈ	Ð;WÐ;WÐ;WÑ#XÔ#XˆDÔ ð	Yð 	Yð 	Yñ 	Yô 	Yð 	Yð 	Yð 	Yð 	Yð 	Yð 	Yøøøð 	Yð 	Yð 	Yð 	Yð Ø�$Ô&Ø�#Ø�%Ø•#•kÑ"Ô"Ø•#•h‘-”-ð
ˆð	NÝŒN˜7°4¸dÈBÐVZÐ[Ñ[Ô[Ð[Ð[øÝÔ,ð 	Tð 	Tð 	TØ”xÐ5 1¤8Ð5­s°1©v¬v×<Ò<Ñ>Ô>ˆGÝÐKÀ'ÐKÐKÑLÔLÐRSÐSøøøøÝð 	Nð 	Nð 	NÝÐEÀ!ÐEÐEÑFÔFÈAÐMøøøøð	Nøøøð ŒZð 	"ð 	"Ý#œ~Ñ/Ô/ˆDÔØ"ˆDŒOØ !ˆDÔð	"ð 	"ð 	"ñ 	"ô 	"ð 	"ð 	"ð 	"ð 	"ð 	"ð 	"øøøð 	"ð 	"ð 	"ð 	"õ 	�ŠÐ4Ñ5Ô5Ð5Ð5Ð5sP   Á	CÁACÃCÃCÄD  Ä FÄ/AE1Å1FÅ>FÆFÆ 'GÇGÇGc                 ó`   — t          ¦   «         }|sd S t          j        |dgdddd¬¦  «         d S )Nú-qTr©   Fr"   )r   r(   r)   )r•   r·   s     r   Ú_stop_termux_recordingz*TermuxAudioRecorder._stop_termux_recording;  sA   € Ý,Ñ.Ô.ˆØð 	ØˆFÝŒ˜ �°tÀ$ÐPRÐZ_Ð`Ñ`Ô`Ð`Ð`Ð`r   c                 ór  — | j         5  | j        s	 d d d ¦  «         d S d| _        | j        }d | _        | j        }d| _        d d d ¦  «         n# 1 swxY w Y   |                      ¦   «          |rt          j                             |¦  «        sd S t          j
        ¦   «         |z
  dk     r(	 t          j        |¦  «         n# t          $ r Y nw xY wd S t          j                             |¦  «        dk    r(	 t          j        |¦  «         n# t          $ r Y nw xY wd S t                               d|¦  «         |S )NFr   rd   z"Termux voice recording stopped: %s)r�   r�   r’   r‘   r“   r¾   r3   r®   Úisfileru   rv   Úunlinkr   Úgetsizer{   rµ   )r•   r®   Ú
started_ats      r   rz   zTermuxAudioRecorder.stopA  s°  € ØŒZð 	"ð 	"Ø”?ð Øð	"ð 	"ð 	"ñ 	"ô 	"ð 	"ð 	"ð 	"ð $ˆDŒOØÔ'ˆDØ#'ˆDÔ ØÔ)ˆJØ !ˆDÔð	"ð 	"ð 	"ñ 	"ô 	"ð 	"ð 	"ð 	"ð 	"ð 	"ð 	"øøøð 	"ð 	"ð 	"ð 	"ð 	×#Ò#Ñ%Ô%Ð%Øð 	�2œ7Ÿ>š>¨$Ñ/Ô/ð 	Ø�4ÝŒ>ÑÔ˜jÑ(¨3Ò.Ð.ðÝ”	˜$‘”��øÝð ð ð Ø�ðøøøà�4ÝŒ7�?Š?˜4Ñ Ô  AÒ%Ð%ðÝ”	˜$‘”��øÝð ð ð Ø�ðøøøà�4Ý�ŠÐ8¸$Ñ?Ô?Ð?Øˆs:   ˆ	Až#AÁAÁAÂ*B? Â?
CÃCÃ5D
 Ä

DÄDc                 óŠ  — | j         5  | j        }d| _        d | _        d| _        d d d ¦  «         n# 1 swxY w Y   	 |                      ¦   «          n# t
          $ r Y nw xY w|rEt          j                             |¦  «        r&	 t          j	        |¦  «         n# t          $ r Y nw xY wt                               d¦  «         d S )NFr   z Termux voice recording cancelled)r�   r’   r�   r“   r¾   r+   r3   r®   rÀ   rÁ   r   r{   rµ   )r•   r®   s     r   ÚcancelzTermuxAudioRecorder.cancel]  s"  € ØŒZð 	"ð 	"ØÔ'ˆDØ#ˆDŒOØ#'ˆDÔ Ø !ˆDÔð		"ð 	"ð 	"ñ 	"ô 	"ð 	"ð 	"ð 	"ð 	"ð 	"ð 	"øøøð 	"ð 	"ð 	"ð 	"ð
	Ø×'Ò'Ñ)Ô)Ð)Ð)øÝð 	ð 	ð 	ØˆDð	øøøàð 	•B”G—N’N 4Ñ(Ô(ð 	ðÝ”	˜$‘”��øÝð ð ð Ø�ðøøøå�ŠÐ6Ñ7Ô7Ð7Ð7Ð7s/   ˆ1±5¸5½A Á
AÁAÂB Â
B&Â%B&c                 ó.   — |                       ¦   «          d S r/   )rÅ   r”   s    r   ÚshutdownzTermuxAudioRecorder.shutdownn  s   € Ø�Š‰Œˆˆˆr   ©r   Nr/   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__Úsupports_silence_autostopr–   ÚpropertyrD   r™   Úfloatr�   rj   r¡   r»   r¾   r   r°   rz   rÅ   rÇ   r   r   r   rŠ   rŠ   õ   s"  € € € € € ØLÐLà %Ððð ð ð ð ð˜dð ð ð ñ „Xðð ð3 ð 3ð 3ð 3ñ „Xð3ð
 ð!˜Sð !ð !ð !ñ „Xð!ð*6ð *6ð *6ð *6ð *6ðXað að að að�h˜s”mð ð ð ð ð88ð 8ð 8ð 8ð"ð ð ð ð ð r   rŠ   c                   óÚ   — e Zd ZdZdZdd„Zedefd„¦   «         Zede	fd„¦   «         Z
edefd„¦   «         Zdd	„Zddd
„Zddeddfd„Zdee         fd„Zdd„Zdd„Zedefd„¦   «         ZdS )ÚAudioRecorderaè  Thread-safe audio recorder using sounddevice.InputStream.

    Usage::

        recorder = AudioRecorder()
        recorder.start(on_silence_stop=my_callback)
        # ... user speaks ...
        wav_path = recorder.stop()   # returns path to WAV file
        # or
        recorder.cancel()            # discard without saving

    If ``on_silence_stop`` is provided, recording automatically stops when
    the user is silent for ``silence_duration`` seconds and calls the callback.
    Tr   Nc                 óF  — t          j        ¦   «         | _        d | _        g | _        d| _        d| _        d| _        d| _        d| _	        d| _
        d| _        d| _        d| _        d| _        d | _        t           | _        t$          | _        d| _        d| _        d| _        d S )NFrŒ   rd   g      .@r   )r�   rŽ   r�   Ú_streamÚ_framesr�   r‘   Ú_has_spokenÚ_speech_startÚ
_dip_startÚ_min_speech_durationÚ_max_dip_toleranceÚ_silence_startÚ_resume_startÚ_resume_dip_startÚ_on_silence_stopÚSILENCE_RMS_THRESHOLDÚ_silence_thresholdÚSILENCE_DURATION_SECONDSÚ_silence_durationÚ	_max_waitÚ	_peak_rmsr“   r”   s    r   r–   zAudioRecorder.__init__‡  s¡   € Ý”^Ñ%Ô%ˆŒ
Ø ˆŒØ"$ˆŒØˆŒØ"%ˆÔà ˆÔØ$'ˆÔØ!$ˆŒØ+.ˆÔ!Ø),ˆÔØ%(ˆÔØ$'ˆÔØ(+ˆÔØ $ˆÔÝ'<ˆÔÝ(@ˆÔØ $ˆŒàˆŒà!"ˆÔÐÐr   c                 óJ   — | j         sdS t          j        ¦   «         | j        z
  S r›   rœ   r”   s    r   r�   zAudioRecorder.elapsed_seconds¡  rž   r   c                 ó   — | j         S )zBCurrent audio input RMS level (0-32767). Updated each audio chunk.r    r”   s    r   r¡   zAudioRecorder.current_rms§  s   € ð Ô Ð r   c                 ó   — | j         S )z,Whether audio recording is currently active.r˜   r”   s    r   r™   zAudioRecorder.is_recording¬  s   € ð ŒÐr   c                 ót  ‡ ‡— ‰ j         �dS t          ¦   «         \  }Šˆˆ fd„}d}	 |                     t          t          t
          |¬¦  «        }|                     ¦   «          nN# t          $ rA}|�&	 |                     ¦   «          n# t          $ r Y nw xY wt          d|› d�¦  «        |‚d}~ww xY w|‰ _         dS )a_  Create the audio InputStream once and keep it alive.

        The stream stays open for the lifetime of the recorder.  Between
        recordings the callback simply discards audio chunks (``_recording``
        is ``False``).  This avoids the CoreAudio bug where closing and
        re-opening an ``InputStream`` hangs indefinitely on macOS.
        Nc           	      ón  •‡	— |rt                                d|¦  «         ‰j        sd S ‰j                             |                      ¦   «         ¦  «         t          ‰
                     ‰
                     |  	                    ‰
j
        ¦  «        dz  ¦  «        ¦  «        ¦  «        }|‰_        |‰j        k    r|‰_        ‰j        ��kt          j        ¦   «         }|‰j        z
  }|‰j        k    r©d‰_        ‰j        dk    r|‰_        nD‰j        s=|‰j        z
  ‰j        k    r*d‰_        t                                d|‰j        z
  ¦  «         ‰j        sd‰_        nåd‰_        ‰j        dk    r|‰_        nË|‰j        z
  ‰j        k    rd‰_        d‰_        n©‰j        r@‰j        dk    r4‰j        dk    r|‰_        n„|‰j        z
  ‰j        k    rd‰_        d‰_        nb‰j        dk    rW‰j        dk    r|‰_        nD|‰j        z
  ‰j        k    r1t                                d|‰j        z
  ¦  «         d‰_        d‰_        d}‰j        rT|‰j        k    rI‰j        dk    r|‰_        nj|‰j        z
  ‰j        k    r"t                                d	‰j        ¦  «         d}n4‰j        s-|‰j        k    r"t                                d
‰j        ¦  «         d}|r`‰j        5  ‰j        Š	d ‰_        d d d ¦  «         n# 1 swxY w Y   ‰	r3ˆ	fd„}t=          j        |d¬¦  «                              ¦   «          d S d S d S d S )Nzsounddevice status: %srW   rŒ   Tz(Speech confirmed (%.2fs above threshold)r   z'Speech attempt reset (dip lasted %.2fs)Fz'Silence detected (%.1fs), auto-stoppingz%No speech within %.0fs, auto-stoppingc                  óˆ   •— 	  ‰¦   «          d S # t           $ r(} t                               d| d¬¦  «         Y d } ~ d S d } ~ ww xY w)NzSilence callback failed: %sT)Úexc_info)r+   r{   Úerror)r‡   Úcbs    €r   Ú_safe_cbzAAudioRecorder._ensure_stream.<locals>._callback.<locals>._safe_cb  sg   ø€ ð^Ø " ¡¤   øÝ#,ð ^ð ^ð ^Ý &§¢Ð-JÈAÐX\ Ñ ]Ô ]Ð ]Ð ]Ð ]Ð ]Ð ]Ð ]Ð ]øøøøð^øøøs   ƒ
 �
A™<¼A©ÚtargetÚdaemon)!r{   r|   r�   rÔ   rF   Úcopyrj   ÚsqrtÚmeanrq   Úfloat64r“   rã   rÝ   ru   rv   r‘   rß   r×   rÖ   rÕ   rØ   rÚ   rÜ   rÛ   rÙ   rá   rµ   râ   r�   r�   ÚThreadr»   )ÚindataÚframesÚ	time_infoÚstatusÚrmsÚnowÚelapsedÚshould_firerí   rì   r   r•   s            @€€r   Ú	_callbackz/AudioRecorder._ensure_stream.<locals>._callbackÀ  s  øø€ Øð ?Ý—’Ð5°vÑ>Ô>Ð>à”?ð Ø�ØŒL×Ò §¢¡¤Ñ.Ô.Ð.õ �b—g’g˜bŸgšg f§m¢m°B´JÑ&?Ô&?À1Ñ&DÑEÔEÑFÔFÑGÔGˆCØ #ˆDÔØ�T”^Ò#Ð#Ø!$�”ð Ô$Ñ0Ý”nÑ&Ô&�Ø Ô 0Ñ0�à˜Ô0Ò0Ð0à&)�D”OØÔ)¨SÒ0Ð0Ø-0˜Ô*Ð*Ø!Ô-ð ?°#¸Ô8JÑ2JÈdÔNgÒ2gÐ2gØ+/˜Ô(ÝŸšÐ%OØ%(¨4Ô+=Ñ%=ñ?ô ?ð ?ð
  Ô+ð 5Ø.1˜Ô+Ð+ð 25˜Ô.ØÔ-°Ò4Ð4Ø14˜DÔ.Ð.Ø  4Ô#5Ñ5¸Ô9RÒRÐRØ25˜DÔ/Ø14˜DÔ.øØÔ%ð .ð Ô)¨AÒ-Ð-ØÔ1°SÒ8Ð8Ø58˜DÔ2Ð2Ø  4Ô#9Ñ9¸TÔ=TÒTÐTà14˜DÔ.Ø58˜DÔ2øØÔ'¨!Ò+Ð+ð ”¨#Ò-Ð-Ø*-˜œ˜Ø˜tœÑ.°$Ô2IÒIÐIåŸšÐ%NØ%(¨4¬?Ñ%:ñ<ô <ð <à-0˜Ô*Ø*-˜œð
 $�ØÔ#ð '¨¨tÔ/FÒ(FÐ(FàÔ*¨cÒ1Ð1Ø.1˜Ô+Ð+Ø˜tÔ2Ñ2°dÔ6LÒLÐLÝŸšÐ$MØ$(Ô$:ñ<ô <ð <à&*˜øØÔ)ð '¨g¸¼Ò.GÐ.GÝ—K’KÐ GØ $¤ñ0ô 0ð 0à"&�Kàð 
OØœð 5ð 5Ø!Ô2˜Ø04˜Ô-ð5ð 5ð 5ñ 5ô 5ð 5ð 5ð 5ð 5ð 5ð 5øøøð 5ð 5ð 5ð 5ð ð Oð^ð ^ð ^ð ^ð ^õ
 "Ô(°ÀÐFÑFÔF×LÒLÑNÔNÐNÐNÐNð_ 1Ð0ðJ
Oð 
OðOð Os   ËK5Ë5K9Ë<K9)rh   Úchannelsrf   Úcallbackz#Failed to open audio input stream: z6. Check that a microphone is connected and accessible.)
rÓ   r   ÚInputStreamrk   r±   ÚDTYPEr»   r+   Úcloserª   )r•   r
   rþ   Ústreamr‡   r   s   `    @r   Ú_ensure_streamzAudioRecorder._ensure_stream³  s#  øø€ ð Œ<Ð#ØˆFå‘”‰ˆˆBð^	Oð ^	Oð ^	Oð ^	Oð ^	Oð ^	OðB ˆð	Ø—^’^Ý&Ý!ÝØ"ð	 $ñ ô ˆFð �LŠL‰NŒNˆNˆNøÝð 		ð 		ð 		ØÐ!ðØ—L’L‘N”N�N�NøÝ ð ð ð Ø�DðøøøåðG°að Gð Gð Gñô ð ðøøøøð		øøøð ˆŒˆˆs;   ¦<A# Á#
B.Á-B)Á0BÂB)Â
BÂB)ÂBÂB)Â)B.c                 óx  — 	 t          ¦   «          n7# t          t          f$ r#}t          dt          j        › d�¦  «        |‚d}~ww xY w| j        5  | j        r	 ddd¦  «         dS g | _        t          j
        ¦   «         | _        d| _        d| _        d| _        d| _        d| _        d| _        d| _        d| _        || _        ddd¦  «         n# 1 swxY w Y   |                      ¦   «          | j        5  d| _        ddd¦  «         n# 1 swxY w Y   t,                               dt0          t2          ¦  «         dS )	a}  Start capturing audio from the default input device.

        The underlying InputStream is created once and kept alive across
        recordings.  Subsequent calls simply reset detection state and
        toggle frame collection via ``_recording``.

        Args:
            on_silence_stop: Optional callback invoked (in a daemon thread) when
                silence is detected after speech. The callback receives no arguments.
                Use this to auto-stop recording and trigger transcription.

        Raises ``RuntimeError`` if sounddevice/numpy are not installed
        or if a recording is already in progress.
        z9Voice mode requires sounddevice and numpy.
Install with: z! -m pip install sounddevice numpyNFrŒ   r   Tz.Voice recording started (rate=%d, channels=%d))r   r   r   rª   ÚsysÚ
executabler�   r�   rÔ   ru   rv   r‘   rÕ   rÖ   r×   rÚ   rÛ   rÜ   rã   r“   rÝ   r  r{   rµ   rk   r±   )r•   r¶   r‡   s      r   r»   zAudioRecorder.start6  só  € ð	Ý‰OŒOˆOˆOøÝ�WÐ%ð 	ð 	ð 	ÝðSÝ!$¤ðSð Sð Sñô ð ðøøøøð	øøøð ŒZð 	4ð 	4ØŒð Øð	4ð 	4ð 	4ñ 	4ô 	4ð 	4ð 	4ð 	4ð ˆDŒLÝ#œ~Ñ/Ô/ˆDÔØ$ˆDÔØ!$ˆDÔØ!ˆDŒOØ"%ˆDÔØ!$ˆDÔØ%(ˆDÔ"ØˆDŒNØ !ˆDÔØ$3ˆDÔ!ð	4ð 	4ð 	4ñ 	4ô 	4ð 	4ð 	4ð 	4ð 	4ð 	4ð 	4øøøð 	4ð 	4ð 	4ð 	4ð" 	×ÒÑÔÐàŒZð 	#ð 	#Ø"ˆDŒOð	#ð 	#ð 	#ñ 	#ô 	#ð 	#ð 	#ð 	#ð 	#ð 	#ð 	#øøøð 	#ð 	#ð 	#ð 	#å�ŠÐDÅkÕS[Ñ\Ô\Ð\Ð\Ð\s?   ‚ ‘A¢A Á AÁ	CÁ%ACÃCÃCÃ5D	Ä	DÄDrY   r%   c                 óN  ‡— | j         €dS | j         Šd| _         ˆfd„}t          j        |d¬¦  «        }|                     ¦   «          t	          d¦  «                             ¦   «         |z   }|                     ¦   «         rtt	          d¦  «                             ¦   «         |k     rO|                     d¬¦  «         |                     ¦   «         r%t	          d¦  «                             ¦   «         |k     °O|                     ¦   «         rt           	                    d|¦  «         dS dS )	zAClose the audio stream with a timeout to prevent CoreAudio hangs.Nc                  ó|   •— 	 ‰                       ¦   «          ‰                      ¦   «          d S # t          $ r Y d S w xY wr/   )rz   r  r+   )r  s   €r   Ú	_do_closez;AudioRecorder._close_stream_with_timeout.<locals>._do_closel  sH   ø€ ðØ—’‘”�Ø—’‘”���øÝð ð ð Ø��ðøøøs   ƒ(- ­
;º;Trî   ru   gš™™™™™¹?©r%   u:   Audio stream close timed out after %.1fs â€” forcing ahead)
rÓ   r�   rõ   r»   Ú
__import__rv   Úis_aliver¯   r{   Úwarning)r•   r%   r  r‚   r†   r  s        @r   Ú_close_stream_with_timeoutz(AudioRecorder._close_stream_with_timeoutd  s$  ø€ àŒ<ÐØˆFà”ˆØˆŒð	ð 	ð 	ð 	ð 	õ Ô I°dÐ;Ñ;Ô;ˆØ	�Š‰	Œ	ˆ	å˜fÑ%Ô%×/Ò/Ñ1Ô1°GÑ;ˆØ�jŠj‰lŒlð 	 �z¨&Ñ1Ô1×;Ò;Ñ=Ô=ÀÒHÐHØ�FŠF˜3ˆFÑÔÐð �jŠj‰lŒlð 	 �z¨&Ñ1Ô1×;Ò;Ñ=Ô=ÀÒHÐHà�:Š:‰<Œ<ð 	bÝ�NŠNÐWÐY`ÑaÔaÐaÐaÐað	bð 	br   c                 ó  — | j         5  | j        s	 ddd¦  «         dS d| _        d| _        | j        s	 ddd¦  «         dS t	          ¦   «         \  }}|                     | j        d¬¦  «        }g | _        t          j        ¦   «         | j        z
  }t           
                    d|t          |¦  «        ¦  «         t          t          dz  ¦  «        }t          |¦  «        |k     r6t                               dt          |¦  «        ¦  «         	 ddd¦  «         dS | j        t           k     r4t           
                    d| j        t           ¦  «         	 ddd¦  «         dS |                      |¦  «        cddd¦  «         S # 1 swxY w Y   dS )	uþ   Stop recording and write captured audio to a WAV file.

        The underlying stream is kept alive for reuse â€” only frame
        collection is stopped.

        Returns:
            Path to the WAV file, or ``None`` if no audio was captured.
        NFr   )Úaxisz+Voice recording stopped (%.1fs, %d samples)rd   z,Recording too short (%d samples), discardingz2Recording too quiet (peak RMS=%d < %d), discarding)r�   r�   r“   rÔ   r   rs   ru   rv   r‘   r{   rµ   Úlenrj   rk   r|   rã   rÞ   Ú
_write_wav)r•   rR   r   Ú
audio_datarü   Úmin_sampless         r   rz   zAudioRecorder.stop|  s.  € ð ŒZð  	/ð  	/Ø”?ð Øð 	/ð  	/ð  	/ñ  	/ô  	/ð  	/ð  	/ð  	/ð $ˆDŒOØ !ˆDÔð ”<ð Øð 	/ð  	/ð  	/ñ  	/ô  	/ð  	/ð  	/ð  	/õ "‘O”O‰EˆAˆrØŸš¨¬¸1˜Ñ=Ô=ˆJØˆDŒLå”nÑ&Ô&¨Ô)9Ñ9ˆGÝ�KŠKÐEÀwÕPSÐT^ÑP_ÔP_Ñ`Ô`Ð`õ �k¨CÑ/Ñ0Ô0ˆKÝ�:‰Œ Ò,Ð,Ý—’ÐKÍSÐQ[É_Ì_Ñ]Ô]Ð]Øð/ 	/ð  	/ð  	/ñ  	/ô  	/ð  	/ð  	/ð  	/ð6 Œ~Õ 5Ò5Ð5Ý—’ÐPØ œNÕ,AñCô Cð Càð= 	/ð  	/ð  	/ñ  	/ô  	/ð  	/ð  	/ð  	/ð@ —?’? :Ñ.Ô.ðA 	/ð  	/ð  	/ð  	/ñ  	/ô  	/ð  	/ð  	/ð  	/ð  	/ð  	/ð  	/øøøð  	/ð  	/ð  	/ð  	/ð  	/ð  	/s)   ˆ	E>žE>ÁCE>Ä7E>ÅE>Å>FÆFc                 ó°   — | j         5  d| _        g | _        d| _        d| _        ddd¦  «         n# 1 swxY w Y   t
                               d¦  «         dS )zoStop recording and discard all captured audio.

        The underlying stream is kept alive for reuse.
        FNr   zVoice recording cancelled)r�   r�   rÔ   rÝ   r“   r{   rµ   r”   s    r   rÅ   zAudioRecorder.cancel§  sš   € ð
 ŒZð 	"ð 	"Ø#ˆDŒOØˆDŒLØ$(ˆDÔ!Ø !ˆDÔð		"ð 	"ð 	"ñ 	"ô 	"ð 	"ð 	"ð 	"ð 	"ð 	"ð 	"øøøð 	"ð 	"ð 	"ð 	"õ
 	�ŠÐ/Ñ0Ô0Ð0Ð0Ð0s   ˆ1±5¸5c                 óÊ   — | j         5  d| _        g | _        d| _        ddd¦  «         n# 1 swxY w Y   |                      ¦   «          t
                               d¦  «         dS )z<Release the audio stream.  Call when voice mode is disabled.FNzAudioRecorder shut down)r�   r�   rÔ   rÝ   r  r{   rµ   r”   s    r   rÇ   zAudioRecorder.shutdown³  s¢   € àŒZð 	)ð 	)Ø#ˆDŒOØˆDŒLØ$(ˆDÔ!ð	)ð 	)ð 	)ñ 	)ô 	)ð 	)ð 	)ð 	)ð 	)ð 	)ð 	)øøøð 	)ð 	)ð 	)ð 	)ð
 	×'Ò'Ñ)Ô)Ð)Ý�ŠÐ-Ñ.Ô.Ð.Ð.Ð.s   ˆ*ª.±.c                 óp  — t          j        t          d¬¦  «         t          j        d¦  «        }t           j                             t          d|› d�¦  «        }t          j        |d¦  «        5 }| 	                    t          ¦  «         |                     t          ¦  «         |                     t          ¦  «         |                     |                      ¦   «         ¦  «         ddd¦  «         n# 1 swxY w Y   t           j                             |¦  «        }t$                               d||¦  «         |S )	zTWrite numpy int16 audio data to a WAV file.

        Returns the file path.
        Tr£   r¥   r¦   ú.wavÚwbNzWAV written: %s (%d bytes))r3   r«   r¬   ru   r­   r®   r¯   ÚwaverH   Úsetnchannelsr±   ÚsetsampwidthÚSAMPLE_WIDTHÚsetframeraterk   ÚwriteframesÚtobytesrÂ   r{   rµ   )r  r¸   Úwav_pathÚwfÚ	file_sizes        r   r  zAudioRecorder._write_wav¿  s2  € õ 	Œ•I¨Ð-Ñ-Ô-Ð-Ý”M /Ñ2Ô2ˆ	Ý”7—<’<¥	Ð+G¸	Ð+GÐ+GÐ+GÑHÔHˆåŒY�x Ñ&Ô&ð 	1¨"Ø�OŠO�HÑ%Ô%Ð%Ø�OŠO�LÑ)Ô)Ð)Ø�OŠO�KÑ(Ô(Ð(Ø�NŠN˜:×-Ò-Ñ/Ô/Ñ0Ô0Ð0ð		1ð 	1ð 	1ñ 	1ô 	1ð 	1ð 	1ð 	1ð 	1ð 	1ð 	1øøøð 	1ð 	1ð 	1ð 	1õ ”G—O’O HÑ-Ô-ˆ	Ý�ŠÐ0°(¸IÑFÔFÐFØˆs   Á.A6C0Ã0C4Ã7C4rÈ   r/   )rY   )rÉ   rÊ   rË   rÌ   rÍ   r–   rÎ   rÏ   r�   rj   r¡   rD   r™   r  r»   r  r   r°   rz   rÅ   rÇ   Ústaticmethodr  r   r   r   rÑ   rÑ   u  s„  € € € € € ðð ð !%Ðð#ð #ð #ð #ð4 ð3 ð 3ð 3ð 3ñ „Xð3ð
 ð!˜Sð !ð !ð !ñ „Xð!ð ð˜dð ð ð ñ „XððAð Að Að AðF,]ð ,]ð ,]ð ,]ð ,]ð\bð b°%ð bÀ$ð bð bð bð bð0)/�h˜s”mð )/ð )/ð )/ð )/ðV
1ð 
1ð 
1ð 
1ð/ð /ð /ð /ð ð #ð ð ð ñ „\ðð ð r   rÑ   c                  óV   — t          ¦   «         rt          ¦   «         S t          ¦   «         S )z=Return the best recorder backend for the current environment.)r0   rŠ   rÑ   r   r   r   Úcreate_audio_recorderr(  Ô  s&   € å&Ñ(Ô(ð %Ý"Ñ$Ô$Ð$Ý‰?Œ?Ðr   >   õ%   Ð¿Ñ€Ð¾Ð´Ð¾Ð»Ð¶ÐµÐ½Ð¸Ðµ Ñ�Ð»ÐµÐ´ÑƒÐµÑ‚õ(   Ð¿Ñ€Ð¾Ð´Ð¾Ð»Ð¶ÐµÐ½Ð¸Ðµ Ñ�Ð»ÐµÐ´ÑƒÐµÑ‚...õ*   ã�”è¦–è�´ã�‚ã‚Šã�Œã�¨ã�†ã�”ã�–ã�„ã�¾ã�—ã�Ÿõ,   sottotitoli creati dalla comunitÃ  amara.orgõ5   sous-titres rÃ©alisÃ©s par la communautÃ© d'amara.orgúbye.úthe endúthe end.ú	amara.orgú	thank youú
thank you.úsous-titresúwww.mooji.orgúplease subscribeúplease subscribe.úlike and subscribeúlike and subscribe.úthanks for watchingúthanks for watching.úthank you for watchingúsubscribe to my channelúthank you for watching.úsubscribe to my channel.úuntertitel von stephanie geigesÚbyeÚyouz9^(?:thank you|thanks|bye|you|ok|okay|the end|\.|\s|,|!)+$)ÚflagsÚ
transcriptc                 óà   — |                       ¦   «                              ¦   «         }|sdS |                     d¦  «        t          v s	|t          v rdS t                               |¦  «        rdS dS )zBCheck if a transcript is a known Whisper hallucination on silence.Tz.!F)r´   rJ   ÚrstripÚWHISPER_HALLUCINATIONSÚ_HALLUCINATION_REPEAT_REÚmatch)rD  Úcleaneds     r   Úis_whisper_hallucinationrK    st   € à×ÒÑ Ô ×&Ò&Ñ(Ô(€GØð Øˆtà‡~‚~�dÑÔÕ5Ð5Ð5¸ÕDZÐ9ZÐ9ZØˆtå×%Ò% gÑ.Ô.ð ØˆtØˆ5r   r#  Úmodelc                 óê   — ddl m}  || |¬¦  «        }|                     d¦  «        rJt          |                     dd¦  «        ¦  «        r't                               d|d         ¦  «         dddd	œS |S )
a›  Transcribe a WAV recording using the existing Whisper pipeline.

    Delegates to ``tools.transcription_tools.transcribe_audio()``.
    Filters out known Whisper hallucinations on silent audio.

    Args:
        wav_path: Path to the WAV file.
        model: Whisper model name (default: from config or ``whisper-1``).

    Returns:
        Dict with ``success``, ``transcript``, and optionally ``error``.
    r   )Útranscribe_audio)rL  ÚsuccessrD  r'   z"Filtered Whisper hallucination: %rT)rO  rD  Úfiltered)Útools.transcription_toolsrN  r5   rK  r{   rµ   )r#  rL  rN  r,   s       r   Útranscribe_recordingrR    s‘   € ð ;Ð:Ð:Ð:Ð:Ð:àÐ˜h¨eÐ4Ñ4Ô4€Fð ‡z‚z�)ÑÔð EÕ!9¸&¿*º*À\ÐSUÑ:VÔ:VÑ!WÔ!Wð EÝ�ŠÐ8¸&ÀÔ:NÑOÔOÐOØ¨r¸tÐDÐDÐDà€Mr   Ú_active_playbackc                  óp  — t           5  t          } daddd¦  «         n# 1 swxY w Y   | rT|                      ¦   «         €@	 |                      ¦   «          t                               d¦  «         n# t          $ r Y nw xY w	 t          ¦   «         \  }}|                     ¦   «          dS # t          $ r Y dS w xY w)z/Interrupt the currently playing audio (if any).NzAudio playback interrupted)	Ú_playback_lockrS  ÚpollÚ	terminater{   rµ   r+   r   rz   )Úprocr
   rR   s      r   Ústop_playbackrY  7  s  € õ 
ð  ð  ÝˆØÐð ð  ð  ñ  ô  ð  ð  ð  ð  ð  ð  øøøð  ð  ð  ð  ð ð �—	’	‘”Ð#ð	Ø�NŠNÑÔÐÝ�KŠKÐ4Ñ5Ô5Ð5Ð5øÝð 	ð 	ð 	ØˆDð	øøøðÝ‘”‰ˆˆAØ
�Š‰	Œ	ˆ	ˆ	ˆ	øÝð ð ð Øˆˆðøøøs0   ˆ
ž"¥"Á .A/ Á/
A<Á;A<Â %B' Â'
B5Â4B5Ú	file_pathc                 óT  — t           j                             | ¦  «        st                               d| ¦  «         dS |                      d¦  «        �rÎ	 t          ¦   «         \  }}t          j        | d¦  «        5 }| 	                    | 
                    ¦   «         ¦  «        }|                     ||j        ¬¦  «        }|                     ¦   «         }ddd¦  «         n# 1 swxY w Y   |                     ||¬¦  «         t          |¦  «        |z  }t!          j        ¦   «         |z   dz   }|                     ¦   «         rˆ|                     ¦   «         j        rot!          j        ¦   «         |k     rXt!          j        d	¦  «         |                     ¦   «         r0|                     ¦   «         j        rt!          j        ¦   «         |k     °X|                     ¦   «          d
S # t,          t.          f$ r Y n1t0          $ r%}	t                               d|	¦  «         Y d}	~	nd}	~	ww xY wt5          j        ¦   «         }
g }|
dk    r|                     d| g¦  «         |                     ddddd| g¦  «         |
dk    r|                     dd| g¦  «         |D �]}}t;          j        |d         ¦  «        }|�r]	 t?          j         |t>          j!        t>          j!        ¬¦  «        }tD          5  |a#ddd¦  «         n# 1 swxY w Y   | $                    d¬¦  «         tD          5  da#ddd¦  «         n# 1 swxY w Y    d
S # t>          j%        $ rn t                               d|d         ¦  «         | &                    ¦   «          | $                    ¦   «          tD          5  da#ddd¦  «         n# 1 swxY w Y   Y �Œ#t0          $ rN}	t                               d|d         |	¦  «         tD          5  da#ddd¦  «         n# 1 swxY w Y   Y d}	~	�Œud}	~	ww xY w�Œt                               d| ¦  «         dS )ay  Play an audio file through the default output device.

    Strategy:
    1. WAV files via ``sounddevice.play()`` when available.
    2. System commands: ``afplay`` (macOS), ``ffplay`` (cross-platform),
       ``aplay`` (Linux ALSA).

    Playback can be interrupted by calling ``stop_playback()``.

    Returns:
        ``True`` if playback succeeded, ``False`` otherwise.
    zAudio file not found: %sFr  Úrbre   Nrg   ri   rb   Tzsounddevice playback failed: %sÚDarwinÚafplayÚffplayz-nodispz	-autoexitz	-loglevelÚquietÚLinuxÚaplayr½   r   )r*   r³   i,  r  z+System player %s timed out, killing processzSystem player %s failed: %sz No audio player available for %s)'r3   r®   rÀ   r{   r  Úendswithr   r  rH   Ú
readframesÚ
getnframesÚ
frombufferrV   Úgetframeratert   r  ru   rv   rw   rx   ry   rz   r   r   r+   r|   ÚplatformÚsystemrF   r   r   r(   ÚPopenÚDEVNULLrU  rS  ÚwaitÚTimeoutExpiredÚkill)rZ  r
   r   r$  r÷   r  Úsample_rateÚduration_secsr†   r‡   ri  ÚplayersÚcmdÚexerX  s                  r   Úplay_audio_filert  K  s>  € õ Œ7�>Š>˜)Ñ$Ô$ð Ý�ŠÐ1°9Ñ=Ô=Ð=Øˆuð ×Ò˜&Ñ!Ô!ñ ?ð	?Ý"‘_”_‰FˆB�Ý”˜9 dÑ+Ô+ð 0¨rØŸš r§}¢}¡¤Ñ7Ô7�ØŸ]š]¨6¸¼˜]ÑBÔB�
Ø ŸošoÑ/Ô/�ð0ð 0ð 0ñ 0ô 0ð 0ð 0ð 0ð 0ð 0ð 0øøøð 0ð 0ð 0ð 0ð
 �GŠG�J¨;ˆGÑ7Ô7Ð7õ   
™OœO¨kÑ9ˆMÝ”~Ñ'Ô'¨-Ñ7¸#Ñ=ˆHØ—-’-‘/”/ð ! b§m¢m¡o¤oÔ&<ð !ÅÄÑAQÔAQÐT\ÒA\ÐA\Ý”
˜4Ñ Ô Ð ð —-’-‘/”/ð ! b§m¢m¡o¤oÔ&<ð !ÅÄÑAQÔAQÐT\ÒA\ÐA\à�GŠG‰IŒIˆIØ�4øÝ�WÐ%ð 	ð 	ð 	ØˆDÝð 	?ð 	?ð 	?Ý�LŠLÐ:¸AÑ>Ô>Ð>Ð>Ð>Ð>Ð>Ð>øøøøð	?øøøõ Œ_ÑÔ€FØ€Gà�ÒÐØ�Š˜ )Ð,Ñ-Ô-Ð-Ø‡N‚N�H˜i¨°kÀ7ÈIÐVÑWÔWÐWØ�ÒÐØ�Š˜  yÐ1Ñ2Ô2Ð2àð ,ñ ,ˆÝŒl˜3˜qœ6Ñ"Ô"ˆØñ 	,ð,Ý!Ô'¨µJÔ4FÍzÔOaÐbÑbÔb�Ý#ð ,ð ,Ø'+Ð$ð,ð ,ð ,ñ ,ô ,ð ,ð ,ð ,ð ,ð ,ð ,øøøð ,ð ,ð ,ð ,à—	’	 #�	Ñ&Ô&Ð&Ý#ð ,ð ,Ø'+Ð$ð,ð ,ð ,ñ ,ô ,ð ,ð ,ð ,ð ,ð ,ð ,øøøð ,ð ,ð ,ð ,à�t�tøÝÔ,ð ,ð ,ð ,Ý—’ÐLÈcÐRSÌfÑUÔUÐUØ—	’	‘”�Ø—	’	‘”�Ý#ð ,ð ,Ø'+Ð$ð,ð ,ð ,ñ ,ô ,ð ,ð ,ð ,ð ,ð ,ð ,øøøð ,ð ,ð ,ð ,øùåð ,ð ,ð ,Ý—’Ð:¸CÀ¼FÀAÑFÔFÐFÝ#ð ,ð ,Ø'+Ð$ð,ð ,ð ,ñ ,ô ,ð ,ð ,ð ,ð ,ð ,ð ,øøøð ,ð ,ð ,ð ,øøøøùøøøøð,øøøñ	,õ( ‡N‚NÐ5°yÑAÔAÐAØˆ5só   Á&G Á:ACÃG ÃC"Ã"G Ã%C"Ã&C5G ÇHÇ0	HÇ9HÈHÊ/2L5Ë!K0Ë$L5Ë0K4	Ë4L5Ë7K4	Ë8 L5ÌL'ÌL5Ì'L+	Ì+L5Ì.L+	Ì/L5Ì5APÎN#ÎPÎ#N'Î'PÎ*N'Î+PÎ1	PÎ:)PÏ#O2Ï&PÏ2O6Ï6PÏ9O6Ï:PÐPc                  óœ  — ddl m} m}m}  |¦   «         } ||¦  «        } | |¦  «        }|o|dk    }g }t	          ¦   «         }t          ¦   «         p|}	|	s|                     ddg¦  «         t          ¦   «         }
|	o	|o|
d         }g }|r|                     d¦  «         n=|	r|                     d¦  «         n%|                     d	t          ¦   «         › d
�¦  «         |s|                     d¦  «         ni|dk    r|                     d¦  «         nM|dk    r|                     d¦  «         n1|dk    r|                     d¦  «         n|                     d¦  «         |
d         D ]}|                     d|› �¦  «         Œ|
 
                    dg ¦  «        D ]}|                     d|› �¦  «         Œ||	||d                     |¦  «        |
dœS )z¸Check if all voice mode requirements are met.

    Returns:
        Dict with ``available``, ``audio_available``, ``stt_available``,
        ``missing_packages``, and ``details``.
    r   )Ú_get_providerÚ_load_stt_configÚis_stt_enabledÚnoner   r	   rA   z)Audio capture: OK (Termux:API microphone)zAudio capture: OKzAudio capture: MISSING (r@   z5STT provider: DISABLED in config (stt.enabled: false)Úlocalz'STT provider: OK (local faster-whisper)ÚgroqzSTT provider: OK (Groq)ÚopenaizSTT provider: OK (OpenAI)z`STT provider: MISSING (pip install faster-whisper, or set GROQ_API_KEY / VOICE_TOOLS_OPENAI_KEY)rB   zEnvironment: rC   Ú
)rA   Úaudio_availableÚstt_availableÚmissing_packagesrº   Úenvironment)rQ  rv  rw  rx  r0   r   ÚextendrT   rF   r   r5   r¯   )rv  rw  rx  Ú
stt_configÚstt_enabledÚstt_providerr  ÚmissingrP   Ú	has_audioÚ	env_checkrA   Údetails_partsr  Únotices                  r   Úcheck_voice_requirementsr‹  œ  st  € ð ZÐYÐYÐYÐYÐYÐYÐYÐYÐYØ!Ð!Ñ#Ô#€JØ �. Ñ,Ô,€KØ �= Ñ,Ô,€LØÐ: L°FÒ$:€Mà€GÝ4Ñ6Ô6€NÝ Ñ"Ô"Ð4 n€Iàð 1Ø�Š˜ wÐ/Ñ0Ô0Ð0õ )Ñ*Ô*€IàÐF˜mÐF°	¸+Ô0F€IØ€Màð ZØ×ÒÐHÑIÔIÐIÐIØ	ð ZØ×ÒÐ0Ñ1Ô1Ð1Ð1à×ÒÐXÕ8SÑ8UÔ8UÐXÐXÐXÑYÔYÐYàð 
Ø×ÒÐTÑUÔUÐUÐUØ	˜Ò	 Ð	 Ø×ÒÐFÑGÔGÐGÐGØ	˜Ò	Ð	Ø×ÒÐ6Ñ7Ô7Ð7Ð7Ø	˜Ò	!Ð	!Ø×ÒÐ8Ñ9Ô9Ð9Ð9à×Òð<ñ	
ô 	
ð 	
ð
 ˜ZÔ(ð 8ð 8ˆØ×ÒÐ6¨WÐ6Ð6Ñ7Ô7Ð7Ð7Ø—-’- 	¨2Ñ.Ô.ð 7ð 7ˆØ×ÒÐ5¨VÐ5Ð5Ñ6Ô6Ð6Ð6ð Ø$Ø&Ø#Ø—9’9˜]Ñ+Ô+Ø ðð ð r   é  Úmax_age_secondsc                 ó"  — t           j                             t          ¦  «        sdS d}t	          j        ¦   «         }t          j        t          ¦  «        D ]œ}|                     ¦   «         r†|j                             d¦  «        rl|j         	                    d¦  «        rR	 || 
                    ¦   «         j        z
  }|| k    rt          j        |j        ¦  «         |dz  }ŒŒ# t          $ r Y Œ˜w xY wŒ�|rt                               d|¦  «         |S )z³Remove old temporary voice recording files.

    Args:
        max_age_seconds: Delete files older than this (default: 1 hour).

    Returns:
        Number of files deleted.
    r   r¦   r  rU   z"Cleaned up %d old voice recordings)r3   r®   Úisdirr¬   ru   ÚscandirÚis_fileÚnameÚ
startswithrc  ÚstatÚst_mtimerÁ   r   r{   r|   )r�  Údeletedrû   ÚentryÚages        r   Úcleanup_temp_recordingsr™  Þ  s  € õ Œ7�=Š=�Ñ#Ô#ð Øˆqà€GÝ
Œ)‰+Œ+€Cå”�IÑ&Ô&ð ð ˆØ�=Š=‰?Œ?ð 	˜uœz×4Ò4°\ÑBÔBð 	ÀuÄz×GZÒGZÐ[aÑGbÔGbð 	ðØ˜EŸJšJ™LœLÔ1Ñ1�Ø˜Ò(Ð(Ý”I˜eœjÑ)Ô)Ð)Ø˜q‘L�GøøÝð ð ð Ø�ðøøøøð ð DÝ�ŠÐ9¸7ÑCÔCÐCØ€Ns   Â A C!Ã!
C.Ã-C.)r[   r\   rU   r/   rÈ   )rŒ  )@rÌ   Úloggingr3   rh  Úrer   r(   r  Útempfiler�   ru   r  Útypingr   r   r   r   Ú	getLoggerrÉ   r{   r   rD   r   rG   r   r   r°   r   r   r-   r0   ÚdictrT   rk   r±   r  r  rÞ   rà   r®   r¯   Ú
gettempdirr¬   rj   rÏ   rˆ   rŠ   rÑ   r(  rG  ÚcompileÚ
IGNORECASErH  rK  rR  rS  rj  Ú__annotations__rŽ   rU  rY  rt  r‹  r™  r   r   r   ú<module>r¤     sØ  ðð	ð 	ð 	ð €€€Ø 	€	€	€	Ø €€€Ø 	€	€	€	Ø €€€Ø Ð Ð Ð Ø 
€
€
€
Ø €€€Ø Ð Ð Ð Ø €€€Ø €€€Ø ,Ð ,Ð ,Ð ,Ð ,Ð ,Ð ,Ð ,Ð ,Ð ,Ð ,Ð ,à	ˆÔ	˜8Ñ	$Ô	$€ðð ð ð˜$ð ð ð ð ð AÐ @Ð @Ð @Ð @Ð @ð+ Sð +ð +ð +ð +ð4 H¨S¤Mð 4ð 4ð 4ð 4ð 4ð ð ð ð ð T¨ð Tð Tð Tð Tð\ $ð \ð \ð \ð \ðB €Ø€Ø€Ø€ð Ð ØÐ ð ŒG�LŠLÐ,˜Ô,Ñ.Ô.°Ñ?Ô?€	ð&4ð &4˜ð &4¨eð &4À3ð &4Ètð &4ð &4ð &4ð &4ðXzð zð zð zð zñ zô zð zð@\ð \ð \ð \ð \ñ \ô \ð \ð~
˜}Ð/BÑBð ð ð ð ðð ð Ð ð> &˜2œ:Ø@Ø
Œ-ðñ ô Ð ð¨ð °ð ð ð ð ð"ð  3ð ¨x¸¬}ð ÈÈSÐRUÈXÌð ð ð ð ð< 04Ð �(˜:Ô+Ô,Ð 3Ð 3Ñ 3Ø�”Ñ!Ô!€ðð ð ð ð(K˜sð K tð Kð Kð Kð Kðb< $ s¨C x¤.ð <ð <ð <ð <ðDð ¨Sð ¸Cð ð ð ð ð ð r   