2525from ._proto .track_pb2 import TrackSource
2626from ._utils import RingQueue , task_done_logger
2727from .audio_frame import AudioFrame
28+ from .log import logger
2829from .participant import Participant
2930from .track import Track
31+ from .frame_processor import FrameProcessor
3032
3133
3234@dataclass
@@ -62,7 +64,7 @@ def __init__(
6264 sample_rate : int = 48000 ,
6365 num_channels : int = 1 ,
6466 frame_size_ms : int | None = None ,
65- noise_cancellation : Optional [NoiseCancellationOptions ] = None ,
67+ noise_cancellation : Optional [NoiseCancellationOptions | FrameProcessor [ AudioFrame ] ] = None ,
6668 ** kwargs ,
6769 ) -> None :
6870 """Initialize an `AudioStream` instance.
@@ -76,8 +78,8 @@ def __init__(
7678 sample_rate (int, optional): The sample rate for the audio stream in Hz.
7779 Defaults to 48000.
7880 num_channels (int, optional): The number of audio channels. Defaults to 1.
79- noise_cancellation (Optional[NoiseCancellationOptions], optional):
80- If noise cancellation is used, pass a `NoiseCancellationOptions` instance
81+ noise_cancellation (Optional[NoiseCancellationOptions | FrameProcessor[AudioFrame] ], optional):
82+ If noise cancellation is used, pass a `NoiseCancellationOptions` or `FrameProcessor[AudioFrame]` instance
8183 created by the noise cancellation module.
8284
8385 Example:
@@ -105,9 +107,12 @@ def __init__(
105107
106108 self ._audio_filter_module = None
107109 self ._audio_filter_options = None
108- if noise_cancellation is not None :
110+ if isinstance ( noise_cancellation , NoiseCancellationOptions ) :
109111 self ._audio_filter_module = noise_cancellation .module_id
110112 self ._audio_filter_options = noise_cancellation .options
113+ elif isinstance (noise_cancellation , FrameProcessor ):
114+ self ._processor = noise_cancellation
115+
111116 self ._task = self ._loop .create_task (self ._run ())
112117 self ._task .add_done_callback (task_done_logger )
113118
@@ -132,7 +137,7 @@ def from_participant(
132137 sample_rate : int = 48000 ,
133138 num_channels : int = 1 ,
134139 frame_size_ms : int | None = None ,
135- noise_cancellation : Optional [NoiseCancellationOptions ] = None ,
140+ noise_cancellation : Optional [NoiseCancellationOptions | FrameProcessor [ AudioFrame ] ] = None ,
136141 ) -> AudioStream :
137142 """Create an `AudioStream` from a participant's audio track.
138143
@@ -182,7 +187,7 @@ def from_track(
182187 sample_rate : int = 48000 ,
183188 num_channels : int = 1 ,
184189 frame_size_ms : int | None = None ,
185- noise_cancellation : Optional [NoiseCancellationOptions ] = None ,
190+ noise_cancellation : Optional [NoiseCancellationOptions | FrameProcessor [ AudioFrame ] ] = None ,
186191 ) -> AudioStream :
187192 """Create an `AudioStream` from an existing audio track.
188193
@@ -268,6 +273,14 @@ async def _run(self):
268273 if audio_event .HasField ("frame_received" ):
269274 owned_buffer_info = audio_event .frame_received .frame
270275 frame = AudioFrame ._from_owned_info (owned_buffer_info )
276+ if self ._processor is not None and self ._processor .enabled :
277+ try :
278+ frame = self ._processor ._process (frame )
279+ except Exception :
280+ logger .warning (
281+ "Frame processing failed, passing through original frame" ,
282+ exc_info = True ,
283+ )
271284 event = AudioFrameEvent (frame )
272285 self ._queue .put (event )
273286 elif audio_event .HasField ("eos" ):
0 commit comments