
    qi                         d Z ddlZddlmZmZmZ ddlZddlm	Z	 ddl
mZ ddlmZmZmZ 	 ddlZ G d
 de      Zy# e$ r7Z e	j(                  de         e	j(                  d        ed	e       dZ[ww xY w)zSoundfile-based audio mixer for file playback integration.

Provides an audio mixer that combines incoming audio with audio loaded from
files using the soundfile library. Supports multiple audio formats and
runtime configuration changes.
    N)AnyDictMapping)logger)BaseAudioMixer)MixerControlFrameMixerEnableFrameMixerUpdateSettingsFramezException: zUIn order to use the soundfile mixer, you need to `pip install pipecat-ai[soundfile]`.zMissing module: c                        e Zd ZdZdddddeeef   dededed	ef
 fd
Zde	fdZ
d ZdefdZdedefdZdefdZdefdZdefdZdefdZd	efdZdedefdZdefdZ xZS )SoundfileMixerae  Audio mixer that combines incoming audio with file-based audio.

    This is an audio mixer that mixes incoming audio with audio from a
    file. It uses the soundfile library to load files so it supports multiple
    formats. The audio files need to only have one channel (mono) and it needs
    to match the sample rate of the output transport.

    Multiple files can be loaded, each with a different name. The
    `MixerUpdateSettingsFrame` has the following settings available: `sound`
    (str) and `volume` (float) to be able to update to a different sound file or
    to change the volume at runtime.
    g?T)volumemixingloopsound_filesdefault_soundr   r   r   c                    t        |   di | || _        || _        d| _        d| _        i | _        || _        || _        || _	        y)a  Initialize the soundfile mixer.

        Args:
            sound_files: Mapping of sound names to file paths for loading.
            default_sound: Name of the default sound to play initially.
            volume: Mixing volume level (0.0 to 1.0). Defaults to 0.4.
            mixing: Whether mixing is initially enabled. Defaults to True.
            loop: Whether to loop audio files when they end. Defaults to True.
            **kwargs: Additional arguments passed to parent class.
        r   N )
super__init___sound_files_volume_sample_rate
_sound_pos_sounds_current_sound_mixing_loop)selfr   r   r   r   r   kwargs	__class__s          V/opt/pipecat/venv/lib/python3.12/site-packages/pipecat/audio/mixers/soundfile_mixer.pyr   zSoundfileMixer.__init__/   sP    ( 	"6"'')+
    sample_ratec                    K   || _         | j                  j                         D ].  \  }}t        j                  | j
                  ||       d{    0 y7 w)zInitialize the mixer and load all sound files.

        Args:
            sample_rate: The sample rate of the output transport in Hz.
        N)r   r   itemsasyncio	to_thread_load_sound_file)r   r#   
sound_name	file_names       r!   startzSoundfileMixer.startN   sV      (%)%6%6%<%<%> 	R!J	##D$9$9:yQQQ	RQs   AAAAc                    K   yw)zyClean up mixer resources.

        Currently performs no cleanup as sound data is managed by garbage collection.
        Nr   )r   s    r!   stopzSoundfileMixer.stopX   s     
 	s   framec                    K   t        |t              r| j                  |       d{    yt        |t              r#| j	                  |j
                         d{    y7 97 w)zProcess mixer control frames to update settings or enable/disable mixing.

        Args:
            frame: The mixer control frame to process.
        N)
