U
    Hi2                     @   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eedddZeee	d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   zVddl }ddl}|t| d}|| }| }|  |	||
 }|W S  tk
r } z|  W Y S d}~X Y nX dS )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BytesIOZ
readframesZ
getnframesZgetframeratecloselin2ulawZgetsampwidth	Exception)r   r   r   Zwav_ioframesZsample_rateZ
mulaw_dataer   r   r   wav_to_mulaw!   s    z!AudioFormatConverter.wav_to_mulaw)r   r   c                 C   s,   zt |  W dS  tk
r&   Y dS X dS )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
        TFN)base64	b64decoder#   )r   r   r   r   validate_audio_payload?   s
    
z+AudioFormatConverter.validate_audio_payloadN)r   r   r   r   ZMCUBE_FORMATstaticmethodbytesr&   r   boolr)   r   r   r   r   r      s   r   c                   @   sz   e Zd ZdZdd ZeddddZeddd	d
Ze	e dddZ
ddddZeedddZe	e d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__N)r	   r   c                 C   s
   || _ dS )z#Update the current audio timestamp.N)r.   )r2   r	   r   r   r   update_timestamp\   s    z#AudioTimingManager.update_timestamp)r
   r   c                 C   s0   | j | _|| _tjr,td| d| j  d 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)r.   r/   r0   r   SHOW_TIMING_MATHprintr2   r
   r   r   r   start_response_tracking`   s    z*AudioTimingManager.start_response_trackingr   c                 C   sD   | j dk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 = r5   )r/   r.   r   r6   r7   )r2   durationr   r   r   calculate_response_durationm   s    
 z.AudioTimingManager.calculate_response_durationc                 C   s   d| _ d| _dS )zReset response tracking state.N)r/   r0   r1   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)
        r0   r8   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.r>   r1   r   r   r   get_current_item_id   s    z&AudioTimingManager.get_current_item_id)r   r   r   r   r3   r   r4   r   r9   r   r<   r=   r,   r?   r@   r   r   r   r   r-   Q   s   r-   c                   @   s   e Zd ZdZdd ZdeddddZee d	d
dZdd	ddZ	e
d	ddZeeddddZdd	ddZed	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_bufferr1   r   r   r   r3      s    zAudioBufferManager.__init__responsePartN)	mark_namer   c                 C   s   | j | dS )z
        Add a synchronization mark to the queue.
        
        Args:
            mark_name: Name of the mark for identification
        N)rC   append)r2   rF   r   r   r   add_mark   s    zAudioBufferManager.add_markr:   c                 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)rC   popr1   r   r   r   remove_mark   s    zAudioBufferManager.remove_markc                 C   s   | j   dS )zClear all marks from the queue.N)rC   clearr1   r   r   r   clear_marks   s    zAudioBufferManager.clear_marksc                 C   s   t | jdkS )z.Check if there are pending marks in the queue.r   )lenrC   r1   r   r   r   has_pending_marks   s    z$AudioBufferManager.has_pending_marks)chunkmetadatar   c                 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
        )rO   rP   r	   N)rD   rG   r	   )r2   rO   rP   r   r   r   add_audio_chunk   s
    z"AudioBufferManager.add_audio_chunkc                 C   s   | j   dS )zClear the audio buffer.N)rD   rK   r1   r   r   r   clear_audio_buffer   s    z%AudioBufferManager.clear_audio_bufferc                 C   s
   t | jS )z)Get the current size of the audio buffer.)rM   rD   r1   r   r   r   get_buffer_size   s    z"AudioBufferManager.get_buffer_size)rE   )r   r   r   r   r3   r   rH   r   rJ   rL   r,   rN   r   rQ   rR   r   rS   r   r   r   r   rA      s   		rA   c                   @   s   e Zd ZdZdd Zeeeeee	f  dddZ
eeeee	f ddd	Zeeee	f d
ddZeddddZee dddZedddZddddZee dddZeee dddZeee d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 rB   )r   Zformat_converterr-   timing_managerrA   buffer_managerr1   r   r   r   r3      s    zAudioService.__init__)audio_bytesr   r   c                 C   s  |st d| d dS |s8t dt| d dS zddlm} ddl}d}z|j||dd	}d}W n tk
rD } zz|j||d
d	}d
}W n tk
r2 } zrz|||}d}W nT tk
r  }	 z4t 	d| d| d|	  W Y W Y W Y *W dS d}	~	X Y nX W 5 d}~X Y nX W 5 d}~X Y nX 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k
r } z6ddl}t 	d|  t 	d|   W Y dS d}~X Y nX dS )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   )AudioSegmentZmp3)formatZwavzauto-detectedu3   ❌ Failed to load audio in any format. MP3 error: z, WAV error: z, Auto error:       zutf-8Z	playAudior   Zaudio_i  )contentTypeZ
sampleRater   name)eventmediau&   ❌ Error processing raw audio bytes: u   ❌ Traceback: )r   warningrM   ZpydubrY   r   	from_filer    r#   errorr   MCUBE_SAMPLE_RATEZset_frame_rateZset_channelsZset_sample_widthraw_datar   r"   timer'   	b64encodedecoder   	traceback
format_exc)r2   rW   r   rY   r   Zoriginal_formatZ	pcm_audioZ	mp3_errorZ	wav_error
auto_errorZtarget_sample_rateZ	pcm_bytesr   Zmulaw_audiorf   Zaudio_base64Zaudio_messager%   ri   r   r   r   process_raw_audio_bytes   sX    J
z$AudioService.process_raw_audio_bytes)r   r^   r   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)r_   streamIdr^   )rV   rH   )r2   r   r^   r   r   r   create_checkpoint_message*  s
    z&AudioService.create_checkpoint_message)r   r   c                 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
        Z
clearAudio)r_   rn   )rV   rR   )r2   r   r   r   r   create_clear_message<  s    

z!AudioService.create_clear_messageN)r^   r   c                 C   s&   | j  }tjr"|r"td|  dS )z'Handle a playedStream event from Mcube.zProcessed playedStream: N)rV   rJ   r   r6   r7   )r2   r^   Zremoved_markr   r   r   handle_played_stream_eventL  s    

z'AudioService.handle_played_stream_eventr:   c                 C   s
   | j  S )z
        Calculate timing for audio interruption.
        
        Returns:
            Elapsed time for truncation, or None if no response is tracked
        )rU   r<   r1   r   r   r   calculate_interruption_timingR  s    z*AudioService.calculate_interruption_timingc                 C   s"   | j jdk	o | j o | j jdk	S )z
        Determine if an interruption should be processed.
        
        Returns:
            True if interruption should be handled
        N)rU   r0   rV   rN   r/   r1   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)rU   r=   rV   rL   r1   r   r   r   reset_interruption_statef  s    
z%AudioService.reset_interruption_statec                 C   s
   | j  S )z/Get the ID of the currently tracked audio item.)rU   r@   r1   r   r   r   r@   k  s    z AudioService.get_current_item_id)datar   c              	   C   s.   z|d d W S  t tfk
r(   Y dS X dS )z&Extract audio payload from Mcube data.r`   r   N)KeyError	TypeErrorr2   ru   r   r   r   _extract_mcube_payloado  s    z#AudioService._extract_mcube_payloadc              
   C   s4   zt |d d W S  tttfk
r.   Y dS X dS )z"Extract timestamp from Mcube data.r`   r	   N)r   rv   rw   
ValueErrorrx   r   r   r   _extract_mcube_timestampv  s    z%AudioService._extract_mcube_timestamp)r   r   r   r   r3   r+   r   r   r   r   rl   ro   rp   rq   r   rr   r,   rs   rt   r@   dictry   r{   r   r   r   r   rT      s   N	rT   )r'   r   typingr   r   r   dataclassesr   configr   services.log_utilsr   r   r   r-   rA   rT   r   r   r   r   <module>   s   ;C;