mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'DaforLynx/thunder-fix' into 'master'
Lightning strikes are less audible; cave music restored; fixed audio permanently stopping on logout See merge request veloren/veloren!3487
This commit is contained in:
commit
7f94580b20
@ -2,22 +2,22 @@
|
|||||||
tracks: [
|
tracks: [
|
||||||
(
|
(
|
||||||
path: "voxygen.audio.ambience.wind",
|
path: "voxygen.audio.ambience.wind",
|
||||||
length: 14.17,
|
length: 14.2,
|
||||||
tag: Wind,
|
tag: Wind,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
path: "voxygen.audio.ambience.rain",
|
path: "voxygen.audio.ambience.rain",
|
||||||
length: 16.97,
|
length: 17.0,
|
||||||
tag: Rain,
|
tag: Rain,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
path:"voxygen.audio.ambience.thunder",
|
path:"voxygen.audio.ambience.thunder",
|
||||||
length: 31.97,
|
length: 32.0,
|
||||||
tag: Thunder,
|
tag: Thunder,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
path:"voxygen.audio.ambience.leaves",
|
path:"voxygen.audio.ambience.leaves",
|
||||||
length: 25.97,
|
length: 26.0,
|
||||||
tag: Leaves,
|
tag: Leaves,
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
BIN
assets/voxygen/audio/sfx/ambient/lightning_1.ogg
(Stored with Git LFS)
BIN
assets/voxygen/audio/sfx/ambient/lightning_1.ogg
(Stored with Git LFS)
Binary file not shown.
@ -1496,20 +1496,22 @@ impl Client {
|
|||||||
if let Some(position) = self.current::<comp::Pos>() {
|
if let Some(position) = self.current::<comp::Pos>() {
|
||||||
player_alt = position.0.z;
|
player_alt = position.0.z;
|
||||||
}
|
}
|
||||||
let mut contains_cave = false;
|
//let mut contains_cave = false;
|
||||||
let mut terrain_alt = 0.0;
|
let mut terrain_alt = 0.0;
|
||||||
let mut contains_dungeon = false;
|
let mut contains_dungeon = false;
|
||||||
let mut contains_settlement = false;
|
let mut contains_settlement = false;
|
||||||
if let Some(chunk) = self.current_chunk() {
|
if let Some(chunk) = self.current_chunk() {
|
||||||
terrain_alt = chunk.meta().alt();
|
terrain_alt = chunk.meta().alt();
|
||||||
contains_cave = chunk.meta().contains_cave();
|
//contains_cave = chunk.meta().contains_cave();
|
||||||
contains_dungeon = chunk.meta().contains_dungeon();
|
contains_dungeon = chunk.meta().contains_dungeon();
|
||||||
contains_settlement = chunk.meta().contains_settlement();
|
contains_settlement = chunk.meta().contains_settlement();
|
||||||
}
|
}
|
||||||
if player_alt < (terrain_alt - 25.0) && contains_cave {
|
if player_alt < terrain_alt - 40.0 {
|
||||||
SitesKind::Cave
|
if contains_dungeon {
|
||||||
} else if player_alt < (terrain_alt - 25.0) && contains_dungeon {
|
|
||||||
SitesKind::Dungeon
|
SitesKind::Dungeon
|
||||||
|
} else {
|
||||||
|
SitesKind::Cave
|
||||||
|
}
|
||||||
} else if contains_settlement {
|
} else if contains_settlement {
|
||||||
SitesKind::Settlement
|
SitesKind::Settlement
|
||||||
} else {
|
} else {
|
||||||
|
@ -89,7 +89,11 @@ impl AmbientMgr {
|
|||||||
channel.next_track_change = track.length;
|
channel.next_track_change = track.length;
|
||||||
// Play the file of the current tag at the current multiplier;
|
// Play the file of the current tag at the current multiplier;
|
||||||
let current_multiplier = channel.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 {
|
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
|
// Gets appropriate volume for each tag
|
||||||
pub fn get_tag_volume(tag: AmbientChannelTag, client: &Client, camera: &Camera) -> f32 {
|
pub fn get_tag_volume(tag: AmbientChannelTag, client: &Client, camera: &Camera) -> f32 {
|
||||||
match tag {
|
match tag {
|
||||||
@ -134,11 +147,10 @@ impl AmbientChannelTag {
|
|||||||
/ 15.0_f32.powi(2))
|
/ 15.0_f32.powi(2))
|
||||||
.min(1.33);
|
.min(1.33);
|
||||||
|
|
||||||
((alt_multiplier
|
(alt_multiplier
|
||||||
* tree_multiplier
|
* tree_multiplier
|
||||||
* (wind_speed_multiplier + ((cam_pos.z - terrain_alt).abs() / 150.0).powi(2)))
|
* (wind_speed_multiplier + ((cam_pos.z - terrain_alt).abs() / 150.0).powi(2)))
|
||||||
+ ((alt_multiplier * 0.15) * tree_multiplier))
|
+ (alt_multiplier * 0.15) * tree_multiplier
|
||||||
.min(1.15)
|
|
||||||
},
|
},
|
||||||
AmbientChannelTag::Rain => {
|
AmbientChannelTag::Rain => {
|
||||||
let focus_off = camera.get_focus_pos().map(f32::trunc);
|
let focus_off = camera.get_focus_pos().map(f32::trunc);
|
||||||
@ -153,10 +165,10 @@ impl AmbientChannelTag {
|
|||||||
let camera_multiplier =
|
let camera_multiplier =
|
||||||
1.0 - ((cam_pos.z - terrain_alt).abs() / 75.0).powi(2).min(1.0);
|
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 => {
|
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 {
|
if rain_intensity < 0.7 {
|
||||||
0.0
|
0.0
|
||||||
@ -179,8 +191,8 @@ impl AmbientChannelTag {
|
|||||||
// the closer the camera is to the ground
|
// the closer the camera is to the ground
|
||||||
let tree_multiplier = 1.0
|
let tree_multiplier = 1.0
|
||||||
- (((1.0 - tree_density)
|
- (((1.0 - tree_density)
|
||||||
+ ((cam_pos.z - terrain_alt + 20.0).abs() / 150.0).powi(2))
|
+ ((cam_pos.z - terrain_alt - 20.0).abs() / 150.0).powi(2))
|
||||||
.min(1.0));
|
.min(1.1));
|
||||||
|
|
||||||
// Take into account wind speed too, which amplifies tree noise
|
// Take into account wind speed too, which amplifies tree noise
|
||||||
let wind_speed_multiplier = (client.weather_at_player().wind.magnitude_squared()
|
let wind_speed_multiplier = (client.weather_at_player().wind.magnitude_squared()
|
||||||
@ -188,7 +200,7 @@ impl AmbientChannelTag {
|
|||||||
.min(1.0);
|
.min(1.0);
|
||||||
|
|
||||||
if tree_multiplier > 0.1 {
|
if tree_multiplier > 0.1 {
|
||||||
(tree_multiplier * (1.0 + wind_speed_multiplier)).min(1.33)
|
tree_multiplier * (1.0 + wind_speed_multiplier)
|
||||||
} else {
|
} else {
|
||||||
0.0
|
0.0
|
||||||
}
|
}
|
||||||
@ -225,7 +237,8 @@ fn get_target_volume(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Is the camera underneath the terrain? Fade out the lower it goes beneath.
|
// 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<AmbientCollection> {
|
pub fn load_ambience_items() -> AssetHandle<AmbientCollection> {
|
||||||
|
@ -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 {
|
pub struct UiChannel {
|
||||||
sink: Sink,
|
sink: Sink,
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,11 @@ use common::assets::{AssetExt, AssetHandle};
|
|||||||
use rodio::{source::Source, OutputStream, OutputStreamHandle, StreamError};
|
use rodio::{source::Source, OutputStream, OutputStreamHandle, StreamError};
|
||||||
use vek::*;
|
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)]
|
#[derive(Default, Clone)]
|
||||||
pub struct Listener {
|
pub struct Listener {
|
||||||
pos: Vec3<f32>,
|
pos: Vec3<f32>,
|
||||||
@ -216,20 +221,14 @@ impl AudioFrontend {
|
|||||||
self.music_channels.last_mut()
|
self.music_channels.last_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Play an sfx file given the position, SfxEvent, and whether it is
|
/// Find sound based on given trigger_item
|
||||||
/// underwater or not
|
/// Randomizes if multiple sounds are found
|
||||||
pub fn emit_sfx(
|
/// Errors if no sounds are found
|
||||||
&mut self,
|
fn get_sfx_file<'a>(
|
||||||
trigger_item: Option<(&SfxEvent, &SfxTriggerItem)>,
|
trigger_item: Option<(&'a SfxEvent, &'a SfxTriggerItem)>,
|
||||||
position: Vec3<f32>,
|
) -> Option<&'a str> {
|
||||||
volume: Option<f32>,
|
trigger_item.map(|(event, item)| {
|
||||||
underwater: bool,
|
match item.files.len() {
|
||||||
) {
|
|
||||||
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() {
|
|
||||||
0 => {
|
0 => {
|
||||||
debug!("Sfx event {:?} is missing audio file.", event);
|
debug!("Sfx event {:?} is missing audio file.", event);
|
||||||
"voxygen.audio.sfx.placeholder"
|
"voxygen.audio.sfx.placeholder"
|
||||||
@ -243,9 +242,22 @@ impl AudioFrontend {
|
|||||||
let rand_step = rand::random::<usize>() % item.files.len();
|
let rand_step = rand::random::<usize>() % item.files.len();
|
||||||
&item.files[rand_step]
|
&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<f32>,
|
||||||
|
volume: Option<f32>,
|
||||||
|
underwater: bool,
|
||||||
|
) {
|
||||||
|
if let Some(sfx_file) = Self::get_sfx_file(trigger_item) {
|
||||||
// Play sound in empty channel at given position
|
// 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 sound = load_ogg(sfx_file).amplify(volume.unwrap_or(1.0));
|
||||||
|
|
||||||
let listener = self.listener.clone();
|
let listener = self.listener.clone();
|
||||||
@ -275,27 +287,9 @@ impl AudioFrontend {
|
|||||||
trigger_item: Option<(&SfxEvent, &SfxTriggerItem)>,
|
trigger_item: Option<(&SfxEvent, &SfxTriggerItem)>,
|
||||||
volume: Option<f32>,
|
volume: Option<f32>,
|
||||||
) {
|
) {
|
||||||
// Find sound based on given trigger_item
|
if let Some(sfx_file) = Self::get_sfx_file(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::<usize>() % item.files.len();
|
|
||||||
&item.files[rand_step]
|
|
||||||
},
|
|
||||||
};
|
|
||||||
// Play sound in empty channel
|
// 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));
|
let sound = load_ogg(sfx_file).amplify(volume.unwrap_or(1.0));
|
||||||
|
|
||||||
if let Some(channel) = self.get_ui_channel() {
|
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
|
/// 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<f32>) {
|
||||||
if self.audio_stream.is_some() {
|
if self.audio_stream.is_some() {
|
||||||
if let Some(channel) = self.get_ambient_channel(channel_tag) {
|
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));
|
channel.play(load_ogg(sound));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -503,19 +497,18 @@ impl AudioFrontend {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stop_ambient_sounds(&mut self) {
|
pub fn stop_all_ambience(&mut self) { self.ambient_channels.retain(|x| Some(x).is_none()) }
|
||||||
for channel in self.ambient_channels.iter_mut() {
|
|
||||||
channel.stop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Sfx channels do not repopulate themselves yet
|
||||||
pub fn stop_all_sfx(&mut self) {
|
pub fn stop_all_sfx(&mut self) {
|
||||||
for channel in self.sfx_channels.iter_mut() {
|
if let Some(audio_stream) = &self.audio_stream {
|
||||||
channel.stop()
|
for channel in &mut self.sfx_channels {
|
||||||
|
*channel = SfxChannel::new(audio_stream);
|
||||||
}
|
}
|
||||||
for channel in self.ui_channels.iter_mut() {
|
for channel in &mut self.ui_channels {
|
||||||
channel.stop()
|
*channel = UiChannel::new(audio_stream);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// The following is for the disabled device switcher
|
// The following is for the disabled device switcher
|
||||||
|
@ -435,13 +435,13 @@ impl SfxMgr {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
Outcome::Lightning { pos } => {
|
Outcome::Lightning { pos } => {
|
||||||
let power = (1.0 - pos.distance(audio.listener.pos) / 6_000.0)
|
let power = (1.0 - pos.distance(audio.listener.pos) / 5_000.0)
|
||||||
.clamped(0.0, 1.0)
|
.max(0.0)
|
||||||
.powf(0.75);
|
.powi(7);
|
||||||
if power > 0.0 {
|
if power > 0.0 {
|
||||||
let sfx_trigger_item = triggers.get_key_value(&SfxEvent::Lightning);
|
let sfx_trigger_item = triggers.get_key_value(&SfxEvent::Lightning);
|
||||||
// TODO: Don't use UI sfx, add a way to control position falloff
|
// 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, .. } => {
|
Outcome::GroundSlam { pos, .. } => {
|
||||||
|
@ -1206,7 +1206,7 @@ impl PlayState for SessionState {
|
|||||||
// Stop all sounds
|
// Stop all sounds
|
||||||
// TODO: Abstract this behavior to all instances of PlayStateResult::Pop
|
// TODO: Abstract this behavior to all instances of PlayStateResult::Pop
|
||||||
// somehow
|
// somehow
|
||||||
global_state.audio.stop_ambient_sounds();
|
global_state.audio.stop_all_ambience();
|
||||||
global_state.audio.stop_all_sfx();
|
global_state.audio.stop_all_sfx();
|
||||||
return PlayStateResult::Pop;
|
return PlayStateResult::Pop;
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user