o
    iF                  
   @   s  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 ddlmZ dd	lmZ zdd
lmZ ddlmZ W n  eyr Z ze	 de  e	 d e!de dZ[ww eG dd deZ"G dd deZ#dS )z3Resemble AI text-to-speech service implementations.    N)	dataclass)AsyncGeneratorOptional)logger)CancelFrameEndFrame
ErrorFrameFrame
StartFrameTTSAudioRawFrameTTSStartedFrameTTSStoppedFrame)TTSSettings)WebsocketTTSService)
traced_tts)connect)StatezException: zNIn order to use Resemble AI, you need to `pip install pipecat-ai[resembleai]`.zMissing module: c                   @   s   e Zd ZdZdS )ResembleAITTSSettingsz"Settings for ResembleAITTSService.N)__name__
__module____qualname____doc__ r   r   S/home/ubuntu/.local/lib/python3.10/site-packages/pipecat/services/resembleai/tts.pyr   '   s    r   c                       sR  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dZ
defddZd7dedefddZdef fddZdef fddZdef fddZd d! Zd"d# Zd$d% Zd&d' Zd(d) Zd*efd+d,Zd*efd-d.Zd8d*ee fd/d0Zd1d2 Zd3d4 Zeded*edee df fd5d6Z!  Z"S )9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          
         s   | j dddd}	|dur| dd ||	_|dur|	| t jd|d|	d| || _|| _|p4d| _|p9d| _	d	| _
d| _d	| _d| _i | _i | _d
| _d| _i | _dS )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=ResembleAITTSService.Settings(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   )Settings"_warn_init_param_moved_to_settingsr&   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__r   r   r-   9   s<   



zResembleAITTSService.__init__returnc                 C   s   dS )zCheck if this service can generate processing metrics.

        Returns:
            True, as Resemble AI service supports metrics generation.
        Tr   r;   r   r   r   can_generate_metrics   s   z)ResembleAITTSService.can_generate_metrics textc              	   C   s:   | j j|d| j| j| j| jdd}|  jd7  _t|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&   r4   r1   r2   r0   jsondumps)r;   rD   msgr   r   r   
_build_msg   s   

