Ë
    îÿæiÖƒ  ã                   óþ  — 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mZm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Zddl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/ ddl-m0Z1 ddl2m3Z3 dedefd„Z4 ejj                  dd«      Z6 G d„ d«      Z7 e«       Z8d„ Z9dZde:de:fd„Z;dedee<   fd„Z= ejj                  d«      Z>e>r-e>j                  d«      D � cg c]  } | j�                  «       ‘Œ c} ndgZA e=e8eA«        G d„ de,«      ZB G d „ d!e,«      ZC G d"„ d#e,«      ZD e7«       ZEe8j�                  d$«      d%„ «       ZGe8j�                  d&«      d'„ «       ZHe8j“                  d&«      d(e<fd)„«       ZJe8j—                  d&«      d(e<fd*„«       ZLd+eBfd,„ZMe8j“                  d-«      d+eBfd.„«       ZNd/e<d0eOfd1„ZPe8j“                  d2«       e d3«       e!d3«       e!d«       e!d4«       e!d5«       e!d6«       e!d7«       e!d4«       e!d«       e!d8«       e!d«      fd9e$d:e<d;ee<   d<eQd=e:d>eRd?e:d@eQdAee<   dBe:dCee<   fdD„«       ZSdaTdaUe8j“                  dE«       e d3«       e!dF«       e!dG«       e!dH«       e!dI«      fd9e$d:e<dJe<dKe<dLe:f
dM„«       ZV	 d[dNe%dOej®                  dPe:d;ee<   dQeQdReQfdS„ZXe8j³                  dT«      dNe%fdU„«       ZZ G dV„ dW«      Z[dX„ Z\e]dYk(  r e\«        yyc c} w )\a  Main module for MLX Audio API server.

