aboutsummaryrefslogtreecommitdiffstats
path: root/mumd
diff options
context:
space:
mode:
authorGustav Sörnäs <gustav@sornas.net>2021-06-19 19:37:29 +0200
committerGustav Sörnäs <gustav@sornas.net>2021-06-19 19:37:29 +0200
commit99ed190ca9691a46719c8a88d3f2437ba8e3c2ff (patch)
tree0aa604663f704017f9d1705e881476d6631b12a3 /mumd
parentee13c1662eba47ffe2716bdc26bbfcd0609ff521 (diff)
parentb2e9021341794ab52edcf4598c8d454515f758c4 (diff)
downloadmum-99ed190ca9691a46719c8a88d3f2437ba8e3c2ff.tar.gz
Merge remote-tracking branch 'origin/main' into ogg
Diffstat (limited to 'mumd')
-rw-r--r--mumd/src/audio.rs21
-rw-r--r--mumd/src/audio/input.rs15
-rw-r--r--mumd/src/audio/output.rs38
-rw-r--r--mumd/src/state/channel.rs13
4 files changed, 64 insertions, 23 deletions
diff --git a/mumd/src/audio.rs b/mumd/src/audio.rs
index 46fcca4..fb22b44 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 sound_effects;
@@ -20,11 +24,15 @@ use self::input::{AudioInputDevice, DefaultAudioInputDevice};
use self::output::{AudioOutputDevice, ClientStream, DefaultAudioOutputDevice};
use self::sound_effects::NotificationEvents;
+/// The sample rate used internally.
const SAMPLE_RATE: u32 = 48000;
+/// Input audio state. Input audio is picket up from an [AudioInputDevice] (e.g.
+/// a microphone) and sent over the network.
pub struct AudioInput {
device: DefaultAudioInputDevice,
+ /// Outgoing voice packets that should be sent over the network.
channel_receiver:
Arc<tokio::sync::Mutex<Box<dyn Stream<Item = VoicePacket<Serverbound>> + Unpin>>>,
}
@@ -69,12 +77,20 @@ impl AudioInput {
}
}
+/// Audio output state. The audio is received from each client over the network,
+/// decoded, merged and finally played to an [AudioOutputDevice] (e.g. speaker,
+/// headphones, ...).
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>>,
}
@@ -103,6 +119,7 @@ impl AudioOutput {
self.sounds = sound_effects::load_sound_effects(overrides, self.device.num_channels());
}
+ /// Decodes a voice packet.
pub fn decode_packet_payload(
&self,
stream_type: VoiceStreamType,
@@ -115,10 +132,12 @@ 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);
}
+ /// Sets the incoming volume of a user.
pub fn set_user_volume(&self, id: u32, volume: f32) {
match self.user_volumes.lock().unwrap().entry(id) {
Entry::Occupied(mut entry) => {
@@ -130,6 +149,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) => {
@@ -141,6 +161,7 @@ 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().add_sound_effect(samples);
diff --git a/mumd/src/audio/input.rs b/mumd/src/audio/input.rs
index 25d15d5..37ea60a 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>>,
@@ -43,11 +45,19 @@ pub fn callback<T: Sample>(
}
}
+/// Something that can listen to audio and send it somewhere.
+///
+/// One sample is assumed to be an encoded opus frame. See [opus::Encoder].
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 +69,7 @@ pub struct DefaultAudioInputDevice {
}
impl DefaultAudioInputDevice {
+ /// Initializes the default audio input.
pub fn new(
input_volume: f32,
phase_watcher: watch::Receiver<StatePhase>,
@@ -162,17 +173,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 924893a..6aa67dd 100644
--- a/mumd/src/audio/output.rs
+++ b/mumd/src/audio/output.rs
@@ -1,3 +1,5 @@
+//! Receives audio packets from the networking and plays them.
+
use crate::audio::SAMPLE_RATE;
use crate::error::{AudioError, AudioStream};
use crate::network::VoiceStreamType;
@@ -16,12 +18,13 @@ use tokio::sync::watch;
type ClientStreamKey = (VoiceStreamType, u32);
+/// State for decoding audio received from another user.
pub struct ClientAudioData {
buf: Bounded<Vec<f32>>,
output_channels: opus::Channels,
- //we need two since a client can hypothetically send both mono
- //and stereo packets, and we can't switch a decoder on the fly
- //to reuse it
+ // We need both since a client can hypothetically send both mono
+ // and stereo packets, and we can't switch a decoder on the fly
+ // to reuse it.
mono_decoder: opus::Decoder,
stereo_decoder: opus::Decoder,
}
@@ -62,6 +65,7 @@ impl ClientAudioData {
}
}
+/// Collected state for client opus decoders and sound effects.
pub struct ClientStream {
buffer_clients: HashMap<ClientStreamKey, ClientAudioData>,
buffer_effects: VecDeque<f32>,
@@ -90,6 +94,7 @@ impl ClientStream {
)
}
+ /// Decodes a voice packet.
pub fn decode_packet(&mut self, client: ClientStreamKey, payload: VoicePacketPayload) {
match payload {
VoicePacketPayload::Opus(bytes, _eot) => {
@@ -101,12 +106,19 @@ impl ClientStream {
}
}
+ /// Extends the sound effect buffer queue with some received values.
pub fn add_sound_effect(&mut self, values: &[f32]) {
self.buffer_effects.extend(values.iter().copied());
}
}
+/// 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 {
+ /// Adds two values in some saturating way. See trait documentation.
fn saturating_add(self, rhs: Self) -> Self;
}
@@ -140,14 +152,20 @@ pub trait AudioOutputDevice {
fn client_streams(&self) -> Arc<Mutex<ClientStream>>;
}
+/// The default audio output device, as determined by [cpal].
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)>>>,
@@ -180,14 +198,6 @@ impl DefaultAudioOutputDevice {
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_channels = match output_config.channels {
- 1 => opus::Channels::Mono,
- 2 => opus::Channels::Stereo,
- _ => {
- warn!("Trying to output to an unsupported number of channels ({}), defaulting to mono", output_config.channels);
- opus::Channels::Mono
- }
- };
let output_stream = match output_supported_sample_format {
SampleFormat::F32 => output_device.build_output_stream(
@@ -196,7 +206,6 @@ impl DefaultAudioOutputDevice {
Arc::clone(&client_streams),
output_volume_receiver,
user_volumes,
- output_channels,
),
err_fn,
),
@@ -206,7 +215,6 @@ impl DefaultAudioOutputDevice {
Arc::clone(&client_streams),
output_volume_receiver,
user_volumes,
- output_channels,
),
err_fn,
),
@@ -216,7 +224,6 @@ impl DefaultAudioOutputDevice {
Arc::clone(&client_streams),
output_volume_receiver,
user_volumes,
- output_channels,
),
err_fn,
),
@@ -258,11 +265,12 @@ impl AudioOutputDevice for DefaultAudioOutputDevice {
}
}
+/// Returns a function that fills a buffer with audio from client streams
+/// modified according to some audio configuration.
pub fn callback<T: Sample + AddAssign + SaturatingAdd + std::fmt::Display>(
user_bufs: Arc<Mutex<ClientStream>>,
output_volume_receiver: watch::Receiver<f32>,
user_volumes: Arc<Mutex<HashMap<u32, (f32, bool)>>>,
- output_channels: opus::Channels,
) -> impl FnMut(&mut [T], &OutputCallbackInfo) + Send + 'static {
move |data: &mut [T], _info: &OutputCallbackInfo| {
for sample in data.iter_mut() {
diff --git a/mumd/src/state/channel.rs b/mumd/src/state/channel.rs
index 6995e1e..2ed05c5 100644
--- a/mumd/src/state/channel.rs
+++ b/mumd/src/state/channel.rs
@@ -169,13 +169,10 @@ pub fn into_channel(
impl From<&Channel> for mumlib::state::Channel {
fn from(channel: &Channel) -> Self {
- mumlib::state::Channel {
- description: channel.description.clone(),
- links: Vec::new(),
- max_users: channel.max_users,
- name: channel.name.clone(),
- children: Vec::new(),
- users: Vec::new(),
- }
+ mumlib::state::Channel::new(
+ channel.name.clone(),
+ channel.description.clone(),
+ channel.max_users,
+ )
}
}