aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGustav Sörnäs <gustav@sornas.net>2021-06-25 08:29:02 +0200
committerGustav Sörnäs <gustav@sornas.net>2021-06-26 07:55:51 +0200
commita6c574b04802b5522cf1db12caa85e77062a71b8 (patch)
tree6d2018e63ec1ba89d1c0e26e0d28c809a03b24e1
parentdd6fdbc60f53290af99ba0640c284917a85070e5 (diff)
downloadmum-a6c574b04802b5522cf1db12caa85e77062a71b8.tar.gz
move hashmap to audio output
-rw-r--r--mumd/src/audio.rs30
-rw-r--r--mumd/src/audio/sound_effects.rs110
-rw-r--r--mumd/src/state.rs30
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(