o
    i B                     @  sZ  d dl m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	m
Z
 d dlmZmZ d dlmZmZ d dlZd dlmZ ddlmZ dd	lmZ dd
l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' Z(e)e(j* G dd dej+Z,e	ej- e.B e,B Z/G dd deZ0dZ1G dd dZ2G dd dZ3d ddZ4dS )!    )annotationsN)AsyncGeneratorAsyncIterator	Generator)as_filefiles)Any
NamedTuple)rtc   )get_job_contextlogger)	NOT_GIVEN
NotGivenOr)is_givenlog_exceptions)cancel_and_waitaudio_frames_from_file   )AgentSession)AgentStateChangedEventc                   @  s2   e Zd ZdZdZdZdZdZdZdZ	dd
dZ
dS )BuiltinAudioClipzcity-ambience.oggzforest-ambience.oggzoffice-ambience.oggzcrowded-room.oggzkeyboard-typing.oggzkeyboard-typing2.oggzhold_music.oggreturnstrc                 C  s    t d| j }ttt|S )Nzlivekit.agents.resources)r   valuer   _resource_stackenter_contextr   )self	file_path r!   g/var/www/html/livekit_bhavya/venv/lib/python3.10/site-packages/livekit/agents/voice/background_audio.pypath&      zBuiltinAudioClip.pathN)r   r   )__name__
__module____qualname__CITY_AMBIENCEFOREST_AMBIENCEOFFICE_AMBIENCECROWDED_ROOMKEYBOARD_TYPINGKEYBOARD_TYPING2
HOLD_MUSICr#   r!   r!   r!   r"   r      s    r   c                   @  s2   e Zd ZU dZded< dZded< dZded< dS )	AudioConfigz
    Definition for the audio to be played in the background

    Args:
        volume: The volume of the audio (0.0-1.0)
        probability: The probability of the audio being played, when multiple
            AudioConfigs are provided (0.0-1.0)
    AudioSourcesource      ?floatvolumeprobabilityN)r%   r&   r'   __doc____annotations__r4   r5   r!   r!   r!   r"   r/   .   s
   
 	r/   i  c                   @  s   e Zd ZeedddBd
dZdCddZdDddZdEddZdddFd!d"Zeed#dGd*d+Z	dHd,d-Z
dHd.d/ZdId2d3Zeed4dJd9d:Zeed4dHd;d<ZdHd=d>Zeed4dHd?d@ZdAS )KBackgroundAudioPlayer   )ambient_soundthinking_soundstream_timeout_msr:   @NotGivenOr[AudioSource | AudioConfig | list[AudioConfig] | None]r;   r<   intr   Nonec                C  s~   t |r|nd| _t |r|nd| _tjddtd| _tjdddd|d| _d| _	t
 | _d| _d| _g | _d| _d| _dS )u  
        Initializes the BackgroundAudio component with optional ambient and thinking sounds.

        This component creates and publishes a continuous audio track to a LiveKit room while managing
        the playback of ambient and agent “thinking” sounds. It supports three types of audio sources:
        - A BuiltinAudioClip enum value, which will use a pre-defined sound from the package resources
        - A file path (string) pointing to an audio file, which can be looped.
        - An AsyncIterator that yields rtc.AudioFrame

        When a list (or AudioConfig) is supplied, the component considers each sound’s volume and probability:
        - The probability value determines the chance that a particular sound is selected for playback.
        - A total probability below 1.0 means there is a chance no sound will be selected (resulting in silence).

        Args:
            ambient_sound (NotGivenOr[Union[AudioSource, AudioConfig, List[AudioConfig], None]], optional):
                The ambient sound to be played continuously. For file paths, the sound will be looped.
                For AsyncIterator sources, ensure the iterator is infinite or looped.

            thinking_sound (NotGivenOr[Union[AudioSource, AudioConfig, List[AudioConfig], None]], optional):
                The sound to be played when the associated agent enters a “thinking” state. This can be a single
                sound source or a list of AudioConfig objects (with volume and probability settings).

        Ni  r   )queue_size_msi  )	blocksizecapacityr<   )r   _ambient_sound_thinking_soundr
   r0   _AUDIO_SOURCE_BUFFER_MS_audio_source
