diff options
| author | Gustav Sörnäs <gustav@sornas.net> | 2021-06-25 08:29:02 +0200 |
|---|---|---|
| committer | Gustav Sörnäs <gustav@sornas.net> | 2021-06-26 07:55:51 +0200 |
| commit | a6c574b04802b5522cf1db12caa85e77062a71b8 (patch) | |
| tree | 6d2018e63ec1ba89d1c0e26e0d28c809a03b24e1 | |
| parent | dd6fdbc60f53290af99ba0640c284917a85070e5 (diff) | |
| download | mum-a6c574b04802b5522cf1db12caa85e77062a71b8.tar.gz | |
move hashmap to audio output
| -rw-r--r-- | mumd/src/audio.rs | 30 | ||||
| -rw-r--r-- | mumd/src/audio/sound_effects.rs | 110 | ||||
| -rw-r--r-- | mumd/src/state.rs | 30 |
3 files changed, 71 insertions, 99 deletions
diff --git a/mumd/src/audio.rs b/mumd/src/audio.rs index bbaf2e1..aeedf2b 100644 --- a/mumd/src/audio.rs +++ b/mumd/src/audio.rs @@ -18,12 +18,13 @@ use mumble_protocol::Serverbound; use mumlib::config::SoundEffect; use std::collections::{hash_map::Entry, HashMap}; use std::fmt::Debug; +use std::path::PathBuf; use std::sync::{Arc, Mutex}; use tokio::sync::watch; use self::input::{AudioInputDevice, DefaultAudioInputDevice}; use self::output::{AudioOutputDevice, ClientStream, DefaultAudioOutputDevice}; -use self::sound_effects::NotificationEvents; +use self::sound_effects::{NotificationEvent, SoundEffects, SoundEffectId}; /// The sample rate used internally. const SAMPLE_RATE: u32 = 48000; @@ -101,8 +102,11 @@ pub struct AudioOutput { /// Shared with [DefaultAudioOutputDevice]. client_streams: Arc<Mutex<ClientStream>>, - /// Which sound effect should be played on an event. - sounds: HashMap<NotificationEvents, Vec<f32>>, + /// Opened sound effects. + sound_effects: SoundEffects, + + /// Which file should be played on specific events. + sound_effect_events: HashMap<NotificationEvent, SoundEffectId>, } impl AudioOutput { @@ -116,18 +120,17 @@ impl AudioOutput { let mut res = Self { device: default, - sounds: HashMap::new(), - client_streams, user_volumes, + client_streams, + sound_effects: SoundEffects::new(2), + sound_effect_events: HashMap::new(), }; res.load_sound_effects(&[]); Ok(res) } - /// 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()); + pub fn load_sound_effects(&mut self, sound_effects: &[SoundEffect]) { + todo!() } /// Decodes a voice packet. @@ -173,8 +176,13 @@ impl AudioOutput { } /// Queues a sound effect. - pub fn play_effect(&self, effect: NotificationEvents) { - let samples = self.sounds.get(&effect).unwrap(); + pub fn play_effect(&self, effect: NotificationEvent) { + let id = self + .sound_effect_events + .get(&effect) + .cloned() + .unwrap_or_else(SoundEffects::default_sound_effect); + let samples = &self.sound_effects[id]; self.client_streams.lock().unwrap().add_sound_effect(samples); } } diff --git a/mumd/src/audio/sound_effects.rs b/mumd/src/audio/sound_effects.rs index 06d2e4d..42a9bcd 100644 --- a/mumd/src/audio/sound_effects.rs +++ b/mumd/src/audio/sound_effects.rs @@ -11,6 +11,7 @@ use std::fs::File; #[cfg(feature = "ogg")] use std::io::Cursor; use std::io::Read; +use std::ops::Index; use std::path::{Path, PathBuf}; use strum::IntoEnumIterator; use strum_macros::EnumIter; @@ -22,7 +23,6 @@ pub struct SoundEffectId(usize); pub struct SoundEffects { data: Vec<Vec<f32>>, - loaded_paths: HashMap<PathBuf, SoundEffectId>, num_channels: usize, } @@ -30,7 +30,7 @@ pub struct SoundEffects { impl fmt::Debug for SoundEffects { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SoundEffects") - .field("loaded_paths", &self.loaded_paths) + .field("num_channels", &self.num_channels) .finish_non_exhaustive() } } @@ -39,28 +39,29 @@ impl SoundEffects { pub fn new(num_channels: usize) -> Self { SoundEffects { data: vec![unpack_audio(get_default_sfx(), AudioFileKind::Wav).unwrap().0], - loaded_paths: HashMap::new(), num_channels, } } - pub fn get<P: AsRef<Path>>(&mut self, path: &P) -> &[f32] { - let idx = match self.loaded_paths.entry(path.as_ref().to_owned()) { - Entry::Occupied(o) => *o.get(), - Entry::Vacant(v) => { - if let Ok(samples) = open_and_unpack_audio(v.key(), self.num_channels) { - let idx = SoundEffectId(self.data.len()); - self.data.push(samples); - v.insert(idx); - idx - } else { - // Default sound effect - SoundEffectId(0) - } + pub fn open<P: AsRef<Path>>(&mut self, path: &P) -> Result<SoundEffectId, ()> { + open_and_unpack_audio(&path.as_ref(), self.num_channels) + .map(|samples| { + let idx = SoundEffectId(self.data.len()); + self.data.push(samples); + idx + }) + } - } - }; - &self.data[idx.0] + pub fn default_sound_effect() -> SoundEffectId { + SoundEffectId(0) + } +} + +impl Index<SoundEffectId> for SoundEffects { + type Output = [f32]; + + fn index(&self, index: SoundEffectId) -> &Self::Output { + &self.data[index.0] } } @@ -90,7 +91,7 @@ struct AudioSpec { /// An event where a notification is shown and a sound effect is played. #[derive(Debug, Eq, PartialEq, Clone, Copy, Hash, EnumIter)] -pub enum NotificationEvents { +pub enum NotificationEvent { ServerConnect, ServerDisconnect, UserConnected, @@ -103,21 +104,21 @@ pub enum NotificationEvents { Undeafen, } -impl TryFrom<&str> for NotificationEvents { +impl TryFrom<&str> for NotificationEvent { 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), + "server_connect" => Ok(NotificationEvent::ServerConnect), + "server_disconnect" => Ok(NotificationEvent::ServerDisconnect), + "user_connected" => Ok(NotificationEvent::UserConnected), + "user_disconnected" => Ok(NotificationEvent::UserDisconnected), + "user_joined_channel" => Ok(NotificationEvent::UserJoinedChannel), + "user_left_channel" => Ok(NotificationEvent::UserLeftChannel), + "mute" => Ok(NotificationEvent::Mute), + "unmute" => Ok(NotificationEvent::Unmute), + "deafen" => Ok(NotificationEvent::Deafen), + "undeafen" => Ok(NotificationEvent::Undeafen), _ => { Err(()) } @@ -125,42 +126,6 @@ impl TryFrom<&str> for NotificationEvents { } } -/// Loads files into an "event -> data"-map, with support for overriding -/// specific events with another sound file. -pub fn load_sound_effects(overrides: &[SoundEffect], num_channels: usize) -> HashMap<NotificationEvents, Vec<f32>> { - let overrides: HashMap<_, _> = overrides - .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 { - warn!("Unknown notification event '{}'", event); - None - } - }) - .collect(); - - // Construct a hashmap that maps every [NotificationEvent] to a vector of - // plain floating point audio data with the global sample rate as a - // Vec<f32>. We do this by iterating over all [NotificationEvent]-variants - // and opening either the file passed as an override or the fallback sound - // effect (if omitted). We then use dasp to convert to the correct sample rate. - NotificationEvents::iter() - .map(|event| { - let file = overrides.get(&event); - // Try to open the file if overriden, otherwise use the default sound effect. - let samples = file - .and_then(|file| { - let path = PathBuf::from(file); - open_and_unpack_audio(&path, num_channels).ok() - }) - .unwrap_or_else(|| unpack_audio(get_default_sfx(), AudioFileKind::Wav).unwrap().0); - (event, samples) - }) - .collect() -} - /// Opens the audio data located in a file and returns the contained audio data. /// /// The file kind is read from the file extension. @@ -168,14 +133,14 @@ pub fn load_sound_effects(overrides: &[SoundEffect], num_channels: usize) -> Has /// # Errors /// /// Returns an error if a file extension isn't known, the file doesn't exist or something went -/// wrong when unpacking the audio data. +/// wrong when opening or unpacking the audio data. fn open_and_unpack_audio<P: AsRef<Path>>(path: &P, num_channels: usize) -> Result<Vec<f32>, ()> { let kind = path .as_ref() .extension() .and_then(|ext| AudioFileKind::try_from(ext.to_str().unwrap()).ok()) .ok_or(())?; - let bytes = get_sfx(path); + let bytes = get_sfx(path)?; // Unpack the samples. let (samples, spec) = unpack_audio(bytes, kind)?; // If the audio is mono (single channel), pad every sample with @@ -262,14 +227,13 @@ fn unpack_wav(data: Cow<'_, [u8]>) -> Result<(Vec<f32>, AudioSpec), ()> { /// Open and return the data contained in a file, or the default sound effect if /// the file couldn't be found. // moo -fn get_sfx<P: AsRef<Path>>(file: P) -> Cow<'static, [u8]> { +fn get_sfx<P: AsRef<Path>>(file: P) -> Result<Cow<'static, [u8]>, ()> { let mut buf: Vec<u8> = Vec::new(); if let Ok(mut file) = File::open(file.as_ref()) { file.read_to_end(&mut buf).unwrap(); - Cow::from(buf) + Ok(Cow::from(buf)) } else { - warn!("File not found: '{}'", file.as_ref().display()); - get_default_sfx() + Err(()) } } diff --git a/mumd/src/state.rs b/mumd/src/state.rs index ba461fd..271bf54 100644 --- a/mumd/src/state.rs +++ b/mumd/src/state.rs @@ -2,7 +2,7 @@ pub mod channel; pub mod server; pub mod user; -use crate::audio::{AudioInput, AudioOutput, sound_effects::NotificationEvents}; +use crate::audio::{AudioInput, AudioOutput, sound_effects::NotificationEvent}; use crate::error::StateError; use crate::network::tcp::{DisconnectedReason, TcpEvent, TcpEventData}; use crate::network::{ConnectionInfo, VoiceStreamType}; @@ -163,7 +163,7 @@ impl State { this_channel_name, )); self.push_event(MumbleEventKind::UserConnected(msg.get_name().to_string(), this_channel_name)); - self.audio_output.play_effect(NotificationEvents::UserConnected); + self.audio_output.play_effect(NotificationEvent::UserConnected); } } } @@ -219,7 +219,7 @@ impl State { )); self.push_event(MumbleEventKind::UserLeftChannel(username.clone(), channel)); } - self.audio_output.play_effect(NotificationEvents::UserLeftChannel); + self.audio_output.play_effect(NotificationEvent::UserLeftChannel); } else if to_channel == this_channel { // User moved from somewhere else to our channel if let Some(channel) = self.server().unwrap().channels().get(&from_channel) { @@ -231,7 +231,7 @@ impl State { )); self.push_event(MumbleEventKind::UserJoinedChannel(username.clone(), channel)); } - self.audio_output.play_effect(NotificationEvents::UserJoinedChannel); + self.audio_output.play_effect(NotificationEvent::UserJoinedChannel); } } @@ -281,7 +281,7 @@ impl State { .to_string(); notifications::send(format!("{} disconnected", &user_name)); self.push_event(MumbleEventKind::UserDisconnected(user_name, channel_name)); - self.audio_output.play_effect(NotificationEvents::UserDisconnected); + self.audio_output.play_effect(NotificationEvent::UserDisconnected); } self.server_mut() @@ -320,7 +320,7 @@ impl State { pub fn initialized(&self) { self.broadcast_phase(StatePhase::Connected(VoiceStreamType::Tcp)); self.audio_output - .play_effect(NotificationEvents::ServerConnect); + .play_effect(NotificationEvent::ServerConnect); } /// Store a new event @@ -436,15 +436,15 @@ pub fn handle_command( if let Some((mute, deafen)) = action { if server.deafened() != deafen { state.audio_output.play_effect(if deafen { - NotificationEvents::Deafen + NotificationEvent::Deafen } else { - NotificationEvents::Undeafen + NotificationEvent::Undeafen }); } else if server.muted() != mute { state.audio_output.play_effect(if mute { - NotificationEvents::Mute + NotificationEvent::Mute } else { - NotificationEvents::Unmute + NotificationEvent::Unmute }); } let mut msg = msgs::UserState::new(); @@ -544,15 +544,15 @@ pub fn handle_command( if let Some((mute, deafen)) = action { if server.deafened() != deafen { state.audio_output.play_effect(if deafen { - NotificationEvents::Deafen + NotificationEvent::Deafen } else { - NotificationEvents::Undeafen + NotificationEvent::Undeafen }); } else if server.muted() != mute { state.audio_output.play_effect(if mute { - NotificationEvents::Mute + NotificationEvent::Mute } else { - NotificationEvents::Unmute + NotificationEvent::Unmute }); } let mut msg = msgs::UserState::new(); @@ -655,7 +655,7 @@ pub fn handle_command( .unwrap(); state .audio_output - .play_effect(NotificationEvents::ServerDisconnect); + .play_effect(NotificationEvent::ServerDisconnect); now!(Ok(None)) } Command::ServerStatus { host, port } => ExecutionContext::Ping( |