This module provides a FastAPI-based server for hosting MLX Audio models,
including Text-to-Speech (TTS), Speech-to-Text (STT), and Speech-to-Speech (S2S) models.
It offers an OpenAI-compatible API for Audio completions and model management.
é    N)ÚasdictÚis_dataclass)ÚPath)ÚAnyÚDictÚListÚOptional)Úunquote)ÚFastAPIÚFileÚFormÚHTTPExceptionÚResponseÚ
UploadFileÚ	WebSocketÚWebSocketDisconnect)ÚCORSMiddleware)ÚStreamingResponse)Ú	BaseModel)Úread)Úwrite)Ú
load_modelÚobjÚreturnc                 óR  — t        | «      rt        | t        «      st        | «      } t        | t        «      r-| j                  «       D ��ci c]  \  }}|t        |«      “Œ c}}S t        | t        «      r| D �cg c]  }t        |«      ‘Œ c}S t        | t        «      r.t        j                  | «      ryt        j                  | «      ry| S t        | t        j                  «      r7t        j                  | «      ryt        j                  | «      ryt        | «      S | S c c}}w c c}w )zPRecursively sanitize NaN, Infinity, and -Infinity values for JSON serialization.N)r   Ú
isinstanceÚtyper   ÚdictÚitemsÚsanitize_for_jsonÚlistÚfloatÚnpÚisnanÚisinfÚfloating)r   ÚkÚvÚitems       úe/Volumes/fast/ai/experiments/voice-extract-mac/.venv/lib/python3.12/site-packages/mlx_audio/server.pyr    r    .   sä   € ô �CÔ¤¨C´Ô!6Ü�S‹kˆä�#”tÔØ47·I±I´KÔ@±K©D¨A¨q�Ô$ QÓ'Ñ'°KÒ@Ð@Ü	�CœÔ	Ù47Ó8±C¨DÔ! $Õ'°CÑ8Ð8Ü	�CœÔ	Ü�8‰8�CŒ=ØÜ�X‰X�cŒ]ØØˆ
Ü	�CœŸ™Ô	%Ü�8‰8�CŒ=ØÜ�X‰X�cŒ]ØÜ�S‹zÐàˆ
ùó! Aùâ8s   Á
DÁ8D$ÚMLX_AUDIO_NUM_WORKERSÚ2c                   ó4   — e Zd Zd„ Zdefd„Zdedefd„Zd„ Zy)ÚModelProviderc                 óD   — i | _         t        j                  «       | _        y ©N)ÚmodelsÚasyncioÚLockÚlock©Úselfs    r*   Ú__init__zModelProvider.__init__L   s   € Ø13ˆŒÜ—L‘L“Nˆ�	ó    Ú
model_namec                 ól   — || j                   vrt        |«      | j                   |<   | j                   |   S r0   )r1   r   ©r6   r9   s     r*   r   zModelProvider.load_modelP   s0   € Ø˜TŸ[™[Ñ(Ü&0°Ó&<ˆD�K‰K˜
Ñ#à�{‰{˜:Ñ&Ð&r8   r   c              ƒ   óè   K  — | j                   4 ƒd {  –—†  || j                  v r| j                  |= 	 d d d «      ƒd {  –—†  y	 d d d «      ƒd {  –—†  y7 ŒC7 Œ7 Œ	# 1 ƒd {  –—†7  sw Y   y xY w­w)NTF)r4   r1   r;   s     r*   Úremove_modelzModelProvider.remove_modelV   sN   è ø€ Ø—9—9“9Ø˜TŸ[™[Ñ(Ø—K‘K 
Ð+Ø÷ —9�9ð ÷	 —9‘9ø�9ø�9ø—9—9‘9üs`   ‚A2“A”A2—A´A2¿AÁ A2ÁAÁA2ÁAÁA2ÁA2ÁA2ÁA/Á#A&Á$A/Á+A2c              ƒ   óÎ   K  — | j                   4 ƒd {  –—†  t        | j                  j                  «       «      cd d d «      ƒd {  –—†  S 7 Œ87 Œ# 1 ƒd {  –—†7  sw Y   y xY w­wr0   )r4   r!   r1   Úkeysr5   s    r*   Úget_available_modelsz"ModelProvider.get_available_models]   s7   è ø€ Ø—9—9“9Ü˜Ÿ™×(Ñ(Ó*Ó+÷ —9’9ø�9ø—9—9‘9üsC   ‚A%“A”A%—#AºA%ÁAÁA%ÁA%ÁA"ÁAÁA"ÁA%N)	Ú__name__Ú
__module__Ú__qualname__r7   Ústrr   Úboolr=   r@   © r8   r*   r.   r.   K   s+   „ ò#ð' Só 'ð¨Sð °Tó ó,r8   r.   c                 óš   — 	 t        | «      S # t        $ r4 	 t        | «      cY S # t        $ r t        j                  | › d�«      ‚w xY ww xY w)Nz is not an int or float)ÚintÚ
ValueErrorr"   ÚargparseÚArgumentTypeError)Úvalues    r*   Úint_or_floatrM   e   s]   € ðPÜ�5‹zÐøÜò Pð	PÜ˜“<ÒøÜò 	PÜ×,Ñ,°¨wÐ6MÐ-NÓOÐOð	PúðPús   ‚
 �	A
—
$¡A
¤"AÁA
Úworkersc           
      óÖ   — t        j                  d«      x}r	 t        |«      } | S | S # t        $ r7 t	        dt        t        j
                  «       t        |«      z  «      «      } Y | S w xY w)Nr+   é   )ÚosÚgetenvrH   rI   ÚmaxÚ	cpu_countr"   )rN   Únum_workers_envs     r*   Úcalculate_default_workersrV   p   sk   € ÜŸ)™)Ð$;Ó<Ð<€Ð<ð	KÜ˜/Ó*ˆGð €Nˆ7€Nøô ò 	KÜ˜!œS¤§¡£´%¸Ó2HÑ!HÓIÓJ‰GØ€Nð	Kús   ™( ¨<A(Á'A(ÚappÚallowed_originsc                 óª   — | j                   D �cg c]  }|j                  t        usŒ|‘Œ c}| _         | j                  t        |ddgdg¬«       yc c}w )z5(Re)configure CORS middleware with the given origins.TÚ*)Úallow_originsÚallow_credentialsÚallow_methodsÚallow_headersN)Úuser_middlewareÚclsr   Úadd_middleware)rW   rX   Úms      r*   Ú
setup_corsrc   z   s_   € ð ×&Ò&óÙ&ˆa¨!¯%©%´~Ò*EŠÐ&ñ€CÔð ×ÑÜØ%ØØ�eØ�eð õ ùò	s
   �A§AÚMLX_AUDIO_ALLOWED_ORIGINSÚ,rZ   c                   ó^  — e Zd ZU eed<   eed<   dZedz  ed<   dZedz  ed<   dZedz  ed<   dZ	edz  ed	<   dZ
edz  ed
<   dZedz  ed<   dZedz  ed<   dZedz  ed<   dZedz  ed<   dZedz  ed<   dZedz  ed<   dZedz  ed<   dZedz  ed<   dZeed<   dZeed<   dZeed<   dZeed<   y)ÚSpeechRequestÚmodelÚinputNÚinstructÚvoiceg      ð?ÚspeedÚmaleÚgenderÚpitchÚaÚ	lang_codeÚ	ref_audioÚref_textgffffffæ?Útemperaturegffffffî?Útop_pé(   Útop_kÚrepetition_penaltyÚmp3Úresponse_formatFÚstreamg       @Ústreaming_intervali°  Ú
max_tokensÚverbose)rA   rB   rC   rD   Ú__annotations__rj   rk   rl   r"   rn   ro   rq   rr   rs   rt   ru   rw   rH   rx   rz   r{   rE   r|   r}   r~   rF   r8   r*   rg   rg   ›   sì   … ØƒJØƒJØ€Hˆc�D‰jÓØ€Eˆ3�‰:ÓØ€Eˆ5�4‰<ÓØ€FˆC�$‰JÓØ€Eˆ5�4‰<ÓØ€Iˆs�T‰zÓØ €Iˆs�T‰zÓ Ø€Hˆc�D‰jÓØ #€K�˜‘Ó#Ø€Eˆ5�4‰<ÓØ€Eˆ3�‰:ÓØ'*Ð˜ ™Ó*Ø"'€O�S˜4‘ZÓ'Ø€FˆDÓØ #Ð˜Ó#Ø€J�ÓØ€GˆTÔr8   rg   c                   ó¨   — e Zd ZU eed<   dZedz  ed<   dZeed<   dZe	ed<   dZ
eed	<   d
Ze	ed<   dZeed<   dZedz  ed<   dZe	ed<   dZedz  ed<   y)ÚTranscriptionRequestrh   NÚlanguageFr~   é   r}   ç      >@Úchunk_durationé   Úframe_thresholdr{   Úcontexté   Úprefill_step_sizeÚtext)rA   rB   rC   rD   r   r‚   r~   rE   r}   rH   r…   r"   r‡   r{   rˆ   rŠ   r‹   rF   r8   r*   r�   r�   ±   sn   … ØƒJØ€Hˆc�D‰jÓØ€GˆTÓØ€J�ÓØ €N�EÓ Ø€O�SÓØ€FˆDÓØ€GˆS�4‰ZÓØ!Ð�sÓ!Ø€Dˆ#�‰*Ôr8   r�   c                   ó,   — e Zd ZU eed<   eed<   eed<   y)ÚSeparationResponseÚtargetÚresidualÚsample_rateN)rA   rB   rC   rD   r   rH   rF   r8   r*   r�   r�   ¾   s   … ØƒKØƒMØÔr8   r�   Ú/c               ƒ   ó   K  — ddiS ­w)NÚmessagezMWelcome to the MLX Audio API server! Visit https://localhost:3000 for the UI.rF   rF   r8   r*   Úrootr”   È   s   è ø€ ð 	Ðbðð ùs   ‚z
/v1/modelsc            	   ƒ   óÄ   K  — t         j                  «       ƒ d{  –—† } g }| D ]3  }|j                  |dt        t	        j                  «       «      ddœ«       Œ5 d|dœS 7 ŒC­w)zG
    Get list of models - provided in OpenAI API compliant format.
    Nrh   Úsystem)ÚidÚobjectÚcreatedÚowned_byr!   )r˜   Údata)Úmodel_providerr@   ÚappendrH   Útime)r1   Úmodels_datarh   s      r*   Úlist_modelsr    Ï   sd   è ø€ ô
 "×6Ñ6Ó8×8€FØ€KÛˆØ×ÑàØ!ÜœtŸy™y›{Ó+Ø$ñ	õ	
ð ð  kÑ2Ð2ð 9ús   ‚A šA›AA r9   c              ƒ   óF   K  — t         j                  | «       dd| › d�dœS ­w)zÀ
    Add a new model to the API.

    Args:
        model_name (str): The name of the model to add.

    Returns:
        dict (dict): A dictionary containing the status of the operation.
    ÚsuccesszModel z added successfully©Ústatusr“   )rœ   r   )r9   s    r*   Ú	add_modelr¥   â   s+   è ø€ ô ×Ñ˜jÔ)Ø¨f°Z°LÐ@SÐ,TÑUÐUùs   ‚!c              ƒ   óº   K  — t        | «      j                  d«      } t        j                  | «      ƒ d{  –—† }|rt	        d¬«      S t        dd| › d�¬«      ‚7 Œ#­w)	a   
    Remove a model from the API.

    Args:
        model_name (str): The name of the model to remove.

    Returns:
        Response (str): A 204 No Content response if successful.

    Raises:
        HTTPException (str): If the model is not found.
    Ú"NéÌ   )Ústatus_codei”  zModel 'z' not found©r©   Údetail)r
   Ústriprœ   r=   r   r   )r9   Úremoveds     r*   r=   r=   ñ   sY   è ø€ ô ˜Ó$×*Ñ*¨3Ó/€JÜ"×/Ñ/°
Ó;×;€GÙÜ CÔ(Ð(ä¨°g¸j¸\ÈÐ4UÔVÐVð	 <ús   ‚3AµA¶$AÚpayloadc                ó¤  K  — |j                   }g }d }|rwt        |t        «      rgt        j                  j                  |«      st        dd|› �¬«      ‚ddlm} t        | d«      xr | j                  dk(  } ||| j                  |¬«      }| j                  |j                  |j                  |j                  |j                   |j"                  |j$                  |j&                  ||j(                  |j*                  |j,                  |j.                  |j0                  |j2                  |j4                  |j6                  |j8                  ¬	«      D ]�  }|j2                  rUt;        j<                  «       }t?        ||j@                  |j                  |jB                  ¬
«       |jE                  «       ­–— Œd|jG                  |j@                  «       |�Œ‚|j                  }Œ� |j2                  ry |st        dd¬«      ‚tI        jJ                  |«      }	t;        j<                  «       }t?        ||	||jB                  ¬
«       |jE                  «       ­–— y ­w)Ni�  z Reference audio file not found: rª   r   )Ú
load_audioÚ
model_typeÚspark)r�   Úvolume_normalize)rk   rl   rn   ro   rj   rq   rr   rs   rt   ru   rw   rx   r{   r|   r}   r~   ©ÚformatzNo audio generated)&rr   r   rD   rQ   ÚpathÚexistsr   Úmlx_audio.tts.generater°   Úhasattrr±   r�   Úgenerateri   rk   rl   rn   ro   rj   rq   rs   rt   ru   rw   rx   r{   r|   r}   r~   ÚioÚBytesIOÚaudio_writeÚaudiorz   Úgetvaluer�   r#   Úconcatenate)
rh   r®   rr   Úaudio_chunksr�   r°   Ú	normalizeÚresultÚbufferÚconcatenated_audios
             r*   Úgenerate_audiorÆ     sæ  è ø€ à×!Ñ!€IØ€LØ€KÙ”Z 	¬3Ô/Ü�w‰w�~‰~˜iÔ(ÜØÐ*JÈ9È+Ð(Vôð õ 	6ô ˜E <Ó0ÒP°U×5EÑ5EÈÑ5Pˆ	áØ 5×#4Ñ#4Àyô
ˆ	ð —.‘.Ø�‰Ø�m‰mØ�m‰mØ�~‰~Ø�m‰mØ×!Ñ!Ø×#Ñ#ØØ×!Ñ!Ø×'Ñ'Ø�m‰mØ�m‰mØ"×5Ñ5Ø�~‰~Ø"×5Ñ5Ø×%Ñ%Ø—‘ð# !ö ˆð( �>Š>Ü—Z‘Z“\ˆFÜØ˜Ÿ™ f×&8Ñ&8À×AXÑAXõð —/‘/Ó#Ô#à×Ñ §¡Ô-ØÑ"Ø$×0Ñ0‘ð;ð> ‡~‚~ØáÜ¨Ð4HÔIÐIäŸ™¨Ó5ÐÜ�Z‰Z‹\€FÜ�Ð*¨KÀ×@WÑ@WÕXØ
�/‰/Ó
Ôùs   ‚GIÇB Iz/v1/audio/speechc              ƒ   ó°   K  — t         j                  | j                  «      }t        t	        || «      d| j
                  › �dd| j
                  › �i¬«      S ­w)z>Generate speech audio following the OpenAI text-to-speech API.zaudio/zContent-Dispositionzattachment; filename=speech.)Ú
media_typeÚheaders)rœ   r   rh   r   rÆ   rz   )r®   rh   s     r*   Ú
tts_speechrÊ   F  s]   è ø€ ô ×%Ñ% g§m¡mÓ4€EÜÜ�u˜gÓ&Ø˜G×3Ñ3Ð4Ð5à!Ð%AÀ'×BYÑBYÐAZÐ#[ð
ôð ùs   ‚AAÚtmp_pathÚ
gen_kwargsc           
   #   ó”  K  — 	  | j                   |fi |¤Ž}t        |d«      r’t        |d«      r†d}|D ]~  }t        |t        «      r||z  }||dœ}n>|j                  t        |dd«      t        |dd«      t        |dd«      t        |d	d«      d
œ}t        j                  t        |«      «      dz   –— Œ€ n#t        j                  t        |«      «      dz   –— t        j                  j                  |«      rt        j                  |«       yy# t        j                  j                  |«      rt        j                  |«       w w xY w­w)zCGenerator that yields transcription chunks and cleans up temp file.Ú__iter__Ú__next__Ú )r‹   ÚaccumulatedÚ
start_timeNÚend_timeÚis_finalr‚   )r‹   ÚstartÚendrÔ   r‚   Ú
)rº   r¹   r   rD   r‹   ÚgetattrÚjsonÚdumpsr    rQ   r¶   r·   Úremove)Ú	stt_modelrË   rÌ   rÃ   Úaccumulated_textÚchunkÚ
chunk_datas          r*   Úgenerate_transcription_streamrà   S  s!  è ø€ ð à#�×#Ñ# HÑ;°
Ñ;ˆô �6˜:Ô&¬7°6¸:Ô+FØ!ÐÛ�ä˜e¤SÔ)Ø$¨Ñ-Ð$Ø*/Ð@PÑ!Q‘Jð !&§
¡
Ü!(¨°¸dÓ!CÜ& u¨j¸$Ó?Ü$+¨E°:¸tÓ$DÜ$+¨E°:¸tÓ$Dñ"�Jô —j‘jÔ!2°:Ó!>Ó?À$ÑFÓFñ  ô" —*‘*Ô.¨vÓ6Ó7¸$Ñ>Ò>ä�7‰7�>‰>˜(Ô#Ü�I‰I�hÕð $øŒ2�7‰7�>‰>˜(Ô#Ü�I‰I�hÕð $üs   ‚E„CD Ã6EÄ7EÅEz/v1/audio/transcriptions.Frƒ   r„   r†   r‰   Úfilerh   r‚   r~   r}   r…   r‡   r{   rˆ   rŠ   r‹   c              ƒ   óÆ  K  — t        |||||||||	|
¬«
      }| j                  «       ƒ d{  –—† }t        j                  |«      }t	        |d¬«      \  }}|j                  «        t        j                  j                  | j                  «      \  }}dt        j                  «       › d|r|nd› �}t        |||«       t        j                  |j                  «      }|j                  dhd	¬
«      }t!        j"                  |j$                  «      }|j'                  «       D ��ci c]  \  }}||j(                  v sŒ||“Œ }}}t+        t-        |||«      d¬«      S 7 �Œ-c c}}w ­w)z5Transcribe audio using an STT model in OpenAI format.)
rh   r‚   r~   r}   r…   r‡   r{   rˆ   rŠ   r‹   NF©Ú	always_2dz/tmp/Ú.ry   rh   T)ÚexcludeÚexclude_nonezapplication/x-ndjson)rÈ   )r�   r   r»   r¼   Ú
audio_readÚcloserQ   r¶   ÚsplitextÚfilenamerž   r½   rœ   r   rh   Ú
model_dumpÚinspectÚ	signaturerº   r   Ú
parametersr   rà   )rá   rh   r‚   r~   r}   r…   r‡   r{   rˆ   rŠ   r‹   r®   r›   Útmpr¾   ÚsrÚ_ÚextrË   rÜ   rÌ   rî   r'   r(   s                           r*   Ústt_transcriptionsrô   s  sL  è ø€ ô  #ØØØØØ%Ø'ØØØ+Øô€Gð —‘“×€DÜ
�*‰*�TÓ
€CÜ˜3¨%Ô0�I€Eˆ2Ø‡I�I„KÜ�W‰W×Ñ˜dŸm™mÓ,�F€A€sØ”t—y‘y“{�m 1©C¡S°UÐ$;Ð<€HÜ�˜% Ô$ä×)Ñ)¨'¯-©-Ó8€Ið ×#Ñ#¨W¨IÀDÐ#ÓI€Jô ×!Ñ! )×"4Ñ"4Ó5€IØ#-×#3Ñ#3Ô#5ÔSÑ#5™4˜1˜a¸¸i×>RÑ>RÒ9R�!�Q‘$Ð#5€JÑSäÜ% i°¸:ÓFØ)ôð ð# ûó Tùs(   ‚)E!«E¬C5E!Ä!EÄ8EÄ=E!ÅE!z/v1/audio/separationsz"mlx-community/sam-audio-large-fp16ÚspeechÚmidpointé   ÚdescriptionÚmethodÚstepsc              ƒ   ó4  K  — ddl m}m} | j                  «       ƒ d{  –—† }t	        j
                  |«      }t        j                  |d¬«      \  }	}
|j                  «        dt        j                  «       › d�}t        j                  ||	|
«       	 t        €|j                  |«      a
t        €|j                  |«      at        |g|g¬«      }d	|d	z  z  }||d
œ}t        j                  |j                  |j                  |j                   |j"                  |d¬«      }t%        j&                  «        t)        j*                  |j,                  d   «      }t)        j*                  |j.                  d   «      }t        j0                  }d„ }t3         |||«       |||«      |¬«      t4        j6                  j9                  |«      rt5        j:                  |«       S S 7 �Œ½# t4        j6                  j9                  |«      rt5        j:                  |«       w w xY w­w)aø  Separate audio using SAM Audio model.

    Args:
        file: Audio file to process
        model: SAM Audio model name (default: mlx-community/sam-audio-large-fp16)
        description: Text description of what to separate (e.g., "speech", "guitar", "drums")
        method: ODE solver method - "midpoint" or "euler" (default: midpoint)
        steps: Number of ODE steps - 2, 4, 8, 16, or 32 (default: 16)

    Returns:
        JSON with base64-encoded target and residual audio, plus sample rate
    r   )ÚSAMAudioÚSAMAudioProcessorNFrã   z/tmp/separation_z.wav)ÚdescriptionsÚaudiosé   )rù   Ú	step_sizeé2   )rÿ   rþ   Ú
anchor_idsÚanchor_alignmentÚode_optÚode_decode_chunk_sizec                 óâ   — t        j                  «       }t        j                  || |d¬«       |j	                  d«       t        j                  |j                  «       «      j                  d«      S )NÚwavr´   r   zutf-8)	r»   r¼   Úsfr   ÚseekÚbase64Ú	b64encoder   Údecode)Úaudio_arrayrñ   rÄ   s      r*   Úaudio_to_base64z*audio_separations.<locals>.audio_to_base64ï  sN   € Ü—Z‘Z“\ˆFÜ�H‰H�V˜[¨"°UˆHÔ;Ø�K‰K˜ŒNÜ×#Ñ# F§K¡K£MÓ2×9Ñ9¸'ÓBÐBr8   )rŽ   r�   r�   )Úmlx_audio.stsrü   rý   r   r»   r¼   r	  ré   rž   r   ÚSAM_PROCESSORÚfrom_pretrainedÚ	SAM_MODELÚseparate_longrÿ   rþ   r  r  ÚmxÚclear_cacher#   ÚarrayrŽ   r�   r�   r�   rQ   r¶   r·   rÛ   )rá   rh   rø   rù   rú   rü   rý   r›   rð   r¾   rñ   rË   Úbatchr  r  rÃ   Útarget_audioÚresidual_audior�   r  s                       r*   Úaudio_separationsr  «  sÉ  è ø€ ÷* :ð —‘“×€DÜ
�*‰*�TÓ
€CÜ—‘˜ u�Ó-�I€Eˆ2Ø‡I�I„Kð "¤$§)¡)£+ ¨dÐ3€HÜ‡H�HˆX�u˜bÔ!ð2 äÐ Ø-×=Ñ=¸eÓDˆMÜÐØ ×0Ñ0°Ó7ˆIô Ø%˜Ø�:ô
ˆð ˜ ™‘Oˆ	Ø#°)Ñ<ˆô ×(Ñ(Ø—<‘<Ø×+Ñ+Ø×'Ñ'Ø"×3Ñ3ØØ"$ð )ó 
ˆô 	�‰Ôô —x‘x §¡¨aÑ 0Ó1ˆÜŸ™ &§/¡/°!Ñ"4Ó5ˆÜ×+Ñ+ˆò	Cô "Ù" <°Ó=Ù$ ^°[ÓAØ#ô
ô �7‰7�>‰>˜(Ô#Ü�I‰I�hÕð $ðu úôt �7‰7�>‰>˜(Ô#Ü�I‰I�hÕð $üs)   ‚HžGŸA2HÂDG Æ%7HÇ7HÈHÚ	websocketr  r�   Ú
is_partialÚ	streamingc           	   ƒ   óê  K  — dt        j                  |j                  «      j                  v }|r±|r¯|j                  t	        j
                  |«      d|d¬«      }d}	|}
|D ]^  }t        |t        «      r|nt        |dt        |«      «      }|	|z  }	t        |dd«      }|r|
€|}
| j                  d	|d
œ«      ƒ d{  –—†  Œ` | j                  d|	d|
|dœ«      ƒ d{  –—†  ydt        j                  «       › d�}t        |||«       	 |j                  ||d¬«      }t        |d«      r!|j                  rt        |j                  «      nd}| j                  |j                  |t        |d|«      |dœ«      ƒ d{  –—†  t         j"                  j%                  |«      rt!        j&                  |«       yy7 Œø7 ŒÙ7 Œ># t         j"                  j%                  |«      rt!        j&                  |«       w w xY w­w)aÐ  Handle both streaming and non-streaming model inference over WebSocket.

    Streaming models (whose generate() accepts a ``stream`` parameter) receive
    the audio as an ``mx.array`` and yield token deltas sent as
    ``{"type": "delta", "delta": "..."}`` messages, followed by a
    ``{"type": "complete", ...}`` message.

    Non-streaming models fall back to temp-file + batch generate, sending the
    legacy ``{"text": ..., "is_partial": ...}`` format.
    r{   TF)r{   r‚   r~   rÐ   r‹   r‚   NÚdelta)r   r   Úcomplete)r   r‹   Úsegmentsr‚   r  z/tmp/realtime_z.mp3)r‚   r~   r"  )r‹   r"  r‚   r  )rí   rî   rº   rï   r  r  r   rD   rØ   Ú	send_jsonrž   r½   r¹   r"  r    r‹   rQ   r¶   r·   rÛ   )r  rÜ   r  r�   r‚   r  r  Úsupports_streamÚresult_iterrÑ   Údetected_languagerÞ   r   Ú