AudioMixer_audio_mixerpublicationasyncioLock_lock_republish_task_mixer_atask_play_tasks_ambient_handle_thinking_handle)r   r:   r;   r<   r!   r!   r"   __init__E   s   !


zBackgroundAudioPlayer.__init__soundslist[AudioConfig]AudioConfig | Nonec                 C  s   t dd |D }|dkrdS |dk rt |krdS |dkr!dn|}t t|d }d}|D ]}|jdkr8q0|j| }||7 }||krI|  S q0|d S )z
        Selects a sound from a list of BackgroundSound based on their probabilities.
        Returns None if no sound is selected (when sum of probabilities < 1.0).
        c                 s  s    | ]}|j V  qd S N)r5   ).0soundr!   r!   r"   	<genexpr>}   s    z@BackgroundAudioPlayer._select_sound_from_list.<locals>.<genexpr>r   Nr2   g        )sumrandomminr5   )r   rS   total_probabilitynormalize_factorr
cumulativerX   	norm_probr!   r!   r"   _select_sound_from_listx   s"   

z-BackgroundAudioPlayer._select_sound_from_listr1   4AudioSource | AudioConfig | list[AudioConfig] | None tuple[AudioSource, float] | Nonec                 C  st   |d u rd S t |tr| |dfS t |tr(| |}|d u r"d S |j|jfS t |tr6| |j|jfS |dfS )Nr2   )
isinstancer   _normalize_builtin_audiolistrc   r1   r4   r/   )r   r1   selectedr!   r!   r"   _normalize_sound_source   s   



z-BackgroundAudioPlayer._normalize_sound_sourcer0   #AsyncIterator[rtc.AudioFrame] | strc                 C  s   t |tr	| S |S rV   )rf   r   r#   )r   r1   r!   r!   r"   rg      s   
z.BackgroundAudioPlayer._normalize_builtin_audioFloopaudio-AudioSource | AudioConfig | list[AudioConfig]rm   bool
PlayHandlec                  s   j std|}|du rt      S |\}}|r(t|tr(tdt  t	
 |||fdd  fdd j  S )a  
        Plays an audio once or in a loop.

        Args:
            audio (Union[AudioSource, AudioConfig, List[AudioConfig]]):
                The audio to play. Can be:
                - A string pointing to a file path
                - An AsyncIterator that yields `rtc.AudioFrame`
                - An AudioConfig object with volume and probability
                - A list of AudioConfig objects, where one will be selected based on probability

                If a string is provided and `loop` is True, the sound will be looped.
                If an AsyncIterator is provided, it is played until exhaustion (and cannot be looped
                automatically).
            loop (bool, optional):
                Whether to loop the audio. Only applicable if `audio` is a string or contains strings.
                Defaults to False.

        Returns:
            PlayHandle: An object representing the playback handle. This can be
            awaited or stopped manually.
        zBackgroundAudio is not startedNz}Looping sound via AsyncIterator is not supported. Use a string file path or your own 'infinite' AsyncIterator with loop=Falsec                   s    j S rV   )rO   remove_)r   taskr!   r"   <lambda>   s    z,BackgroundAudioPlayer.play.<locals>.<lambda>c                   s      S rV   )_mark_playout_doners   )play_handler!   r"   rv      s    )rN   RuntimeErrorrj   rq   rw   rf   r   