zResembleAITTSService._build_msgframec                    s.   t  |I dH  | j| _|  I dH  dS )zStart the Resemble AI TTS service.

        Args:
            frame: The start frame containing initialization parameters.
        N)r,   startr"   r2   _connectr;   rO   r>   r   r   rP      s   zResembleAITTSService.startc                    &   t  |I dH  |  I dH  dS )z[Stop the Resemble AI TTS service.

        Args:
            frame: The end frame.
        N)r,   stop_disconnectrR   r>   r   r   rT         zResembleAITTSService.stopc                    rS   )z`Cancel the Resemble AI TTS service.

        Args:
            frame: The cancel frame.
        N)r,   cancelrU   rR   r>   r   r   rW      rV   zResembleAITTSService.cancelc                    s<   |   I dH  | jr| js| | | j| _dS dS dS )z%Connect to the Resemble AI WebSocket.N)_connect_websocketr3   r5   create_task_receive_task_handler_report_errorrA   r   r   r   rQ      s
   zResembleAITTSService._connectc                    s2   | j r| | j I dH  d| _ |  I dH  dS )z*Disconnect from the Resemble AI WebSocket.N)r5   cancel_task_disconnect_websocketrA   r   r   r   rU      s
   z ResembleAITTSService._disconnectc              
      s   z0| j r| j jtju rW dS td dd| j i}t| j|dI dH | _ | 	dI dH  W dS  t
y^ } z!| jd| |dI dH  d| _ | 	d	| I dH  W Y d}~dS d}~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)r3   stater   OPENr   debugr.   websocket_connectr/   _call_event_handler	Exception
push_error)r;   headerser   r   r   rX      s   
"z'ResembleAITTSService._connect_websocketc              
      s(  zwz|   I dH  | jrtd d| j_| j I dH  W n ty> } z| jd| |dI dH  W Y d}~n"d}~ww W d| _| j	  | j
	  | j	  | dI dH  dS W d| _| j	  | j
	  | j	  | dI dH  dS d| _| j	  | j
	  | j	  | dI dH  w )z*Close WebSocket connection to Resemble AI.NzDisconnecting from Resemble AIr   ra   rb   on_disconnected)stop_all_metricsr3   r   rh   close_timeoutcloserk   rl   r7   clearr:   r6   rj   r;   rn   r   r   r   r]      s:   
&








z*ResembleAITTSService._disconnect_websocketc                 C   s   | 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)r3   rk   rA   r   r   r   _get_websocket   s   	z#ResembleAITTSService._get_websocket
context_idc                    s   |   I dH  dS )z)Stop metrics when the bot is interrupted.N)rp   r;   rv   r   r   r   on_audio_context_interrupted  s   z1ResembleAITTSService.on_audio_context_interruptedc                    s   dS )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   rw   r   r   r   on_audio_context_completed  s   z/ResembleAITTSService.on_audio_context_completedc                    s   t |  d dS )z9Flush any pending audio and finalize the current context.z: flushing audioN)r   tracerw   r   r   r   flush_audio  s   z ResembleAITTSService.flush_audioc              	      s   |   2 z3 dH W }zt|}W n tjy)   | jd| dI dH  Y qw |s-q|d}|d}| j|t|}| |sFq|dkr|dd}|sTqt	
|}t|d	kr`q|| jvrpt | j|< d
| j|< | j| }|| | j|d
st|| jk rqd| j|< t|| jkr| j}	|	d d	kr|	d8 }	t|d|	 }
||	d | j|< | j| }t|
d	krqt|
| jd|d}| ||I dH  t|| jks|di }|r|dg }|dg }g }t||D ]\}}|rt|dkr|d	 }|||f q|r| ||I dH  q|dkr|  I dH  | j|t }|rZt|}t|d d	krE|dd }|rZt|| jd|d}| ||I dH  || jv rd| j|= || jv rx| j|= || jv rx| j|= | ddg|I dH  | |I dH  q|dkr|dd}|dd}|dd	}| jd| d| d| dI dH  || jv r| j|= || jv r| j|= | t|dI dH  |  I dH  | t|  d | d!| d"I dH  |d#v r|  I dH  |   I dH  qt!"|  d$|  q6 dS )%z5Process incoming WebSocket messages from Resemble AI.NzReceived invalid JSON: )rc   typerH   audioaudio_contentrC   r   FT   rJ   )r}   r"   num_channelsrv   audio_timestampsgraph_charsgraph_times	audio_end)r   r   )Resetr   error
error_nameUnknownmessagezUnknown errorstatus_codezError: z	 (status z): rv   z error: z - r   )i  i  z unknown message type: )#ru   rK   loadsJSONDecodeErrorrl   getr6   straudio_context_availablebase64	b64decodelenr7   	bytearrayr:   extendr9   r8   bytesr   r"   append_to_audio_contextzipappendadd_word_timestampsstop_ttfb_metricsremove_audio_context
push_framer   rp   r   r]   rX   r   warning)r;   r   rM   msg_typerH   rv   r~   audio_bytesbuffer
chunk_sizechunk_to_sendrO   
timestampsr   r   
word_timeschartimes
start_time	remainingr   rc   r   r   r   r   _process_messages  s   












&
 z&ResembleAITTSService._process_messagesc              
      sv   	 z	|   I dH  W n. ty9 } z"| jd| |dI dH  t|  d |  I dH  W Y d}~nd}~ww q)z2Main loop for receiving messages from Resemble AI.TNzError in receive loop: rb   z* Resemble AI connection lost, reconnecting)r   rk   rl   r   rh   rX   rt   r   r   r   _receive_messages  s   z&ResembleAITTSService._receive_messagesc              
   C  sX  t |  d| d z| jr| jjtju r|  I dH  | |s8| |I dH  | 	 I dH  t
|dV  || j| j< | j|d}z|  |I dH  | |I dH  W n1 ty } z%td| dV  t|dV  |  I dH  |  I dH  W Y d}~W dS d}~ww dV  W dS  ty } ztd| dV  W Y d}~dS d}~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   )rD   ra   r   )r   rh   r3   rf   r   CLOSEDrQ   r   create_audio_contextstart_ttfb_metricsr   r6   r4   rN   ru   sendstart_tts_usage_metricsrk   r   r   rU   )r;   rD   rv   rM   rn   r   r   r   run_tts  s6   
 zResembleAITTSService.run_tts)rC   )N)#r   r   r   r   r   r)   __annotations__r   r   intr-   boolrB   rN   r
   rP   r   rT   r   rW   rQ   rU   rX   r]   ru   rx   ry   r{   r   r   r   r   r	   r   __classcell__r   r   r>   r   r   .   sV   
 	Q
			 (r   )$r   r   rK   dataclassesr   typingr   r   logurur   pipecat.frames.framesr   r   r   r	   r
   r   r   r   pipecat.services.settingsr   pipecat.services.tts_servicer   (pipecat.utils.tracing.service_decoratorsr   websockets.asyncio.clientr   ri   websockets.protocolr   ModuleNotFoundErrorrn   r   rk   r   r   r   r   r   r   <module>   s,   (