chunk_langrË   rÃ   r"  s                    r*   Ú_stream_transcriptionr(    sô  è ø€ ð& ¤'×"3Ñ"3°I×4FÑ4FÓ"G×"RÑ"RÐR€Oá™9Ø×(Ñ(Ü�H‰H�[Ó!¨$¸È5ð )ó 
ˆð ˆØ$ÐÛ ˆEä# E¬3Ô/‘´W¸UÀFÌCÐPUËJÓ5Wð ð ˜5Ñ ˆKä  ¨
°DÓ9ˆJÙÐ/Ð7Ø$.Ð!Ø×%Ñ%¨wÀÑ&GÓH×HÑHð !ð ×!Ñ!à"Ø#Ø Ø-Ø(ñó
÷ 	
ñ 	
ð $¤D§I¡I£K =°Ð5ˆÜ�H˜k¨;Ô7ð	$Ø×'Ñ'¨¸8ÈUÐ'ÓSˆFô ˜6 :Ô.°6·?²?ô " &§/¡/Ô2àð ð
 ×%Ñ%à"ŸK™KØ (Ü '¨°
¸HÓ EØ",ñ	ó÷ ð ô �w‰w�~‰~˜hÔ'Ü—	‘	˜(Õ#ð (ð= Iøð	
øð&ùô �w‰w�~‰~˜hÔ'Ü—	‘	˜(Õ#ð (üsO   ‚B8G3Â:F3Â; G3ÃF5Ã*G3ÄA1F9 Å8F7Å9F9 Å=7G3Æ5G3Æ7F9 Æ97G0Ç0G3z!/v1/audio/transcriptions/realtimec              ƒ   ó¸  K  — | j                  «       ƒ d{  –—†  	 | j                  «       ƒ d{  –—† }|j                  dd«      }|j                  dd«      }|j                  dd«      }|j                  dd«      }t        d	|› d
|› d|› d|› �«       t        d«       t        j                  |«      }t        d«       t        j                  d«      }d}t        ||z  dz  «      }	t        d|	› d|› d|› d�«       g }
t        |dz  «      }t        |dz  «      }t        |dz  «      }d}d}t        j                  «       }d}t        j                  «       }d}d}| j                  dddœ«      ƒ d{  –—†  t        d«       	 	 | j                  «       ƒ d{  –—† }d|v �r®t        j                  |d   t        j                  ¬«      }t!        |«      |	z  }d}d}t#        |«      D ]H  }||	z  }||	z   }||| }t!        |«      |	k(  sŒ!	 |j%                  |j'                  «       |«      rd}|d z  }ŒJ t        j                  «       }|rˆ|j-                  t        j.                  «      d"z  } |
j1                  | «       |d z  }d}|}t!        |
«      |d#z  z  t!        | «      k  rKt        d$|› d%|› d&t!        |
«      › dt!        |
«      |z  d'›d(�	«       n|d z  }|d)z  dk(  rt        d*|› d+�«       ||z
  }!d}"d}#t!        |
