summaryrefslogtreecommitdiff
path: root/client/src/audio.rs
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2025-01-15 14:27:43 +0100
committermetamuffin <metamuffin@disroot.org>2025-01-15 14:27:43 +0100
commitd465b5e9edfa3147af900414a11484b3feb50337 (patch)
treee86649607ca5c9e0a8f807e1d4c750ff347bb31f /client/src/audio.rs
parent18f863bcd7e68ba18d3e9ce7a1899a4245f364de (diff)
downloadweareserver-d465b5e9edfa3147af900414a11484b3feb50337.tar
weareserver-d465b5e9edfa3147af900414a11484b3feb50337.tar.bz2
weareserver-d465b5e9edfa3147af900414a11484b3feb50337.tar.zst
multi-channel audio buffering
Diffstat (limited to 'client/src/audio.rs')
-rw-r--r--client/src/audio.rs74
1 files changed, 57 insertions, 17 deletions
diff --git a/client/src/audio.rs b/client/src/audio.rs
index c72c6b3..5751c0e 100644
--- a/client/src/audio.rs
+++ b/client/src/audio.rs
@@ -1,5 +1,21 @@
+/*
+ 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 <https://www.gnu.org/licenses/>.
+*/
use std::{
- collections::VecDeque,
+ collections::{HashMap, VecDeque},
sync::mpsc::{Receiver, SyncSender, sync_channel},
};
@@ -12,13 +28,14 @@ use cpal::{
Stream,
traits::{DeviceTrait, HostTrait},
};
+use glam::Vec3;
use log::warn;
pub struct Audio {
_instream: Stream,
_outstream: Stream,
rx: Receiver<Vec<u8>>,
- tx: SyncSender<Vec<u8>>,
+ tx: SyncSender<APlayPacket>,
}
impl Audio {
pub fn new() -> Result<Self> {
@@ -72,13 +89,22 @@ impl Audio {
tx,
})
}
- pub fn update(&mut self) {
- for e in self.rx.try_iter() {
- let _ = self.tx.try_send(e);
+ pub fn pop_output(&mut self) -> Option<Vec<u8>> {
+ self.rx.try_recv().ok()
+ }
+ pub fn incoming_packet(&mut self, channel: u128, pos: Vec3, data: Vec<u8>) {
+ 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<u8>,
+}
+
pub struct AEncoder {
encoder: Encoder,
sender: SyncSender<Vec<u8>>,
@@ -105,48 +131,62 @@ impl AEncoder {
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(())
}
}
+const BUFFER_SIZE: usize = 48_000;
pub struct ADecoder {
decoder: Decoder,
- receiver: Receiver<Vec<u8>>,
- buffer: VecDeque<f32>,
+ receiver: Receiver<APlayPacket>,
+ channels: HashMap<u128, usize>,
+ playback: usize,
+ buffer: Box<[[f32; 2]; BUFFER_SIZE]>,
}
impl ADecoder {
- pub fn new() -> Result<(Self, SyncSender<Vec<u8>>)> {
+ pub fn new() -> Result<(Self, SyncSender<APlayPacket>)> {
let (tx, receiver) = sync_channel(1024);
Ok((
Self {
decoder: Decoder::new(SampleRate::Hz48000, Channels::Mono)?,
receiver,
- buffer: VecDeque::new(),
+ 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(buf) = self.receiver.try_recv() {
+ if let Ok(p) = self.receiver.try_recv() {
let mut output = [0f32; 120];
let size = self.decoder.decode_float(
- Some(buf.as_slice()),
+ Some(p.data.as_slice()),
output.as_mut_slice(),
false,
)?;
- eprintln!("dec {size}");
- self.buffer.extend(&output[..size]);
+
+ 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 {
- *x = self.buffer.pop_front().unwrap_or(0.);
+ 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(())
}