diff options
| author | Gustav Sörnäs <gustav@sornas.net> | 2021-06-19 19:37:29 +0200 |
|---|---|---|
| committer | Gustav Sörnäs <gustav@sornas.net> | 2021-06-19 19:37:29 +0200 |
| commit | 99ed190ca9691a46719c8a88d3f2437ba8e3c2ff (patch) | |
| tree | 0aa604663f704017f9d1705e881476d6631b12a3 /mumd | |
| parent | ee13c1662eba47ffe2716bdc26bbfcd0609ff521 (diff) | |
| parent | b2e9021341794ab52edcf4598c8d454515f758c4 (diff) | |
| download | mum-99ed190ca9691a46719c8a88d3f2437ba8e3c2ff.tar.gz | |
Merge remote-tracking branch 'origin/main' into ogg
Diffstat (limited to 'mumd')
| -rw-r--r-- | mumd/src/audio.rs | 21 | ||||
| -rw-r--r-- | mumd/src/audio/input.rs | 15 | ||||
| -rw-r--r-- | mumd/src/audio/output.rs | 38 | ||||
| -rw-r--r-- | mumd/src/state/channel.rs | 13 |
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, + ) } } |
