diff options
| -rw-r--r-- | mumd/src/audio.rs | 75 | ||||
| -rw-r--r-- | mumd/src/audio/input.rs | 110 | ||||
| -rw-r--r-- | mumd/src/error.rs | 4 |
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), } } } |