isinstancer
   _update_settingsr	   _enable_mixingenable)r   r.   s     r!   process_framezSoundfileMixer.process_frame_   s]      e56''... 	 /0%%ell333 /3s!   %A%A!3A%A#A%#A%audioreturnc                 ,   K   | j                  |      S w)zMix transport audio with the current sound file.

        Args:
            audio: Raw audio bytes from the transport to mix.

        Returns:
            Mixed audio bytes combining transport and file audio.
        )_mix_with_sound)r   r5   s     r!   mixzSoundfileMixer.mixk   s      ##E**s   r3   c                    K   || _         yw)zEnable or disable audio mixing.N)r   )r   r3   s     r!   r2   zSoundfileMixer._enable_mixingv           	c                   K   |j                   j                         D ]d  \  }}|xdk(  r | j                  |       d{    'xdk(  r | j                  |       d{    Gdk(  sL| j	                  |       d{    f y7 F7 (7 w)z+Update mixer settings from a control frame.soundNr   r   )settingsr%   _change_sound_update_volume_update_loop)r   r.   settingvalues       r!   r1   zSoundfileMixer._update_settingsz   s{     #nn224 	3NGU,,U333--e444++E222	3 442s9   <BBBB	B)B=B	>BB	Br>   c                 x   K   || j                   v r|| _        d| _        yt        j                  d| d       yw)zxChange the currently playing sound file.

        Args:
            sound: Name of the sound file to switch to.
        r   zSound z is not availableN)r   r   r   r   error)r   r>   s     r!   r@   zSoundfileMixer._change_sound   s;      D%%%"'DDOLL6%(9:;s   8:c                    K   || _         yw)zUpdate the mixing volume level.N)r   )r   r   s     r!   rA   zSoundfileMixer._update_volume   r;   r<   c                    K   || _         yw)zUpdate the looping behavior.N)r   )r   r   s     r!   rB   zSoundfileMixer._update_loop   s     
r<   r)   r*   c           	         	 t        j                  d|        t        j                  |d      \  }}|| j                  k(  rC|j                         }t        j                  |t        j                        | j                  |<   y
t        j                  d| d| d| j                   d       y
# t        $ r%}t        j                  d| d	|        Y d
}~y
d
}~ww xY w)z*Load an audio file into memory for mixing.zLoading mixer sound from int16dtypezSound file z has incorrect sample rate z (should be )zUnable to open file z: N)r   debugsfreadr   tobytesnp
frombufferrJ   r   warning	ExceptionrF   )r   r)   r*   r>   r#   r5   es          r!   r(   zSoundfileMixer._load_sound_file   s    	BLL4YK@A!#'!BE;d///+-==bhh+OZ(!),G}T`aearar`sstu  	BLL/	{"QC@AA	Bs   BB0 )B0 0	C9CCc                 p   | j                   r| j                  | j                  vr|S t        j                  |t        j
                        }t        |      }| j                  | j                     }| j                  |z   t        |      kD  r| j                  s|S d| _        | j                  }| j                  |z   }|| _        ||| }t        j                  ||| j                  z  z   dd      j                  t        j
                        }|j                  t        j
                        j                         S )zHMix raw audio frames with chunks of the same length from the sound file.rK   r   i i  )r   r   r   rR   rS   rJ   lenr   r   clipr   astyperQ   )	r   r5   audio_np
chunk_sizer>   	start_posend_possound_npmixed_audios	            r!   r8   zSoundfileMixer._mix_with_sound   s    ||4#6#6$,,#FL==bhh7]
 T001 ??Z'#e*4::DOOO	//J.!7+gghDLL)@@&%PWWXZX`X`a!!"((+3355r"   )__name__
__module____qualname____doc__r   strfloatboolr   intr+   r-   r   r4   bytesr9   r2   r
   r1   r@   rA   rB   r(   r8   __classcell__)r    s   @r!   r   r   !   s    $  S#X& 	
   >Rs R
): 
	+u 	+ 	+4 	3,D 	3
< 
<5 t B3 B3 B"6U 6r"   r   )rd   r&   typingr   r   r   numpyrR   logurur   %pipecat.audio.mixers.base_audio_mixerr   pipecat.frames.framesr   r	   r
   	soundfilerO   ModuleNotFoundErrorrV   rF   rU   r   r   r"   r!   <module>rr      s     % %   @ _ _,b6^ b6  ,FLL;qc"#FLL_ &qc*
++,s   < A82A33A8