diff options
Diffstat (limited to 'mumd/src/audio.rs')
| -rw-r--r-- | mumd/src/audio.rs | 132 |
1 files changed, 9 insertions, 123 deletions
diff --git a/mumd/src/audio.rs b/mumd/src/audio.rs index 0424033..fb22b44 100644 --- a/mumd/src/audio.rs +++ b/mumd/src/audio.rs @@ -4,74 +4,29 @@ pub mod input; pub mod output; +pub mod sound_effects; pub mod transformers; -use crate::audio::input::{AudioInputDevice, DefaultAudioInputDevice}; -use crate::audio::output::{AudioOutputDevice, ClientStream, DefaultAudioOutputDevice}; use crate::error::AudioError; use crate::network::VoiceStreamType; use crate::state::StatePhase; -use dasp_interpolate::linear::Linear; -use dasp_signal::{self as signal, Signal}; use futures_util::stream::Stream; use futures_util::StreamExt; -use log::*; use mumble_protocol::voice::{VoicePacket, VoicePacketPayload}; use mumble_protocol::Serverbound; use mumlib::config::SoundEffect; -use std::borrow::Cow; use std::collections::{hash_map::Entry, HashMap}; -use std::convert::TryFrom; -use std::fmt::Debug; -use std::fs::File; -use std::io::Read; use std::sync::{Arc, Mutex}; -use strum::IntoEnumIterator; -use strum_macros::EnumIter; use tokio::sync::watch; +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; -/// 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, - ServerDisconnect, - UserConnected, - UserDisconnected, - UserJoinedChannel, - UserLeftChannel, - Mute, - Unmute, - Deafen, - Undeafen, -} - -impl TryFrom<&str> for NotificationEvents { - type Error = (); - fn try_from(s: &str) -> Result<Self, Self::Error> { - match s { - "server_connect" => Ok(NotificationEvents::ServerConnect), - "server_disconnect" => Ok(NotificationEvents::ServerDisconnect), - "user_connected" => Ok(NotificationEvents::UserConnected), - "user_disconnected" => Ok(NotificationEvents::UserDisconnected), - "user_joined_channel" => Ok(NotificationEvents::UserJoinedChannel), - "user_left_channel" => Ok(NotificationEvents::UserLeftChannel), - "mute" => Ok(NotificationEvents::Mute), - "unmute" => Ok(NotificationEvents::Unmute), - "deafen" => Ok(NotificationEvents::Deafen), - "undeafen" => Ok(NotificationEvents::Undeafen), - _ => { - warn!("Unknown notification event '{}' in config", s); - Err(()) - } - } - } -} - /// Input audio state. Input audio is picket up from an [AudioInputDevice] (e.g. /// a microphone) and sent over the network. pub struct AudioInput { @@ -158,61 +113,10 @@ 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() - .filter_map(|sound_effect| { - let (event, file) = (&sound_effect.event, &sound_effect.file); - if let Ok(event) = NotificationEvents::try_from(event.as_str()) { - Some((event, file)) - } else { - None - } - }) - .collect(); - - // This makes sure that every [NotificationEvent] is present in [self.sounds]. - self.sounds = NotificationEvents::iter() - .map(|event| { - let bytes = overrides - .get(&event) - .map(|file| get_sfx(file)) - .unwrap_or_else(get_default_sfx); - let reader = hound::WavReader::new(bytes.as_ref()).unwrap(); - let spec = reader.spec(); - let samples = match spec.sample_format { - hound::SampleFormat::Float => reader - .into_samples::<f32>() - .map(|e| e.unwrap()) - .collect::<Vec<_>>(), - hound::SampleFormat::Int => reader - .into_samples::<i16>() - .map(|e| cpal::Sample::to_f32(&e.unwrap())) - .collect::<Vec<_>>(), - }; - let iter: Box<dyn Iterator<Item = f32>> = match spec.channels { - 1 => Box::new(samples.into_iter().flat_map(|e| vec![e, e])), - 2 => Box::new(samples.into_iter()), - _ => unimplemented!("Only mono and stereo sound is supported. See #80."), - }; - let mut signal = signal::from_interleaved_samples_iter::<_, [f32; 2]>(iter); - let interp = Linear::new(Signal::next(&mut signal), Signal::next(&mut signal)); - let samples = signal - .from_hz_to_hz(interp, spec.sample_rate as f64, SAMPLE_RATE as f64) - .until_exhausted() - // if the source audio is stereo and is being played as mono, discard the right audio - .flat_map(|e| { - if self.device.num_channels() == 1 { - vec![e[0]] - } else { - e.to_vec() - } - }) - .collect::<Vec<f32>>(); - (event, samples) - }) - .collect(); + /// Sets the sound effects according to some overrides, using some default + /// value if an event isn't overriden. + pub fn load_sound_effects(&mut self, overrides: &[SoundEffect]) { + self.sounds = sound_effects::load_sound_effects(overrides, self.device.num_channels()); } /// Decodes a voice packet. @@ -263,21 +167,3 @@ impl AudioOutput { self.client_streams.lock().unwrap().add_sound_effect(samples); } } - -/// Reads a sound effect from disk. -// moo -fn get_sfx(file: &str) -> Cow<'static, [u8]> { - let mut buf: Vec<u8> = Vec::new(); - if let Ok(mut file) = File::open(file) { - file.read_to_end(&mut buf).unwrap(); - Cow::from(buf) - } else { - warn!("File not found: '{}'", file); - get_default_sfx() - } -} - -/// Gets the default sound effect. -fn get_default_sfx() -> Cow<'static, [u8]> { - Cow::from(include_bytes!("fallback_sfx.wav").as_ref()) -} |
