o
    i2                     @   s   d dl Z d dlZd dlmZmZmZ d dlmZ d dlm	Z	 d dl
mZ eG dd dZG dd	 d	ZG d
d dZG dd dZG dd dZdS )    N)OptionalDictAny)	dataclass)Config)Logc                   @   sb   e Zd ZU dZdZee ed< dZee	 ed< dZ
ee	 ed< dZee	 ed< dZee	 ed< dS )AudioMetadataz
    Represents metadata for a single audio chunk, including timing, item, and stream information.
    Used to track and annotate audio data as it flows through the processing pipeline.
    N	timestampitem_id	stream_idpayloadformat_type)__name__
__module____qualname____doc__r	   r   int__annotations__r
   strr   r   r    r   r   ;/var/www/html/live_calls/homebook/services/audio_service.pyr   	   s   
 r   c                   @   s@   e Zd ZdZdZededefddZedede	fdd	Z
d
S )AudioFormatConverterz
    Converts audio payloads for Mcube format.
    Ensures compatibility and provides a single place to update format logic if requirements change.
    audio/x-mulaw	wav_bytesreturnc              
   C   s   z+ddl }ddl}|t| d}|| }| }|  |	||
 }|W S  ty? } z| W  Y d}~S d}~ww )u   
        Convert WAV audio bytes to μ-law format for MCube.
        
        Args:
            wav_bytes: Raw WAV audio data
            
        Returns:
            μ-law encoded audio bytes
        r   Nrb)waveaudioopopenioBytesIO
readframes
getnframesgetframeratecloselin2ulawgetsampwidth	Exception)r   r   r   wav_ioframessample_rate
mulaw_dataer   r   r   wav_to_mulaw!   s   z!AudioFormatConverter.wav_to_mulawr   c                 C   s&   zt |  W dS  ty   Y dS w )z
        Validate that an audio payload is properly formatted base64.
        
        Args:
            payload: Audio payload to validate
            
        Returns:
            True if payload is valid base64, False otherwise
        TF)base64	b64decoder'   )r   r   r   r   validate_audio_payload?   s   
z+AudioFormatConverter.validate_audio_payloadN)r   r   r   r   MCUBE_FORMATstaticmethodbytesr-   r   boolr0   r   r   r   r   r      s    r   c                   @   s|   e Zd ZdZdd ZdeddfddZd	eddfd
dZde	e fddZ
dddZd	edefddZde	e fddZdS )AudioTimingManagerz
    Tracks and manages audio timing for responses and interruptions.
    Responsible for calculating durations, tracking the start of responses, and supporting precise interruption logic.
    c                 C   s   d| _ d | _d | _d S )Nr   )current_timestampresponse_start_timestamplast_item_idselfr   r   r   __init__W   s   
zAudioTimingManager.__init__r	   r   Nc                 C   s
   || _ dS )z#Update the current audio timestamp.N)r6   )r:   r	   r   r   r   update_timestamp\      
