summaryrefslogtreecommitdiff
path: root/client/src/audio.rs
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2025-01-13 22:01:31 +0100
committermetamuffin <metamuffin@disroot.org>2025-01-13 22:01:31 +0100
commit18f863bcd7e68ba18d3e9ce7a1899a4245f364de (patch)
tree5f9a3c89e7c5145d1ea9d220f6b94605392aeb28 /client/src/audio.rs
parentdd5be4b219d7845c1d62c4be9c71049b5065b895 (diff)
downloadweareserver-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.rs153
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(())
+ }
+}