aboutsummaryrefslogtreecommitdiffstats
path: root/mumd/src/audio.rs
diff options
context:
space:
mode:
authorGustav Sörnäs <gustav@sornas.net>2021-01-05 12:19:54 +0100
committerGustav Sörnäs <gustav@sornas.net>2021-01-05 12:20:21 +0100
commitba4aa72654f2d57d59f6e25151315213bec21192 (patch)
tree6ff7fe03e3ebbc4b351fae778fd1c02f2d6148de /mumd/src/audio.rs
parent531dc28e2c09a13336b057044bf8721d4b2c4c00 (diff)
parent2cef2e31a139e453474298b3278c98f18eba7203 (diff)
downloadmum-ba4aa72654f2d57d59f6e25151315213bec21192.tar.gz
Merge remote-tracking branch 'origin/choose-sfx' into main
Diffstat (limited to 'mumd/src/audio.rs')
-rw-r--r--mumd/src/audio.rs151
1 files changed, 96 insertions, 55 deletions
diff --git a/mumd/src/audio.rs b/mumd/src/audio.rs
index 83818d5..680433c 100644
--- a/mumd/src/audio.rs
+++ b/mumd/src/audio.rs
@@ -13,49 +13,28 @@ use futures::Stream;
use futures::stream::StreamExt;
use futures::task::{Context, Poll};
use log::*;
+use mumble_protocol::Serverbound;
use mumble_protocol::voice::{VoicePacketPayload, VoicePacket};
+use mumlib::config::SoundEffect;
use opus::Channels;
-use std::collections::hash_map::Entry;
-use std::collections::{HashMap, VecDeque};
-use std::fmt::Debug;
-use std::future::{Future};
-use std::pin::Pin;
-use std::sync::{Arc, Mutex};
+use std::{
+ borrow::Cow,
+ collections::{hash_map::Entry, HashMap, VecDeque},
+ convert::TryFrom,
+ fmt::Debug,
+ fs::File,
+ future::Future,
+ io::Read,
+ pin::Pin,
+ sync::{Arc, Mutex},
+};
+use strum::IntoEnumIterator;
+use strum_macros::EnumIter;
use tokio::sync::watch;
-use mumble_protocol::Serverbound;
-
-//TODO? move to mumlib
-pub const EVENT_SOUNDS: &[(&[u8], NotificationEvents)] = &[
- (include_bytes!("resources/connect.wav"), NotificationEvents::ServerConnect),
- (
- include_bytes!("resources/disconnect.wav"),
- NotificationEvents::ServerDisconnect,
- ),
- (
- include_bytes!("resources/channel_join.wav"),
- NotificationEvents::UserConnected,
- ),
- (
- include_bytes!("resources/channel_leave.wav"),
- NotificationEvents::UserDisconnected,
- ),
- (
- include_bytes!("resources/channel_join.wav"),
- NotificationEvents::UserJoinedChannel,
- ),
- (
- include_bytes!("resources/channel_leave.wav"),
- NotificationEvents::UserLeftChannel,
- ),
- (include_bytes!("resources/mute.wav"), NotificationEvents::Mute),
- (include_bytes!("resources/unmute.wav"), NotificationEvents::Unmute),
- (include_bytes!("resources/deafen.wav"), NotificationEvents::Deafen),
- (include_bytes!("resources/undeafen.wav"), NotificationEvents::Undeafen),
-];
const SAMPLE_RATE: u32 = 48000;
-#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)]
+#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash, EnumIter)]
pub enum NotificationEvents {
ServerConnect,
ServerDisconnect,
@@ -69,6 +48,28 @@ pub enum NotificationEvents {
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(())
+ }
+ }
+ }
+}
+
pub struct Audio {
output_config: StreamConfig,
_output_stream: cpal::Stream,
@@ -220,10 +221,41 @@ impl Audio {
output_stream.play().unwrap();
- let sounds = EVENT_SOUNDS
+ let mut res = Self {
+ output_config,
+ _output_stream: output_stream,
+ _input_stream: input_stream,
+ input_volume_sender,
+ input_channel_receiver: Arc::new(Mutex::new(Box::new(opus_stream))),
+ client_streams,
+ sounds: HashMap::new(),
+ output_volume_sender,
+ user_volumes,
+ play_sounds,
+ };
+ res.load_sound_effects(&[]);
+ res
+ }
+
+ pub fn load_sound_effects(&mut self, sound_effects: &[SoundEffect]) {
+ let overrides: HashMap<_, _> = sound_effects
.iter()
- .map(|(bytes, event)| {
- let reader = hound::WavReader::new(*bytes).unwrap();
+ .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();
+
+ 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
@@ -246,24 +278,17 @@ impl Audio {
.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 output_config.channels == 1 { vec![e[0]] } else { e.to_vec() })
+ .flat_map(
+ |e| if self.output_config.channels == 1 {
+ vec![e[0]]
+ } else {
+ e.to_vec()
+ }
+ )
.collect::<Vec<f32>>();
- (*event, samples)
+ (event, samples)
})
.collect();
-
- Self {
- output_config,
- _output_stream: output_stream,
- _input_stream: input_stream,
- input_volume_sender,
- input_channel_receiver: Arc::new(Mutex::new(Box::new(opus_stream))),
- client_streams,
- sounds,
- output_volume_sender,
- user_volumes,
- play_sounds,
- }
}
pub fn decode_packet(&self, session_id: u32, payload: VoicePacketPayload) {
@@ -359,6 +384,22 @@ impl Audio {
}
}
+// 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()
+ }
+}
+
+fn get_default_sfx() -> Cow<'static, [u8]> {
+ Cow::from(include_bytes!("fallback_sfx.wav").as_ref())
+}
+
struct StreamingNoiseGate<S: StreamingSignal> {
open: usize,
signal: S,