diff options
| -rw-r--r-- | Cargo.lock | 3 | ||||
| -rw-r--r-- | mumd/Cargo.toml | 3 | ||||
| -rw-r--r-- | mumd/src/audio.rs | 198 | ||||
| -rw-r--r-- | mumd/src/audio/input.rs | 2 |
4 files changed, 203 insertions, 3 deletions
@@ -1198,7 +1198,10 @@ dependencies = [ "argparse", "bytes", "cpal", + "dasp_frame", "dasp_interpolate", + "dasp_ring_buffer", + "dasp_sample", "dasp_signal", "futures", "futures-util", diff --git a/mumd/Cargo.toml b/mumd/Cargo.toml index 8c6958c..caf1a9b 100644 --- a/mumd/Cargo.toml +++ b/mumd/Cargo.toml @@ -23,6 +23,9 @@ cpal = "0.13" bytes = "1.0" dasp_interpolate = { version = "0.11", features = ["linear"] } dasp_signal = "0.11" +dasp_frame = "0.11" +dasp_sample = "0.11" +dasp_ring_buffer = "0.11" futures = "0.3" futures-util = "0.3" hound = "3.4" diff --git a/mumd/src/audio.rs b/mumd/src/audio.rs index 0666268..0998f06 100644 --- a/mumd/src/audio.rs +++ b/mumd/src/audio.rs @@ -4,7 +4,7 @@ pub mod output; use crate::audio::output::SaturatingAdd; use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; -use cpal::{SampleFormat, SampleRate, Stream, StreamConfig}; +use cpal::{SampleFormat, SampleRate, StreamConfig}; use dasp_interpolate::linear::Linear; use dasp_signal::{self as signal, Signal}; use log::*; @@ -14,6 +14,16 @@ use std::collections::hash_map::Entry; use std::collections::{HashMap, VecDeque}; use std::sync::{Arc, Mutex}; use tokio::sync::{mpsc, watch}; +use dasp_frame::Frame; +use dasp_sample::{Sample, SignedSample}; +use dasp_ring_buffer::Fixed; +use futures::Stream; +use futures::task::{Context, Poll}; +use std::pin::Pin; +use tokio::stream::StreamExt; +use std::convert::identity; +use std::future::Future; +use std::mem; //TODO? move to mumlib pub const EVENT_SOUNDS: &[(&'static [u8], NotificationEvents)] = &[ @@ -62,8 +72,8 @@ pub enum NotificationEvents { pub struct Audio { output_config: StreamConfig, - _output_stream: Stream, - _input_stream: Stream, + _output_stream: cpal::Stream, + _input_stream: cpal::Stream, input_channel_receiver: Option<mpsc::Receiver<VoicePacketPayload>>, input_volume_sender: watch::Sender<f32>, @@ -354,3 +364,185 @@ impl Audio { play_sounds.extend(samples.iter().skip(l)); } } + +struct NoiseGate<S: Signal> { + open: bool, + signal: S, + buffer: dasp_ring_buffer::Fixed<Vec<S::Frame>>, + activate_threshold: <<S::Frame as Frame>::Sample as Sample>::Float, + deactivate_threshold: <<S::Frame as Frame>::Sample as Sample>::Float, +} + +impl<S: Signal> NoiseGate<S> { + pub fn new(signal: S, activate_threshold: <<S::Frame as Frame>::Sample as Sample>::Float, deactivate_threshold: <<S::Frame as Frame>::Sample as Sample>::Float) -> NoiseGate<S> { + Self { + open: false, + signal, + buffer: Fixed::from(vec![<S::Frame as Frame>::EQUILIBRIUM; 4096]), + activate_threshold, + deactivate_threshold, + } + } +} + +impl<S: Signal> Signal for NoiseGate<S> { + type Frame = S::Frame; + + fn next(&mut self) -> Self::Frame { + let frame = self.signal.next(); + self.buffer.push(frame); + + if self.open && self.buffer + .iter() + .all(|f| f.to_float_frame() + .channels() + .all(|s| abs(s - <<<S::Frame as Frame>::Sample as Sample>::Float as Sample>::EQUILIBRIUM) <= self.deactivate_threshold)) { + self.open = false; + } else if !self.open && self.buffer + .iter() + .any(|f| f.to_float_frame() + .channels() + .any(|s| abs(s - <<<S::Frame as Frame>::Sample as Sample>::Float as Sample>::EQUILIBRIUM) >= self.activate_threshold)) { + self.open = true; + } + + if self.open { + frame + } else { + S::Frame::EQUILIBRIUM + } + } + + fn is_exhausted(&self) -> bool { + self.signal.is_exhausted() + } +} + +fn abs<S: SignedSample>(sample: S) -> S { + let zero = S::EQUILIBRIUM; + if sample >= zero { + sample + } else { + -sample + } +} + +trait StreamingSignal { + type Frame: Frame; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Frame>; +} + +trait StreamingSignalExt: StreamingSignal { + fn next(&mut self) -> Next<'_, Self> { + Next { + stream: self + } + } +} + +struct Next<'a, S: ?Sized> { + stream: &'a mut S +} + +impl<'a, S: StreamingSignal + Unpin> Future for Next<'a, S> { + type Output = S::Frame; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { + match S::poll_next(Pin::new(self.stream), cx) { + Poll::Ready(val) => { + Poll::Ready(val) + } + Poll::Pending => Poll::Pending + } + } +} + +struct FromStream<S: Stream> { + stream: S, + next: Option<S::Item>, +} + +async fn from_stream<S>(mut stream: S) -> FromStream<S> + where + S: Stream + Unpin, + S::Item: Frame { + let next = stream.next().await; + FromStream { stream, next } +} + +impl<S> StreamingSignal for FromStream<S> + where + S: Stream + Unpin, + S::Item: Frame + Unpin { + type Frame = S::Item; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Frame> { + let s = self.get_mut(); + match s.next.take() { + Some(v) => { + match S::poll_next(Pin::new(&mut s.stream), cx) { + Poll::Ready(val) => { + s.next = val; + Poll::Ready(v) + } + Poll::Pending => Poll::Pending + } + } + None => Poll::Ready(<Self::Frame as Frame>::EQUILIBRIUM) + } + } +} + + +struct FromInterleavedSamplesStream<S, F> + where + F: Frame { + stream: S, + next: Option<Vec<F::Sample>>, +} + +async fn from_interleaved_samples_stream<S, F>(mut stream: S) -> FromInterleavedSamplesStream<S, F> + where + S: Stream + Unpin, + S::Item: Sample, + F: Frame<Sample = S::Item> { + let mut data = Vec::with_capacity(F::CHANNELS); + for _ in 0..F::CHANNELS { + data.push(stream.next().await); + } + let data = data.into_iter().flat_map(identity).collect::<Vec<_>>(); + FromInterleavedSamplesStream { stream, next: if data.len() == F::CHANNELS { Some(data) } else { None } } +} + +impl<S, F> StreamingSignal for FromInterleavedSamplesStream<S, F> + where + S: Stream + Unpin, + S::Item: Sample + Unpin, + F: Frame<Sample = S::Item> { + type Frame = F; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Frame> { + let s = self.get_mut(); + if s.next.is_some() { + if s.next.as_ref().unwrap().len() == F::CHANNELS { + let mut data_buf = mem::replace(&mut s.next, Some(Vec::new())).unwrap().into_iter(); + Poll::Ready(F::from_samples(&mut data_buf).unwrap()) + } else { + match S::poll_next(Pin::new(&mut s.stream), cx) { + Poll::Ready(Some(v)) => { + s.next.as_mut().unwrap().push(v); + Poll::Pending + } + Poll::Ready(None) => { + s.next = None; + Poll::Ready(F::EQUILIBRIUM) + } + Poll::Pending => Poll::Pending, + } + } + } else { + Poll::Ready(F::EQUILIBRIUM) + } + } +}
\ No newline at end of file diff --git a/mumd/src/audio/input.rs b/mumd/src/audio/input.rs index fe0d21f..914891b 100644 --- a/mumd/src/audio/input.rs +++ b/mumd/src/audio/input.rs @@ -4,6 +4,7 @@ use mumble_protocol::voice::VoicePacketPayload; use std::collections::VecDeque; use std::sync::{Arc, Mutex}; use tokio::sync::{mpsc, watch}; +use log::*; pub fn callback<T: Sample>( mut opus_encoder: opus::Encoder, @@ -26,6 +27,7 @@ pub fn callback<T: Sample>( let buf = Arc::new(Mutex::new(VecDeque::new())); move |data: &[T], _info: &InputCallbackInfo| { + debug!("{:?}", _info); let mut buf = buf.lock().unwrap(); let input_volume = *input_volume_receiver.borrow(); let out: Vec<f32> = data |