z#AudioTimingManager.update_timestampr
   c                 C   s4   | j | _|| _tjrtd| d| j  d dS dS )z
        Start tracking a new response for timing calculations.
        
        Args:
            item_id: ID of the response item to track
        z$Starting response tracking for item z at msN)r6   r7   r8   r   SHOW_TIMING_MATHprintr:   r
   r   r   r   start_response_tracking`   s
   z*AudioTimingManager.start_response_trackingc                 C   sD   | j du rdS | j| j  }tjr td| j d| j  d| d |S )z
        Calculate the duration of the current response.
        
        Returns:
            Duration in milliseconds, or None if no response is being tracked
        NzResponse duration: z - z = r>   )r7   r6   r   r?   r@   )r:   durationr   r   r   calculate_response_durationm   s   
 z.AudioTimingManager.calculate_response_durationc                 C   s   d| _ d| _dS )zReset response tracking state.N)r7   r8   r9   r   r   r   reset_response_tracking~   s   
z*AudioTimingManager.reset_response_trackingc                 C   s
   || j kS )z
        Determine if a new item should start being tracked.
        
        Args:
            item_id: ID of the item to check
            
        Returns:
            True if item should be tracked (is different from current)
        r8   rA   r   r   r   should_item_be_tracked   s   

z)AudioTimingManager.should_item_be_trackedc                 C   s   | j S )z)Get the ID of the currently tracked item.rF   r9   r   r   r   get_current_item_id   s   z&AudioTimingManager.get_current_item_idr   N)r   r   r   r   r;   r   r<   r   rB   r   rD   rE   r4   rG   rH   r   r   r   r   r5   Q   s    
r5   c                   @   s   e Zd ZdZdd Zddeddfdd	Zdee fd
dZdddZ	de
fddZdededdfddZdddZdefddZdS )AudioBufferManagerz
    Handles buffering of audio chunks and synchronization marks.
    Maintains queues for both audio data and marks, supporting smooth streaming and interruption handling.
    c                 C   s   g | _ g | _d S N)
mark_queueaudio_bufferr9   r   r   r   r;      s   
zAudioBufferManager.__init__responsePart	mark_namer   Nc                 C   s   | j | dS )z
        Add a synchronization mark to the queue.
        
        Args:
            mark_name: Name of the mark for identification
        N)rL   append)r:   rO   r   r   r   add_mark   s   zAudioBufferManager.add_markc                 C   s   | j r	| j dS dS )z
        Remove and return the oldest mark from the queue.
        
        Returns:
            The removed mark name, or None if queue is empty
        r   N)rL   popr9   r   r   r   remove_mark   s   zAudioBufferManager.remove_markc                 C      | j   dS )zClear all marks from the queue.N)rL   clearr9   r   r   r   clear_marks      zAudioBufferManager.clear_marksc                 C   s   t | jdkS )z.Check if there are pending marks in the queue.r   )lenrL   r9   r   r   r   has_pending_marks   rW   z$AudioBufferManager.has_pending_markschunkmetadatac                 C   s   | j |||jd dS )z
        Add an audio chunk to the buffer with metadata.
        
        Args:
            chunk: Audio data chunk
            metadata: Associated metadata
        )rZ   r[   r	   N)rM   rP   r	   )r:   rZ   r[   r   r   r   add_audio_chunk   s
   z"AudioBufferManager.add_audio_chunkc                 C   rT   )zClear the audio buffer.N)rM   rU   r9   r   r   r   clear_audio_buffer   rW   z%AudioBufferManager.clear_audio_bufferc                 C   s
   t | jS )z)Get the current size of the audio buffer.)rX   rM   r9   r   r   r   get_buffer_size   r=   z"AudioBufferManager.get_buffer_size)rN   rI   )r   r   r   r   r;   r   rQ   r   rS   rV   r4   rY   r   r\   r]   r   r^   r   r   r   r   rJ      s    	
	
rJ   c                	   @   s   e Zd ZdZdd Zdededeeee	f  fddZ
ded	edeee	f fd
dZdedeee	f fddZd	eddfddZdee fddZdefddZdddZdee fddZdedee fddZdedee fddZdS )AudioServicez
    Coordinates all audio processing operations for the application.
    Uses the format converter, timing manager, and buffer manager to process audio,
    manage synchronization, and handle interruptions for Service Type 4 (ElevenLabs).
    c                 C   s   t  | _t | _t | _d S rK   )r   format_converterr5   timing_managerrJ   buffer_managerr9   r   r   r   r;      s   zAudioService.__init__audio_bytesr   r   c                 C   s  |st d| d dS |st dt| d dS zddlm} ddl}d}z|j||dd	}d}W nl ty } z`z|j||d
