webrtc_audio_processing/
lib.rs1#![warn(clippy::all)]
6#![warn(missing_docs)]
7
8mod config;
9
10use std::{error, fmt, sync::Arc};
11use webrtc_audio_processing_sys as ffi;
12
13pub use config::*;
14pub use ffi::NUM_SAMPLES_PER_FRAME;
15
16#[derive(Debug)]
20pub struct Error {
21 code: i32,
23}
24
25impl fmt::Display for Error {
26 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
27 write!(f, "ffi::AudioProcessing::Error code: {}", self.code)
28 }
29}
30
31impl error::Error for Error {}
32
33#[derive(Clone)]
38pub struct Processor {
39 inner: Arc<AudioProcessing>,
40 deinterleaved_capture_frame: Vec<Vec<f32>>,
43 deinterleaved_render_frame: Vec<Vec<f32>>,
44}
45
46impl Processor {
47 pub fn new(config: &ffi::InitializationConfig) -> Result<Self, Error> {
51 Ok(Self {
52 inner: Arc::new(AudioProcessing::new(config)?),
53 deinterleaved_capture_frame: vec![
54 vec![0f32; NUM_SAMPLES_PER_FRAME as usize];
55 config.num_capture_channels as usize
56 ],
57 deinterleaved_render_frame: vec![
58 vec![0f32; NUM_SAMPLES_PER_FRAME as usize];
59 config.num_render_channels as usize
60 ],
61 })
62 }
63
64 pub fn process_capture_frame(&mut self, frame: &mut [f32]) -> Result<(), Error> {
68 Self::deinterleave(frame, &mut self.deinterleaved_capture_frame);
69 self.inner.process_capture_frame(&mut self.deinterleaved_capture_frame)?;
70 Self::interleave(&self.deinterleaved_capture_frame, frame);
71 Ok(())
72 }
73
74 pub fn process_capture_frame_noninterleaved(
79 &mut self,
80 frame: &mut Vec<Vec<f32>>,
81 ) -> Result<(), Error> {
82 self.inner.process_capture_frame(frame)
83 }
84
85 pub fn process_render_frame(&mut self, frame: &mut [f32]) -> Result<(), Error> {
89 Self::deinterleave(frame, &mut self.deinterleaved_render_frame);
90 self.inner.process_render_frame(&mut self.deinterleaved_render_frame)?;
91 Self::interleave(&self.deinterleaved_render_frame, frame);
92 Ok(())
93 }
94
95 pub fn process_render_frame_noninterleaved(
99 &mut self,
100 frame: &mut Vec<Vec<f32>>,
101 ) -> Result<(), Error> {
102 self.inner.process_render_frame(frame)
103 }
104
105 pub fn get_stats(&self) -> Stats {
107 self.inner.get_stats()
108 }
109
110 pub fn set_config(&mut self, config: Config) {
114 self.inner.set_config(config);
115 }
116
117 pub fn set_output_will_be_muted(&self, muted: bool) {
120 self.inner.set_output_will_be_muted(muted);
121 }
122
123 fn deinterleave<T: AsMut<[f32]>>(src: &[f32], dst: &mut [T]) {
141 let num_channels = dst.len();
142 let num_samples = dst[0].as_mut().len();
143 assert_eq!(src.len(), num_channels * num_samples);
144 for channel_index in 0..num_channels {
145 for sample_index in 0..num_samples {
146 dst[channel_index].as_mut()[sample_index] =
147 src[num_channels * sample_index + channel_index];
148 }
149 }
150 }
151
152 fn interleave<T: AsRef<[f32]>>(src: &[T], dst: &mut [f32]) {
154 let num_channels = src.len();
155 let num_samples = src[0].as_ref().len();
156 assert_eq!(dst.len(), num_channels * num_samples);
157 for channel_index in 0..num_channels {
158 for sample_index in 0..num_samples {
159 dst[num_channels * sample_index + channel_index] =
160 src[channel_index].as_ref()[sample_index];
161 }
162 }
163 }
164}
165
166struct AudioProcessing {
168 inner: *mut ffi::AudioProcessing,
169}
170
171impl AudioProcessing {
172 fn new(config: &ffi::InitializationConfig) -> Result<Self, Error> {
173 let mut code = 0;
174 let inner = unsafe { ffi::audio_processing_create(config, &mut code) };
175 if !inner.is_null() {
176 Ok(Self { inner })
177 } else {
178 Err(Error { code })
179 }
180 }
181
182 fn process_capture_frame(&self, frame: &mut Vec<Vec<f32>>) -> Result<(), Error> {
183 let mut frame_ptr = frame.iter_mut().map(|v| v.as_mut_ptr()).collect::<Vec<*mut f32>>();
184 unsafe {
185 let code = ffi::process_capture_frame(self.inner, frame_ptr.as_mut_ptr());
186 if ffi::is_success(code) {
187 Ok(())
188 } else {
189 Err(Error { code })
190 }
191 }
192 }
193
194 fn process_render_frame(&self, frame: &mut Vec<Vec<f32>>) -> Result<(), Error> {
195 let mut frame_ptr = frame.iter_mut().map(|v| v.as_mut_ptr()).collect::<Vec<*mut f32>>();
196 unsafe {
197 let code = ffi::process_render_frame(self.inner, frame_ptr.as_mut_ptr());
198 if ffi::is_success(code) {
199 Ok(())
200 } else {
201 Err(Error { code })
202 }
203 }
204 }
205
206 fn get_stats(&self) -> Stats {
207 unsafe { ffi::get_stats(self.inner).into() }
208 }
209
210 fn set_config(&self, config: Config) {
211 unsafe {
212 ffi::set_config(self.inner, &config.into());
213 }
214 }
215
216 fn set_output_will_be_muted(&self, muted: bool) {
217 unsafe {
218 ffi::set_output_will_be_muted(self.inner, muted);
219 }
220 }
221}
222
223impl Drop for AudioProcessing {
224 fn drop(&mut self) {
225 unsafe {
226 ffi::audio_processing_delete(self.inner);
227 }
228 }
229}
230
231unsafe impl Sync for AudioProcessing {}
234unsafe impl Send for AudioProcessing {}
235
236#[cfg(test)]
237mod tests {
238 use super::*;
239 use std::{thread, time::Duration};
240
241 #[test]
242 fn test_create_failure() {
243 let config =
244 InitializationConfig { num_capture_channels: 0, ..InitializationConfig::default() };
245 assert!(Processor::new(&config).is_err());
246 }
247
248 #[test]
249 fn test_create_drop() {
250 let config = InitializationConfig {
251 num_capture_channels: 1,
252 num_render_channels: 1,
253 ..InitializationConfig::default()
254 };
255 let _p = Processor::new(&config).unwrap();
256 }
257
258 #[test]
259 fn test_deinterleave_interleave() {
260 let num_channels = 2usize;
261 let num_samples = 3usize;
262
263 let interleaved = (0..num_channels * num_samples).map(|v| v as f32).collect::<Vec<f32>>();
264 let mut deinterleaved = vec![vec![-1f32; num_samples]; num_channels];
265 Processor::deinterleave(&interleaved, &mut deinterleaved);
266 assert_eq!(vec![vec![0f32, 2f32, 4f32], vec![1f32, 3f32, 5f32]], deinterleaved);
267
268 let mut interleaved_out = vec![-1f32; num_samples * num_channels];
269 Processor::interleave(&deinterleaved, &mut interleaved_out);
270 assert_eq!(interleaved, interleaved_out);
271 }
272
273 fn sample_stereo_frames() -> (Vec<f32>, Vec<f32>) {
274 let num_samples_per_frame = NUM_SAMPLES_PER_FRAME as usize;
275
276 let mut render_frame = Vec::with_capacity(num_samples_per_frame * 2);
278 for i in 0..num_samples_per_frame {
279 render_frame.push((i as f32 / 40.0).cos() * 0.4);
280 render_frame.push((i as f32 / 40.0).cos() * 0.2);
281 }
282
283 let mut capture_frame = Vec::with_capacity(num_samples_per_frame * 2);
286 for i in 0..num_samples_per_frame {
287 capture_frame.push((i as f32 / 20.0).sin() * 0.4 + render_frame[i * 2] * 0.2);
288 capture_frame.push((i as f32 / 20.0).sin() * 0.2 + render_frame[i * 2 + 1] * 0.2);
289 }
290
291 (render_frame, capture_frame)
292 }
293
294 #[test]
295 fn test_nominal() {
296 let config = InitializationConfig {
297 num_capture_channels: 2,
298 num_render_channels: 2,
299 ..InitializationConfig::default()
300 };
301 let mut ap = Processor::new(&config).unwrap();
302
303 let config = Config {
304 echo_cancellation: Some(EchoCancellation {
305 suppression_level: EchoCancellationSuppressionLevel::High,
306 stream_delay_ms: None,
307 enable_delay_agnostic: false,
308 enable_extended_filter: false,
309 }),
310 ..Config::default()
311 };
312 ap.set_config(config);
313
314 let (render_frame, capture_frame) = sample_stereo_frames();
315
316 let mut render_frame_output = render_frame.clone();
317 ap.process_render_frame(&mut render_frame_output).unwrap();
318
319 assert_eq!(render_frame, render_frame_output);
321
322 let mut capture_frame_output = capture_frame.clone();
323 ap.process_capture_frame(&mut capture_frame_output).unwrap();
324
325 assert_ne!(capture_frame, capture_frame_output);
328
329 let stats = ap.get_stats();
330 assert!(stats.echo_return_loss.is_some());
331 println!("{:#?}", stats);
332 }
333
334 #[test]
335 #[ignore]
336 fn test_nominal_threaded() {
337 let config = InitializationConfig {
338 num_capture_channels: 2,
339 num_render_channels: 2,
340 ..InitializationConfig::default()
341 };
342 let ap = Processor::new(&config).unwrap();
343
344 let (render_frame, capture_frame) = sample_stereo_frames();
345
346 let mut config_ap = ap.clone();
347 let config_thread = thread::spawn(move || {
348 thread::sleep(Duration::from_millis(100));
349
350 let config = Config {
351 echo_cancellation: Some(EchoCancellation {
352 suppression_level: EchoCancellationSuppressionLevel::High,
353 stream_delay_ms: None,
354 enable_delay_agnostic: false,
355 enable_extended_filter: false,
356 }),
357 ..Config::default()
358 };
359 config_ap.set_config(config);
360 });
361
362 let mut render_ap = ap.clone();
363 let render_thread = thread::spawn(move || {
364 for _ in 0..100 {
365 let mut render_frame_output = render_frame.clone();
366 render_ap.process_render_frame(&mut render_frame_output).unwrap();
367
368 thread::sleep(Duration::from_millis(10));
369 }
370 });
371
372 let mut capture_ap = ap.clone();
373 let capture_thread = thread::spawn(move || {
374 for i in 0..100 {
375 let mut capture_frame_output = capture_frame.clone();
376 capture_ap.process_capture_frame(&mut capture_frame_output).unwrap();
377
378 let stats = capture_ap.get_stats();
379 if i < 5 {
380 assert!(stats.echo_return_loss.is_none());
382 } else if i >= 95 {
383 assert!(stats.echo_return_loss.is_some());
385 }
386
387 thread::sleep(Duration::from_millis(10));
388 }
389 });
390
391 config_thread.join().unwrap();
392 render_thread.join().unwrap();
393 capture_thread.join().unwrap();
394 }
395}