aboutsummaryrefslogtreecommitdiffstats
path: root/mumd/src/audio/output.rs
diff options
context:
space:
mode:
authorGustav Sörnäs <gustav@sornas.net>2020-10-21 03:37:22 +0200
committerGustav Sörnäs <gustav@sornas.net>2020-10-21 03:37:22 +0200
commitc9846394ac6cddd82442baadcabad653a28fed66 (patch)
treeb92afa32fa1e56f5542d921c8f9f1f3befa06648 /mumd/src/audio/output.rs
parent1c18f57afe3eae3d61ad44c899d57332e0f71e0c (diff)
downloadmum-c9846394ac6cddd82442baadcabad653a28fed66.tar.gz
refactor audio into input.rs and output.rs
Diffstat (limited to 'mumd/src/audio/output.rs')
-rw-r--r--mumd/src/audio/output.rs90
1 files changed, 90 insertions, 0 deletions
diff --git a/mumd/src/audio/output.rs b/mumd/src/audio/output.rs
new file mode 100644
index 0000000..94e4b21
--- /dev/null
+++ b/mumd/src/audio/output.rs
@@ -0,0 +1,90 @@
+use cpal::{OutputCallbackInfo, Sample};
+use mumble_protocol::voice::VoicePacketPayload;
+use opus::Channels;
+use std::collections::{HashMap, VecDeque};
+use std::ops::AddAssign;
+use std::sync::{Arc, Mutex};
+
+pub struct ClientStream {
+ buffer: VecDeque<f32>, //TODO ring buffer?
+ opus_decoder: opus::Decoder,
+}
+
+impl ClientStream {
+ pub fn new(sample_rate: u32, channels: u16) -> Self {
+ Self {
+ buffer: VecDeque::new(),
+ opus_decoder: opus::Decoder::new(
+ sample_rate,
+ match channels {
+ 1 => Channels::Mono,
+ 2 => Channels::Stereo,
+ _ => unimplemented!("Only 1 or 2 channels supported, got {}", channels),
+ },
+ )
+ .unwrap(),
+ }
+ }
+
+ pub fn decode_packet(&mut self, payload: VoicePacketPayload, channels: usize) {
+ match payload {
+ VoicePacketPayload::Opus(bytes, _eot) => {
+ let mut out: Vec<f32> = vec![0.0; 720 * channels * 4]; //720 is because that is the max size of packet we can get that we want to decode
+ let parsed = self
+ .opus_decoder
+ .decode_float(&bytes, &mut out, false)
+ .expect("Error decoding");
+ out.truncate(parsed);
+ self.buffer.extend(out);
+ }
+ _ => {
+ unimplemented!("Payload type not supported");
+ }
+ }
+ }
+}
+
+pub trait SaturatingAdd {
+ fn saturating_add(self, rhs: Self) -> Self;
+}
+
+impl SaturatingAdd for f32 {
+ fn saturating_add(self, rhs: Self) -> Self {
+ match self + rhs {
+ a if a < -1.0 => -1.0,
+ a if a > 1.0 => 1.0,
+ a => a,
+ }
+ }
+}
+
+impl SaturatingAdd for i16 {
+ fn saturating_add(self, rhs: Self) -> Self {
+ i16::saturating_add(self, rhs)
+ }
+}
+
+impl SaturatingAdd for u16 {
+ fn saturating_add(self, rhs: Self) -> Self {
+ u16::saturating_add(self, rhs)
+ }
+}
+
+pub fn curry_callback<T: Sample + AddAssign + SaturatingAdd>(
+ buf: Arc<Mutex<HashMap<u32, ClientStream>>>,
+) -> impl FnMut(&mut [T], &OutputCallbackInfo) + Send + 'static {
+ move |data: &mut [T], _info: &OutputCallbackInfo| {
+ for sample in data.iter_mut() {
+ *sample = Sample::from(&0.0);
+ }
+
+ let mut lock = buf.lock().unwrap();
+ for client_stream in lock.values_mut() {
+ for sample in data.iter_mut() {
+ *sample = sample.saturating_add(Sample::from(
+ &client_stream.buffer.pop_front().unwrap_or(0.0),
+ ));
+ }
+ }
+ }
+}