
    q	h                         S SK r S SKrS SKrS SKJr  S SKJrJr  SSK	J
r
  SSKJr  \\
   r\ " S S5      5       r " S	 S
5      rg)    N)	dataclass)AsyncIteratorOptional   )
AudioFrame)loggerc                   p    \ rS rSr% \\S'   \R                  \S'   \R                  \S'   \\S'   \\S'   Sr	g)	_Contribution   streamdatabufferhad_data	exhausted N)
__name__
__module____qualname____firstlineno___Stream__annotations__npndarraybool__static_attributes__r       I/var/www/html/env/lib/python3.13/site-packages/livekit/rtc/audio_mixer.pyr
   r
      s%    O
**JJNOr   r
   c                       \ rS rSrSSSS.S\S\S\S\S	\S
S4S jjrS\\   S
S4S jrS\\   S
S4S jr	SS jr
S
\4S jrSS jrSS jrSS jrS\\   S\R                   S
\4S jrSrg)
AudioMixer   r   d   )	blocksizestream_timeout_mscapacitysample_ratenum_channelsr"   r#   r$   returnNc                   [        5       U l        0 U l        Xl        X l        US:  a  UO[        US-  5      U l        X@l        [        R                  " US9U l
        SU l        [        R                  " U R                  5       5      U l        g)au  
Initialize the AudioMixer.

The mixer accepts multiple async audio streams and mixes them into a single output stream.
Each output frame is generated with a fixed chunk size determined by the blocksize (in samples).
If blocksize is not provided (or 0), it defaults to 100ms.

Each input stream is processed in parallel, accumulating audio data until at least one chunk
of samples is available. If an input stream does not provide data within the specified timeout,
a warning is logged. The mixer can be closed immediately
(dropping unconsumed frames) or allowed to flush remaining data using end_input().

Args:
    sample_rate (int): The audio sample rate in Hz.
    num_channels (int): The number of audio channels.
    blocksize (int, optional): The size of the audio block (in samples) for mixing. If not provided,
        defaults to sample_rate // 10.
    stream_timeout_ms (int, optional): The maximum wait time in milliseconds for each stream to provide
        audio data before timing out. Defaults to 100 ms.
    capacity (int, optional): The maximum number of mixed frames to store in the output queue.
        Defaults to 100.
r   
   )maxsizeFN)set_streams_buffers_sample_rate_num_channelsint_chunk_size_stream_timeout_msasyncioQueue_queue_endingcreate_task_mixer_mixer_task)selfr%   r&   r"   r#   r$   s         r   __init__AudioMixer.__init__   st    > '*e35!,".-6]	KSUDU@V'8;B==QY;Z #)0)<)<T[[])Kr   r   c                 
   U R                   (       a  [        S5      eU R                  R                  U5        XR                  ;  a=  [
        R                  " SU R                  4[
        R                  S9U R                  U'   gg)z
Add an audio stream to the mixer.

The stream is added to the internal set of streams and an empty buffer is initialized for it,
if not already present.

Args:
    stream (AsyncIterator[AudioFrame]): An async iterator that produces AudioFrame objects.
z-Cannot add stream after mixer has been closedr   dtypeN)	r6   RuntimeErrorr,   addr-   r   emptyr/   int16r:   r   s     r   
add_streamAudioMixer.add_streamA   s`     <<NOO&!&$&HHa1C1C-DBHH$UDMM&! 'r   c                 r    U R                   R                  U5        U R                  R                  US5        g)z
Remove an audio stream from the mixer.

This method removes the specified stream and its associated buffer from the mixer.

Args:
    stream (AsyncIterator[AudioFrame]): The audio stream to remove.
N)r,   discardr-   poprD   s     r   remove_streamAudioMixer.remove_streamR   s*     	f%&$'r   c                     U $ Nr   r:   s    r   	__aiter__AudioMixer.__aiter__^   s    r   c                 h   #    U R                   R                  5       I S h  vN nUc  [        eU$  N7frM   )r5   getStopAsyncIteration)r:   items     r   	__anext__AudioMixer.__anext__a   s-     [[__&&<$$ 's   202c                    #    SU l         U R                  R                  5         [        R                  " [
        R                  5         U R                  I Sh  vN   SSS5        g N! , (       d  f       g= f7f)z
Immediately stop mixing and close the mixer.

This cancels the mixing task, and any unconsumed output in the queue may be dropped.
TN)r6   r9   cancel
contextlibsuppressr3   CancelledErrorrN   s    r   acloseAudioMixer.acloseg   sV      !  !7!78"""" 98" 98s0   AA9A(A&A(	A9&A((
A62A9c                     SU l         g)z
Signal that no more streams will be added.

This method marks the mixer as closed so that it flushes any remaining buffered output before ending.
Note that existing streams will still be processed until exhausted.
TN)r6   rN   s    r   	end_inputAudioMixer.end_inputr   s     r   c                   #     U R                   (       a  U R                  (       d  GOU R                  (       d   [        R                  " S5      I S h  vN   MV  [	        U R                  5       Vs/ s H\  nU R                  UU R                  R                  U[        R                  " SU R                  4[        R                  S95      5      PM^     nn[        R                  " USS06I S h  vN n/ nSn/ nU H  n[        U[        5      (       d  M  UR                  UR                   R#                  [        R$                  5      5        UR&                  U R                  UR(                  '   UR*                  (       a  SnUR,                  (       d  M  UR&                  R.                  S   S:X  d  M  UR                  UR(                  5        M     U H  nU R1                  U5        M     U(       d!  [        R                  " S5      I S h  vN   GM  [        R2                  " [        R4                  " USS9SS9n[        R6                  " US	S
5      R#                  [        R                  5      n[9        UR;                  5       U R<                  U R                  U R>                  5      n	U R@                  RC                  U	5      I S h  vN   GM  U R@                  RC                  S 5      I S h  vN   g  GNs  snf  GN N N8 N7f)NTg{Gz?r   r>   return_exceptionsFgMbP?axisi i  )"r6   r,   r3   sleeplist_get_contributionr-   rR   r   rB   r/   rC   gather
isinstancer
   appendr   astypefloat32r   r   r   r   shaperJ   sumstackclipr   tobytesr.   r1   r5   put)
r:   r   tasksresultscontributionsany_dataremovalscontribmixedframes
             r   r8   AudioMixer._mixer{   sJ    ||DMM==mmD))) #4==1
 2F	 &&MM%%fbhh4;M;M7NVXV^V^._` 2   $NNEJTJJGMHH"!'=99$$W\\%8%8%DE07gnn-###H$$$)=)=a)@A)EOOGNN3 # #""6* # mmE***FF288M:CEGGE65188BE!2!2D4F4FHXHXE ++//%(((W Z kkood###O * K& + )#sv   ALK7L0A#K:L.K?/B#LL5ALLB<LL%L1L2L:LLLLbufc                 p  #    UR                   S   S:  nSnUR                   S   U R                  :  a  U(       d   [        R                  " UR	                  5       U R
                  S-  S9I S h  vN n[        R                  " UR                  R                  5       [        R                  S9R                  S	U R                   5      nUR"                  (       a  [        R$                  " X&4SS
9OUnSnUR                   S   U R                  :  a	  U(       d  M  UR                   S   U R                  :  a  US U R                   X R                  S  p'O[        R&                  " U R                  UR                   S   -
  U R                   4[        R                  S9n[        R$                  " X(4SS
9[        R(                  " SU R                   4[        R                  S9p'[+        XX#U5      $  GN! [        R                   a    [        R                  " SU S35         GM	  [         a    Sn GM  f = f7f)Nr   Fi  )timeoutzAudioMixer: stream z timeout, ignoringTr>   rc   )rm   r1   r3   wait_forrU   r2   TimeoutErrorr   warningrS   r   
frombufferr   rq   rC   reshaper/   sizeconcatenatezerosrB   r
   )	r:   r   r|   r   r   rz   new_datarx   pads	            r   rg   AudioMixer._get_contribution   s     99Q<!#	iilT---i	%..$$&0G0G$0N  }}UZZ%7%7%9JRRD&&H >AXX"..#q98CH iilT---ii  99Q<4+++1!1!12C8H8H8J4KS((D,,syy|;T=O=OPXZX`X`aCz2!T//0A  VcYGG- '' !4VH<NOP%  	sN   9H63G1 /G.0G1 4B$H6CH6.G1 1.H3H6#H3.H62H33H6)	r-   r1   r6   r9   r/   r5   r.   r2   r,   )r'   r   )r'   N)r   r   r   r   r0   r;   r   r   rE   rJ   rO   rU   r\   r_   r8   r   r   r
   rg   r   r   r   r   r   r      s     !$)L)L )L
 )L )L )L 
)LVVz!: Vt V"
(M*$= 
($ 
( 	#.$`H#J/H68jjH	Hr   r   )r3   numpyr   rY   dataclassesr   typingr   r   audio_framer   logr   r   r
   r   r   r   r   <module>r      sK       ! * # 


#   sH sHr   