aboutsummaryrefslogtreecommitdiffstats
path: root/mumd
diff options
context:
space:
mode:
Diffstat (limited to 'mumd')
-rw-r--r--mumd/src/audio.rs75
-rw-r--r--mumd/src/audio/input.rs110
-rw-r--r--mumd/src/error.rs4
3 files changed, 122 insertions, 67 deletions
diff --git a/mumd/src/audio.rs b/mumd/src/audio.rs
index facca9c..5a839bc 100644
--- a/mumd/src/audio.rs
+++ b/mumd/src/audio.rs
@@ -2,6 +2,7 @@ pub mod input;
pub mod output;
mod noise_gate;
+use crate::audio::input::{DefaultAudioInputDevice, AudioInputDevice};
use crate::audio::output::SaturatingAdd;
use crate::audio::noise_gate::{from_interleaved_samples_stream, OpusEncoder, StreamingNoiseGate, StreamingSignalExt};
use crate::error::{AudioError, AudioStream};
@@ -68,80 +69,23 @@ impl TryFrom<&str> for NotificationEvents {
}
pub struct AudioInput {
- _stream: cpal::Stream,
+ _device: DefaultAudioInputDevice,
channel_receiver: Arc<tokio::sync::Mutex<Box<dyn Stream<Item = VoicePacket<Serverbound>> + Unpin>>>,
- volume_sender: watch::Sender<f32>,
}
impl AudioInput {
pub fn new(input_volume: f32, phase_watcher: watch::Receiver<StatePhase>) -> Result<Self, AudioError> {
+ let mut default = DefaultAudioInputDevice::new(input_volume, phase_watcher)?;
let sample_rate = SampleRate(SAMPLE_RATE);
- let host = cpal::default_host();
-
- let input_device = host
- .default_input_device()
- .ok_or(AudioError::NoDevice(AudioStream::Input))?;
- let input_supported_config = input_device
- .supported_input_configs()
- .map_err(|e| AudioError::NoConfigs(AudioStream::Input, e))?
- .find_map(|c| {
- if c.min_sample_rate() <= sample_rate && c.max_sample_rate() >= sample_rate {
- Some(c)
- } else {
- None
- }
- })
- .ok_or(AudioError::NoSupportedConfig(AudioStream::Input))?
- .with_sample_rate(sample_rate);
- let input_supported_sample_format = input_supported_config.sample_format();
- let input_config: StreamConfig = input_supported_config.into();
-
- let err_fn = |err| error!("An error occurred on the output audio stream: {}", err);
-
- let (sample_sender, sample_receiver) = futures_channel::mpsc::channel(1_000_000);
-
- let (input_volume_sender, input_volume_receiver) = watch::channel::<f32>(input_volume);
-
- let input_stream = match input_supported_sample_format {
- SampleFormat::F32 => input_device.build_input_stream(
- &input_config,
- input::callback::<f32>(
- sample_sender,
- input_volume_receiver,
- phase_watcher,
- ),
- err_fn,
- ),
- SampleFormat::I16 => input_device.build_input_stream(
- &input_config,
- input::callback::<i16>(
- sample_sender,
- input_volume_receiver,
- phase_watcher,
- ),
- err_fn,
- ),
- SampleFormat::U16 => input_device.build_input_stream(
- &input_config,
- input::callback::<u16>(
- sample_sender,
- input_volume_receiver,
- phase_watcher,
- ),
- err_fn,
- ),
- }
- .map_err(|e| AudioError::InvalidStream(AudioStream::Input, e))?;
-
let opus_stream = OpusEncoder::new(
4,
- input_config.sample_rate.0,
- input_config.channels as usize,
+ sample_rate.0,
+ default.get_num_channels(),
StreamingSignalExt::into_interleaved_samples(
StreamingNoiseGate::new(
- from_interleaved_samples_stream::<_, f32>(sample_receiver), //TODO group frames correctly
+ from_interleaved_samples_stream::<_, f32>(default.sample_receiver()), //TODO group frames correctly
10_000
)
)
@@ -156,11 +100,10 @@ impl AudioInput {
}
);
- input_stream.play().map_err(|e| AudioError::OutputPlayError(e))?;
+ default.play()?;
let res = Self {
- _stream: input_stream,
- volume_sender: input_volume_sender,
+ _device: default,
channel_receiver: Arc::new(tokio::sync::Mutex::new(Box::new(opus_stream))),
};
Ok(res)
@@ -171,7 +114,7 @@ impl AudioInput {
}
pub fn set_volume(&self, input_volume: f32) {
- self.volume_sender.send(input_volume).unwrap();
+ self._device.set_volume(input_volume);
}
}
diff --git a/mumd/src/audio/input.rs b/mumd/src/audio/input.rs
index 176747d..f4e9c4c 100644
--- a/mumd/src/audio/input.rs
+++ b/mumd/src/audio/input.rs
@@ -1,8 +1,11 @@
-use cpal::{InputCallbackInfo, Sample};
+use cpal::{InputCallbackInfo, Sample, SampleFormat, SampleRate, StreamConfig};
+use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
use tokio::sync::watch;
use log::*;
use crate::state::StatePhase;
+use crate::audio::SAMPLE_RATE;
+use crate::error::{AudioError, AudioStream};
pub fn callback<T: Sample>(
mut input_sender: futures_channel::mpsc::Sender<f32>,
@@ -24,3 +27,108 @@ pub fn callback<T: Sample>(
}
}
}
+
+pub trait AudioInputDevice {
+ fn play(&self) -> Result<(), AudioError>;
+ fn pause(&self) -> Result<(), AudioError>;
+ fn set_volume(&self, volume: f32);
+ fn sample_receiver(&mut self) -> futures_channel::mpsc::Receiver<f32>;
+ fn get_num_channels(&self) -> usize;
+}
+
+pub struct DefaultAudioInputDevice {
+ _stream: cpal::Stream,
+ sample_receiver: Option<futures_channel::mpsc::Receiver<f32>>,
+ volume_sender: watch::Sender<f32>,
+ channels: u16,
+}
+
+impl DefaultAudioInputDevice {
+ pub fn new(input_volume: f32, phase_watcher: watch::Receiver<StatePhase>) -> Result<Self, AudioError> {
+ let sample_rate = SampleRate(SAMPLE_RATE);
+
+ let host = cpal::default_host();
+
+ let input_device = host
+ .default_input_device()
+ .ok_or(AudioError::NoDevice(AudioStream::Input))?;
+ let input_supported_config = input_device
+ .supported_input_configs()
+ .map_err(|e| AudioError::NoConfigs(AudioStream::Input, e))?
+ .find_map(|c| {
+ if c.min_sample_rate() <= sample_rate && c.max_sample_rate() >= sample_rate {
+ Some(c)
+ } else {
+ None
+ }
+ })
+ .ok_or(AudioError::NoSupportedConfig(AudioStream::Input))?
+ .with_sample_rate(sample_rate);
+ let input_supported_sample_format = input_supported_config.sample_format();
+ let input_config: StreamConfig = input_supported_config.into();
+
+ let err_fn = |err| error!("An error occurred on the output audio stream: {}", err);
+
+ let (sample_sender, sample_receiver) = futures_channel::mpsc::channel(1_000_000);
+
+ let (volume_sender, input_volume_receiver) = watch::channel::<f32>(input_volume);
+
+ let input_stream = match input_supported_sample_format {
+ SampleFormat::F32 => input_device.build_input_stream(
+ &input_config,
+ callback::<f32>(
+ sample_sender,
+ input_volume_receiver,
+ phase_watcher,
+ ),
+ err_fn,
+ ),
+ SampleFormat::I16 => input_device.build_input_stream(
+ &input_config,
+ callback::<i16>(
+ sample_sender,
+ input_volume_receiver,
+ phase_watcher,
+ ),
+ err_fn,
+ ),
+ SampleFormat::U16 => input_device.build_input_stream(
+ &input_config,
+ callback::<u16>(
+ sample_sender,
+ input_volume_receiver,
+ phase_watcher,
+ ),
+ err_fn,
+ ),
+ }
+ .map_err(|e| AudioError::InvalidStream(AudioStream::Input, e))?;
+
+ let res = Self {
+ _stream: input_stream,
+ sample_receiver: Some(sample_receiver),
+ volume_sender,
+ channels: input_config.channels,
+ };
+ Ok(res)
+ }
+}
+
+impl AudioInputDevice for DefaultAudioInputDevice {
+ fn play(&self) -> Result<(), AudioError> {
+ self._stream.play().map_err(|e| AudioError::InputPlayError(e))
+ }
+ fn pause(&self) -> Result<(), AudioError> {
+ self._stream.pause().map_err(|e| AudioError::InputPauseError(e))
+ }
+ fn set_volume(&self, volume: f32) {
+ self.volume_sender.send(volume).unwrap();
+ }
+ fn sample_receiver(&mut self) -> futures_channel::mpsc::Receiver<f32> {
+ let ret = self.sample_receiver.take();
+ ret.unwrap()
+ }
+ fn get_num_channels(&self) -> usize {
+ self.channels as usize
+ }
+}
diff --git a/mumd/src/error.rs b/mumd/src/error.rs
index f7818a1..2887dd0 100644
--- a/mumd/src/error.rs
+++ b/mumd/src/error.rs
@@ -86,6 +86,8 @@ pub enum AudioError {
NoSupportedConfig(AudioStream),
InvalidStream(AudioStream, cpal::BuildStreamError),
OutputPlayError(cpal::PlayStreamError),
+ InputPlayError(cpal::PlayStreamError),
+ InputPauseError(cpal::PauseStreamError),
}
impl fmt::Display for AudioError {
@@ -96,6 +98,8 @@ impl fmt::Display for AudioError {
AudioError::NoSupportedConfig(s) => write!(f, "No supported {} config found", s),
AudioError::InvalidStream(s, e) => write!(f, "Invalid {} stream: {}", s, e),
AudioError::OutputPlayError(e) => write!(f, "Playback error: {}", e),
+ AudioError::InputPlayError(e) => write!(f, "Recording error: {}", e),
+ AudioError::InputPauseError(e) => write!(f, "Recording error: {}", e),
}
}
}