diff options
| author | Gustav Sörnäs <gustav@sornas.net> | 2021-03-24 18:33:01 +0100 |
|---|---|---|
| committer | Gustav Sörnäs <gustav@sornas.net> | 2021-03-24 18:35:00 +0100 |
| commit | d6257d720bfb3e6dda8b561101b2eba4ae3b9494 (patch) | |
| tree | e15bcbf562ef125709e6a7079be494268d384ed8 /mumd/src/audio | |
| parent | b081b5850e1b033b8841008093e2ab3babc152df (diff) | |
| download | mum-d6257d720bfb3e6dda8b561101b2eba4ae3b9494.tar.gz | |
move noise gate to own file
Diffstat (limited to 'mumd/src/audio')
| -rw-r--r-- | mumd/src/audio/noise_gate.rs | 335 |
1 files changed, 335 insertions, 0 deletions
diff --git a/mumd/src/audio/noise_gate.rs b/mumd/src/audio/noise_gate.rs new file mode 100644 index 0000000..824ffe0 --- /dev/null +++ b/mumd/src/audio/noise_gate.rs @@ -0,0 +1,335 @@ +use dasp_frame::Frame; +use dasp_sample::{SignedSample, ToSample, Sample}; +use dasp_signal::Signal; +use futures_util::stream::Stream; +use opus::Channels; +use std::future::Future; +use std::pin::Pin; +use std::task::{Context, Poll}; + +type FloatSample<S> = <<S as Frame>::Sample as Sample>::Float; + +pub struct StreamingNoiseGate<S: StreamingSignal> { + open: usize, + signal: S, + deactivation_delay: usize, + alltime_high: FloatSample<S::Frame>, +} + +impl<S: StreamingSignal> StreamingNoiseGate<S> { + pub fn new( + signal: S, + deactivation_delay: usize, + ) -> StreamingNoiseGate<S> { + Self { + open: 0, + signal, + deactivation_delay, + alltime_high: FloatSample::<S::Frame>::EQUILIBRIUM, + } + } +} + +impl<S> StreamingSignal for StreamingNoiseGate<S> + where + S: StreamingSignal + Unpin, + FloatSample<S::Frame>: Unpin, + <S as StreamingSignal>::Frame: Unpin { + type Frame = S::Frame; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Frame> { + const MUTE_PERCENTAGE: f32 = 0.1; + + let s = self.get_mut(); + + let frame = match S::poll_next(Pin::new(&mut s.signal), cx) { + Poll::Ready(v) => v, + Poll::Pending => return Poll::Pending, + }; + + if let Some(highest) = frame.to_float_frame().channels().find(|e| abs(e.clone()) > s.alltime_high) { + s.alltime_high = highest; + } + + match s.open { + 0 => { + if frame.to_float_frame().channels().any(|e| abs(e) >= s.alltime_high.mul_amp(MUTE_PERCENTAGE.to_sample())) { + s.open = s.deactivation_delay; + } + } + _ => { + if frame.to_float_frame().channels().any(|e| abs(e) >= s.alltime_high.mul_amp(MUTE_PERCENTAGE.to_sample())) { + s.open = s.deactivation_delay; + } else { + s.open -= 1; + } + } + } + + if s.open != 0 { + Poll::Ready(frame) + } else { + Poll::Ready(<S::Frame as 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 + } +} + +pub trait StreamingSignal { + type Frame: Frame; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Frame>; + + fn is_exhausted(&self) -> bool { + false + } +} + +impl<S> StreamingSignal for S + where + S: Signal + Unpin { + type Frame = S::Frame; + + fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Frame> { + Poll::Ready(self.get_mut().next()) + } +} + +pub trait StreamingSignalExt: StreamingSignal { + fn next(&mut self) -> Next<'_, Self> { + Next { + stream: self + } + } + + fn into_interleaved_samples(self) -> IntoInterleavedSamples<Self> + where + Self: Sized { + IntoInterleavedSamples { signal: self, current_frame: None } + } +} + +impl<S> StreamingSignalExt for S + where S: StreamingSignal {} + +pub 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 + } + } +} + +pub struct IntoInterleavedSamples<S: StreamingSignal> { + signal: S, + current_frame: Option<<S::Frame as Frame>::Channels>, +} + +impl<S> Stream for IntoInterleavedSamples<S> + where + S: StreamingSignal + Unpin, + <<S as StreamingSignal>::Frame as Frame>::Channels: Unpin { + type Item = <S::Frame as Frame>::Sample; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> { + let s = self.get_mut(); + loop { + if s.current_frame.is_some() { + if let Some(channel) = s.current_frame.as_mut().unwrap().next() { + return Poll::Ready(Some(channel)); + } + } + match S::poll_next(Pin::new(&mut s.signal), cx) { + Poll::Ready(val) => { + s.current_frame = Some(val.channels()); + } + Poll::Pending => return Poll::Pending, + } + } + } +} + +struct FromStream<S> { + stream: S, + underlying_exhausted: bool, +} + +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(); + if s.underlying_exhausted { + return Poll::Ready(<Self::Frame as Frame>::EQUILIBRIUM); + } + match S::poll_next(Pin::new(&mut s.stream), cx) { + Poll::Ready(Some(val)) => { + Poll::Ready(val) + } + Poll::Ready(None) => { + s.underlying_exhausted = true; + return Poll::Ready(<Self::Frame as Frame>::EQUILIBRIUM); + } + Poll::Pending => Poll::Pending, + } + } + + fn is_exhausted(&self) -> bool { + self.underlying_exhausted + } +} + + +pub struct FromInterleavedSamplesStream<S, F> + where + F: Frame { + stream: S, + next_buf: Vec<F::Sample>, + underlying_exhausted: bool, +} + +pub fn from_interleaved_samples_stream<S, F>(stream: S) -> FromInterleavedSamplesStream<S, F> + where + S: Stream + Unpin, + S::Item: Sample, + F: Frame<Sample = S::Item> { + FromInterleavedSamplesStream { + stream, + next_buf: Vec::new(), + underlying_exhausted: false, + } +} + +impl<S, F> StreamingSignal for FromInterleavedSamplesStream<S, F> + where + S: Stream + Unpin, + S::Item: Sample + Unpin, + F: Frame<Sample = S::Item> + Unpin { + type Frame = F; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Frame> { + let s = self.get_mut(); + if s.underlying_exhausted { + return Poll::Ready(F::EQUILIBRIUM); + } + while s.next_buf.len() < F::CHANNELS { + match S::poll_next(Pin::new(&mut s.stream), cx) { + Poll::Ready(Some(v)) => { + s.next_buf.push(v); + } + Poll::Ready(None) => { + s.underlying_exhausted = true; + return Poll::Ready(F::EQUILIBRIUM); + } + Poll::Pending => return Poll::Pending, + } + } + + let mut data = s.next_buf.iter().cloned(); + let n = F::from_samples(&mut data).unwrap(); + s.next_buf.clear(); + Poll::Ready(n) + } + + fn is_exhausted(&self) -> bool { + self.underlying_exhausted + } +} + +pub struct OpusEncoder<S> { + encoder: opus::Encoder, + frame_size: u32, + sample_rate: u32, + stream: S, + input_buffer: Vec<f32>, + exhausted: bool, +} + +impl<S, I> OpusEncoder<S> + where + S: Stream<Item = I>, + I: ToSample<f32> { + pub fn new(frame_size: u32, sample_rate: u32, channels: usize, stream: S) -> Self { + let encoder = opus::Encoder::new( + sample_rate, + match channels { + 1 => Channels::Mono, + 2 => Channels::Stereo, + _ => unimplemented!( + "Only 1 or 2 channels supported, got {})", + channels + ), + }, + opus::Application::Voip, + ).unwrap(); + Self { + encoder, + frame_size, + sample_rate, + stream, + input_buffer: Vec::new(), + exhausted: false, + } + } +} + +impl<S, I> Stream for OpusEncoder<S> + where + S: Stream<Item = I> + Unpin, + I: Sample + ToSample<f32> { + type Item = Vec<u8>; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> { + let s = self.get_mut(); + if s.exhausted { + return Poll::Ready(None); + } + let opus_frame_size = (s.frame_size * s.sample_rate / 400) as usize; + loop { + while s.input_buffer.len() < opus_frame_size { + match S::poll_next(Pin::new(&mut s.stream), cx) { + Poll::Ready(Some(v)) => { + s.input_buffer.push(v.to_sample::<f32>()); + } + Poll::Ready(None) => { + s.exhausted = true; + return Poll::Ready(None); + } + Poll::Pending => return Poll::Pending, + } + } + if s.input_buffer.iter().any(|&e| e != 0.0) { + break; + } + s.input_buffer.clear(); + } + + let encoded = s.encoder.encode_vec_float(&s.input_buffer, opus_frame_size).unwrap(); + s.input_buffer.clear(); + Poll::Ready(Some(encoded)) + } +} |
