
    qi#G                     ^   d Z ddlZddlZddlmZ ddlmZmZ ddlm	Z	 ddl
mZmZmZmZmZmZmZmZ ddlmZmZ ddlmZ dd	lmZ 	 dd
lmZ ddlmZ e G d de             Z# G d de      Z$y# e$ r7Z  e	jB                  de          e	jB                  d        e"de        dZ [ ww xY w)z3Resemble AI text-to-speech service implementations.    N)	dataclass)AsyncGeneratorOptional)logger)CancelFrameEndFrame
ErrorFrameFrame
StartFrameTTSAudioRawFrameTTSStartedFrameTTSStoppedFrame)TTSSettings_warn_deprecated_param)WebsocketTTSService)
traced_tts)connect)StatezException: zNIn order to use Resemble AI, you need to `pip install pipecat-ai[resembleai]`.zMissing module: c                       e Zd ZdZy)ResembleAITTSSettingsz"Settings for ResembleAITTSService.N)__name__
__module____qualname____doc__     Q/opt/pipecat/venv/lib/python3.12/site-packages/pipecat/services/resembleai/tts.pyr   r   '   s    ,r   r   c                   J    e Zd ZU dZeZeed<   dddddddd	ed
ee   dedee   dee   dee	   dee   f fdZ
defdZd%dedefdZdef fdZdef fdZdef fdZd Zd Zd Zd Zd ZdefdZdefd Zd&dee   fd!Zd" Zd# Zedededee df   fd$       Z! xZ"S )'ResembleAITTSServicea0  Resemble AI TTS service with WebSocket streaming and word timestamps.

    Provides text-to-speech using Resemble AI's streaming WebSocket API.
    Supports word-level timestamps and audio context management for handling
    multiple simultaneous synthesis requests with proper interruption support.
    	_settingsNz*wss://websocket.cluster.resemble.ai/streamPCM_16wavi"V  )voice_idurl	precisionoutput_formatsample_ratesettingsapi_keyr#   r$   r%   r&   r'   r(   c                h   t        ddd      }	|t        dt         d       ||	_        ||	j                  |       t	        
|   d|d|	d| || _        || _        |xs d| _        |xs d| _	        d	| _
        d| _        d	| _        d| _        i | _        i | _        d
| _        d| _        i | _        y)a'  Initialize the Resemble AI TTS service.

        Args:
            api_key: Resemble AI API key for authentication.
            voice_id: Voice UUID to use for synthesis.

                .. deprecated:: 0.0.105
                    Use ``settings=ResembleAITTSSettings(voice=...)`` instead.

            url: WebSocket URL for Resemble AI TTS API.
            precision: PCM bit depth (PCM_32, PCM_24, PCM_16, or MULAW).
            output_format: Audio format (wav or mp3).
            sample_rate: Audio sample rate (8000, 16000, 22050, 32000, or 44100). Defaults to 22050.
            settings: Runtime-updatable settings. When provided alongside deprecated
                parameters, ``settings`` values take precedence.
            **kwargs: Additional arguments passed to the parent service.
        N)modelvoicelanguager#   r,   F)r'   reuse_context_id_within_turnr(   r!   r"   r   i  iD  r   )r   r   r,   apply_updatesuper__init___api_key_url
_precision_output_format_resemble_sample_rate
_websocket_request_id_counter_receive_task_request_id_to_context_audio_buffers_buffer_threshold_bytes_jitter_buffer_bytes_playback_started)selfr)   r#   r$   r%   r&   r'   r(   kwargsdefault_settings	__class__s             r   r1   zResembleAITTSService.__init__9   s    < 1
 ":/DgN%-"
 ))(3 	
#).%	
 		
  	 $/x+4u%&"#$ ! 79#
 57'+$
 %*!24r   returnc                      y)zCheck if this service can generate processing metrics.

        Returns:
            True, as Resemble AI service supports metrics generation.
        Tr   r?   s    r   can_generate_metricsz)ResembleAITTSService.can_generate_metrics   s     r   textc           	          | j                   j                  |d| j                  | j                  | j                  | j
                  dd}| xj                  dz  c_        t        j                  |      S )zBuild a JSON message for the Resemble AI WebSocket API.

        Args:
            text: The text or SSML to synthesize.

        Returns:
            JSON string containing the request payload.
        FT)
