Completely redoing ambient sound again.

This commit is contained in:
DaforLynx 2022-03-19 02:14:54 -07:00 committed by IsseW
parent 16ca1410be
commit aafd13508d
8 changed files with 249 additions and 245 deletions

View File

@ -111,7 +111,9 @@ impl Outcome {
| Outcome::Utterance { pos, .. }
| Outcome::Glider { pos, .. } => Some(*pos),
Outcome::BreakBlock { pos, .. } => Some(pos.map(|e| e as f32 + 0.5)),
Outcome::ExpChange { .. } | Outcome::ComboChange { .. } | Outcome::SkillPointGain { .. } => None,
Outcome::ExpChange { .. }
| Outcome::ComboChange { .. }
| Outcome::SkillPointGain { .. } => None,
}
}
}

View File

@ -1,6 +1,9 @@
//! Handles ambient non-positional sounds
use crate::{
audio::{channel::AmbientChannelTag, AudioFrontend},
audio::{
channel::{AmbientChannel, AmbientChannelTag},
AudioFrontend,
},
scene::Camera,
};
use client::Client;
@ -10,12 +13,13 @@ use common::{
};
use common_state::State;
use serde::Deserialize;
use strum::IntoEnumIterator;
use std::time::Instant;
use tracing::warn;
use vek::*;
#[derive(Debug, Default, Deserialize)]
struct AmbientCollection {
pub struct AmbientCollection {
tracks: Vec<AmbientItem>,
}
@ -29,49 +33,11 @@ pub struct AmbientItem {
tag: AmbientChannelTag,
}
pub struct AmbientWindMgr {
ambience: AssetHandle<AmbientCollection>,
began_playing: Instant,
next_track_change: f32,
volume: f32,
tree_multiplier: f32,
pub struct AmbientMgr {
pub ambience: AssetHandle<AmbientCollection>,
}
pub struct AmbientRainMgr {
ambience: AssetHandle<AmbientCollection>,
began_playing: Instant,
next_track_change: f32,
volume: f32,
rain_intensity: f32,
}
impl Default for AmbientWindMgr {
fn default() -> Self {
Self {
ambience: load_ambience_items(),
began_playing: Instant::now(),
next_track_change: 0.0,
volume: 0.0,
tree_multiplier: 0.0,
}
}
}
impl Default for AmbientRainMgr {
fn default() -> Self {
Self {
ambience: load_ambience_items(),
began_playing: Instant::now(),
next_track_change: 0.0,
volume: 0.0,
rain_intensity: 0.0,
}
}
}
impl AmbientWindMgr {
/// Checks whether the previous track has completed. If so, sends a
/// request to play the next (random) track
impl AmbientMgr {
pub fn maintain(
&mut self,
audio: &mut AudioFrontend,
@ -79,157 +45,156 @@ impl AmbientWindMgr {
client: &Client,
camera: &Camera,
) {
if audio.sfx_enabled() && !self.ambience.read().tracks.is_empty() {
let focus_off = camera.get_focus_pos().map(f32::trunc);
let cam_pos = camera.dependents().cam_pos + focus_off;
let (terrain_alt, tree_density) = if let Some(chunk) = client.current_chunk() {
(chunk.meta().alt(), chunk.meta().tree_density())
} else {
(0.0, 0.0)
};
// The following code is specifically for wind, as it is the only
// non-positional ambient sound in the game. Others can be added
// as seen fit.
let target_volume = {
// Wind volume increases with altitude
let alt_multiplier = (cam_pos.z / 1200.0).abs();
// Tree density factors into wind volume. The more trees,
// the lower wind volume. The trees make more of an impact
// the closer the camera is to the ground.
self.tree_multiplier = ((1.0 - tree_density)
+ ((cam_pos.z - terrain_alt).abs() / 150.0).powi(2))
.min(1.0);
let mut volume_multiplier = alt_multiplier * self.tree_multiplier;
// Checks if the camera is underwater to stop ambient sounds
if state
.terrain()
.get((cam_pos).map(|e| e.floor() as i32))
.map(|b| b.is_liquid())
.unwrap_or(false)
{
volume_multiplier *= 0.1;
}
// Is the camera roughly under the terrain?
if cam_pos.z < terrain_alt - 10.0 {
volume_multiplier = 0.0;
let sfx_volume = audio.get_sfx_volume();
// iterate through each tag
for tag in AmbientChannelTag::iter() {
// iterate through the supposed number of channels - one for each tag
for index in 0..AmbientChannelTag::iter().len() {
// if index would exceed current number of channels, create a new one with
// current tag
if index >= audio.ambient_channels.len() {
audio.new_ambient_channel(tag);
}
// update with sfx volume
audio.ambient_channels[index].set_volume(sfx_volume);
// if current channel's tag is not the current tag, move on to next channel
if audio.ambient_channels[index].get_tag() == tag {
// maintain: get the correct multiplier of whatever the tag of the current
// channel is
let target_volume =
audio.ambient_channels[index].maintain(state, client, camera);
// get multiplier of the current channel
let initial_volume = audio.ambient_channels[index].get_multiplier();
volume_multiplier.clamped(0.0, 1.0)
};
// lerp multiplier of current channel
audio.ambient_channels[index].set_multiplier(Lerp::lerp(
initial_volume,
target_volume,
0.01,
));
// Transitions the ambient sounds (more) smoothly
if audio.get_ambient_channel(AmbientChannelTag::Wind).is_none() {
audio.new_ambient_channel(AmbientChannelTag::Wind);
} else {
self.volume = audio.get_ambient_volume(AmbientChannelTag::Wind);
audio.set_ambient_volume(
AmbientChannelTag::Wind,
Lerp::lerp(self.volume, target_volume, 0.01),
);
}
// set the duration of the loop to whatever the current value is (0.0 by
// default)
let next_track_change = audio.ambient_channels[index].get_next_track_change();
if self.began_playing.elapsed().as_secs_f32() > self.next_track_change {
let ambience = self.ambience.read();
let wind_track = &ambience
.tracks
.iter()
.find(|track| track.tag == AmbientChannelTag::Wind);
self.began_playing = Instant::now();
if let Some(wind_track) = wind_track {
self.next_track_change = wind_track.length;
audio.play_ambient(AmbientChannelTag::Wind, &wind_track.path, target_volume);
}
}
}
}
}
// if the sound should loop at this point:
if audio.ambient_channels[index]
.get_began_playing()
.elapsed()
.as_secs_f32()
> next_track_change
{
let ambience = self.ambience.read();
let track = ambience.tracks.iter().find(|track| track.tag == tag);
// set the track's start point at this instant
audio.ambient_channels[index].set_began_playing(Instant::now());
if let Some(track) = track {
// set loop duration to the one specified in the ron
audio.ambient_channels[index].set_next_track_change(track.length);
// play the file of the current tag at the current multiplier
let current_multiplier = audio.ambient_channels[index].get_multiplier();
audio.play_ambient(tag, &track.path, current_multiplier);
}
};
impl AmbientRainMgr {
/// Checks whether the previous track has completed. If so, sends a
/// request to play the next (random) track
pub fn maintain(
&mut self,
audio: &mut AudioFrontend,
state: &State,
client: &Client,
camera: &Camera,
) {
if audio.sfx_enabled() && !self.ambience.read().tracks.is_empty() {
let focus_off = camera.get_focus_pos().map(f32::trunc);
let cam_pos = camera.dependents().cam_pos + focus_off;
let terrain_alt = if let Some(chunk) = client.current_chunk() {
chunk.meta().alt()
} else {
0.0
};
// multipler at end will have to change depending on how intense rain normally
// is
self.rain_intensity = client.current_weather().rain * 5.0;
let mut volume_multiplier = self.rain_intensity;
// TODO: make rain diminish with distance above terrain
let target_volume = {
// Checks if the camera is underwater to stop ambient sounds
if state
.terrain()
.get((cam_pos).map(|e| e.floor() as i32))
.map(|b| b.is_liquid())
.unwrap_or(false)
{
volume_multiplier *= 0.1;
}
// Is the camera roughly under the terrain?
if cam_pos.z < terrain_alt - 10.0 {
volume_multiplier = 0.0;
}
volume_multiplier = volume_multiplier.clamped(0.0, 1.0);
// possibly remove noise
if volume_multiplier < 0.05 {
0.0
// remove channel if not playing
if audio.ambient_channels[index].get_multiplier() == 0.0 {
audio.ambient_channels[index].stop();
audio.ambient_channels.remove(index);
};
// move on to next tag
break;
} else {
volume_multiplier
}
};
// Transitions the ambient sounds (more) smoothly
if audio.get_ambient_channel(AmbientChannelTag::Rain).is_none() {
audio.new_ambient_channel(AmbientChannelTag::Rain);
} else {
self.volume = audio.get_ambient_volume(AmbientChannelTag::Rain);
audio.set_ambient_volume(
AmbientChannelTag::Rain,
Lerp::lerp(self.volume, target_volume, 0.01),
);
}
if self.began_playing.elapsed().as_secs_f32() > self.next_track_change {
let ambience = self.ambience.read();
let rain_track = &ambience
.tracks
.iter()
.find(|track| track.tag == AmbientChannelTag::Rain);
self.began_playing = Instant::now();
if let Some(rain_track) = rain_track {
self.next_track_change = rain_track.length;
audio.play_ambient(AmbientChannelTag::Rain, &rain_track.path, target_volume);
// channel tag and current tag don't match, move on to next channel
continue;
}
}
}
}
}
fn load_ambience_items() -> AssetHandle<AmbientCollection> {
impl AmbientChannel {
pub fn maintain(&mut self, state: &State, client: &Client, camera: &Camera) -> f32 {
let tag = self.get_tag();
let focus_off = camera.get_focus_pos().map(f32::trunc);
let cam_pos = camera.dependents().cam_pos + focus_off;
let mut target_volume: f32 = match tag {
// Get target volume of wind
AmbientChannelTag::Wind => self.get_wind_volume(client, camera),
// get target volume of rain
AmbientChannelTag::Rain => self.get_rain_volume(client),
};
// TODO: make rain diminish with distance above terrain
target_volume = self.check_camera(state, client, cam_pos, target_volume);
return target_volume;
}
fn check_camera(
&mut self,
state: &State,
client: &Client,
cam_pos: Vec3<f32>,
initial_volume: f32,
) -> f32 {
let mut volume_multiplier = initial_volume;
let terrain_alt = if let Some(chunk) = client.current_chunk() {
chunk.meta().alt()
} else {
0.0
};
// Checks if the camera is underwater to stop ambient sounds
if state
.terrain()
.get((cam_pos).map(|e| e.floor() as i32))
.map(|b| b.is_liquid())
.unwrap_or(false)
{
volume_multiplier *= 0.1;
}
// Is the camera roughly under the terrain?
if cam_pos.z < terrain_alt - 10.0 {
volume_multiplier = 0.0;
}
volume_multiplier.clamped(0.0, 1.0)
}
fn get_wind_volume(&mut self, client: &Client, camera: &Camera) -> f32 {
let focus_off = camera.get_focus_pos().map(f32::trunc);
let cam_pos = camera.dependents().cam_pos + focus_off;
let (terrain_alt, tree_density) = if let Some(chunk) = client.current_chunk() {
(chunk.meta().alt(), chunk.meta().tree_density())
} else {
(0.0, 0.0)
};
// Wind volume increases with altitude
let alt_multiplier = (cam_pos.z / 1200.0).abs();
// Tree density factors into wind volume. The more trees,
// the lower wind volume. The trees make more of an impact
// the closer the camera is to the ground.
let tree_multiplier =
((1.0 - tree_density) + ((cam_pos.z - terrain_alt).abs() / 150.0).powi(2)).min(1.0);
return alt_multiplier * tree_multiplier;
}
fn get_rain_volume(&mut self, client: &Client) -> f32 {
// multipler at end will have to change depending on how intense rain normally
// is
let rain_intensity = client.current_weather().rain * 5.0;
return rain_intensity;
}
}
pub fn load_ambience_items() -> AssetHandle<AmbientCollection> {
AmbientCollection::load_or_insert_with("voxygen.audio.ambient", |error| {
warn!(
"Error reading ambience config file, ambience will not be available: {:#?}",

View File

@ -22,6 +22,8 @@ use crate::audio::{
};
use rodio::{OutputStreamHandle, Sample, Sink, Source, SpatialSink};
use serde::Deserialize;
use strum::EnumIter;
use std::time::Instant;
use tracing::warn;
use vek::*;
@ -157,7 +159,7 @@ impl MusicChannel {
/// AmbientChannelTags are used for non-positional sfx. Currently the only use
/// is for wind.
#[derive(Debug, PartialEq, Clone, Copy, Deserialize)]
#[derive(Debug, PartialEq, Clone, Copy, Deserialize, EnumIter)]
pub enum AmbientChannelTag {
Wind,
Rain,
@ -168,6 +170,8 @@ pub struct AmbientChannel {
tag: AmbientChannelTag,
multiplier: f32,
sink: Sink,
began_playing: Instant,
next_track_change: f32,
}
impl AmbientChannel {
@ -178,6 +182,8 @@ impl AmbientChannel {
tag,
multiplier,
sink,
began_playing: Instant::now(),
next_track_change: 0.0,
},
Err(_) => {
warn!("Failed to create rodio sink. May not play ambient sounds.");
@ -185,6 +191,8 @@ impl AmbientChannel {
tag,
multiplier,
sink: Sink::new_idle().0,
began_playing: Instant::now(),
next_track_change: 0.0,
}
},
}
@ -211,6 +219,20 @@ impl AmbientChannel {
pub fn get_multiplier(&mut self) -> f32 { self.multiplier }
pub fn get_tag(&self) -> AmbientChannelTag { self.tag }
pub fn set_tag(&mut self, tag: AmbientChannelTag) { self.tag = tag }
pub fn get_began_playing(&self) -> Instant { self.began_playing }
pub fn get_next_track_change(&self) -> f32 { self.next_track_change }
pub fn set_began_playing(&mut self, began_playing: Instant) {
self.began_playing = began_playing
}
pub fn set_next_track_change(&mut self, next_track_change: f32) {
self.next_track_change = next_track_change
}
}
/// An SfxChannel uses a positional audio sink, and is designed for short-lived
@ -264,9 +286,8 @@ impl SfxChannel {
pub fn update(&mut self, listener: &Listener) {
const FALLOFF: f32 = 0.13;
self.sink.set_emitter_position(
((self.pos - listener.pos) * FALLOFF).into_array(),
);
self.sink
.set_emitter_position(((self.pos - listener.pos) * FALLOFF).into_array());
self.sink
.set_left_ear_position(listener.ear_left_rpos.into_array());
self.sink
@ -281,7 +302,7 @@ pub struct UIChannel {
impl UIChannel {
pub fn new(stream: &OutputStreamHandle) -> Self {
Self {
sink: Sink::try_new(stream).unwrap()
sink: Sink::try_new(stream).unwrap(),
}
}

View File

@ -7,7 +7,9 @@ pub mod music;
pub mod sfx;
pub mod soundcache;
use channel::{AmbientChannel, AmbientChannelTag, MusicChannel, MusicChannelTag, SfxChannel, UIChannel};
use channel::{
AmbientChannel, AmbientChannelTag, MusicChannel, MusicChannelTag, SfxChannel, UIChannel,
};
use fader::Fader;
use music::MusicTransitionManifest;
use sfx::{SfxEvent, SfxTriggerItem};
@ -38,16 +40,16 @@ pub struct AudioFrontend {
//pub device_list: Vec<String>,
//pub audio_device: Option<Device>,
pub stream: Option<rodio::OutputStream>,
audio_stream: Option<rodio::OutputStreamHandle>,
pub audio_stream: Option<rodio::OutputStreamHandle>,
music_channels: Vec<MusicChannel>,
ambient_channels: Vec<AmbientChannel>,
sfx_channels: Vec<SfxChannel>,
ui_channels: Vec<UIChannel>,
sfx_volume: f32,
music_volume: f32,
master_volume: f32,
listener: Listener,
pub music_channels: Vec<MusicChannel>,
pub ambient_channels: Vec<AmbientChannel>,
pub sfx_channels: Vec<SfxChannel>,
pub ui_channels: Vec<UIChannel>,
pub sfx_volume: f32,
pub music_volume: f32,
pub master_volume: f32,
pub listener: Listener,
mtm: AssetHandle<MusicTransitionManifest>,
}
@ -216,7 +218,11 @@ impl AudioFrontend {
/// Function to play sfx from external places. Useful for UI and
/// inventory events
pub fn emit_sfx_item(&mut self, trigger_item: Option<(&SfxEvent, &SfxTriggerItem)>, vol: Option<f32>) {
pub fn emit_sfx_item(
&mut self,
trigger_item: Option<(&SfxEvent, &SfxTriggerItem)>,
vol: Option<f32>,
) {
if let Some((event, item)) = trigger_item {
let sfx_file = match item.files.len() {
0 => {
@ -302,7 +308,7 @@ impl AudioFrontend {
} else {
channel.play(sound);
}
}
}
}
Ok(())
}
@ -371,29 +377,29 @@ impl AudioFrontend {
}
// sets the volume of the channel with the given tag to the given volume
fn set_ambient_volume(&mut self, channel_tag: AmbientChannelTag, volume_multiplier: f32) {
if self.audio_stream.is_some() {
let sfx_volume = self.get_sfx_volume();
if let Some(channel) = self.get_ambient_channel(channel_tag) {
channel.set_multiplier(volume_multiplier);
channel.set_volume(sfx_volume);
}
}
}
// fn set_ambient_volume(&mut self, channel_tag: AmbientChannelTag,
// volume_multiplier: f32) { if self.audio_stream.is_some() {
// let sfx_volume = self.get_sfx_volume();
// if let Some(channel) = self.get_ambient_channel(channel_tag) {
// channel.set_multiplier(volume_multiplier);
// channel.set_volume(sfx_volume);
// }
// }
// }
// retrieves volume (pre-sfx-setting) of the channel with a given tag
fn get_ambient_volume(&mut self, channel_tag: AmbientChannelTag) -> f32 {
if self.audio_stream.is_some() {
if let Some(channel) = self.get_ambient_channel(channel_tag) {
let channel_multiplier = channel.get_multiplier();
channel_multiplier
} else {
0.0
}
} else {
0.0
}
}
// fn get_ambient_volume(&mut self, channel_tag: AmbientChannelTag) -> f32 {
// if self.audio_stream.is_some() {
// if let Some(channel) = self.get_ambient_channel(channel_tag) {
// let channel_multiplier = channel.get_multiplier();
// channel_multiplier
// } else {
// 0.0
// }
// } else {
// 0.0
// }
// }
fn play_music(&mut self, sound: &str, channel_tag: MusicChannelTag) {
if self.music_enabled() {

View File

@ -82,7 +82,7 @@
mod event_mapper;
use specs::{WorldExt};
use specs::WorldExt;
use crate::{
audio::AudioFrontend,

View File

@ -223,7 +223,10 @@ fn main() {
// Setup audio
let mut audio = match settings.audio.output {
AudioOutput::Off => AudioFrontend::no_audio(),
AudioOutput::Automatic => AudioFrontend::new(settings.audio.num_sfx_channels, settings.audio.num_ui_channels),
AudioOutput::Automatic => AudioFrontend::new(
settings.audio.num_sfx_channels,
settings.audio.num_ui_channels,
),
// AudioOutput::Device(ref dev) => Some(dev.clone()),
};

View File

@ -19,12 +19,7 @@ pub use self::{
trail::TrailMgr,
};
use crate::{
audio::{
ambient::{AmbientRainMgr, AmbientWindMgr},
music::MusicMgr,
sfx::SfxMgr,
AudioFrontend,
},
audio::{ambient, ambient::AmbientMgr, music::MusicMgr, sfx::SfxMgr, AudioFrontend},
render::{
create_skybox_mesh, CloudsLocals, Consts, Drawer, GlobalModel, Globals, GlobalsBindGroup,
Light, Model, PointLightMatrix, PostProcessLocals, Renderer, Shadow, ShadowLocals,
@ -107,8 +102,9 @@ pub struct Scene {
figure_mgr: FigureMgr,
pub sfx_mgr: SfxMgr,
music_mgr: MusicMgr,
ambient_wind_mgr: AmbientWindMgr,
ambient_rain_mgr: AmbientRainMgr,
ambient_mgr: AmbientMgr,
// ambient_wind_mgr: AmbientWindMgr,
// ambient_rain_mgr: AmbientRainMgr,
}
pub struct SceneData<'a> {
@ -320,8 +316,11 @@ impl Scene {
figure_mgr: FigureMgr::new(renderer),
sfx_mgr: SfxMgr::default(),
music_mgr: MusicMgr::default(),
ambient_wind_mgr: AmbientWindMgr::default(),
ambient_rain_mgr: AmbientRainMgr::default(),
ambient_mgr: AmbientMgr {
ambience: ambient::load_ambience_items(),
},
// ambient_wind_mgr: AmbientWindMgr::default(),
// ambient_rain_mgr: AmbientRainMgr::default(),
}
}
@ -414,10 +413,10 @@ impl Scene {
) {
span!(_guard, "handle_outcome", "Scene::handle_outcome");
let underwater = state
.terrain()
.get(cam_pos.map(|e| e.floor() as i32))
.map(|b| b.is_liquid())
.unwrap_or(false);
.terrain()
.get(cam_pos.map(|e| e.floor() as i32))
.map(|b| b.is_liquid())
.unwrap_or(false);
self.particle_mgr.handle_outcome(outcome, scene_data);
self.sfx_mgr
.handle_outcome(outcome, audio, scene_data.client, state, underwater);
@ -1080,10 +1079,12 @@ impl Scene {
client,
);
self.music_mgr.maintain(audio, scene_data.state, client);
self.ambient_wind_mgr
.maintain(audio, scene_data.state, client, &self.camera);
self.ambient_rain_mgr
self.ambient_mgr
.maintain(audio, scene_data.state, client, &self.camera);
// self.ambient_wind_mgr
// .maintain(audio, scene_data.state, client, &self.camera);
// self.ambient_rain_mgr
// .maintain(audio, scene_data.state, client, &self.camera);
}
pub fn global_bind_group(&self) -> &GlobalsBindGroup { &self.globals_bind_group }

View File

@ -245,7 +245,9 @@ impl SessionState {
let sfx_triggers = self.scene.sfx_mgr.triggers.read();
let sfx_trigger_item = sfx_triggers.get_key_value(&SfxEvent::from(&inv_event));
global_state.audio.emit_sfx_item(sfx_trigger_item, Some(1.0));
global_state
.audio
.emit_sfx_item(sfx_trigger_item, Some(1.0));
match inv_event {
InventoryUpdateEvent::BlockCollectFailed { pos, reason } => {
@ -358,7 +360,7 @@ impl PlayState for SessionState {
let client = self.client.borrow();
(client.presence(), client.registered())
};
if client_presence.is_some() {
let camera = self.scene.camera_mut();
@ -1577,12 +1579,16 @@ impl PlayState for SessionState {
&scene_data,
&client,
);
// Process outcomes from client
for outcome in outcomes {
self.scene
.handle_outcome(&outcome, &scene_data, &mut global_state.audio, &client.state(), cam_pos);
self.scene.handle_outcome(
&outcome,
&scene_data,
&mut global_state.audio,
&client.state(),
cam_pos,
);
self.hud
.handle_outcome(&outcome, scene_data.client, global_state);
}