/*
wearechat - generic multiplayer game with voip
Copyright (C) 2025 metamuffin
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, version 3 of the License only.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
use std::{
collections::{HashMap, 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 glam::Vec3;
use log::warn;
pub struct Audio {
_instream: Stream,
_outstream: Stream,
rx: Receiver>,
tx: SyncSender,
}
impl Audio {
pub fn new() -> Result {
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 pop_output(&mut self) -> Option> {
self.rx.try_recv().ok()
}
pub fn incoming_packet(&mut self, channel: u128, pos: Vec3, data: Vec) {
if let Err(e) = self.tx.send(APlayPacket { pos, data, channel }) {
warn!("audio output buffer overflow: {e:?}")
}
}
}
pub struct APlayPacket {
pos: Vec3,
channel: u128,
data: Vec,
}
pub struct AEncoder {
encoder: Encoder,
sender: SyncSender>,
buffer: VecDeque,
}
impl AEncoder {
pub fn new() -> Result<(Self, Receiver>)> {
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)?;
let _ = self.sender.try_send(out[..size].to_vec());
}
Ok(())
}
}
const BUFFER_SIZE: usize = 48_000;
pub struct ADecoder {
decoder: Decoder,
receiver: Receiver,
channels: HashMap,
playback: usize,
buffer: Box<[[f32; 2]; BUFFER_SIZE]>,
}
impl ADecoder {
pub fn new() -> Result<(Self, SyncSender)> {
let (tx, receiver) = sync_channel(1024);
Ok((
Self {
decoder: Decoder::new(SampleRate::Hz48000, Channels::Mono)?,
receiver,
channels: HashMap::new(),
playback: 0,
buffer: unsafe { Box::new_zeroed().assume_init() },
},
tx,
))
}
pub fn data(&mut self, samples: &mut [f32]) -> Result<()> {
while self.buffer.len() < samples.len() {
if let Ok(p) = self.receiver.try_recv() {
let mut output = [0f32; 120];
let size = self.decoder.decode_float(
Some(p.data.as_slice()),
output.as_mut_slice(),
false,
)?;
let channel_cursor = self.channels.entry(p.channel).or_insert(self.playback);
let free_space = *channel_cursor - self.playback;
for i in 0..size.min(free_space) {
// TODO positional audio
let _ = p.pos;
self.buffer[*channel_cursor] = [output[i], output[i]];
*channel_cursor += 1;
*channel_cursor %= BUFFER_SIZE
}
} else {
break;
}
}
for x in samples.array_chunks_mut::<2>() {
*x = self.buffer[self.playback];
self.buffer[self.playback] = [0.; 2];
self.playback += 1;
self.playback %= BUFFER_SIZE;
}
Ok(())
}
}