ValueErrorrJ   create_task
_play_taskadd_done_callbackrO   append)r   rn   rm   
normalizedsound_sourcer4   r!   )rx   r   ru   r"   play   s$   
zBackgroundAudioPlayer.play)agent_sessiontrack_publish_optionsroomrtc.Roomr   NotGivenOr[AgentSession]r   #NotGivenOr[rtc.TrackPublishOptions]c          	   	     s   | j 4 I dH { || _|pd| _|pd| _zt }| r#td W n	 ty-   Y nw | 	 I dH  t
|  | _| jd| j | jrP| jd| j | jrx| | j}|rx|\}}t||}t|trr| j|dd| _n| || _W d  I dH  dS 1 I dH sw   Y  dS )at  
        Starts the background audio system, publishing the audio track
        and beginning playback of any configured ambient sound.

        If `ambient_sound` is provided (and contains file paths), they will loop
        automatically. If `ambient_sound` contains AsyncIterators, they are assumed
        to be already infinite or looped.

        Args:
            room (rtc.Room):
                The LiveKit Room object where the audio track will be published.
            agent_session (NotGivenOr[AgentSession], optional):
                The session object used to track the agent's state (e.g., "thinking").
                Required if `thinking_sound` is provided.
            track_publish_options (NotGivenOr[rtc.TrackPublishOptions], optional):
                Options used when publishing the audio track. If not given, defaults will
                be used.
        NzLBackground audio is not supported in console mode. Audio will not be played.reconnectedagent_state_changedTrl   )rL   _room_agent_session_track_publish_optionsr   is_fake_jobr   warningry   _publish_trackrJ   r{   _run_mixer_taskrN   on_on_reconnected_agent_state_changedrC   rj   r/   rf   r   r   rP   )	r   r   r   r   job_ctxr   r   r4   selected_soundr!   r!   r"   start   s:   



.zBackgroundAudioPlayer.startc              	     s.  | j 4 I dH  | js	 W d  I dH  dS t| j I dH  | jr+t| jI dH  t| jI dH  d| _| j I dH  | j I dH  | jrQ| j	d| j
 | j	d| j tt | jdurp| jj| jjI dH  W d   n1 szw   Y  W d  I dH  dS 1 I dH sw   Y  dS )z
        Gracefully closes the background audio system, canceling all ongoing
        playback tasks and unpublishing the audio track.
        Nr   r   )rL   rN   r   rO   rM   rH   acloserF   r   offr   r   r   
