From 023b69bca57d47b6b2800b7295d28ed2884096e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustav=20S=C3=B6rn=C3=A4s?= Date: Sat, 19 Jun 2021 13:54:58 +0200 Subject: move sound effect to new file --- mumd/src/audio.rs | 124 +++------------------------------------- mumd/src/audio/fallback_sfx.wav | Bin 0 -> 32002 bytes mumd/src/audio/sound_effects.rs | 116 +++++++++++++++++++++++++++++++++++++ mumd/src/fallback_sfx.wav | Bin 32002 -> 0 bytes mumd/src/state.rs | 15 ++--- 5 files changed, 129 insertions(+), 126 deletions(-) create mode 100644 mumd/src/audio/fallback_sfx.wav create mode 100644 mumd/src/audio/sound_effects.rs delete mode 100644 mumd/src/fallback_sfx.wav (limited to 'mumd') 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 { - 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::() - .map(|e| e.unwrap()) - .collect::>(), - hound::SampleFormat::Int => reader - .into_samples::() - .map(|e| cpal::Sample::to_f32(&e.unwrap())) - .collect::>(), - }; - let iter: Box> = 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::>(); - (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 = 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/audio/fallback_sfx.wav b/mumd/src/audio/fallback_sfx.wav new file mode 100644 index 0000000..82ee4d4 Binary files /dev/null and b/mumd/src/audio/fallback_sfx.wav 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 { + 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> { + 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::() + .map(|e| e.unwrap()) + .collect::>(), + hound::SampleFormat::Int => reader + .into_samples::() + .map(|e| cpal::Sample::to_f32(&e.unwrap())) + .collect::>(), + }; + let iter: Box> = 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::>(); + (event, samples) + }) + .collect() +} + +// moo +fn get_sfx(file: &str) -> Cow<'static, [u8]> { + let mut buf: Vec = 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/fallback_sfx.wav deleted file mode 100644 index 82ee4d4..0000000 Binary files a/mumd/src/fallback_sfx.wav and /dev/null differ 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 { -- cgit v1.2.1 From 82369a0a9107267d8fd84bc0960530cc5a402ab3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustav=20S=C3=B6rn=C3=A4s?= Date: Sat, 19 Jun 2021 13:55:14 +0200 Subject: add lewton --- mumd/Cargo.toml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'mumd') diff --git a/mumd/Cargo.toml b/mumd/Cargo.toml index 1e8e63f..6de74fa 100644 --- a/mumd/Cargo.toml +++ b/mumd/Cargo.toml @@ -12,9 +12,10 @@ license = "MIT" readme = "../README.md" [features] -default = ["notifications"] +default = ["notifications", "ogg"] notifications = ["libnotify"] +ogg = ["lewton"] [dependencies] mumlib = { version = "0.4", path = "../mumlib" } @@ -44,7 +45,8 @@ tokio-util = { version = "0.6", features = ["codec", "net"] } bincode = "1.3.2" chrono = "0.4" -libnotify = { version = "1.0", optional = true } +libnotify = { version = "1", optional = true } +lewton = { version = "0.10", optional = true } #compressor = "0.3" #daemonize = "0.4" -- cgit v1.2.1 From 008025b9dc2c833d14d9bc2a3c1dbc5328d80a9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustav=20S=C3=B6rn=C3=A4s?= Date: Sat, 19 Jun 2021 14:22:55 +0200 Subject: refactor out wav unpacking --- mumd/src/audio/sound_effects.rs | 105 ++++++++++++++++++++++++++++++++-------- 1 file changed, 84 insertions(+), 21 deletions(-) (limited to 'mumd') diff --git a/mumd/src/audio/sound_effects.rs b/mumd/src/audio/sound_effects.rs index 3d70bbb..aac9852 100644 --- a/mumd/src/audio/sound_effects.rs +++ b/mumd/src/audio/sound_effects.rs @@ -2,12 +2,37 @@ 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 std::borrow::Cow; +use std::collections::HashMap; +use std::convert::TryFrom; +use std::fs::File; +use std::io::Read; +use std::path::Path; use strum::IntoEnumIterator; use strum_macros::EnumIter; use crate::audio::SAMPLE_RATE; +enum AudioFileKind { + Wav, +} + +impl TryFrom<&str> for AudioFileKind { + type Error = (); + + fn try_from(s: &str) -> Result { + match s { + ".wav" => Ok(AudioFileKind::Wav), + _ => Err(()), + } + } +} + +struct AudioSpec { + channels: u32, + sample_rate: u32, +} + #[derive(Debug, Eq, PartialEq, Clone, Copy, Hash, EnumIter)] pub enum NotificationEvents { ServerConnect, @@ -24,6 +49,7 @@ pub enum NotificationEvents { impl TryFrom<&str> for NotificationEvents { type Error = (); + fn try_from(s: &str) -> Result { match s { "server_connect" => Ok(NotificationEvents::ServerConnect), @@ -57,35 +83,41 @@ pub fn load_sound_effects(overrides: &[SoundEffect], num_channels: usize) -> Has }) .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. 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 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::() - .map(|e| e.unwrap()) - .collect::>(), - hound::SampleFormat::Int => reader - .into_samples::() - .map(|e| cpal::Sample::to_f32(&e.unwrap())) - .collect::>(), - }; + // Open the file if overriden, otherwise use the default sound effect. + let file = overrides.get(&event); + let (data, kind) = file + .map(|file| ( + get_sfx(file), + AudioFileKind::Wav + )) + .unwrap_or_else(|| (get_default_sfx(), AudioFileKind::Wav)); + // Unpack the samples. + let (samples, spec) = unpack_audio(data, kind); + // If the audio is mono (single channel), pad every sample with + // itself, since we later assume that audio is stored as LRLRLR (or + // RLRLRL). Without this, mono audio would be played in double + // speed. let iter: Box> = 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."), }; + // Create a dasp signal containing stereo sound. let mut signal = signal::from_interleaved_samples_iter::<_, [f32; 2]>(iter); + // Create a linear interpolator, in case we need to convert the sample rate. let interp = Linear::new(Signal::next(&mut signal), Signal::next(&mut signal)); + // Create our resulting samples. 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 + // If the source audio is stereo and is being played as mono, discard the first channel. .flat_map(|e| { if num_channels == 1 { vec![e[0]] @@ -99,18 +131,49 @@ pub fn load_sound_effects(overrides: &[SoundEffect], num_channels: usize) -> Has .collect() } +/// Unpack audio data. The required audio spec is read from the file and returned as well. +fn unpack_audio(data: Cow<[u8]>, kind: AudioFileKind) -> (Vec, AudioSpec) { + match kind { + AudioFileKind::Wav => unpack_wav(data), + } +} + +/// Unpack wav data. +fn unpack_wav(data: Cow<[u8]>) -> (Vec, AudioSpec) { + let reader = hound::WavReader::new(data.as_ref()).unwrap(); + let spec = reader.spec(); + let samples = match spec.sample_format { + hound::SampleFormat::Float => reader + .into_samples::() + .map(|e| e.unwrap()) + .collect::>(), + hound::SampleFormat::Int => reader + .into_samples::() + .map(|e| cpal::Sample::to_f32(&e.unwrap())) + .collect::>(), + }; + let spec = AudioSpec { + channels: spec.channels as u32, + sample_rate: spec.sample_rate, + }; + (samples, spec) +} + +/// 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(file: &str) -> Cow<'static, [u8]> { +fn get_sfx>(file: P) -> Cow<'static, [u8]> { let mut buf: Vec = Vec::new(); - if let Ok(mut file) = File::open(file) { + if let Ok(mut file) = File::open(file.as_ref()) { file.read_to_end(&mut buf).unwrap(); Cow::from(buf) } else { - warn!("File not found: '{}'", file); + warn!("File not found: '{}'", file.as_ref().display()); get_default_sfx() } } +/// Get the default sound effect. fn get_default_sfx() -> Cow<'static, [u8]> { Cow::from(include_bytes!("fallback_sfx.wav").as_ref()) } -- cgit v1.2.1 From d2e1919a5c700997bd31dd087b2bf51a9da00e7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustav=20S=C3=B6rn=C3=A4s?= Date: Sat, 19 Jun 2021 14:36:29 +0200 Subject: read file extension when opening file --- mumd/src/audio/sound_effects.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'mumd') diff --git a/mumd/src/audio/sound_effects.rs b/mumd/src/audio/sound_effects.rs index aac9852..21a514b 100644 --- a/mumd/src/audio/sound_effects.rs +++ b/mumd/src/audio/sound_effects.rs @@ -90,13 +90,17 @@ pub fn load_sound_effects(overrides: &[SoundEffect], num_channels: usize) -> Has // effect (if omitted). We then use dasp to convert to the correct sample rate. NotificationEvents::iter() .map(|event| { - // Open the file if overriden, otherwise use the default sound effect. let file = overrides.get(&event); + // Try to open the file if overriden, otherwise use the default sound effect. let (data, kind) = file - .map(|file| ( - get_sfx(file), - AudioFileKind::Wav - )) + .and_then(|file| { + // Try to get the file kind from the extension. + let kind = file + .split('.') + .last() + .and_then(|ext| AudioFileKind::try_from(ext).ok())?; + Some((get_sfx(file), kind)) + }) .unwrap_or_else(|| (get_default_sfx(), AudioFileKind::Wav)); // Unpack the samples. let (samples, spec) = unpack_audio(data, kind); -- cgit v1.2.1 From ee89022431c54eae1f64099439a23cd316a0c7d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustav=20S=C3=B6rn=C3=A4s?= Date: Sat, 19 Jun 2021 14:46:33 +0200 Subject: try to unpack ogg data --- mumd/src/audio/sound_effects.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'mumd') diff --git a/mumd/src/audio/sound_effects.rs b/mumd/src/audio/sound_effects.rs index 21a514b..8a271e0 100644 --- a/mumd/src/audio/sound_effects.rs +++ b/mumd/src/audio/sound_effects.rs @@ -6,6 +6,7 @@ use std::borrow::Cow; use std::collections::HashMap; use std::convert::TryFrom; use std::fs::File; +use std::io::Cursor; use std::io::Read; use std::path::Path; use strum::IntoEnumIterator; @@ -14,6 +15,7 @@ use strum_macros::EnumIter; use crate::audio::SAMPLE_RATE; enum AudioFileKind { + Ogg, Wav, } @@ -22,6 +24,7 @@ impl TryFrom<&str> for AudioFileKind { fn try_from(s: &str) -> Result { match s { + ".ogg" => Ok(AudioFileKind::Ogg), ".wav" => Ok(AudioFileKind::Wav), _ => Err(()), } @@ -138,10 +141,26 @@ pub fn load_sound_effects(overrides: &[SoundEffect], num_channels: usize) -> Has /// Unpack audio data. The required audio spec is read from the file and returned as well. fn unpack_audio(data: Cow<[u8]>, kind: AudioFileKind) -> (Vec, AudioSpec) { match kind { + AudioFileKind::Ogg => unpack_ogg(data), AudioFileKind::Wav => unpack_wav(data), } } +/// Unpack ogg data. +fn unpack_ogg(data: Cow<[u8]>) -> (Vec, AudioSpec) { + let mut reader = lewton::inside_ogg::OggStreamReader::new(Cursor::new(data.as_ref())).unwrap(); + let mut samples = Vec::new(); + while let Ok(Some(mut frame)) = reader.read_dec_packet_itl() { + samples.append(&mut frame); + } + let samples = samples.iter().map(|s| cpal::Sample::to_f32(s)).collect(); + let spec = AudioSpec { + channels: reader.ident_hdr.audio_channels as u32, + sample_rate: reader.ident_hdr.audio_sample_rate, + }; + (samples, spec) +} + /// Unpack wav data. fn unpack_wav(data: Cow<[u8]>) -> (Vec, AudioSpec) { let reader = hound::WavReader::new(data.as_ref()).unwrap(); -- cgit v1.2.1 From f12b40d09d77f75e5349b8acd2369e79ffa01122 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustav=20S=C3=B6rn=C3=A4s?= Date: Sat, 19 Jun 2021 15:26:41 +0200 Subject: check correct file extension --- mumd/src/audio/sound_effects.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'mumd') diff --git a/mumd/src/audio/sound_effects.rs b/mumd/src/audio/sound_effects.rs index 8a271e0..7e4e96a 100644 --- a/mumd/src/audio/sound_effects.rs +++ b/mumd/src/audio/sound_effects.rs @@ -24,8 +24,8 @@ impl TryFrom<&str> for AudioFileKind { fn try_from(s: &str) -> Result { match s { - ".ogg" => Ok(AudioFileKind::Ogg), - ".wav" => Ok(AudioFileKind::Wav), + "ogg" => Ok(AudioFileKind::Ogg), + "wav" => Ok(AudioFileKind::Wav), _ => Err(()), } } -- cgit v1.2.1 From 13b010e939acec028158bba9c42bbb17c694e879 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustav=20S=C3=B6rn=C3=A4s?= Date: Sat, 19 Jun 2021 16:05:20 +0200 Subject: note interleaved --- mumd/src/audio/sound_effects.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'mumd') diff --git a/mumd/src/audio/sound_effects.rs b/mumd/src/audio/sound_effects.rs index 7e4e96a..82c281b 100644 --- a/mumd/src/audio/sound_effects.rs +++ b/mumd/src/audio/sound_effects.rs @@ -108,9 +108,9 @@ pub fn load_sound_effects(overrides: &[SoundEffect], num_channels: usize) -> Has // Unpack the samples. let (samples, spec) = unpack_audio(data, kind); // If the audio is mono (single channel), pad every sample with - // itself, since we later assume that audio is stored as LRLRLR (or - // RLRLRL). Without this, mono audio would be played in double - // speed. + // itself, since we later assume that audio is stored interleaved as + // LRLRLR (or RLRLRL). Without this, mono audio would be played in + // double speed. let iter: Box> = match spec.channels { 1 => Box::new(samples.into_iter().flat_map(|e| vec![e, e])), 2 => Box::new(samples.into_iter()), -- cgit v1.2.1 From 91f5b61ca9b4c55683f7c3c24c11618e00ce99e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustav=20S=C3=B6rn=C3=A4s?= Date: Sat, 19 Jun 2021 16:17:58 +0200 Subject: force stereo output --- mumd/src/audio/output.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'mumd') diff --git a/mumd/src/audio/output.rs b/mumd/src/audio/output.rs index a2f6bcc..9f2ac25 100644 --- a/mumd/src/audio/output.rs +++ b/mumd/src/audio/output.rs @@ -126,7 +126,7 @@ impl DefaultAudioOutputDevice { .supported_output_configs() .map_err(|e| AudioError::NoConfigs(AudioStream::Output, e))? .find_map(|c| { - if c.min_sample_rate() <= sample_rate && c.max_sample_rate() >= sample_rate { + if c.min_sample_rate() <= sample_rate && c.max_sample_rate() >= sample_rate && c.channels() == 2 { Some(c) } else { None -- cgit v1.2.1 From a3b963e10a28befbdcd5b448fefe5a719772db3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustav=20S=C3=B6rn=C3=A4s?= Date: Sat, 19 Jun 2021 16:25:15 +0200 Subject: fallback to default sfx if ogg is disabled --- mumd/src/audio/sound_effects.rs | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'mumd') diff --git a/mumd/src/audio/sound_effects.rs b/mumd/src/audio/sound_effects.rs index 82c281b..ae96fa3 100644 --- a/mumd/src/audio/sound_effects.rs +++ b/mumd/src/audio/sound_effects.rs @@ -6,6 +6,7 @@ use std::borrow::Cow; use std::collections::HashMap; use std::convert::TryFrom; use std::fs::File; +#[cfg(feature = "ogg")] use std::io::Cursor; use std::io::Read; use std::path::Path; @@ -146,6 +147,7 @@ fn unpack_audio(data: Cow<[u8]>, kind: AudioFileKind) -> (Vec, AudioSpec) { } } +#[cfg(feature = "ogg")] /// Unpack ogg data. fn unpack_ogg(data: Cow<[u8]>) -> (Vec, AudioSpec) { let mut reader = lewton::inside_ogg::OggStreamReader::new(Cursor::new(data.as_ref())).unwrap(); @@ -161,6 +163,12 @@ fn unpack_ogg(data: Cow<[u8]>) -> (Vec, AudioSpec) { (samples, spec) } +#[cfg(not(feature = "ogg"))] +fn unpack_ogg(_: Cow<[u8]>) -> (Vec, AudioSpec) { + warn!("Can't open .ogg without the ogg-feature enabled."); + unpack_wav(get_default_sfx()) +} + /// Unpack wav data. fn unpack_wav(data: Cow<[u8]>) -> (Vec, AudioSpec) { let reader = hound::WavReader::new(data.as_ref()).unwrap(); -- cgit v1.2.1 From 319d34024a44c33d46123eceedce913eaded4878 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustav=20S=C3=B6rn=C3=A4s?= Date: Sat, 19 Jun 2021 16:25:39 +0200 Subject: dont warn about unused import when building without notifications --- mumd/src/notifications.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'mumd') diff --git a/mumd/src/notifications.rs b/mumd/src/notifications.rs index bccf4dd..5094a07 100644 --- a/mumd/src/notifications.rs +++ b/mumd/src/notifications.rs @@ -1,3 +1,4 @@ +#[cfg(feature = "notifications")] use log::*; pub fn init() { -- cgit v1.2.1 From 6b9d837c447c520106b0d888103ad77af93046f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustav=20S=C3=B6rn=C3=A4s?= Date: Sat, 19 Jun 2021 16:32:30 +0200 Subject: documentation --- mumd/src/audio.rs | 2 ++ mumd/src/audio/sound_effects.rs | 3 +++ 2 files changed, 5 insertions(+) (limited to 'mumd') diff --git a/mumd/src/audio.rs b/mumd/src/audio.rs index 785bf7c..a04f0da 100644 --- a/mumd/src/audio.rs +++ b/mumd/src/audio.rs @@ -97,6 +97,8 @@ impl AudioOutput { 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()); } diff --git a/mumd/src/audio/sound_effects.rs b/mumd/src/audio/sound_effects.rs index ae96fa3..49cb009 100644 --- a/mumd/src/audio/sound_effects.rs +++ b/mumd/src/audio/sound_effects.rs @@ -74,6 +74,8 @@ 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> { let overrides: HashMap<_, _> = overrides .iter() @@ -164,6 +166,7 @@ fn unpack_ogg(data: Cow<[u8]>) -> (Vec, AudioSpec) { } #[cfg(not(feature = "ogg"))] +/// Fallback to default sound effect since ogg is disabled. fn unpack_ogg(_: Cow<[u8]>) -> (Vec, AudioSpec) { warn!("Can't open .ogg without the ogg-feature enabled."); unpack_wav(get_default_sfx()) -- cgit v1.2.1 From 402d51d25b8336c85154fa912e7dd1739fdfc55f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustav=20S=C3=B6rn=C3=A4s?= Date: Sat, 19 Jun 2021 16:33:51 +0200 Subject: more doc --- mumd/src/audio/sound_effects.rs | 3 +++ 1 file changed, 3 insertions(+) (limited to 'mumd') diff --git a/mumd/src/audio/sound_effects.rs b/mumd/src/audio/sound_effects.rs index 49cb009..b6f41f1 100644 --- a/mumd/src/audio/sound_effects.rs +++ b/mumd/src/audio/sound_effects.rs @@ -15,6 +15,7 @@ use strum_macros::EnumIter; use crate::audio::SAMPLE_RATE; +/// The different kinds of files we can open. enum AudioFileKind { Ogg, Wav, @@ -32,11 +33,13 @@ impl TryFrom<&str> for AudioFileKind { } } +/// A specification accompanying some audio data. struct AudioSpec { channels: u32, sample_rate: u32, } +/// An event where a notification is shown and a sound effect is played. #[derive(Debug, Eq, PartialEq, Clone, Copy, Hash, EnumIter)] pub enum NotificationEvents { ServerConnect, -- cgit v1.2.1 From a3be71e2f6b21ae4c8bac8b7ffd1d3f147357339 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustav=20S=C3=B6rn=C3=A4s?= Date: Sat, 19 Jun 2021 17:06:44 +0200 Subject: add lifetimes to cows --- mumd/src/audio/sound_effects.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'mumd') diff --git a/mumd/src/audio/sound_effects.rs b/mumd/src/audio/sound_effects.rs index b6f41f1..205b3f7 100644 --- a/mumd/src/audio/sound_effects.rs +++ b/mumd/src/audio/sound_effects.rs @@ -145,7 +145,7 @@ pub fn load_sound_effects(overrides: &[SoundEffect], num_channels: usize) -> Has } /// Unpack audio data. The required audio spec is read from the file and returned as well. -fn unpack_audio(data: Cow<[u8]>, kind: AudioFileKind) -> (Vec, AudioSpec) { +fn unpack_audio(data: Cow<'_, [u8]>, kind: AudioFileKind) -> (Vec, AudioSpec) { match kind { AudioFileKind::Ogg => unpack_ogg(data), AudioFileKind::Wav => unpack_wav(data), @@ -154,7 +154,7 @@ fn unpack_audio(data: Cow<[u8]>, kind: AudioFileKind) -> (Vec, AudioSpec) { #[cfg(feature = "ogg")] /// Unpack ogg data. -fn unpack_ogg(data: Cow<[u8]>) -> (Vec, AudioSpec) { +fn unpack_ogg(data: Cow<'_, [u8]>) -> (Vec, AudioSpec) { let mut reader = lewton::inside_ogg::OggStreamReader::new(Cursor::new(data.as_ref())).unwrap(); let mut samples = Vec::new(); while let Ok(Some(mut frame)) = reader.read_dec_packet_itl() { @@ -170,13 +170,13 @@ fn unpack_ogg(data: Cow<[u8]>) -> (Vec, AudioSpec) { #[cfg(not(feature = "ogg"))] /// Fallback to default sound effect since ogg is disabled. -fn unpack_ogg(_: Cow<[u8]>) -> (Vec, AudioSpec) { +fn unpack_ogg(_: Cow<'_, [u8]>) -> (Vec, AudioSpec) { warn!("Can't open .ogg without the ogg-feature enabled."); unpack_wav(get_default_sfx()) } /// Unpack wav data. -fn unpack_wav(data: Cow<[u8]>) -> (Vec, AudioSpec) { +fn unpack_wav(data: Cow<'_, [u8]>) -> (Vec, AudioSpec) { let reader = hound::WavReader::new(data.as_ref()).unwrap(); let spec = reader.spec(); let samples = match spec.sample_format { -- cgit v1.2.1 From f33a372ef62e337ff141c76f3ec9b6c2a5077630 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustav=20S=C3=B6rn=C3=A4s?= Date: Sun, 20 Jun 2021 02:10:16 +0200 Subject: review --- mumd/src/audio/sound_effects.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'mumd') diff --git a/mumd/src/audio/sound_effects.rs b/mumd/src/audio/sound_effects.rs index 205b3f7..63f08bd 100644 --- a/mumd/src/audio/sound_effects.rs +++ b/mumd/src/audio/sound_effects.rs @@ -70,7 +70,6 @@ impl TryFrom<&str> for NotificationEvents { "deafen" => Ok(NotificationEvents::Deafen), "undeafen" => Ok(NotificationEvents::Undeafen), _ => { - warn!("Unknown notification event '{}' in config", s); Err(()) } } @@ -87,6 +86,7 @@ pub fn load_sound_effects(overrides: &[SoundEffect], num_channels: usize) -> Has if let Ok(event) = NotificationEvents::try_from(event.as_str()) { Some((event, file)) } else { + warn!("Unknown notification event '{}'", event); None } }) @@ -118,7 +118,7 @@ pub fn load_sound_effects(overrides: &[SoundEffect], num_channels: usize) -> Has // LRLRLR (or RLRLRL). Without this, mono audio would be played in // double speed. let iter: Box> = match spec.channels { - 1 => Box::new(samples.into_iter().flat_map(|e| vec![e, e])), + 1 => Box::new(samples.into_iter().flat_map(|e| [e, e])), 2 => Box::new(samples.into_iter()), _ => unimplemented!("Only mono and stereo sound is supported. See #80."), }; -- cgit v1.2.1