aboutsummaryrefslogtreecommitdiffstats
path: root/mumd
diff options
context:
space:
mode:
Diffstat (limited to 'mumd')
-rw-r--r--mumd/src/audio.rs124
-rw-r--r--mumd/src/audio/fallback_sfx.wav (renamed from mumd/src/fallback_sfx.wav)bin32002 -> 32002 bytes
-rw-r--r--mumd/src/audio/sound_effects.rs116
-rw-r--r--mumd/src/state.rs15
4 files changed, 129 insertions, 126 deletions
diff --git a/mumd/src/audio.rs b/mumd/src/audio.rs
index 2e20583..785bf7c 100644
--- a/mumd/src/audio.rs
+++ b/mumd/src/audio.rs
@@ -1,69 +1,26 @@
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;
-const SAMPLE_RATE: u32 = 48000;
-
-#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash, EnumIter)]
-pub enum NotificationEvents {
- ServerConnect,
- ServerDisconnect,
- UserConnected,
- UserDisconnected,
- UserJoinedChannel,
- UserLeftChannel,
- Mute,
- Unmute,
- Deafen,
- Undeafen,
-}
+use self::input::{AudioInputDevice, DefaultAudioInputDevice};
+use self::output::{AudioOutputDevice, ClientStream, DefaultAudioOutputDevice};
+use self::sound_effects::NotificationEvents;
-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(())
- }
- }
- }
-}
+const SAMPLE_RATE: u32 = 48000;
pub struct AudioInput {
device: DefaultAudioInputDevice,
@@ -140,59 +97,8 @@ impl AudioOutput {
Ok(res)
}
- 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();
-
- 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();
+ pub fn load_sound_effects(&mut self, overrides: &[SoundEffect]) {
+ self.sounds = sound_effects::load_sound_effects(overrides, self.device.num_channels());
}
pub fn decode_packet_payload(
@@ -238,19 +144,3 @@ impl AudioOutput {
self.client_streams.lock().unwrap().extend(None, samples);
}
}
-
-// 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())
-}
diff --git a/mumd/src/fallback_sfx.wav b/mumd/src/audio/fallback_sfx.wav
index 82ee4d4..82ee4d4 100644
--- a/mumd/src/fallback_sfx.wav
+++ b/mumd/src/audio/fallback_sfx.wav
Binary files differ
diff --git a/mumd/src/audio/sound_effects.rs b/mumd/src/audio/sound_effects.rs
new file mode 100644
index 0000000..3d70bbb
--- /dev/null
+++ b/mumd/src/audio/sound_effects.rs
@@ -0,0 +1,116 @@
+use dasp_interpolate::linear::Linear;
+use dasp_signal::{self as signal, Signal};
+use log::warn;
+use mumlib::config::SoundEffect;
+use std::{borrow::Cow, collections::HashMap, convert::TryFrom, fs::File, io::Read};
+use strum::IntoEnumIterator;
+use strum_macros::EnumIter;
+
+use crate::audio::SAMPLE_RATE;
+
+#[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(())
+ }
+ }
+ }
+}
+
+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 {
+ None
+ }
+ })
+ .collect();
+
+ 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 num_channels == 1 {
+ vec![e[0]]
+ } else {
+ e.to_vec()
+ }
+ })
+ .collect::<Vec<f32>>();
+ (event, samples)
+ })
+ .collect()
+}
+
+// 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())
+}
diff --git a/mumd/src/state.rs b/mumd/src/state.rs
index d2d77b1..8512715 100644
--- a/mumd/src/state.rs
+++ b/mumd/src/state.rs
@@ -2,9 +2,9 @@ pub mod channel;
pub mod server;
pub mod user;
-use crate::{audio::{AudioInput, AudioOutput, NotificationEvents}, network::tcp::DisconnectedReason};
+use crate::audio::{AudioInput, AudioOutput, sound_effects::NotificationEvents};
use crate::error::StateError;
-use crate::network::tcp::{TcpEvent, TcpEventData};
+use crate::network::tcp::{DisconnectedReason, TcpEvent, TcpEventData};
use crate::network::{ConnectionInfo, VoiceStreamType};
use crate::notifications;
use crate::state::server::Server;
@@ -12,18 +12,15 @@ use crate::state::user::UserDiff;
use chrono::NaiveDateTime;
use log::*;
-use mumble_protocol::control::msgs;
-use mumble_protocol::control::ControlPacket;
+use mumble_protocol::control::{ControlPacket, msgs};
use mumble_protocol::ping::PongPacket;
use mumble_protocol::voice::Serverbound;
use mumlib::command::{ChannelTarget, Command, CommandResponse, MessageTarget, MumbleEvent, MumbleEventKind};
use mumlib::config::Config;
use mumlib::Error;
-use std::{
- iter,
- net::{SocketAddr, ToSocketAddrs},
- sync::{Arc, RwLock},
-};
+use std::iter;
+use std::net::{SocketAddr, ToSocketAddrs};
+use std::sync::{Arc, RwLock};
use tokio::sync::{mpsc, watch};
macro_rules! at {