aboutsummaryrefslogtreecommitdiffstats
path: root/mumd/src
diff options
context:
space:
mode:
authorGustav Sörnäs <gustav@sornas.net>2021-04-08 14:42:18 +0200
committerGustav Sörnäs <gustav@sornas.net>2021-06-13 12:17:58 +0200
commit64978fa17b6e0882d6bacb6e626b0bc9ead2c81d (patch)
tree0b872f676244525b0c3cb017c16c5094dc777e01 /mumd/src
parenteee62e0892b1247ce321cd30747ab90d58f49732 (diff)
downloadmum-64978fa17b6e0882d6bacb6e626b0bc9ead2c81d.tar.gz
document mumd::audio
Diffstat (limited to 'mumd/src')
-rw-r--r--mumd/src/audio.rs25
-rw-r--r--mumd/src/audio/input.rs12
-rw-r--r--mumd/src/audio/output.rs16
3 files changed, 52 insertions, 1 deletions
diff --git a/mumd/src/audio.rs b/mumd/src/audio.rs
index 2e20583..46f7050 100644
--- a/mumd/src/audio.rs
+++ b/mumd/src/audio.rs
@@ -1,3 +1,7 @@
+//! All things audio.
+//!
+//! Audio is handled mostly as signals from [dasp_signal]. Input/output is handled by [cpal].
+
pub mod input;
pub mod output;
pub mod transformers;
@@ -27,8 +31,11 @@ use strum::IntoEnumIterator;
use strum_macros::EnumIter;
use tokio::sync::watch;
+/// The sample rate used internally.
const SAMPLE_RATE: u32 = 48000;
+/// All types of notifications that can be shown. Each notification can be bound to its own audio
+/// file.
#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash, EnumIter)]
pub enum NotificationEvents {
ServerConnect,
@@ -65,9 +72,11 @@ impl TryFrom<&str> for NotificationEvents {
}
}
+/// Input audio state.
pub struct AudioInput {
device: DefaultAudioInputDevice,
+ /// Voice packets received from the network.
channel_receiver:
Arc<tokio::sync::Mutex<Box<dyn Stream<Item = VoicePacket<Serverbound>> + Unpin>>>,
}
@@ -114,10 +123,15 @@ impl AudioInput {
pub struct AudioOutput {
device: DefaultAudioOutputDevice,
+ /// The volume and mute-status of a user ID.
user_volumes: Arc<Mutex<HashMap<u32, (f32, bool)>>>,
+ /// The client stream per user ID. A separate stream is kept for UDP and TCP.
+ ///
+ /// Shared with [DefaultAudioOutputDevice].
client_streams: Arc<Mutex<ClientStream>>,
+ /// Which sound effect should be played on an event.
sounds: HashMap<NotificationEvents, Vec<f32>>,
}
@@ -140,6 +154,7 @@ impl AudioOutput {
Ok(res)
}
+ /// Loads sound effects, getting unspecified effects from [get_default_sfx].
pub fn load_sound_effects(&mut self, sound_effects: &[SoundEffect]) {
let overrides: HashMap<_, _> = sound_effects
.iter()
@@ -153,6 +168,7 @@ impl AudioOutput {
})
.collect();
+ // This makes sure that every [NotificationEvent] is present in [self.sounds].
self.sounds = NotificationEvents::iter()
.map(|event| {
let bytes = overrides
@@ -195,6 +211,7 @@ impl AudioOutput {
.collect();
}
+ /// Decodes a voice packet.
pub fn decode_packet_payload(
&self,
stream_type: VoiceStreamType,
@@ -207,11 +224,13 @@ impl AudioOutput {
.decode_packet((stream_type, session_id), payload);
}
+ /// Sets the volume of the output device.
pub fn set_volume(&self, output_volume: f32) {
self.device.set_volume(output_volume);
}
- pub fn set_user_volume(&self, id: u32, volume: f32) {
+ /// Sets the incoming volume of a user.
+ pub(crate) fn set_user_volume(&self, id: u32, volume: f32) {
match self.user_volumes.lock().unwrap().entry(id) {
Entry::Occupied(mut entry) => {
entry.get_mut().0 = volume;
@@ -222,6 +241,7 @@ impl AudioOutput {
}
}
+ /// Mutes another user.
pub fn set_mute(&self, id: u32, mute: bool) {
match self.user_volumes.lock().unwrap().entry(id) {
Entry::Occupied(mut entry) => {
@@ -233,12 +253,14 @@ impl AudioOutput {
}
}
+ /// Queues a sound effect.
pub fn play_effect(&self, effect: NotificationEvents) {
let samples = self.sounds.get(&effect).unwrap();
self.client_streams.lock().unwrap().extend(None, samples);
}
}
+/// Reads a sound effect from disk.
// moo
fn get_sfx(file: &str) -> Cow<'static, [u8]> {
let mut buf: Vec<u8> = Vec::new();
@@ -251,6 +273,7 @@ fn get_sfx(file: &str) -> Cow<'static, [u8]> {
}
}
+/// Gets the default sound effect.
fn get_default_sfx() -> Cow<'static, [u8]> {
Cow::from(include_bytes!("fallback_sfx.wav").as_ref())
}
diff --git a/mumd/src/audio/input.rs b/mumd/src/audio/input.rs
index a1227e3..10aea91 100644
--- a/mumd/src/audio/input.rs
+++ b/mumd/src/audio/input.rs
@@ -1,3 +1,4 @@
+//! Listens to the microphone and sends it to the networking.
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
use cpal::{InputCallbackInfo, Sample, SampleFormat, SampleRate, StreamConfig};
use log::*;
@@ -8,6 +9,7 @@ use crate::audio::transformers::{NoiseGate, Transformer};
use crate::error::{AudioError, AudioStream};
use crate::state::StatePhase;
+/// Generates a callback that receives [Sample]s and sends them as floats to a [futures_channel::mpsc::Sender].
pub fn callback<T: Sample>(
mut input_sender: futures_channel::mpsc::Sender<Vec<u8>>,
mut transformers: Vec<Box<dyn Transformer + Send + 'static>>,
@@ -44,10 +46,15 @@ pub fn callback<T: Sample>(
}
pub trait AudioInputDevice {
+ /// Starts the device.
fn play(&self) -> Result<(), AudioError>;
+ /// Stops the device.
fn pause(&self) -> Result<(), AudioError>;
+ /// Sets the input volume of the device.
fn set_volume(&self, volume: f32);
+ /// Returns a receiver to this device's values.
fn sample_receiver(&mut self) -> Option<futures_channel::mpsc::Receiver<Vec<u8>>>;
+ /// The amount of channels this device has.
fn num_channels(&self) -> usize;
}
@@ -59,6 +66,7 @@ pub struct DefaultAudioInputDevice {
}
impl DefaultAudioInputDevice {
+ /// Initializes the default audio input.
pub fn new(
input_volume: f32,
phase_watcher: watch::Receiver<StatePhase>,
@@ -162,17 +170,21 @@ impl AudioInputDevice for DefaultAudioInputDevice {
.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) -> Option<futures_channel::mpsc::Receiver<Vec<u8>>> {
self.sample_receiver.take()
}
+
fn num_channels(&self) -> usize {
self.channels as usize
}
diff --git a/mumd/src/audio/output.rs b/mumd/src/audio/output.rs
index a2f6bcc..3d886d6 100644
--- a/mumd/src/audio/output.rs
+++ b/mumd/src/audio/output.rs
@@ -1,3 +1,4 @@
+//! Receives audio packets from the networking and plays them.
use crate::audio::SAMPLE_RATE;
use crate::error::{AudioError, AudioStream};
use crate::network::VoiceStreamType;
@@ -13,6 +14,7 @@ use tokio::sync::watch;
type ClientStreamKey = (VoiceStreamType, u32);
+/// Collected state for client opus decoders and sound effects.
pub struct ClientStream {
buffer_clients: HashMap<ClientStreamKey, (VecDeque<f32>, opus::Decoder)>, //TODO ring buffer?
buffer_effects: VecDeque<f32>,
@@ -44,6 +46,7 @@ impl ClientStream {
})
}
+ /// Decodes a voice packet.
pub fn decode_packet(&mut self, client: ClientStreamKey, payload: VoicePacketPayload) {
match payload {
VoicePacketPayload::Opus(bytes, _eot) => {
@@ -61,6 +64,7 @@ impl ClientStream {
}
}
+ /// Extends either a decoder queue or the buffer effect queue with some received values.
pub fn extend(&mut self, client: Option<ClientStreamKey>, values: &[f32]) {
let buffer = match client {
Some(x) => &mut self.get_client(x).0,
@@ -70,6 +74,12 @@ impl ClientStream {
}
}
+/// Adds two values in some saturating way.
+///
+/// Since we support [f32], [i16] and [u16] we need some way of adding two values
+/// without peaking above/below the edge values. This trait ensures that we can
+/// use all three primitive types as a generic parameter.
+
pub trait SaturatingAdd {
fn saturating_add(self, rhs: Self) -> Self;
}
@@ -107,11 +117,16 @@ pub trait AudioOutputDevice {
pub struct DefaultAudioOutputDevice {
config: StreamConfig,
stream: cpal::Stream,
+ /// The client stream per user ID. A separate stream is kept for UDP and TCP.
+ ///
+ /// Shared with [super::AudioOutput].
client_streams: Arc<Mutex<ClientStream>>,
+ /// Output volume configuration.
volume_sender: watch::Sender<f32>,
}
impl DefaultAudioOutputDevice {
+ /// Initializes the default audio output.
pub fn new(
output_volume: f32,
user_volumes: Arc<Mutex<HashMap<u32, (f32, bool)>>>,
@@ -211,6 +226,7 @@ impl AudioOutputDevice for DefaultAudioOutputDevice {
}
}
+/// Over-engineered way of handling multiple types of samples.
pub fn curry_callback<T: Sample + AddAssign + SaturatingAdd + std::fmt::Display>(
user_bufs: Arc<Mutex<ClientStream>>,
output_volume_receiver: watch::Receiver<f32>,