«      dkD  rš|s6t!        |
«      |k\  r(|r&d}"t        d,||z  d'›d-t!        |
«      |z  d'›d.�«       nb|!|k\  r1t!        |
«      |k\  r#d}#t        d/|!d'›d0t!        |
«      |z  d'›d.�«       n,t!        |
«      |k\  rd}#t        d1t!        |
«      |z  d'›d.�«       |"rGt!        |
«      |k\  r9|}$t        j2                  |
d|$ «      }%|$}d}	 t5        | ||%||d|¬2«      ƒ d{  –—†  |#r›t!        |
«      dkD  r�t!        |
«      }$t        j2                  |
«      }%	 t5        | ||%||d|¬2«      ƒ d{  –—†  g }
d}d}|}t        d6|$› d|$|z  d'›d7�«       n3d9|v r/	 t=        j>                  |d9   «      }(|(j                  d:«      d;k(  rn	 �Œ	 | jC                  «       ƒ d{  –—†  y7 �Œ†7 �Œp7 �Œ27 �Œ# t        $ r Y Œ1w xY w# t(        t*        f$ r }t        d!|› �«       d}|d z  }Y d}~�Œýd}~ww xY w7 �Œ# t        $ rU}ddl}&t9        |«      }'|&j;                  «        t        d3|'› �«       | j                  |'d4d5œ«      ƒ d{  –—†7   Y d}~�Œbd}~ww xY w7 �Œ$# t        $ rU}ddl}&t9        |«      }'|&j;                  «        t        d8|'› �«       | j                  |'d4d5œ«      ƒ d{  –—†7   Y d}~�Œ)d}~ww xY w# t        $ r Y �Œ=w xY w# t@        $ r Y �ŒKt        $ rB}	 | j                  t9        |«      d4d5œ«      ƒ d{  –—†7   n# t        $ r Y nw xY wY d}~�Œ�d}~ww xY w7 �Œ„# t        $ r Y yw xY w# 	 | jC                  «       ƒ d{  –—†7   w # t        $ r Y w w xY wxY w­w)<z%Realtime transcription via WebSocket.Nrh   z-mlx-community/whisper-large-v3-turbo-asr-fp16r‚   r�   i€>  r  TzConfiguration received: model=z, language=z, sample_rate=z, streaming=zLoading STT model...zSTT model loaded successfullyé   é   iè  zVAD initialized: frame_size=z
 samples (zms at zHz)g      à?g      ø?g      @r   FÚreadyzReady to transcriber£   Úbytes)ÚdtyperP   zVAD error (assuming speech): g      à@r   zSpeech detected (r‘   z frames): buffer z.2fzs)é   zSilence detected: skipped z chunksz1Processing initial chunk for real-time feedback: zs, total buffer: ÚszProcessing due to silence gap: zs silence, buffer: z#Processing due to max buffer size: )r  r  z$Error during initial transcription: Úerror)r1  r¤   zProcessed final chunk: zs), buffer clearedzError during transcription: r‹   ÚactionÚstop)"ÚacceptÚreceive_jsonÚgetÚprintrœ   r   Ú	webrtcvadÚVadrH   rž   r#  ÚreceiveÚ	Exceptionr#   Ú
