diff options
author | metamuffin <metamuffin@disroot.org> | 2025-01-13 22:01:31 +0100 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2025-01-13 22:01:31 +0100 |
commit | 18f863bcd7e68ba18d3e9ce7a1899a4245f364de (patch) | |
tree | 5f9a3c89e7c5145d1ea9d220f6b94605392aeb28 /client/src/audio.rs | |
parent | dd5be4b219d7845c1d62c4be9c71049b5065b895 (diff) | |
download | weareserver-18f863bcd7e68ba18d3e9ce7a1899a4245f364de.tar weareserver-18f863bcd7e68ba18d3e9ce7a1899a4245f364de.tar.bz2 weareserver-18f863bcd7e68ba18d3e9ce7a1899a4245f364de.tar.zst |
audio loopback with opus encoder
Diffstat (limited to 'client/src/audio.rs')
-rw-r--r-- | client/src/audio.rs | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/client/src/audio.rs b/client/src/audio.rs new file mode 100644 index 0000000..c72c6b3 --- /dev/null +++ b/client/src/audio.rs @@ -0,0 +1,153 @@ +use std::{ + collections::VecDeque, + sync::mpsc::{Receiver, SyncSender, sync_channel}, +}; + +use anyhow::{Result, anyhow}; +use audiopus::{ + Application, Channels, SampleRate, + coder::{Decoder, Encoder}, +}; +use cpal::{ + Stream, + traits::{DeviceTrait, HostTrait}, +}; +use log::warn; + +pub struct Audio { + _instream: Stream, + _outstream: Stream, + rx: Receiver<Vec<u8>>, + tx: SyncSender<Vec<u8>>, +} +impl Audio { + pub fn new() -> Result<Self> { + let host = cpal::default_host(); + + let indev = host + .default_input_device() + .ok_or(anyhow!("no input device"))?; + let outdev = host + .default_output_device() + .ok_or(anyhow!("no output device"))?; + + let mut inconf = indev.default_input_config()?.config(); + inconf.channels = 1; + inconf.sample_rate = cpal::SampleRate(48_000); + inconf.buffer_size = cpal::BufferSize::Fixed(480); + + let mut outconf = outdev.default_input_config()?.config(); + outconf.channels = 1; + outconf.sample_rate = cpal::SampleRate(48_000); + outconf.buffer_size = cpal::BufferSize::Fixed(480); + + let (mut aenc, rx) = AEncoder::new()?; + let (mut adec, tx) = ADecoder::new()?; + + let instream = indev.build_input_stream( + &inconf, + move |samples: &[f32], _| { + if let Err(e) = aenc.data(samples) { + warn!("encoder error: {e}"); + } + }, + |err| warn!("audio input error: {err}"), + None, + )?; + let outstream = outdev.build_output_stream( + &outconf, + move |samples: &mut [f32], _| { + if let Err(e) = adec.data(samples) { + warn!("decoder error: {e}"); + } + }, + |err| warn!("audio output error: {err}"), + None, + )?; + + Ok(Self { + _instream: instream, + _outstream: outstream, + rx, + tx, + }) + } + pub fn update(&mut self) { + for e in self.rx.try_iter() { + let _ = self.tx.try_send(e); + } + } +} + +pub struct AEncoder { + encoder: Encoder, + sender: SyncSender<Vec<u8>>, + buffer: VecDeque<f32>, +} +impl AEncoder { + pub fn new() -> Result<(Self, Receiver<Vec<u8>>)> { + let (sender, rx) = sync_channel(1024); + Ok(( + Self { + encoder: Encoder::new(SampleRate::Hz48000, Channels::Mono, Application::Voip)?, + sender, + buffer: VecDeque::new(), + }, + rx, + )) + } + pub fn data(&mut self, samples: &[f32]) -> Result<()> { + self.buffer.extend(samples); + while self.buffer.len() >= 120 { + let mut out = [0u8; 120]; + let mut inp = [0f32; 120]; + for i in 0..120 { + inp[i] = self.buffer.pop_front().unwrap(); + } + let size = self.encoder.encode_float(&inp, &mut out)?; + eprintln!("enc {size}"); + let _ = self.sender.try_send(out[..size].to_vec()); + } + Ok(()) + } +} + +pub struct ADecoder { + decoder: Decoder, + receiver: Receiver<Vec<u8>>, + buffer: VecDeque<f32>, +} +impl ADecoder { + pub fn new() -> Result<(Self, SyncSender<Vec<u8>>)> { + let (tx, receiver) = sync_channel(1024); + Ok(( + Self { + decoder: Decoder::new(SampleRate::Hz48000, Channels::Mono)?, + receiver, + buffer: VecDeque::new(), + }, + tx, + )) + } + pub fn data(&mut self, samples: &mut [f32]) -> Result<()> { + while self.buffer.len() < samples.len() { + if let Ok(buf) = self.receiver.try_recv() { + let mut output = [0f32; 120]; + let size = self.decoder.decode_float( + Some(buf.as_slice()), + output.as_mut_slice(), + false, + )?; + eprintln!("dec {size}"); + self.buffer.extend(&output[..size]); + } else { + break; + } + } + + for x in samples { + *x = self.buffer.pop_front().unwrap_or(0.); + } + Ok(()) + } +} |