aboutsummaryrefslogtreecommitdiffstats
path: root/mumd/src
diff options
context:
space:
mode:
Diffstat (limited to 'mumd/src')
-rw-r--r--mumd/src/audio.rs100
-rw-r--r--mumd/src/audio/output.rs165
-rw-r--r--mumd/src/error.rs2
-rw-r--r--mumd/src/state.rs4
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()