d	}d
}W nK ty } z?z|||}d}W n, ty }	 z t 	d| d| d|	  W Y d}	~	W Y d}~W Y d}~W dS d}	~	ww W Y d}~nd}~ww W Y d}~nd}~ww t
j}
||
dd}|j}ddl}||d}ddl}t|d}ddt
j|dt| d  dd}|W S  ty } zddl}t 	d|  t 	d|   W Y d}~dS d}~ww )a#  
        Process raw audio bytes (e.g., from Sarvam TTS) for MCube.
        
        Args:
            audio_bytes: Raw audio data in bytes
            stream_id: Mcube stream identifier
            
        Returns:
            Processed audio message for Mcube, or None if invalid
        u:   ⚠️ No audio bytes provided for processing (stream_id: )Nu@   ⚠️ No stream_id provided for audio processing (audio_bytes: z bytes)r   )AudioSegmentmp3)formatwavzauto-detectedu3   ❌ Failed to load audio in any format. MP3 error: z, WAV error: z, Auto error:       zutf-8	playAudior   audio_i  )contentType
sampleRater   name)eventmediau&   ❌ Error processing raw audio bytes: u   ❌ Traceback: )r   warningrX   pydubre   r   	from_filer    r'   errorr   MCUBE_SAMPLE_RATEset_frame_rateset_channelsset_sample_widthraw_datar   r%   timer.   	b64encodedecoder   	traceback
format_exc)r:   rc   r   re   r   original_format	pcm_audio	mp3_error	wav_error
auto_errortarget_sample_rate	pcm_bytesr   mulaw_audior{   audio_base64audio_messager,   r~   r   r   r   process_raw_audio_bytes   sj   $
z$AudioService.process_raw_audio_bytesro   c                 C   s   | j | d||dS )z
        Create a checkpoint message for audio synchronization.
        
        Args:
            stream_id: Mcube stream identifier
            name: Name of the audio segment
            
        Returns:
            Mcube checkpoint message
        
checkpoint)rp   streamIdro   )rb   rQ   )r:   r   ro   r   r   r   create_checkpoint_message*  s
   z&AudioService.create_checkpoint_messagec                 C   s   | j   d|dS )z
        Create a clear message to clear audio buffer.
        
        Args:
            stream_id: Mcube stream identifier
            
        Returns:
            Mcube clearAudio message
        
clearAudio)rp   r   )rb   r]   )r:   r   r   r   r   create_clear_message<  s   

z!AudioService.create_clear_messageNc                 C   s.   | j  }tjr|rtd|  dS dS dS )z'Handle a playedStream event from Mcube.zProcessed playedStream: N)rb   rS   r   r?   r@   )r:   ro   removed_markr   r   r   handle_played_stream_eventL  s   

z'AudioService.handle_played_stream_eventc                 C   
   | j  S )z
        Calculate timing for audio interruption.
        
        Returns:
            Elapsed time for truncation, or None if no response is tracked
        )ra   rD   r9   r   r   r   calculate_interruption_timingR  s   
z*AudioService.calculate_interruption_timingc                 C   s"   | j jduo| j o| j jduS )z
        Determine if an interruption should be processed.
        
        Returns:
            True if interruption should be handled
        N)ra   r8   rb   rY   r7   r9   r   r   r   should_handle_interruption[  s
   
z'AudioService.should_handle_interruptionc                 C   s   | j   | j  dS )z%Reset all interruption-related state.N)ra   rE   rb   rV   r9   r   r   r   reset_interruption_statef  s   
z%AudioService.reset_interruption_statec                 C   r   )z/Get the ID of the currently tracked audio item.)ra   rH   r9   r   r   r   rH   k  r=   z AudioService.get_current_item_iddatac              	   C   s(   z|d d W S  t tfy   Y dS w )z&Extract audio payload from Mcube data.rq   r   N)KeyError	TypeErrorr:   r   r   r   r   _extract_mcube_payloado  s
   z#AudioService._extract_mcube_payloadc              
   C   s.   z	t |d d W S  tttfy   Y dS w )z"Extract timestamp from Mcube data.rq   r	   N)r   r   r   
ValueErrorr   r   r   r   _extract_mcube_timestampv  s
   z%AudioService._extract_mcube_timestamprI   )r   r   r   r   r;   r3   r   r   r   r   r   r   r   r   r   r   r4   r   r   rH   dictr   r   r   r   r   r   r_      s    "N	
r_   )r.   r   typingr   r   r   dataclassesr   configr   services.log_utilsr   r   r   r5   rJ   r_   r   r   r   r   <module>   s    ;C;