diff options
Diffstat (limited to 'mumd')
| -rw-r--r-- | mumd/src/audio.rs | 100 | ||||
| -rw-r--r-- | mumd/src/audio/output.rs | 165 | ||||
| -rw-r--r-- | mumd/src/error.rs | 2 | ||||
| -rw-r--r-- | mumd/src/state.rs | 4 |
4 files changed, 136 insertions, 135 deletions
diff --git a/mumd/src/audio.rs b/mumd/src/audio.rs index 814050b..28a0707 100644 --- a/mumd/src/audio.rs +++ b/mumd/src/audio.rs @@ -3,14 +3,13 @@ pub mod output; mod noise_gate; use crate::audio::input::{DefaultAudioInputDevice, AudioInputDevice}; -use crate::audio::output::ClientStream; +use crate::audio::output::{DefaultAudioOutputDevice, AudioOutputDevice, ClientStream}; use crate::audio::noise_gate::{from_interleaved_samples_stream, OpusEncoder, StreamingNoiseGate, StreamingSignalExt}; -use crate::error::{AudioError, AudioStream}; +use crate::error::AudioError; use crate::network::VoiceStreamType; use crate::state::StatePhase; -use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; -use cpal::{SampleFormat, SampleRate, StreamConfig}; +use cpal::SampleRate; use dasp_interpolate::linear::Linear; use dasp_signal::{self as signal, Signal}; use futures_util::stream::Stream; @@ -119,11 +118,7 @@ impl AudioInput { } pub struct AudioOutput { - config: StreamConfig, - _stream: cpal::Stream, - - volume_sender: watch::Sender<f32>, - + device: DefaultAudioOutputDevice, user_volumes: Arc<Mutex<HashMap<u32, (f32, bool)>>>, client_streams: Arc<Mutex<ClientStream>>, @@ -133,71 +128,20 @@ pub struct AudioOutput { impl AudioOutput { pub fn new(output_volume: f32) -> Result<Self, AudioError> { - let sample_rate = SampleRate(SAMPLE_RATE); - - let host = cpal::default_host(); - let output_device = host - .default_output_device() - .ok_or(AudioError::NoDevice(AudioStream::Output))?; - let output_supported_config = output_device - .supported_output_configs() - .map_err(|e| AudioError::NoConfigs(AudioStream::Output, 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::Output))? - .with_sample_rate(sample_rate); - let output_supported_sample_format = output_supported_config.sample_format(); - let output_config: StreamConfig = output_supported_config.into(); - - let err_fn = |err| error!("An error occurred on the output audio stream: {}", err); - let user_volumes = Arc::new(std::sync::Mutex::new(HashMap::new())); - let (output_volume_sender, output_volume_receiver) = watch::channel::<f32>(output_volume); - let client_streams = Arc::new(std::sync::Mutex::new(ClientStream::new(sample_rate.0, output_config.channels))); - let output_stream = match output_supported_sample_format { - SampleFormat::F32 => output_device.build_output_stream( - &output_config, - output::curry_callback::<f32>( - Arc::clone(&client_streams), - output_volume_receiver, - Arc::clone(&user_volumes), - ), - err_fn, - ), - SampleFormat::I16 => output_device.build_output_stream( - &output_config, - output::curry_callback::<i16>( - Arc::clone(&client_streams), - output_volume_receiver, - Arc::clone(&user_volumes), - ), - err_fn, - ), - SampleFormat::U16 => output_device.build_output_stream( - &output_config, - output::curry_callback::<u16>( - Arc::clone(&client_streams), - output_volume_receiver, - Arc::clone(&user_volumes), - ), - err_fn, - ), - } - .map_err(|e| AudioError::InvalidStream(AudioStream::Output, e))?; - output_stream.play().map_err(|e| AudioError::OutputPlayError(e))?; + let default = DefaultAudioOutputDevice::new( + output_volume, + Arc::clone(&user_volumes), + )?; + default.play()?; + + let client_streams = default.get_client_streams(); let mut res = Self { - config: output_config, - _stream: output_stream, - client_streams, + device: default, sounds: HashMap::new(), - volume_sender: output_volume_sender, + client_streams, user_volumes, }; res.load_sound_effects(&[]); @@ -246,7 +190,7 @@ impl AudioOutput { .until_exhausted() // if the source audio is stereo and is being played as mono, discard the right audio .flat_map( - |e| if self.config.channels == 1 { + |e| if self.device.get_num_channels() == 1 { vec![e[0]] } else { e.to_vec() @@ -262,24 +206,12 @@ impl AudioOutput { self.client_streams.lock().unwrap().decode_packet( Some((stream_type, session_id)), payload, - self.config.channels as usize, + self.device.get_num_channels(), ); } - pub fn add_client(&self, session_id: u32) { - self.client_streams.lock().unwrap().add_client(session_id); - } - - pub fn remove_client(&self, session_id: u32) { - self.client_streams.lock().unwrap().remove_client(session_id); - } - - pub fn clear_clients(&self) { - self.client_streams.lock().unwrap().clear_clients(); - } - pub fn set_volume(&self, output_volume: f32) { - self.volume_sender.send(output_volume).unwrap(); + self.device.set_volume(output_volume); } pub fn set_user_volume(&self, id: u32, volume: f32) { diff --git a/mumd/src/audio/output.rs b/mumd/src/audio/output.rs index 797cf84..3664be8 100644 --- a/mumd/src/audio/output.rs +++ b/mumd/src/audio/output.rs @@ -1,10 +1,13 @@ use crate::network::VoiceStreamType; -use log::*; +use crate::audio::SAMPLE_RATE; +use crate::error::{AudioError, AudioStream}; -use cpal::{OutputCallbackInfo, Sample}; +use log::*; +use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; +use cpal::{SampleFormat, SampleRate, StreamConfig, OutputCallbackInfo, Sample}; use mumble_protocol::voice::VoicePacketPayload; use opus::Channels; -use std::collections::{HashMap, VecDeque, hash_map::Entry}; +use std::collections::{HashMap, VecDeque}; use std::ops::AddAssign; use std::sync::{Arc, Mutex}; use tokio::sync::watch; @@ -58,53 +61,13 @@ impl ClientStream { F: FnOnce(ConsumerInput) { let iter = self.buffer.iter_mut(); consumer(iter); + //remove empty Vec + self.buffer.retain(|_, v| v.is_empty()); } pub fn extend(&mut self, key: ClientStreamKey, values: &[f32]) { - match self.buffer.entry(key) { - Entry::Occupied(mut entry) => { - entry.get_mut().extend(values.iter().copied()); - } - Entry::Vacant(_) => { - match key { - None => warn!("Can't find session None"), - Some(key) => warn!("Can't find session id {}", key.1), - } - } - } - } - - pub fn add_client(&mut self, session_id: u32) { - for stream_type in [VoiceStreamType::TCP, VoiceStreamType::UDP].iter() { - match self.buffer.entry(Some((*stream_type, session_id))) { - Entry::Occupied(_) => { - warn!("Session id {} already exists", session_id); - } - Entry::Vacant(entry) => { - entry.insert(VecDeque::new()); - } - } - } - } - - pub fn remove_client(&mut self, session_id: u32) { - for stream_type in [VoiceStreamType::TCP, VoiceStreamType::UDP].iter() { - match self.buffer.entry(Some((*stream_type, session_id))) { - Entry::Occupied(entry) => { - entry.remove(); - } - Entry::Vacant(_) => { - warn!( - "Tried to remove session id {} that doesn't exist", - session_id - ); - } - } - } - } - - pub fn clear_clients(&mut self) { - self.buffer.retain(|k , _| k.is_none()); + let entry = self.buffer.entry(key).or_insert(VecDeque::new()); + entry.extend(values.iter().copied()); } } @@ -134,6 +97,114 @@ impl SaturatingAdd for u16 { } } +pub trait AudioOutputDevice { + fn play(&self) -> Result<(), AudioError>; + fn pause(&self) -> Result<(), AudioError>; + fn set_volume(&self, volume: f32); + fn get_num_channels(&self) -> usize; + fn get_client_streams(&self) -> Arc<Mutex<ClientStream>>; +} + +pub struct DefaultAudioOutputDevice { + config: StreamConfig, + _stream: cpal::Stream, + client_streams: Arc<Mutex<ClientStream>>, + volume_sender: watch::Sender<f32>, +} + +impl DefaultAudioOutputDevice { + pub fn new( + output_volume: f32, + user_volumes: Arc<Mutex<HashMap<u32, (f32, bool)>>>, + ) -> Result<Self, AudioError> { + let sample_rate = SampleRate(SAMPLE_RATE); + + let host = cpal::default_host(); + let output_device = host + .default_output_device() + .ok_or(AudioError::NoDevice(AudioStream::Output))?; + let output_supported_config = output_device + .supported_output_configs() + .map_err(|e| AudioError::NoConfigs(AudioStream::Output, 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::Output))? + .with_sample_rate(sample_rate); + let output_supported_sample_format = output_supported_config.sample_format(); + let output_config: StreamConfig = output_supported_config.into(); + let client_streams = Arc::new(std::sync::Mutex::new(ClientStream::new(sample_rate.0, output_config.channels))); + + let err_fn = |err| error!("An error occurred on the output audio stream: {}", err); + + let (output_volume_sender, output_volume_receiver) = watch::channel::<f32>(output_volume); + + let output_stream = match output_supported_sample_format { + SampleFormat::F32 => output_device.build_output_stream( + &output_config, + curry_callback::<f32>( + Arc::clone(&client_streams), + output_volume_receiver, + user_volumes, + ), + err_fn, + ), + SampleFormat::I16 => output_device.build_output_stream( + &output_config, + curry_callback::<i16>( + Arc::clone(&client_streams), + output_volume_receiver, + user_volumes, + ), + err_fn, + ), + SampleFormat::U16 => output_device.build_output_stream( + &output_config, + curry_callback::<u16>( + Arc::clone(&client_streams), + output_volume_receiver, + user_volumes, + ), + err_fn, + ), + } + .map_err(|e| AudioError::InvalidStream(AudioStream::Output, e))?; + + Ok(Self { + config: output_config, + _stream: output_stream, + volume_sender: output_volume_sender, + client_streams, + }) + } +} + +impl AudioOutputDevice for DefaultAudioOutputDevice { + fn play(&self) -> Result<(), AudioError> { + self._stream.play().map_err(|e| AudioError::OutputPlayError(e)) + } + + fn pause(&self) -> Result<(), AudioError> { + self._stream.pause().map_err(|e| AudioError::OutputPauseError(e)) + } + + fn set_volume(&self, volume: f32) { + self.volume_sender.send(volume).unwrap(); + } + + fn get_num_channels(&self) -> usize { + self.config.channels as usize + } + + fn get_client_streams(&self) -> Arc<Mutex<ClientStream>> { + Arc::clone(&self.client_streams) + } +} + pub fn curry_callback<T: Sample + AddAssign + SaturatingAdd + std::fmt::Display>( user_bufs: Arc<Mutex<ClientStream>>, output_volume_receiver: watch::Receiver<f32>, diff --git a/mumd/src/error.rs b/mumd/src/error.rs index 2887dd0..eb63df8 100644 --- a/mumd/src/error.rs +++ b/mumd/src/error.rs @@ -86,6 +86,7 @@ pub enum AudioError { NoSupportedConfig(AudioStream), InvalidStream(AudioStream, cpal::BuildStreamError), OutputPlayError(cpal::PlayStreamError), + OutputPauseError(cpal::PauseStreamError), InputPlayError(cpal::PlayStreamError), InputPauseError(cpal::PauseStreamError), } @@ -98,6 +99,7 @@ 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::OutputPauseError(e) => write!(f, "Playback error: {}", e), AudioError::InputPlayError(e) => write!(f, "Recording error: {}", e), AudioError::InputPauseError(e) => write!(f, "Recording error: {}", e), } diff --git a/mumd/src/state.rs b/mumd/src/state.rs index 313a985..132da74 100644 --- a/mumd/src/state.rs +++ b/mumd/src/state.rs @@ -371,7 +371,6 @@ impl State { } self.server = None; - self.audio_output.clear_clients(); self.phase_watcher .0 @@ -457,8 +456,6 @@ impl State { *self.server_mut().unwrap().session_id_mut() = Some(session); } else { // this is someone else - self.audio_output_mut().add_client(session); - // send notification only if we've passed the connecting phase if matches!(*self.phase_receiver().borrow(), StatePhase::Connected(_)) { let channel_id = msg.get_channel_id(); @@ -569,7 +566,6 @@ impl State { } } - self.audio_output().remove_client(msg.get_session()); self.server_mut() .unwrap() .users_mut() |
