diff options
| author | Gustav Sörnäs <gustav@sornas.net> | 2021-04-08 14:42:18 +0200 |
|---|---|---|
| committer | Gustav Sörnäs <gustav@sornas.net> | 2021-06-13 12:17:58 +0200 |
| commit | 64978fa17b6e0882d6bacb6e626b0bc9ead2c81d (patch) | |
| tree | 0b872f676244525b0c3cb017c16c5094dc777e01 /mumd/src | |
| parent | eee62e0892b1247ce321cd30747ab90d58f49732 (diff) | |
| download | mum-64978fa17b6e0882d6bacb6e626b0bc9ead2c81d.tar.gz | |
document mumd::audio
Diffstat (limited to 'mumd/src')
| -rw-r--r-- | mumd/src/audio.rs | 25 | ||||
| -rw-r--r-- | mumd/src/audio/input.rs | 12 | ||||
| -rw-r--r-- | mumd/src/audio/output.rs | 16 |
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>, |
