use anyhow::Result;
use crossbeam_channel::{Receiver, Sender, bounded};
use std::thread;
use aec3::api::config::EchoCanceller3Config;
use aec3::api::control::EchoControl;
use aec3::audio_processing::stream_config::StreamConfig;
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
use aec3::audio_processing::aec3::echo_canceller3::EchoCanceller3;
use aec3::audio_processing::audio_buffer::AudioBuffer;
use aec3::audio_processing::high_pass_filter::HighPassFilter;
fn interleaved_to_channels(interleaved: &[f32], channels: usize, frames: usize) -> Vec<Vec<f32>> {
let avail_frames = interleaved.len() / channels;
let mut out = vec![vec![0f32; frames]; channels];
let copy_frames = std::cmp::min(avail_frames, frames);
for frame in 0..copy_frames {
for ch in 0..channels {
out[ch][frame] = interleaved[frame * channels + ch];
}
}
out
}
fn channels_to_interleaved(channels_data: &mut [&[f32]], out: &mut [f32]) {
let channels = channels_data.len();
let frames = channels_data[0].len();
for frame in 0..frames {
for ch in 0..channels {
out[frame * channels + ch] = channels_data[ch][frame];
}
}
}
fn processing_thread(
rx_in: Receiver<Vec<f32>>,
rx_render: Receiver<Vec<f32>>,
tx_out: Sender<Vec<f32>>,
sample_rate: usize,
channels: usize,
) {
let cfg = EchoCanceller3Config::default();
let mut aec3 = EchoCanceller3::with_multichannel_config(
cfg,
Some(EchoCanceller3Config::create_default_multichannel_config()),
sample_rate as i32,
channels,
channels,
);
let mut audio_buf =
AudioBuffer::from_sample_rates(sample_rate, channels, sample_rate, channels, sample_rate);
let stream_config = StreamConfig::new(sample_rate, channels, false);
let mut last_metrics = std::time::Instant::now();
let metrics_interval = std::time::Duration::from_secs(5);
let mut render_buf =
AudioBuffer::from_sample_rates(sample_rate, channels, sample_rate, channels, sample_rate);
while let Ok(frame) = rx_in.recv() {
if let Ok(render_frame) = rx_render.try_recv() {
let per_channel_render =
interleaved_to_channels(&render_frame, channels, stream_config.num_frames());
let refs_render: Vec<&[f32]> =
per_channel_render.iter().map(|v| v.as_slice()).collect();
render_buf.copy_from(&refs_render, &stream_config);
render_buf.split_into_frequency_bands();
aec3.analyze_render(&mut render_buf);
render_buf.merge_frequency_bands();
}
let per_channel = interleaved_to_channels(&frame, channels, stream_config.num_frames());
let refs: Vec<&[f32]> = per_channel.iter().map(|v| v.as_slice()).collect();
audio_buf.copy_from(&refs, &stream_config);
aec3.analyze_capture(&mut audio_buf);
audio_buf.split_into_frequency_bands();
let mut hp_filter_channels: Vec<Vec<f32>> = (0..channels)
.map(|ch| audio_buf.split_band(ch, 0).to_vec())
.collect();
let mut hp_filter = HighPassFilter::new(sample_rate as i32, channels);
hp_filter.process(&mut hp_filter_channels);
for ch in 0..channels {
let dst = audio_buf.split_band_mut(ch, 0);
dst.copy_from_slice(&hp_filter_channels[ch]);
}
aec3.set_audio_buffer_delay(116);
aec3.process_capture(&mut audio_buf, false);
audio_buf.merge_frequency_bands();
if last_metrics.elapsed() >= metrics_interval {
let metrics = aec3.metrics();
println!("AEC metrics: {:?}", metrics);
last_metrics = std::time::Instant::now();
}
let mut output = vec![0f32; frame.len()];
let mut out_mut: Vec<Vec<f32>> = vec![vec![0f32; audio_buf.num_frames()]; channels];
let mut out_refs: Vec<&mut [f32]> = out_mut.iter_mut().map(|v| v.as_mut_slice()).collect();
audio_buf.copy_to_stream(&stream_config, &mut out_refs);
let mut out_refs_immut: Vec<&[f32]> = out_refs.iter().map(|r| &**r).collect();
channels_to_interleaved(&mut out_refs_immut, &mut output);
let _ = tx_out.try_send(output.clone());
}
}
fn main() -> Result<()> {
let host = cpal::default_host();
let input_device = host
.default_input_device()
.expect("No input device available");
let output_device = host
.default_output_device()
.expect("No output device available");
let sample_rate_hz = 48_000;
let channels = 2usize;
let frames_per_buffer = (sample_rate_hz / 100) as usize;
let (tx_in, rx_in) = bounded::<Vec<f32>>(16);
let (tx_out, rx_out) = bounded::<Vec<f32>>(16);
let (tx_render, rx_render) = bounded::<Vec<f32>>(16);
thread::spawn(move || processing_thread(rx_in, rx_render, tx_out, sample_rate_hz, channels));
let in_config = cpal::StreamConfig {
channels: channels as u16,
sample_rate: sample_rate_hz as u32,
buffer_size: cpal::BufferSize::Fixed(frames_per_buffer as u32),
};
let tx_in_clone = tx_in.clone();
let input_stream = input_device.build_input_stream(
in_config,
move |data: &[f32], _| {
let vec = data.to_vec();
let _ = tx_in_clone.try_send(vec);
},
move |err| eprintln!("input stream error: {:?}", err),
None,
)?;
let out_config = cpal::StreamConfig {
channels: channels as u16,
sample_rate: sample_rate_hz as u32,
buffer_size: cpal::BufferSize::Fixed(frames_per_buffer as u32),
};
let tx_render_clone = tx_render.clone();
let output_stream = output_device.build_output_stream(
out_config,
move |out: &mut [f32], _| {
let frame_to_play = if let Ok(frame) = rx_out.try_recv() {
let len = out.len().min(frame.len());
out[..len].copy_from_slice(&frame[..len]);
if len < out.len() {
for s in out[len..].iter_mut() {
*s = 0.0;
}
}
frame
} else {
for s in out.iter_mut() {
*s = 0.0;
}
vec![0.0f32; out.len()]
};
let _ = tx_render_clone.try_send(frame_to_play);
},
move |err| eprintln!("output stream error: {:?}", err),
None,
)?;
input_stream.play()?;
output_stream.play()?;
println!("Running karaoke loop — press Ctrl+C to exit");
loop {
std::thread::sleep(std::time::Duration::from_millis(100));
}
}