contextlibsuppress	ExceptionrI   local_participantunpublish_tracksidr   r!   r!   r"   r     s*   
.zBackgroundAudioPlayer.aclosec                 C  s*   | j r| j   d | _t|  | _ d S rV   )rM   cancelrI   rJ   r{   _republish_track_taskr   r!   r!   r"   r   5  s   
z%BackgroundAudioPlayer._on_reconnectedevr   c                 C  s`   | j sd S |jdkr$| jr| j sd S | j d usJ | | j | _d S | jr.| j  d S d S )Nthinking)rD   	new_staterQ   doner   stop)r   r   r!   r!   r"   r   <  s   
z*BackgroundAudioPlayer._agent_state_changedr   rx   rX   r4   r3   c                   sP  t tr
 t tr|rtntdd fdd}| }zH| j|   I d H  W | j	|  
  tdI d H   j rpdtt | I d H  W d    d S 1 siw   Y  d S d S | j	|  
  tdI d H   j rdtt | I d H  W d    w 1 sw   Y  w )	NFr   $AsyncGenerator[rtc.AudioFrame, None]c                   s   2 zD3 d H W } r n<dkrDt j| jt jdt j}|dt  9 }t j|dd|d tj	|t j
 | j| j| jdV  q| V  q6    d S )Nr2   )dtype
   i i  )out)datasample_ratenum_channelssamples_per_channel)np
frombufferr   int16astypefloat32log10clipr
   
AudioFrametobytesr   r   r   rw   )framer   rx   rX   stoppedr4   r!   r"   _gen_wrapperY  s"   
z6BackgroundAudioPlayer._play_task.<locals>._gen_wrapperr   T)r   r   )rf   r   r#   r   _loop_audio_framesr   rH   
add_streamwait_for_playoutremove_streamrw   rJ   sleep	_stop_futr   r   r   ry   r   )r   rx   rX   r4   rm   r   genr!   r   r"   r|   J  s<   



"
z BackgroundAudioPlayer._play_taskc                   s.   | j 2 z3 d H W }| j|I d H  q6 d S rV   )rH   rF   capture_frame)r   r   r!   r!   r"   r   }  s   z%BackgroundAudioPlayer._run_mixer_taskc                   sF   | j d urd S tjd| j}| jj|| jpt	 I d H | _ d S )Nbackground_audio)
rI   r
   LocalAudioTrackcreate_audio_trackrF   r   r   publish_trackr   TrackPublishOptions)r   trackr!   r!   r"   r     s   
z$BackgroundAudioPlayer._publish_trackc              	     sP   | j 4 I d H  |  I d H  W d   I d H  d S 1 I d H s!w   Y  d S rV   )rL   r   r   r!   r!   r"   r     s   .z+BackgroundAudioPlayer._republish_track_taskN)r:   r=   r;   r=   r<   r>   r   r?   )rS   rT   r   rU   )r1   rd   r   re   )r1   r0   r   rk   )rn   ro   rm   rp   r   rq   )r   r   r   r   r   r   r   r?   r   r?   )r   r   r   r?   )
rx   rq   rX   r0   r4   r3   rm   rp   r   r?   )r%   r&   r'   r   rR   rc   rj   rg   r   r   r   r   r   r   r   r|   r   r   r   r!   r!   r!   r"   r8   D   s.    
3


7
9

2
	r8   c                   @  sH   e Zd ZdddZdddZddd	Zdd
dZdddZdddZdS )rq   r   r?   c                 C  s    t jd   | _t jd   | _d S rV   )rJ   Future	_done_futr   r   r!   r!   r"   rR     r$   zPlayHandle.__init__rp   c                 C  s
   | j  S )zA
        Returns True if the sound has finished playing.
        )r   r   r   r!   r!   r"   r     s   
zPlayHandle.donec                 C  sR   |   rdS ttj | jd |   W d   dS 1 s"w   Y  dS )z/
        Stops the sound from playing.
        N)r   r   r   rJ   InvalidStateErrorr   
set_resultrw   r   r!   r!   r"   r     s   
"zPlayHandle.stopc                   s   t | jI dH  dS )z8
        Waits for the sound to finish playing.
        N)rJ   shieldr   r   r!   r!   r"   r     s   zPlayHandle.wait_for_playout Generator[Any, None, PlayHandle]c                   s   d fdd}|   S )Nr   rq   c                     s      I d H   S rV   )r   r!   r   r!   r"   _await_impl  s   z)PlayHandle.__await__.<locals>._await_impl)r   rq   )	__await__)r   r   r!   r   r"   r     s   
zPlayHandle.__await__c                 C  s>   t tj | jd  W d    d S 1 sw   Y  d S rV   )r   r   rJ   r   r   r   r   r!   r!   r"   rw     s   "zPlayHandle._mark_playout_doneNr   )r   rp   )r   r   )	r%   r&   r'   rR   r   r   r   r   rw   r!   r!   r!   r"   rq     s    




rq   r    r   r   r   c                 C s$   	 t | 2 z	3 d H W }|V  q6 qrV   r   )r    r   r!   r!   r"   r     s   r   )r    r   r   r   )5
__future__r   rJ   atexitr   enumr\   collections.abcr   r   r   importlib.resourcesr   r   typingr   r	   numpyr   livekitr
   jobr   logr   typesr   r   utilsr   r   	utils.aior   utils.audior   r   r   eventsr   	ExitStackr   registercloseEnumr   r   r   r0   r/   rE   r8   rq   r   r!   r!   r!   r"   <module>   s<      P(