diff --git a/assets/voxygen/audio/ambient.ron b/assets/voxygen/audio/ambient.ron index b4a619df07..768f2ab049 100644 --- a/assets/voxygen/audio/ambient.ron +++ b/assets/voxygen/audio/ambient.ron @@ -2,22 +2,22 @@ tracks: [ ( path: "voxygen.audio.ambience.wind", - length: 14.17, + length: 14.2, tag: Wind, ), ( path: "voxygen.audio.ambience.rain", - length: 16.97, + length: 17.0, tag: Rain, ), ( path:"voxygen.audio.ambience.thunder", - length: 31.97, + length: 32.0, tag: Thunder, ), ( path:"voxygen.audio.ambience.leaves", - length: 25.97, + length: 26.0, tag: Leaves, ), ] diff --git a/assets/voxygen/audio/sfx/ambient/lightning_1.ogg b/assets/voxygen/audio/sfx/ambient/lightning_1.ogg index 546d7d11fe..2222431396 100644 --- a/assets/voxygen/audio/sfx/ambient/lightning_1.ogg +++ b/assets/voxygen/audio/sfx/ambient/lightning_1.ogg @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:caabc3e9d9066d710ca970189770040c82bd9588fa8911a77adbaa20f222a87f -size 59431 +oid sha256:a49864f72934aeaf1afb8c53ed60112a20520941de76b9c858c869cfcf23df51 +size 56352 diff --git a/client/src/lib.rs b/client/src/lib.rs index c1a1bec58f..a0f04ecf5d 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -1496,20 +1496,22 @@ impl Client { if let Some(position) = self.current::() { player_alt = position.0.z; } - let mut contains_cave = false; + //let mut contains_cave = false; let mut terrain_alt = 0.0; let mut contains_dungeon = false; let mut contains_settlement = false; if let Some(chunk) = self.current_chunk() { terrain_alt = chunk.meta().alt(); - contains_cave = chunk.meta().contains_cave(); + //contains_cave = chunk.meta().contains_cave(); contains_dungeon = chunk.meta().contains_dungeon(); contains_settlement = chunk.meta().contains_settlement(); } - if player_alt < (terrain_alt - 25.0) && contains_cave { - SitesKind::Cave - } else if player_alt < (terrain_alt - 25.0) && contains_dungeon { - SitesKind::Dungeon + if player_alt < terrain_alt - 40.0 { + if contains_dungeon { + SitesKind::Dungeon + } else { + SitesKind::Cave + } } else if contains_settlement { SitesKind::Settlement } else { diff --git a/voxygen/src/audio/ambient.rs b/voxygen/src/audio/ambient.rs index 1e765161ed..a2d90b71de 100644 --- a/voxygen/src/audio/ambient.rs +++ b/voxygen/src/audio/ambient.rs @@ -89,7 +89,11 @@ impl AmbientMgr { channel.next_track_change = track.length; // Play the file of the current tag at the current multiplier; let current_multiplier = channel.multiplier; - audio.play_ambient(tag, &track.path, current_multiplier * ambience_volume); + audio.play_ambient( + tag, + &track.path, + Some(current_multiplier * ambience_volume), + ); } }; @@ -103,6 +107,15 @@ impl AmbientMgr { } impl AmbientChannelTag { + pub fn tag_max_volume(tag: AmbientChannelTag) -> f32 { + match tag { + AmbientChannelTag::Wind => 1.15, + AmbientChannelTag::Rain => 0.95, + AmbientChannelTag::Thunder => 1.33, + AmbientChannelTag::Leaves => 1.33, + } + } + // Gets appropriate volume for each tag pub fn get_tag_volume(tag: AmbientChannelTag, client: &Client, camera: &Camera) -> f32 { match tag { @@ -134,11 +147,10 @@ impl AmbientChannelTag { / 15.0_f32.powi(2)) .min(1.33); - ((alt_multiplier + (alt_multiplier * tree_multiplier * (wind_speed_multiplier + ((cam_pos.z - terrain_alt).abs() / 150.0).powi(2))) - + ((alt_multiplier * 0.15) * tree_multiplier)) - .min(1.15) + + (alt_multiplier * 0.15) * tree_multiplier }, AmbientChannelTag::Rain => { let focus_off = camera.get_focus_pos().map(f32::trunc); @@ -153,10 +165,10 @@ impl AmbientChannelTag { let camera_multiplier = 1.0 - ((cam_pos.z - terrain_alt).abs() / 75.0).powi(2).min(1.0); - ((client.weather_at_player().rain * 2.5) * camera_multiplier).min(0.95) + (client.weather_at_player().rain * 3.0) * camera_multiplier }, AmbientChannelTag::Thunder => { - let rain_intensity = (client.weather_at_player().rain * 2.5).min(1.33); + let rain_intensity = client.weather_at_player().rain * 3.0; if rain_intensity < 0.7 { 0.0 @@ -179,8 +191,8 @@ impl AmbientChannelTag { // the closer the camera is to the ground let tree_multiplier = 1.0 - (((1.0 - tree_density) - + ((cam_pos.z - terrain_alt + 20.0).abs() / 150.0).powi(2)) - .min(1.0)); + + ((cam_pos.z - terrain_alt - 20.0).abs() / 150.0).powi(2)) + .min(1.1)); // Take into account wind speed too, which amplifies tree noise let wind_speed_multiplier = (client.weather_at_player().wind.magnitude_squared() @@ -188,7 +200,7 @@ impl AmbientChannelTag { .min(1.0); if tree_multiplier > 0.1 { - (tree_multiplier * (1.0 + wind_speed_multiplier)).min(1.33) + tree_multiplier * (1.0 + wind_speed_multiplier) } else { 0.0 } @@ -225,7 +237,8 @@ fn get_target_volume( } // Is the camera underneath the terrain? Fade out the lower it goes beneath. - volume_multiplier * ((cam_pos.z - terrain_alt) / 30.0 + 1.0).clamped(0.0, 1.0) + (volume_multiplier * ((cam_pos.z - terrain_alt) / 40.0 + 1.0).clamped(0.0, 1.0)) + .min(AmbientChannelTag::tag_max_volume(tag)) } pub fn load_ambience_items() -> AssetHandle { diff --git a/voxygen/src/audio/channel.rs b/voxygen/src/audio/channel.rs index 3948ac522e..099c9c34d5 100644 --- a/voxygen/src/audio/channel.rs +++ b/voxygen/src/audio/channel.rs @@ -282,6 +282,11 @@ impl SfxChannel { } } +/// An UiChannel uses a non-spatial audio sink, and is designed for short-lived +/// audio which is not spatially controlled, but does not need control over +/// playback or fading/transitions +/// +/// See also: [`Rodio::Sink`](https://docs.rs/rodio/0.11.0/rodio/struct.Sink.html) pub struct UiChannel { sink: Sink, } diff --git a/voxygen/src/audio/mod.rs b/voxygen/src/audio/mod.rs index 286a5130a4..98c30d7681 100644 --- a/voxygen/src/audio/mod.rs +++ b/voxygen/src/audio/mod.rs @@ -21,6 +21,11 @@ use common::assets::{AssetExt, AssetHandle}; use rodio::{source::Source, OutputStream, OutputStreamHandle, StreamError}; use vek::*; +/// Prevents sounds that are too low in volume from playing. This may marginally +/// improve performance in certain situations since less audio channels will be +/// used on average. +const MIN_HEARABLE_VOLUME: f32 = 0.003; + #[derive(Default, Clone)] pub struct Listener { pos: Vec3, @@ -216,20 +221,14 @@ impl AudioFrontend { self.music_channels.last_mut() } - /// Play an sfx file given the position, SfxEvent, and whether it is - /// underwater or not - pub fn emit_sfx( - &mut self, - trigger_item: Option<(&SfxEvent, &SfxTriggerItem)>, - position: Vec3, - volume: Option, - underwater: bool, - ) { - if let Some((event, item)) = trigger_item { - // Find sound based on given trigger_item - // Randomizes if multiple sounds are found - // Errors if no sounds are found - let sfx_file = match item.files.len() { + /// Find sound based on given trigger_item + /// Randomizes if multiple sounds are found + /// Errors if no sounds are found + fn get_sfx_file<'a>( + trigger_item: Option<(&'a SfxEvent, &'a SfxTriggerItem)>, + ) -> Option<&'a str> { + trigger_item.map(|(event, item)| { + match item.files.len() { 0 => { debug!("Sfx event {:?} is missing audio file.", event); "voxygen.audio.sfx.placeholder" @@ -243,9 +242,22 @@ impl AudioFrontend { let rand_step = rand::random::() % item.files.len(); &item.files[rand_step] }, - }; + } + }) + } + + /// Play an sfx file given the position, SfxEvent, and whether it is + /// underwater or not + pub fn emit_sfx( + &mut self, + trigger_item: Option<(&SfxEvent, &SfxTriggerItem)>, + position: Vec3, + volume: Option, + underwater: bool, + ) { + if let Some(sfx_file) = Self::get_sfx_file(trigger_item) { // Play sound in empty channel at given position - if self.audio_stream.is_some() { + if self.audio_stream.is_some() && volume.map_or(true, |v| v > MIN_HEARABLE_VOLUME) { let sound = load_ogg(sfx_file).amplify(volume.unwrap_or(1.0)); let listener = self.listener.clone(); @@ -275,27 +287,9 @@ impl AudioFrontend { trigger_item: Option<(&SfxEvent, &SfxTriggerItem)>, volume: Option, ) { - // Find sound based on given trigger_item - // Randomizes if multiple sounds are found - // Errors if no sounds are found - if let Some((event, item)) = trigger_item { - let sfx_file = match item.files.len() { - 0 => { - debug!("Sfx event {:?} is missing audio file.", event); - "voxygen.audio.sfx.placeholder" - }, - 1 => item - .files - .last() - .expect("Failed to determine sound file for this trigger item."), - _ => { - // If more than one file is listed, choose one at random - let rand_step = rand::random::() % item.files.len(); - &item.files[rand_step] - }, - }; + if let Some(sfx_file) = Self::get_sfx_file(trigger_item) { // Play sound in empty channel - if self.audio_stream.is_some() { + if self.audio_stream.is_some() && volume.map_or(true, |v| v > MIN_HEARABLE_VOLUME) { let sound = load_ogg(sfx_file).amplify(volume.unwrap_or(1.0)); if let Some(channel) = self.get_ui_channel() { @@ -308,10 +302,10 @@ impl AudioFrontend { } /// Plays a file at a given volume in the channel with a given tag - fn play_ambient(&mut self, channel_tag: AmbientChannelTag, sound: &str, volume: f32) { + fn play_ambient(&mut self, channel_tag: AmbientChannelTag, sound: &str, volume: Option) { if self.audio_stream.is_some() { if let Some(channel) = self.get_ambient_channel(channel_tag) { - channel.set_volume(volume); + channel.set_volume(volume.unwrap_or(1.0)); channel.play(load_ogg(sound)); } } @@ -503,19 +497,18 @@ impl AudioFrontend { } } - pub fn stop_ambient_sounds(&mut self) { - for channel in self.ambient_channels.iter_mut() { - channel.stop() - } - } + pub fn stop_all_ambience(&mut self) { self.ambient_channels.retain(|x| Some(x).is_none()) } + // Sfx channels do not repopulate themselves yet pub fn stop_all_sfx(&mut self) { - for channel in self.sfx_channels.iter_mut() { - channel.stop() - } - for channel in self.ui_channels.iter_mut() { - channel.stop() - } + if let Some(audio_stream) = &self.audio_stream { + for channel in &mut self.sfx_channels { + *channel = SfxChannel::new(audio_stream); + } + for channel in &mut self.ui_channels { + *channel = UiChannel::new(audio_stream); + } + }; } // The following is for the disabled device switcher diff --git a/voxygen/src/audio/sfx/mod.rs b/voxygen/src/audio/sfx/mod.rs index e40b01caae..723cc41c22 100644 --- a/voxygen/src/audio/sfx/mod.rs +++ b/voxygen/src/audio/sfx/mod.rs @@ -435,13 +435,13 @@ impl SfxMgr { ); }, Outcome::Lightning { pos } => { - let power = (1.0 - pos.distance(audio.listener.pos) / 6_000.0) - .clamped(0.0, 1.0) - .powf(0.75); + let power = (1.0 - pos.distance(audio.listener.pos) / 5_000.0) + .max(0.0) + .powi(7); if power > 0.0 { let sfx_trigger_item = triggers.get_key_value(&SfxEvent::Lightning); // TODO: Don't use UI sfx, add a way to control position falloff - audio.emit_ui_sfx(sfx_trigger_item, Some(power * 3.0)); + audio.emit_ui_sfx(sfx_trigger_item, Some((power * 3.0).min(2.9))); } }, Outcome::GroundSlam { pos, .. } => { diff --git a/voxygen/src/session/mod.rs b/voxygen/src/session/mod.rs index 9bb4bdc11c..3183489b56 100644 --- a/voxygen/src/session/mod.rs +++ b/voxygen/src/session/mod.rs @@ -1206,7 +1206,7 @@ impl PlayState for SessionState { // Stop all sounds // TODO: Abstract this behavior to all instances of PlayStateResult::Pop // somehow - global_state.audio.stop_ambient_sounds(); + global_state.audio.stop_all_ambience(); global_state.audio.stop_all_sfx(); return PlayStateResult::Pop; },