frombufferÚint16ÚlenÚrangeÚ	is_speechÚtobytesrI   ÚOSErrorÚastypeÚfloat32Úextendr  r(  Ú	tracebackrD   Ú	print_excrÙ   Úloadsr   ré   ))r  Úconfigr9   r‚   r�   r  rÜ   ÚvadÚvad_frame_duration_msÚvad_frame_sizeÚaudio_bufferÚmin_chunk_sizeÚinitial_chunk_sizeÚmax_chunk_sizeÚsilence_skip_countÚspeech_chunk_countÚlast_speech_timeÚsilence_threshold_secondsÚlast_process_timeÚinitial_chunk_processedÚprocessed_samplesr“   Úaudio_chunk_int16Ú
num_framesÚ
has_speechÚspeech_framesÚiÚframe_startÚ	frame_endÚframeÚeÚcurrent_timeÚaudio_chunk_floatÚtime_since_last_speechÚshould_process_initialÚshould_process_finalÚprocess_sizer  rF  Ú	error_msgr›   s)                                            r*   Ústt_realtime_transcriptionsrh  G  sÃ  è ø€ ð ×
Ñ
Ó
×Ððeà ×-Ñ-Ó/×/ˆØ—Z‘ZØÐDó
ˆ
ð —:‘:˜j¨$Ó/ˆØ—j‘j °Ó6ˆØ—J‘J˜{¨DÓ1ˆ	äØ,¨Z¨L¸ÀHÀ:È^Ð\gÐ[hÐhtÐu~Ðtð  Aô	
ô
 	Ð$Ô%Ü"×-Ñ-¨jÓ9ˆ	ÜÐ-Ô.ô �m‰mØó
ˆð !#ÐÜ˜[Ð+@Ñ@À4ÑGÓHˆÜØ*¨>Ð*:¸*ÐEZÐD[Ð[aÐbmÐanÐnqÐrô	
ð
 ˆÜ˜[¨3Ñ.Ó/ˆÜ Ø˜#Ñó
Ðô Ø˜#Ñó
ˆð ÐØÐÜŸ9™9›;ÐØ$'Ð!Ü ŸI™I›KÐØ"'ÐØÐà×!Ñ!¨WÐAVÑ"WÓX×XÐXÜÐ#Ô$àðØ )× 1Ñ 1Ó 3×3�ð ˜'Ò!ä$&§M¡M°'¸'Ñ2BÌ"Ï(É(Ô$SÐ!ô
 !Ð!2Ó3°~ÑE�
Ø"�
Ø !�ô ˜zÖ*�AØ"# nÑ"4�KØ +¨nÑ <�IØ-¨k¸)ÐD�Eô ˜5“z ^Ó3ð	/Ø"Ÿ}™}¨U¯]©]«_¸kÔJØ-1 
Ø -°Ñ 2 øð +ô,  $Ÿy™y›{�Ùà(9×(@Ñ(@ÄÇÁÓ(LÈwÑ(VÐ%Ø ×'Ñ'Ð(9Ô:Ø&¨!Ñ+Ð&Ø)*Ð&Ø'3Ð$ä˜<Ó(¨K¸!©OÑ<¼sÐCTÓ?UÒUäØ/°¨¸aÀ
¸|ÐK\Ô]`ÐamÓ]nÐ\oÐoyÔz}ð  Kó  {Lð  MXñ  {Xð  Y\ð  z]ð  ]_ð  `õð '¨!Ñ+Ð&à)¨BÑ.°!Ò3ÜÐ :Ð;MÐ:NÈgÐVÔWð *6Ð8HÑ)HÐ&Ø).Ð&Ø',Ð$ä�|Ó$ qÒ(ñ 4Ü Ó-Ð1CÒCÙ&à15Ð.ÜØOÐPbÐcnÑPnÐorÐOsð  tEô  FIð  JVó  FWð  Xcñ  Fcð  dgð  Ehð  hið  jõð
 /Ð2KÒKÜ Ó-°Ò?à/3Ð,ÜØ=Ð>TÐUXÐ=YÐYlÔmpÐq}Óm~ð  @Kñ  nKð  LOð  mPð  PQð  Rõô ˜\Ó*¨nÒ<Ø/3Ð,ÜØAÄ#ÀlÓBSÐT_ÑB_Ð`cÐAdÐdeÐfôñ
 *¬c°,Ó.?ÐCUÒ.UØ#5�LÜ"$§(¡(¨<¸¸Ð+FÓ"G�KØ(4Ð%Ø.2Ð+ðÜ3Ø%Ø%Ø'Ø'Ø$Ø'+Ø&/ô÷ ð ñ( (¬C°Ó,=ÀÒ,Aä#& |Ó#4�LÜ"$§(¡(¨<Ó"8�KðÜ3Ø%Ø%Ø'Ø'Ø$Ø',Ø&/ô÷ ð ð (*˜Ø,-Ð)Ø27Ð/Ø,8Ð)ÜØ5°l°^À:ÈlÐ[fÑNfÐgjÐMkÐk}Ð~õð ˜7Ñ"ðÜŸ:™: g¨f¡oÓ6�DØ—x‘x Ó)¨VÒ3Øð 4ñG ð`	Ø—/‘/Ó#×#Ñ#ðK ùð 0ùðZ 	Yùð 4úÜò Ùðûô4 !+¬GÐ4ò /ô "Ð$AÀ!ÀÐ"EÔFØ)-˜JØ)¨QÑ.žMûð/úðTúô %ò Û(ä$'¨£F˜	Ø!×+Ñ+Ô-ÜÐ DÀYÀKÐPÔQØ'×1Ñ1Ø&/¸7ÑCó÷ ÷ ûðúð"úô& %ò Û(ä$'¨£F˜	Ø!×+Ñ+Ô-ÜÐ <¸Y¸KÐHÔIØ'×1Ñ1Ø&/¸7ÑCó÷ ÷ ûðûô  !ò Úðûô ò ÚÜò ð	Ø×%Ñ%´°A³À'Ñ&JÓK×KÒKøÜò 	Ùð	ÿüðúð $úÜò 	Ùð	ûð	Ø—/‘/Ó#×#Ò#øÜò 	Ùð	ÿs  ‚Y–R—YœW ¯R°E W Å0R"Å1W ÆR( ÆR%ÆR( ÆA*W È'R7È-FW Ï	S, ÏS)ÏS, Ï#0W ÐU Ð)UÐ*"U ÑW Ñ,V1 Ñ>W ÒX ÒXÒX ÒYÒW Ò"W Ò%R( Ò(	R4Ò1W Ò3R4Ò4W Ò7S&ÓS!ÓW Ó!S&Ó&W Ó)S, Ó,	U
Ó5AUÔ9T<Ô:UÔ?W ÕU
Õ
W ÕU Õ	V.ÕAV)ÖV ÖV)Ö#W Ö)V.Ö.W Ö1	V>Ö:W Ö=V>Ö>W ×	X×
X, ×X× W=×6W9×7W=×<X×=	X	ØXØX	Ø	XØX, ØXØX, ØX Ø	X)Ø&YØ(X)Ø)YØ,YØ.YÙYÙYÙYÙ	YÙYÙYÙYÙYc                   ó"   — e Zd Zdd„Zd„ Zdd„Zy)ÚMLXAudioStudioServerc                 óx   — || _         d | _        t        |«      | _        | j                  j	                  d¬«       y )NT)Úexist_ok)Ústart_uiÚ
ui_processr   Úlog_dirÚmkdir)r6   rm  ro  s      r*   r7   zMLXAudioStudioServer.__init__5  s0   € Ø ˆŒØˆŒÜ˜G“}ˆŒØ�‰×Ñ DÐÕ)r8   c                 ól  — t        t        «      j                  dz  }	 t        j                  ddgt        |«      t        j                  t        j                  d¬«      }	 t        | j                  d	z  d
«      }t        j                  g d¢t        |«      |t        j                  ¬«      | _        t!        d| j                  › d�«       y# t        $ r t        d«      ‚t        j                  $ r}t        d|«      ‚d}~ww xY w# t        $ r t        d«      ‚t        $ r}t        d|› �«      ‚d}~ww xY w)z%Start UI with logs redirected to fileÚuiÚnpmÚinstallT)ÚcwdÚstdoutÚstderrÚcheckuJ   âœ— Error: 'npm' is not installed or not found in PATH. UI will not start.u!   âœ— Error running 'npm install':
Núui.logÚw)rs  ÚrunÚdev)ru  rv  rw  u   âœ“ UI started (logs: z/ui.log)uN   âœ— Error: 'npm' is not installed or not found in PATH. UI server not started.u   âœ— Failed to start UI: )r   Ú__file__ÚparentÚ
subprocessr{  rD   ÚDEVNULLÚPIPEÚFileNotFoundErrorr;  ÚCalledProcessErrorÚopenro  ÚPopenÚSTDOUTrn  r7  )r6   Úui_pathrÃ   r`  Úui_logs        r*   Ústart_ui_backgroundz(MLXAudioStudioServer.start_ui_background;  s&  € ä”x“.×'Ñ'¨$Ñ.ˆð	Eä—^‘^Ø˜	Ð"Ü˜“LÜ!×)Ñ)Ü!—‘ØôˆFð	<ä˜$Ÿ,™,¨Ñ1°3Ó7ˆFÜ(×.Ñ.Ú%Ü˜“LØÜ!×(Ñ(ô	ˆDŒOô Ð*¨4¯<©<¨.¸ÐAÕBøô! !ò 	ÜØ\óð ô ×,Ñ,ò 	EÜÐ@À!ÓDÐDûð	Eûô !ò 	ÜØ`óð ô ò 	<ÜÐ6°q°cÐ:Ó;Ð;ûð	<ús1   žAC
 Á A)D Ã