voice_uuiddatabinary_response
request_idr&   r'   r%   no_audio_header   )r    r,   r8   r5   r6   r4   jsondumps)r?   rG   msgs      r   
_build_msgzResembleAITTSService._build_msg   sd     ....$22!0055#	
 	  A% zz#r   framec                    K   t         |   |       d{    | j                  | _        | j	                          d{    y7 .7 w)zStart the Resemble AI TTS service.

        Args:
            frame: The start frame containing initialization parameters.
        N)r0   startr'   r6   _connectr?   rS   rB   s     r   rU   zResembleAITTSService.start   sA      gmE"""%)%5%5"mmo 	#s    A	A(A	A A	A	c                 t   K   t         |   |       d{    | j                          d{    y7 7 w)z[Stop the Resemble AI TTS service.

        Args:
            frame: The end frame.
        N)r0   stop_disconnectrW   s     r   rY   zResembleAITTSService.stop   s6      gl5!!!    	"    848688c                 t   K   t         |   |       d{    | j                          d{    y7 7 w)z`Cancel the Resemble AI TTS service.

        Args:
            frame: The cancel frame.
        N)r0   cancelrZ   rW   s     r   r]   zResembleAITTSService.cancel   s6      gnU###    	$ r[   c                    K   | j                          d{    | j                  r=| j                  s0| j                  | j	                  | j
                              | _        yyy7 Nw)z%Connect to the Resemble AI WebSocket.N)_connect_websocketr7   r9   create_task_receive_task_handler_report_errorrE   s    r   rV   zResembleAITTSService._connect   sW     %%'''??4#5#5!%!1!1$2L2LTM_M_2`!aD $6? 	(s   A'A%AA'c                    K   | j                   r*| j                  | j                          d{    d| _         | j                          d{    y7 $7 w)z*Disconnect from the Resemble AI WebSocket.N)r9   cancel_task_disconnect_websocketrE   s    r   rZ   z ResembleAITTSService._disconnect   sN     ""4#5#5666!%D((*** 7 	+s!   +AAAAAAc                   K   	 | j                   r'| j                   j                  t        j                  u ryt	        j
                  d       dd| j                   i}t        | j                  |       d{   | _         | j                  d       d{    y7 #7 # t        $ rL}| j                  d| |       d{  7   d| _         | j                  d	|        d{  7   Y d}~yd}~ww xY ww)
z.Establish WebSocket connection to Resemble AI.NzConnecting to Resemble AI TTSAuthorizationzBearer )additional_headerson_connectedUnknown error occurred: 	error_msg	exceptionon_connection_error)r7   stater   OPENr   debugr2   websocket_connectr3   _call_event_handler	Exception
push_error)r?   headerses      r   r_   z'ResembleAITTSService._connect_websocket   s     
	J4??#8#8EJJ#FLL89&'$--(ABG$5diiT[$\\DO**>::: ]: 	J//.Fqc,JVW/XXX"DO**+@QCIII	Jsu   C72B C7A B 7B8B BB C7B B 	C4(C/C"C/$C'%C/*C7/C44C7c                    K   	 | j                          d{    | j                  rHt        j                  d       d| j                  _        | j                  j                          d{    d| _        | j                  j                          | j                  j                          | j                  j                          | j                  d       d{    y7 7 u# t        $ r)}| j                  d| |       d{  7   Y d}~d}~ww xY w7 ># d| _        | j                  j                          | j                  j                          | j                  j                          | j                  d       d{  7   w xY ww)z*Close WebSocket connection to Resemble AI.NzDisconnecting from Resemble AIr   rj   rk   on_disconnected)stop_all_metricsr7   r   rq   close_timeoutclosert   ru   r;   clearr>   r:   rs   r?   rw   s     r   re   z*ResembleAITTSService._disconnect_websocket   s=    	>'')))=>01-oo++--- #DO%%'""((*''--/**+<=== * . 	Y//.Fqc,JVW/XXX	Y >	 #DO%%'""((*''--/**+<===s   FC# CAC# +C!,C# 0A)FDFC# !C# #	D,DDDD DD FA*FFFFc                 H    | j                   r| j                   S t        d      )zGet the current WebSocket connection.

        Returns:
            The active WebSocket connection.

        Raises:
            Exception: If websocket is not connected.
        zWebsocket not connected)r7   rt   rE   s    r   _get_websocketz#ResembleAITTSService._get_websocket   s!     ????"122r   
context_idc                 @   K   | j                          d{    y7 w)z)Stop metrics when the bot is interrupted.N)rz   r?   r   s     r   on_audio_context_interruptedz1ResembleAITTSService.on_audio_context_interrupted  s     ##%%%s   c                    K   yw)a  Stop metrics after the Resemble AI context finishes playing.

        No close message is needed: Resemble AI signals completion with an
        ``audio_end`` message (handled in ``_process_messages``), after which
        the server-side context is already closed.
        Nr   r   s     r   on_audio_context_completedz/ResembleAITTSService.on_audio_context_completed  s      	s   c                 <   K   t        j                  |  d       yw)z9Flush any pending audio and finalize the current context.z: flushing audioN)r   tracer   s     r   flush_audioz ResembleAITTSService.flush_audio  s     v-./s   c           
        K   | j                         2 3 d{   }	 t        j                  |      }|s"|j                  d      }|j                  d      }| j                  j                  |t        |            }| j                  |      s{|dk(  r'|j                  dd      }|st        j                  |      }t        |      d	k(  r|| j                  vr&t               | j                  |<   d
| j                  |<   | j                  |   }|j                  |       | j                  j                  |d
      s)t        |      | j                   k  rDd| j                  |<   t        |      | j"                  k\  r| j"                  }	|	dz  d	k7  r|	dz  }	t%        |d|	       }
||	d | j                  |<   | j                  |   }t        |
      d	k(  rot'        |
| j(                  d|      }| j+                  ||       d{    t        |      | j"                  k\  r|j                  di       }|s$|j                  dg       }|j                  dg       }g }t-        ||      D ]/  \  }}|s	t        |      dk\  s|d	   }|j/                  ||f       1 |s| j1                  ||       d{    |dk(  r| j3                          d{    | j                  j                  |t                     }|rVt%        |      }t        |      dz  d	k7  r|dd }|r3t'        || j(                  d|      }| j+                  ||       d{    || j                  v r| j                  |= || j                  v r(| j                  |= || j                  v r| j                  |= | j1                  ddg|       d{    | j5                  |       d{    |dk(  r-|j                  dd      }|j                  dd      }|j                  dd	      }| j	                  d| d| d|        d{    || j                  v r| j                  |= || j                  v r| j                  |= | j7                  t9        |             d{    | j;                          d{    | j	                  t=        |  d | d!| "             d{    |d#v s| j?                          d{    | jA                          d{    tC        jD                  |  d$|        7 # t        j                  $ r" | j	                  d|        d{  7   Y Pw xY w7 d7 7 7  7 7 7 B7 7 7 7 7 z6 yw)%z5Process incoming WebSocket messages from Resemble AI.NzReceived invalid JSON: )rl   typerL   audioaudio_content r   FT   rN   )r   r'   num_channelsr   audio_timestampsgraph_charsgraph_times	audio_end)r   r   )Resetr   error
error_nameUnknownmessagezUnknown errorstatus_codezError: z	 (status z): r   z error: z - r   )i  i  z unknown message type: )#r   rO   loadsJSONDecodeErrorru   getr:   straudio_context_availablebase64	b64decodelenr;   	bytearrayr>   extendr=   r<   bytesr   r'   append_to_audio_contextzipappendadd_word_timestampsstop_ttfb_metricsremove_audio_context
push_framer   rz   r	   re   r_   r   warning)r?   r   rQ   msg_typerL   r   r   audio_bytesbuffer
chunk_sizechunk_to_sendrS   
timestampsr   r   
word_timeschartimes
start_time	remainingr   rl   r   s                          r   _process_messagesz&ResembleAITTSService._process_messages  s    !002 J	K J	K'jj)
 wwvH.J 4488S_UJ //
;7" # <$$..}={#q( T%8%886?kD''
39>D**:6,,Z8 k* --11*eD6{T%>%>> 9=D**:6 &kT%A%AA!%!=!=J!A~*"a
$)&**=$>M6<Z[6ID''
3!00<F=)Q. ,+$($4$4%&#-	E 66z5III% &kT%A%AA* !WW%7<
",.."CK",.."CK "$J'*;'D Be SZ1_).qJ&--tZ.@AB
 ""66z:NNN[(,,... ,,00Y[I %fI9~)Q.$-crN	  0"+(,(8(8)*'1	! #:::uMMM !4!44++J7!7!77..z:!T%@%@@ 77
C..0F/UWabbb//
;;;W$ WW\9=
GGI?	!ggmQ7oo '
|9[MYKX &   
 !4!44++J7!7!77..z:ooo&LMMM++---ooj$x
|SVW`Va7b&cddd *,4466611333$'>xjIJUJ	K '' oo2I'0SoTTT| J  O /  N c; N-d
 73Q 3sN  VVT,VVT/GVU'V!V7:V2VVV4U*5VU-A;VU0A,V=U3>VU6A V7U98AVU<V(U>)*VV VV1V2V	V
"V,V/+U$UU$ V#U$$V*V-V0V3V6V9V<V>V VVVVc                   K   	 	 | j                          d{    7 # t        $ rZ}| j                  d| |       d{  7   t        j                  |  d       | j                          d{  7   Y d}~ad}~ww xY ww)z2Main loop for receiving messages from Resemble AI.NzError in receive loop: rk   z* Resemble AI connection lost, reconnecting)r   rt   ru   r   rq   r_   r~   s     r   _receive_messagesz&ResembleAITTSService._receive_messages  s|     0,,... . 0oo2I!0MYZo[[[v%OPQ--///	0sN   B    B  	BA>A0A>3A64A>9B>BBc                  K   t        j                  |  d| d       	 | j                  r&| j                  j                  t        j
                  u r| j                          d{    | j                  |      s@| j                  |       d{    | j                          d{    t        |       || j                  | j                  <   | j                  |      }	 | j                         j                  |       d{    | j!                  |       d{    d y7 7 7 7 )7 # t"        $ r]}t%        d|        t'        |       | j)                          d{  7   | j                          d{  7   Y d}~yd}~ww xY w# t"        $ r}t%        d|        Y d}~yd}~ww xY ww)a  Generate speech from text using Resemble AI's streaming API.

        Args:
            text: The text to synthesize into speech.
            context_id: Unique identifier for this TTS context.

        Yields:
            Frame: Audio frames containing the synthesized speech.
        z: Generating TTS []Nr   )rG   rj   r   )r   rq   r7   ro   r   CLOSEDrV   r   create_audio_contextstart_ttfb_metricsr   r:   r8   rR   r   sendstart_tts_usage_metricsrt   r	   r   rZ   )r?   rG   r   rQ   rw   s        r   run_ttszResembleAITTSService.run_tts  s     	v/vQ78	C??doo&;&;u||&Kmmo%%//
;//
;;;--///%<< EOD''(@(@A//t/,C))+00555224888 J+ & </ 68  )A!'EFF%<<&&(((mmo%%  	C%=aS#ABBB	Cs   GAF $D,%)F D.F &D0'>F &"D6 D2	D6 !D4"D6 &F +G,F .F 0F 2D6 4D6 6	F?4F3E64FFFF GFF 	G(F?:G?GG)r   )N)#r   r   r   r   r   Settings__annotations__r   r   intr1   boolrF   rR   r   rU   r   rY   r   r]   rV   rZ   r_   re   r   r   r   r   r   r   r   r   r
   r   __classcell__)rB   s   @r   r   r   .   s^    %H$$ #'?#+',%*48O5 O5 3-	O5
 O5 C=O5  }O5 c]O5 01O5bd s C . ! !!+ !b+J>&3&S &3 0HSM 0LK\	0 %C# %C3 %C>%QU+;V %C %Cr   r   )%r   r   rO   dataclassesr   typingr   r   logurur   pipecat.frames.framesr   r   r	   r
   r   r   r   r   pipecat.services.settingsr   r   pipecat.services.tts_servicer   (pipecat.utils.tracing.service_decoratorsr   websockets.asyncio.clientr   rr   websockets.protocolr   ModuleNotFoundErrorrw   r   rt   r   r   r   r   r   <module>r      s    :   ! + 	 	 	 J < ?,F) 	K 	 	fC. fC  ,FLL;qc"#FLLab
&qc*
++,s   A0 0B,52B''B,