/* 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(()) } }