aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEskil <eskilq@kth.se>2020-11-21 15:56:27 +0100
committerEskil <eskilq@kth.se>2020-11-21 15:56:27 +0100
commit08397f89fd6fdefed67533bf9403bde07f1ee40e (patch)
tree731613e6e00d66e7d35e782a1171677388c83306
parentff9a37af7ddf5484bb7db61ae01f8aafd9d1e91d (diff)
parentbe4d273fa8778ec87ad4a34502d0f3c75657fbbc (diff)
downloadmum-08397f89fd6fdefed67533bf9403bde07f1ee40e.tar.gz
Merge branch 'sound-effect' into 'main'
Sound effects on events Closes #68 See merge request gustav/mum!32
-rw-r--r--Cargo.lock157
-rw-r--r--mumd/Cargo.toml11
-rw-r--r--mumd/src/audio.rs113
-rw-r--r--mumd/src/audio/output.rs16
-rw-r--r--mumd/src/network/tcp.rs8
-rw-r--r--mumd/src/notify.rs6
-rw-r--r--mumd/src/state.rs173
-rw-r--r--resources/channel_join.wavbin0 -> 32002 bytes
-rw-r--r--resources/channel_leave.wavbin0 -> 32002 bytes
-rw-r--r--resources/connect.wavbin0 -> 32002 bytes
-rw-r--r--resources/deafen.wavbin0 -> 32002 bytes
-rw-r--r--resources/disconnect.wavbin0 -> 303364 bytes
-rw-r--r--resources/mute.wavbin0 -> 32002 bytes
-rw-r--r--resources/undeafen.wavbin0 -> 32002 bytes
-rw-r--r--resources/unmute.wavbin0 -> 32002 bytes
15 files changed, 401 insertions, 83 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 09c3981..09f8efd 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1,6 +1,15 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
+name = "aho-corasick"
+version = "0.7.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
name = "alsa"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -66,9 +75,32 @@ dependencies = [
[[package]]
name = "bindgen"
-version = "0.53.3"
+version = "0.51.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c72a978d268b1d70b0e963217e60fdabd9523a941457a6c42a7315d15c7e89e5"
+checksum = "ebd71393f1ec0509b553aa012b9b58e81dadbdff7130bd3b8cba576e69b32f75"
+dependencies = [
+ "bitflags",
+ "cexpr",
+ "cfg-if",
+ "clang-sys",
+ "clap",
+ "env_logger",
+ "lazy_static",
+ "log",
+ "peeking_take_while",
+ "proc-macro2",
+ "quote",
+ "regex",
+ "rustc-hash",
+ "shlex",
+ "which",
+]
+
+[[package]]
+name = "bindgen"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "99de13bb6361e01e493b3db7928085dcc474b7ba4f5481818e53a89d76b8393f"
dependencies = [
"bitflags",
"cexpr",
@@ -116,9 +148,9 @@ checksum = "ed67cbde08356238e75fc4656be4749481eeffb09e19f320a25237d5221c985d"
[[package]]
name = "cexpr"
-version = "0.4.0"
+version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27"
+checksum = "fce5b5fb86b0c57c20c834c1b412fd09c77c8a59b9473f86272709e78874cd1d"
dependencies = [
"nom",
]
@@ -144,9 +176,9 @@ dependencies = [
[[package]]
name = "clang-sys"
-version = "0.29.3"
+version = "0.28.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fe6837df1d5cba2397b835c8530f51723267e16abbf83892e9e5af4f0e5dd10a"
+checksum = "81de550971c976f176130da4b2978d3b524eaa0fd9ac31f3ceb5ae1231fb4853"
dependencies = [
"glob",
"libc",
@@ -179,6 +211,15 @@ dependencies = [
]
[[package]]
+name = "cmake"
+version = "0.1.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e56268c17a6248366d66d4a47a3381369d068cce8409bb1716ed77ea32163bb"
+dependencies = [
+ "cc",
+]
+
+[[package]]
name = "colored"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -227,7 +268,7 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6570ee6e089131e928d5ec9236db9e818aa3cf850f48b0eec6ef700571271d4"
dependencies = [
- "bindgen",
+ "bindgen 0.53.1",
]
[[package]]
@@ -272,6 +313,19 @@ dependencies = [
]
[[package]]
+name = "env_logger"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3"
+dependencies = [
+ "atty",
+ "humantime",
+ "log",
+ "regex",
+ "termcolor",
+]
+
+[[package]]
name = "fern"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -516,6 +570,21 @@ dependencies = [
]
[[package]]
+name = "hound"
+version = "3.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a164bb2ceaeff4f42542bdb847c41517c78a60f5649671b2a07312b6e117549"
+
+[[package]]
+name = "humantime"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
+dependencies = [
+ "quick-error",
+]
+
+[[package]]
name = "iovec"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -618,6 +687,16 @@ dependencies = [
]
[[package]]
+name = "libsamplerate-sys"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "66575d1d489214f8767f90de2b1e115626d8277ca09d7c39549652a9602309eb"
+dependencies = [
+ "bindgen 0.51.1",
+ "cmake",
+]
+
+[[package]]
name = "lock_api"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -721,6 +800,7 @@ dependencies = [
"cpal",
"futures",
"futures-util",
+ "hound",
"ipc-channel",
"libnotify",
"log",
@@ -729,6 +809,7 @@ dependencies = [
"native-tls",
"openssl",
"opus",
+ "samplerate",
"serde",
"tokio",
"tokio-tls",
@@ -791,9 +872,9 @@ dependencies = [
[[package]]
name = "nom"
-version = "5.1.2"
+version = "4.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af"
+checksum = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6"
dependencies = [
"memchr",
"version_check",
@@ -1000,6 +1081,12 @@ dependencies = [
]
[[package]]
+name = "quick-error"
+version = "1.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
+
+[[package]]
name = "quote"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1061,7 +1148,10 @@ version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8963b85b8ce3074fecffde43b4b0dded83ce2f367dc8d363afc56679f3ee820b"
dependencies = [
+ "aho-corasick",
+ "memchr",
"regex-syntax",
+ "thread_local",
]
[[package]]
@@ -1095,6 +1185,15 @@ dependencies = [
]
[[package]]
+name = "samplerate"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a6913e9f2ad7d0aa9528523f7e4385e160d690c66d43bb8bd7559f1be705aee8"
+dependencies = [
+ "libsamplerate-sys",
+]
+
+[[package]]
name = "schannel"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1227,6 +1326,15 @@ dependencies = [
]
[[package]]
+name = "termcolor"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1256,6 +1364,15 @@ dependencies = [
]
[[package]]
+name = "thread_local"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
+dependencies = [
+ "lazy_static",
+]
+
+[[package]]
name = "time"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1362,9 +1479,9 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "version_check"
-version = "0.9.2"
+version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
+checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
[[package]]
name = "void"
@@ -1449,6 +1566,15 @@ dependencies = [
]
[[package]]
+name = "which"
+version = "3.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724"
+dependencies = [
+ "libc",
+]
+
+[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1477,6 +1603,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
+name = "winapi-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+dependencies = [
+ "winapi 0.3.9",
+]
+
+[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/mumd/Cargo.toml b/mumd/Cargo.toml
index 5c546d1..f497524 100644
--- a/mumd/Cargo.toml
+++ b/mumd/Cargo.toml
@@ -9,7 +9,11 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
-default = ["libnotify"]
+default = ["notifications", "sound-effects"]
+
+notifications = ["libnotify"]
+
+sound-effects = ["hound", "samplerate"]
[dependencies]
mumlib = { path = "../mumlib" }
@@ -23,7 +27,7 @@ ipc-channel = "0.14"
log = "0.4"
mumble-protocol = "0.3"
native-tls = "0.2"
-openssl = { version = "0.10", optional = true }
+openssl = { version = "0.10" }
opus = "0.2"
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "0.2", features = ["blocking", "macros", "rt-core", "sync", "tcp", "time"] }
@@ -32,5 +36,8 @@ tokio-util = { version = "0.3", features = ["codec", "udp"] }
libnotify = { version = "1.0", optional = true }
+hound = { version = "3.4.0", optional = true }
+samplerate = { version = "0.2.2", optional = true }
+
#compressor = "0.3"
#daemonize = "0.4"
diff --git a/mumd/src/audio.rs b/mumd/src/audio.rs
index 05e3ff5..d5617fc 100644
--- a/mumd/src/audio.rs
+++ b/mumd/src/audio.rs
@@ -1,16 +1,66 @@
pub mod input;
pub mod output;
+#[cfg(feature = "sound-effects")]
+use crate::audio::output::SaturatingAdd;
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
use cpal::{SampleFormat, SampleRate, Stream, StreamConfig};
use log::*;
use mumble_protocol::voice::VoicePacketPayload;
use opus::Channels;
+#[cfg(feature = "sound-effects")]
+use samplerate::ConverterType;
use std::collections::hash_map::Entry;
-use std::collections::HashMap;
+use std::collections::{HashMap, VecDeque};
use std::sync::{Arc, Mutex};
use tokio::sync::{mpsc, watch};
+//TODO? move to mumlib
+#[cfg(feature = "sound-effects")]
+pub const EVENT_SOUNDS: &[(&str, NotificationEvents)] = &[
+ ("resources/connect.wav", NotificationEvents::ServerConnect),
+ (
+ "resources/disconnect.wav",
+ NotificationEvents::ServerDisconnect,
+ ),
+ (
+ "resources/channel_join.wav",
+ NotificationEvents::UserConnected,
+ ),
+ (
+ "resources/channel_leave.wav",
+ NotificationEvents::UserDisconnected,
+ ),
+ (
+ "resources/channel_join.wav",
+ NotificationEvents::UserJoinedChannel,
+ ),
+ (
+ "resources/channel_leave.wav",
+ NotificationEvents::UserLeftChannel,
+ ),
+ ("resources/mute.wav", NotificationEvents::Mute),
+ ("resources/unmute.wav", NotificationEvents::Unmute),
+ ("resources/deafen.wav", NotificationEvents::Deafen),
+ ("resources/undeafen.wav", NotificationEvents::Undeafen),
+];
+
+const SAMPLE_RATE: u32 = 48000;
+
+#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)]
+pub enum NotificationEvents {
+ ServerConnect,
+ ServerDisconnect,
+ UserConnected,
+ UserDisconnected,
+ UserJoinedChannel,
+ UserLeftChannel,
+ Mute,
+ Unmute,
+ Deafen,
+ Undeafen,
+}
+
pub struct Audio {
output_config: StreamConfig,
_output_stream: Stream,
@@ -24,11 +74,17 @@ pub struct Audio {
user_volumes: Arc<Mutex<HashMap<u32, (f32, bool)>>>,
client_streams: Arc<Mutex<HashMap<u32, output::ClientStream>>>,
+
+ #[cfg(feature = "sound-effects")]
+ sounds: HashMap<NotificationEvents, Vec<f32>>,
+
+ #[cfg(feature = "sound-effects")]
+ play_sounds: Arc<Mutex<VecDeque<f32>>>,
}
impl Audio {
pub fn new(input_volume: f32, output_volume: f32) -> Self {
- let sample_rate = SampleRate(48000);
+ let sample_rate = SampleRate(SAMPLE_RATE);
let host = cpal::default_host();
let output_device = host
@@ -71,12 +127,14 @@ impl Audio {
let user_volumes = Arc::new(Mutex::new(HashMap::new()));
let (output_volume_sender, output_volume_receiver) = watch::channel::<f32>(output_volume);
+ let play_sounds = Arc::new(Mutex::new(VecDeque::new()));
let client_streams = Arc::new(Mutex::new(HashMap::new()));
let output_stream = match output_supported_sample_format {
SampleFormat::F32 => output_device.build_output_stream(
&output_config,
output::curry_callback::<f32>(
+ Arc::clone(&play_sounds),
Arc::clone(&client_streams),
output_volume_receiver,
Arc::clone(&user_volumes),
@@ -86,6 +144,7 @@ impl Audio {
SampleFormat::I16 => output_device.build_output_stream(
&output_config,
output::curry_callback::<i16>(
+ Arc::clone(&play_sounds),
Arc::clone(&client_streams),
output_volume_receiver,
Arc::clone(&user_volumes),
@@ -95,6 +154,7 @@ impl Audio {
SampleFormat::U16 => output_device.build_output_stream(
&output_config,
output::curry_callback::<u16>(
+ Arc::clone(&play_sounds),
Arc::clone(&client_streams),
output_volume_receiver,
Arc::clone(&user_volumes),
@@ -160,6 +220,34 @@ impl Audio {
output_stream.play().unwrap();
+ #[cfg(feature = "sound-effects")]
+ let sounds = EVENT_SOUNDS
+ .iter()
+ .map(|(path, event)| {
+ let reader = hound::WavReader::open(path).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 samples = samplerate::convert(
+ spec.sample_rate,
+ SAMPLE_RATE,
+ spec.channels as usize,
+ ConverterType::SincBestQuality,
+ &samples,
+ )
+ .unwrap();
+ (*event, samples)
+ })
+ .collect();
+
Self {
output_config,
_output_stream: output_stream,
@@ -167,8 +255,12 @@ impl Audio {
input_volume_sender,
input_channel_receiver: Some(input_receiver),
client_streams,
+ #[cfg(feature = "sound-effects")]
+ sounds,
output_volume_sender,
user_volumes,
+ #[cfg(feature = "sound-effects")]
+ play_sounds,
}
}
@@ -250,4 +342,21 @@ impl Audio {
}
}
}
+
+ #[cfg(feature = "sound-effects")]
+ pub fn play_effect(&self, effect: NotificationEvents) {
+ let samples = self.sounds.get(&effect).unwrap();
+
+ let mut play_sounds = self.play_sounds.lock().unwrap();
+
+ for (val, e) in play_sounds.iter_mut().zip(samples.iter()) {
+ *val = val.saturating_add(*e);
+ }
+
+ let l = play_sounds.len();
+ play_sounds.extend(samples.iter().skip(l));
+ }
+
+ #[cfg(not(feature = "sound-effects"))]
+ pub fn play_effect(&self, _: NotificationEvents) {}
}
diff --git a/mumd/src/audio/output.rs b/mumd/src/audio/output.rs
index ce116a8..5e0cb8d 100644
--- a/mumd/src/audio/output.rs
+++ b/mumd/src/audio/output.rs
@@ -71,8 +71,9 @@ impl SaturatingAdd for u16 {
}
}
-pub fn curry_callback<T: Sample + AddAssign + SaturatingAdd>(
- buf: Arc<Mutex<HashMap<u32, ClientStream>>>,
+pub fn curry_callback<T: Sample + AddAssign + SaturatingAdd + std::fmt::Display>(
+ effect_sound: Arc<Mutex<VecDeque<f32>>>,
+ user_bufs: Arc<Mutex<HashMap<u32, ClientStream>>>,
output_volume_receiver: watch::Receiver<f32>,
user_volumes: Arc<Mutex<HashMap<u32, (f32, bool)>>>,
) -> impl FnMut(&mut [T], &OutputCallbackInfo) + Send + 'static {
@@ -83,8 +84,9 @@ pub fn curry_callback<T: Sample + AddAssign + SaturatingAdd>(
let volume = *output_volume_receiver.borrow();
- let mut lock = buf.lock().unwrap();
- for (id, client_stream) in &mut *lock {
+ let mut effects_sound = effect_sound.lock().unwrap();
+ let mut user_bufs = user_bufs.lock().unwrap();
+ for (id, client_stream) in &mut *user_bufs {
let (user_volume, muted) = user_volumes
.lock()
.unwrap()
@@ -98,5 +100,11 @@ pub fn curry_callback<T: Sample + AddAssign + SaturatingAdd>(
}
}
}
+
+ for sample in data.iter_mut() {
+ *sample = sample.saturating_add(Sample::from(
+ &(effects_sound.pop_front().unwrap_or(0.0) * volume),
+ ));
+ }
}
}
diff --git a/mumd/src/network/tcp.rs b/mumd/src/network/tcp.rs
index 131f066..2a0d01e 100644
--- a/mumd/src/network/tcp.rs
+++ b/mumd/src/network/tcp.rs
@@ -247,13 +247,7 @@ async fn listen(
warn!("Login rejected: {:?}", msg);
}
ControlPacket::UserState(msg) => {
- let mut state = state.lock().unwrap();
- if *state.phase_receiver().borrow() == StatePhase::Connecting {
- state.audio_mut().add_client(msg.get_session());
- state.parse_user_state(*msg);
- } else {
- state.parse_user_state(*msg);
- }
+ state.lock().unwrap().parse_user_state(*msg);
}
ControlPacket::UserRemove(msg) => {
state.lock().unwrap().remove_client(*msg);
diff --git a/mumd/src/notify.rs b/mumd/src/notify.rs
index 0739c58..ee387cc 100644
--- a/mumd/src/notify.rs
+++ b/mumd/src/notify.rs
@@ -1,9 +1,9 @@
pub fn init() {
- #[cfg(feature = "libnotify")]
+ #[cfg(feature = "notifications")]
libnotify::init("mumd").unwrap();
}
-#[cfg(feature = "libnotify")]
+#[cfg(feature = "notifications")]
pub fn send(msg: String) -> Option<bool> {
match libnotify::Notification::new("mumd", Some(msg.as_str()), None).show() {
Ok(_) => Some(true),
@@ -14,7 +14,7 @@ pub fn send(msg: String) -> Option<bool> {
}
}
-#[cfg(not(feature = "libnotify"))]
+#[cfg(not(feature = "notifications"))]
pub fn send(_: String) -> Option<bool> {
None
}
diff --git a/mumd/src/state.rs b/mumd/src/state.rs
index 8fe5e36..91cf734 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::Audio;
+use crate::audio::{Audio, NotificationEvents};
use crate::network::ConnectionInfo;
use crate::notify;
use crate::state::server::Server;
@@ -85,7 +85,6 @@ impl State {
state
}
- //TODO? move bool inside Result
pub fn handle_command(&mut self, command: Command) -> ExecutionContext {
match command {
Command::ChannelJoin { channel_identifier } => {
@@ -219,6 +218,7 @@ impl State {
.0
.broadcast(StatePhase::Disconnected)
.unwrap();
+ self.audio.play_effect(NotificationEvents::ServerDisconnect);
now!(Ok(None))
}
Command::ConfigReload => {
@@ -238,7 +238,7 @@ impl State {
return now!(Err(Error::DisconnectedError));
}
- let server = self.server_mut().unwrap();
+ let server = self.server().unwrap();
let action = match (toggle, server.muted(), server.deafened()) {
(Some(false), false, false) => None,
(Some(false), false, true) => Some((false, false)),
@@ -255,6 +255,19 @@ impl State {
};
if let Some((mute, deafen)) = action {
+ if server.deafened() != deafen {
+ self.audio.play_effect(if deafen {
+ NotificationEvents::Deafen
+ } else {
+ NotificationEvents::Undeafen
+ });
+ } else if server.muted() != mute {
+ self.audio.play_effect(if mute {
+ NotificationEvents::Mute
+ } else {
+ NotificationEvents::Unmute
+ });
+ }
let mut msg = msgs::UserState::new();
if server.muted() != mute {
msg.set_self_mute(mute);
@@ -264,6 +277,7 @@ impl State {
if server.deafened() != deafen {
msg.set_self_deaf(deafen);
}
+ let server = self.server_mut().unwrap();
server.set_muted(mute);
server.set_deafened(deafen);
self.packet_sender.send(msg.into()).unwrap();
@@ -276,7 +290,7 @@ impl State {
return now!(Err(Error::DisconnectedError));
}
- let server = self.server_mut().unwrap();
+ let server = self.server().unwrap();
let action = match (toggle, server.muted(), server.deafened()) {
(Some(false), false, false) => None,
(Some(false), false, true) => Some((false, false)),
@@ -293,6 +307,19 @@ impl State {
};
if let Some((mute, deafen)) = action {
+ if server.deafened() != deafen {
+ self.audio.play_effect(if deafen {
+ NotificationEvents::Deafen
+ } else {
+ NotificationEvents::Undeafen
+ });
+ } else if server.muted() != mute {
+ self.audio.play_effect(if mute {
+ NotificationEvents::Mute
+ } else {
+ NotificationEvents::Unmute
+ });
+ }
let mut msg = msgs::UserState::new();
if server.muted() != mute {
msg.set_self_mute(mute);
@@ -302,6 +329,7 @@ impl State {
if server.deafened() != deafen {
msg.set_self_deaf(deafen);
}
+ let server = self.server_mut().unwrap();
server.set_muted(mute);
server.set_deafened(deafen);
self.packet_sender.send(msg.into()).unwrap();
@@ -385,25 +413,29 @@ impl State {
}
}
- pub fn parse_user_state(&mut self, msg: msgs::UserState) -> Option<UserDiff> {
+ pub fn parse_user_state(&mut self, msg: msgs::UserState) {
if !msg.has_session() {
warn!("Can't parse user state without session");
- return None;
+ return;
}
let session = msg.get_session();
// check if this is initial state
if !self.server().unwrap().users().contains_key(&session) {
- self.parse_initial_user_state(session, msg);
- None
+ self.create_user(msg);
} else {
- Some(self.parse_updated_user_state(session, msg))
+ self.update_user(msg);
}
}
- fn parse_initial_user_state(&mut self, session: u32, msg: msgs::UserState) {
+ fn create_user(&mut self, msg: msgs::UserState) {
if !msg.has_name() {
warn!("Missing name in initial user state");
- } else if msg.get_name() == self.server().unwrap().username().unwrap() {
+ return;
+ }
+
+ let session = msg.get_session();
+
+ if msg.get_name() == self.server().unwrap().username().unwrap() {
// this is us
*self.server_mut().unwrap().session_id_mut() = Some(session);
} else {
@@ -412,27 +444,35 @@ impl State {
// send notification only if we've passed the connecting phase
if *self.phase_receiver().borrow() == StatePhase::Connected {
- let channel_id = if msg.has_channel_id() {
- msg.get_channel_id()
- } else {
- 0
- };
- if let Some(channel) = self.server().unwrap().channels().get(&channel_id) {
- notify::send(format!(
- "{} connected and joined {}",
- &msg.get_name(),
- channel.name()
- ));
+ let channel_id = msg.get_channel_id();
+
+ if channel_id
+ == self.get_users_channel(self.server().unwrap().session_id().unwrap())
+ {
+ if let Some(channel) = self.server().unwrap().channels().get(&channel_id) {
+ notify::send(format!(
+ "{} connected and joined {}",
+ &msg.get_name(),
+ channel.name()
+ ));
+ }
+
+ self.audio.play_effect(NotificationEvents::UserConnected);
}
}
}
+
self.server_mut()
.unwrap()
.users_mut()
.insert(session, user::User::new(msg));
}
- fn parse_updated_user_state(&mut self, session: u32, msg: msgs::UserState) -> UserDiff {
+ fn update_user(&mut self, msg: msgs::UserState) {
+ let session = msg.get_session();
+
+ let from_channel = self.get_users_channel(session);
+
let user = self
.server_mut()
.unwrap()
@@ -453,40 +493,48 @@ impl State {
let diff = UserDiff::from(msg);
user.apply_user_diff(&diff);
+
let user = self.server().unwrap().users().get(&session).unwrap();
- // send notification if the user moved to or from any channel
- //TODO our channel only
- if let Some(channel_id) = diff.channel_id {
- if let Some(channel) = self.server().unwrap().channels().get(&channel_id) {
- notify::send(format!(
- "{} moved to channel {}",
- &user.name(),
- channel.name()
- ));
- } else {
- warn!("{} moved to invalid channel {}", &user.name(), channel_id);
+ if Some(session) != self.server().unwrap().session_id() {
+ //send notification if the user moved to or from any channel
+ if let Some(to_channel) = diff.channel_id {
+ let this_channel =
+ self.get_users_channel(self.server().unwrap().session_id().unwrap());
+ if from_channel == this_channel || to_channel == this_channel {
+ if let Some(channel) = self.server().unwrap().channels().get(&to_channel) {
+ notify::send(format!(
+ "{} moved to channel {}",
+ user.name(),
+ channel.name()
+ ));
+ } else {
+ warn!("{} moved to invalid channel {}", user.name(), to_channel);
+ }
+ self.audio.play_effect(if from_channel == this_channel {
+ NotificationEvents::UserJoinedChannel
+ } else {
+ NotificationEvents::UserLeftChannel
+ });
+ }
}
- }
- // send notification if a user muted/unmuted
- //TODO our channel only
- let notify_desc = match (mute, deaf) {
- (Some(true), Some(true)) => Some(format!("{} muted and deafend themselves", &user.name())),
- (Some(false), Some(false)) => Some(format!("{} unmuted and undeafend themselves", &user.name())),
- (None, Some(true)) => Some(format!("{} deafend themselves", &user.name())),
- (None, Some(false)) => Some(format!("{} undeafend themselves", &user.name())),
- (Some(true), None) => Some(format!("{} muted themselves", &user.name())),
- (Some(false), None) => Some(format!("{} unmuted themselves", &user.name())),
- (Some(true), Some(false)) => Some(format!("{} muted and undeafened themselves", &user.name())),
- (Some(false), Some(true)) => Some(format!("{} unmuted and deafened themselves", &user.name())),
- (None, None) => None,
- };
- if let Some(notify_desc) = notify_desc {
- notify::send(notify_desc);
+ //send notification if a user muted/unmuted
+ if mute != None || deaf != None {
+ let mut s = user.name().to_string();
+ if let Some(mute) = mute {
+ s += if mute { " muted" } else { " unmuted" };
+ }
+ if mute.is_some() && deaf.is_some() {
+ s += " and";
+ }
+ if let Some(deaf) = deaf {
+ s += if deaf { " deafened" } else { " undeafened" };
+ }
+ s += " themselves";
+ notify::send(s);
+ }
}
-
- diff
}
pub fn remove_client(&mut self, msg: msgs::UserRemove) {
@@ -494,8 +542,14 @@ impl State {
warn!("Tried to remove user state without session");
return;
}
- if let Some(user) = self.server().unwrap().users().get(&msg.get_session()) {
- notify::send(format!("{} disconnected", &user.name()));
+
+ let this_channel = self.get_users_channel(self.server().unwrap().session_id().unwrap());
+ let other_channel = self.get_users_channel(msg.get_session());
+ if this_channel == other_channel {
+ self.audio.play_effect(NotificationEvents::UserDisconnected);
+ if let Some(user) = self.server().unwrap().users().get(&msg.get_session()) {
+ notify::send(format!("{} disconnected", &user.name()));
+ }
}
self.audio().remove_client(msg.get_session());
@@ -521,6 +575,7 @@ impl State {
.0
.broadcast(StatePhase::Connected)
.unwrap();
+ self.audio.play_effect(NotificationEvents::ServerConnect);
}
pub fn audio(&self) -> &Audio {
@@ -544,4 +599,14 @@ impl State {
pub fn username(&self) -> Option<&str> {
self.server.as_ref().map(|e| e.username()).flatten()
}
+ fn get_users_channel(&self, user_id: u32) -> u32 {
+ self.server()
+ .unwrap()
+ .users()
+ .iter()
+ .find(|e| *e.0 == user_id)
+ .unwrap()
+ .1
+ .channel()
+ }
}
diff --git a/resources/channel_join.wav b/resources/channel_join.wav
new file mode 100644
index 0000000..82ee4d4
--- /dev/null
+++ b/resources/channel_join.wav
Binary files differ
diff --git a/resources/channel_leave.wav b/resources/channel_leave.wav
new file mode 100644
index 0000000..82ee4d4
--- /dev/null
+++ b/resources/channel_leave.wav
Binary files differ
diff --git a/resources/connect.wav b/resources/connect.wav
new file mode 100644
index 0000000..82ee4d4
--- /dev/null
+++ b/resources/connect.wav
Binary files differ
diff --git a/resources/deafen.wav b/resources/deafen.wav
new file mode 100644
index 0000000..82ee4d4
--- /dev/null
+++ b/resources/deafen.wav
Binary files differ
diff --git a/resources/disconnect.wav b/resources/disconnect.wav
new file mode 100644
index 0000000..fb2ca76
--- /dev/null
+++ b/resources/disconnect.wav
Binary files differ
diff --git a/resources/mute.wav b/resources/mute.wav
new file mode 100644
index 0000000..82ee4d4
--- /dev/null
+++ b/resources/mute.wav
Binary files differ
diff --git a/resources/undeafen.wav b/resources/undeafen.wav
new file mode 100644
index 0000000..82ee4d4
--- /dev/null
+++ b/resources/undeafen.wav
Binary files differ
diff --git a/resources/unmute.wav b/resources/unmute.wav
new file mode 100644
index 0000000..82ee4d4
--- /dev/null
+++ b/resources/unmute.wav
Binary files differ