&DÃ0C<Ã<DÄD3Ä D.Ä.D3c                 óÜ  — | j                   ra| j                  «        t        j                  d«       t	        j
                  d«       t        d|› d|› �«       t        d«       t        d«       	 t        j                  d||||d¬	«       | j                  r%| j                  j                  «        t        d
«       | j                  dz  }|j                  «       r|j                  «        t        d|› �«       y y # | j                  r%| j                  j                  «        t        d
«       | j                  dz  }|j                  «       r|j                  «        t        d|› �«       w w xY w)Nr   zhttp://localhost:3000u"   âœ“ API server starting on http://Ú:u0   âœ“ Studio UI available at http://localhost:3000z"
Press Ctrl+C to stop both serverszmlx_audio.server:appr2   )ÚhostÚportÚreloadrN   Úloopu   âœ“ UI server stoppedry  u   âœ“ UI logs deleted from )rm  r‰  rž   ÚsleepÚ
webbrowserr„  r7  Úuvicornr{  rn  Ú	terminatero  r·   Úunlink)r6   rŒ  r�  rŽ  rN   Úui_log_paths         r*   Ústart_serverz!MLXAudioStudioServer.start_server`  s5  € Ø�=Š=Ø×$Ñ$Ô&Ü�J‰J�qŒMÜ�O‰OÐ3Ô4ÜÐ6°t°f¸A¸d¸VÐDÔEÜÐDÔFÜÐ7Ô8ð	AÜ�K‰KØ&ØØØØØõð �ŠØ—‘×)Ñ)Ô+ÜÐ-Ô.àŸ,™,¨Ñ1ˆKØ×!Ñ!Ô#Ø×"Ñ"Ô$ÜÐ1°+°Ð?Õ@ð $øð �ŠØ—‘×)Ñ)Ô+ÜÐ-Ô.àŸ,™,¨Ñ1ˆKØ×!Ñ!Ô#Ø×"Ñ"Ô$ÜÐ1°+°Ð?Õ@ð $ús   Á/C: Ã:A1E+N)FÚlogs)Ú	localhosté@  Fr   )rA   rB   rC   r7   r‰  r–  rF   r8   r*   rj  rj  4  s   „ ó*ò#<ôJAr8   rj  c                  óP  — t        j                  d¬«      } | j                  dddg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                  «       }t        |j                  t        «      r9t        dt	        t        j                  «       |j                  z  «      «      |_	        t        t        |j                   «       t#        |j$                  |j&                  ¬«      }|j)                  |j*                  |j,                  |j                  €|j.                  nd|j                  ¬«       y )NzMLX Audio API server)rø   z--allowed-originsÚ+rZ   z List of allowed origins for CORS)ÚnargsÚdefaultÚhelpz--hostr˜  zHost to run the server on)r   r�  rž  z--portr™  zPort to run the server onz--reloadFzKEnable auto-reload of the server. Only works when 'workers' is set to None.z	--workersr   a•  Number of workers. Overrides the `MLX_AUDIO_NUM_WORKERS` env variable.
        Can be either an int or a float.
        If an int, it will be the number of workers to use.
        If a float, number of workers will be this fraction of the  number of CPU cores available, with a minimum of 1.
        Defaults to the `MLX_AUDIO_NUM_WORKERS` env variable if set and to 2 if not.
        To use all available CPU cores, set it to 1.0.

        Examples:
        --workers 1 (will use 1 worker)
        --workers 1.0 (will use all available CPU cores)
        --workers 0.5 (will use half the number of CPU cores available)
        --workers 0.0 (will use 1 worker)z
--start-uiÚ
store_truez,Start the Studio UI alongside the API server)r2  rž  z	--log-dirr—  zDirectory to save server logsrP   )rm  ro  )rŒ  r�  rŽ  rN   )rJ   ÚArgumentParserÚadd_argumentrD   rH   rE   rM   Ú
parse_argsr   rN   r"   rS   rQ   rT   rc   rW   rX   rj  rm  ro  r–  rŒ  r�  rŽ  )ÚparserÚargsÚclients      r*   Úmainr¦  }  s“  € Ü×$Ñ$Ð1GÔH€FØ
×ÑØØØ�Ø/ð	 ô ð ×ÑØ”s KÐ6Qð ô ð ×ÑØ”s DÐ/Jð ô ð ×ÑØÜØØZð	 ô ð ×ÑØÜØð-ð	 ô ð" ×ÑØØØ;ð ô ð
 ×ÑØÜØØ,ð	 ô ð ×ÑÓ€DÜ�$—,‘,¤Ô&Ü˜1œc¤"§,¡,£.°4·<±<Ñ"?Ó@ÓAˆŒäŒs�D×(Ñ(Ô)ä!¨4¯=©=À$Ç,Á,ÔO€FØ
×ÑØ�Y‰YØ�Y‰YØ"Ÿl™lÐ2ˆt�{Š{¸Ø—‘ð	 õ r8   Ú__main__)r   )T)^Ú__doc__rJ   r2   r  rí   r»   rÙ   rQ   r  rž   r‘  Údataclassesr   r   Úpathlibr   Útypingr   r   r   r	   Úurllib.parser
   Úmlx.coreÚcorer  Únumpyr#   r’  r8  Úfastapir   r   r   r   r   r   r   r   Úfastapi.middleware.corsr   Úfastapi.responsesr   Úpydanticr   Úmlx_audio.audio_ior   rè   r   r½   Úmlx_audio.utilsr   r    rR   r+   r.   rW   rM   rH   rV   rD   rc   Úallowed_origins_envÚsplitr¬   Údefault_originsrg   r�   r�   rœ   r6  r”   r    Úpostr¥   Údeleter=   rÆ   rÊ   r   rà   rE   r"   rô   r  r  r  Úndarrayr(  r  rh  rj  r¦  rA   )Úorigins   0r*   Ú<module>r½     s9  ðñó Û Û Û Û 	Û Û 	Û Û Û ß ,Ý ß ,Ó ,Ý  å Û Û Û ÷	÷ 	ó 	õ 3Ý /Ý å 1Ý 3Ý &ð˜3ð  3ó ð4 "˜Ÿ	™	Ð"9¸3Ó?Ð ÷,ñ ,ñ. ƒi€òPñ sð °3ó ð�Gð ¨d°3©ió ð*  �b—i‘iÐ ;Ó<Ð ñ ð #6×";Ñ";¸CÔ"@ÓAÑ"@˜€V‡\�\…^Ð"@ÒAà
ˆð ñ ˆ3�Ô  ô�Iô ô,
˜9ô 
ô˜ô ñ “€ð ‡�ˆƒñó ðð ‡�ˆÓñ3ó ð3ð$ ‡�ˆ,ÓðV ò Vó ðVð ‡�ˆLÓðW 3ò Wó ðWð*<¨ó <ð~ ‡�Ð
Óð	˜mò 	ó ð	ð °sð  Èó  ð@ ‡�Ð
$Ó%á˜C“yÙ�c“Ù" 4›jÙ˜“KÙ˜4“jÙ  ›JÙ ›8Ù˜“;Ù! $›ZÙ! $›ZÙ˜t›*ñ0Ø
ð0àð0ð �s‰mð0ð ð	0ð
 ð0ð ð0ð ð0ð ð0ð �c‰]ð0ð ð0ð �3‰-ò0ó &ð0ðf €	Ø€ð ‡�Ð
!Ó"á˜C“yÙÐ:Ó;Ù˜H“~Ù�zÓ"Ù�b“ñR Ø
ðR àðR ð ðR ð ð	R ð
 òR ó #ðR ðx ñC$ØðC$ð —‘ðC$ð ð	C$ð
 �s‰mðC$ð ðC$ð óC$ðL ‡�Ð2Ó3ði°ò ió 4ði÷XFAñ FAòR>ðB ˆzÒÙ…Fð ùò[ Bs   Ã?K: