Beginning to address comments

This commit is contained in:
jiminycrick 2020-11-13 16:27:09 -08:00
parent c44a9092d3
commit a9711eea01
10 changed files with 141 additions and 108 deletions

View File

@ -3,6 +3,7 @@
(
path: "voxygen.audio.ambient.wind",
length: 4.5,
tag: Wind,
),
]
)

View File

@ -46,6 +46,7 @@ use image::DynamicImage;
use network::{Network, Participant, Pid, ProtocolAddr, Stream};
use num::traits::FloatConst;
use rayon::prelude::*;
use specs::Component;
use std::{
collections::VecDeque,
net::SocketAddr,
@ -857,13 +858,14 @@ impl Client {
self.state.terrain().get_key_arc(chunk_pos).cloned()
}
pub fn current_position(&self) -> Option<Vec3<f32>> {
pub fn current<C: Component>(&self) -> Option<C> where
C: Clone,
{
Some(
self.state
.read_storage::<comp::Pos>()
.read_storage::<C>()
.get(self.entity)
.cloned()?
.0,
.cloned()?,
)
}

View File

@ -54,7 +54,7 @@ pub struct TerrainChunkMeta {
biome: BiomeKind,
alt: f32,
tree_density: f32,
cave_alt: f32,
contains_cave: bool,
contains_river: bool,
}
@ -64,7 +64,7 @@ impl TerrainChunkMeta {
biome: BiomeKind,
alt: f32,
tree_density: f32,
cave_alt: f32,
contains_cave: bool,
contains_river: bool,
) -> Self {
Self {
@ -72,7 +72,7 @@ impl TerrainChunkMeta {
biome,
alt,
tree_density,
cave_alt,
contains_cave,
contains_river,
}
}
@ -83,7 +83,7 @@ impl TerrainChunkMeta {
biome: BiomeKind::Void,
alt: 0.0,
tree_density: 0.0,
cave_alt: 0.0,
contains_cave: false,
contains_river: false,
}
}
@ -96,7 +96,7 @@ impl TerrainChunkMeta {
pub fn tree_density(&self) -> f32 { self.tree_density }
pub fn cave_alt(&self) -> f32 { self.cave_alt }
pub fn contains_cave(&self) -> bool { self.contains_cave }
pub fn contains_river(&self) -> bool { self.contains_river }
}

View File

@ -1,5 +1,8 @@
//! Handles ambient wind sounds
use crate::{audio::AudioFrontend, scene::Camera};
//! Handles ambient non-positional sounds
use crate::{
audio::{channel::AmbientChannelTag, AudioFrontend},
scene::Camera,
};
use client::Client;
use common::{assets, state::State, terrain::BlockKind, vol::ReadVol};
use serde::Deserialize;
@ -7,27 +10,28 @@ use std::time::Instant;
use tracing::warn;
#[derive(Debug, Default, Deserialize)]
struct WindCollection {
tracks: Vec<WindItem>,
struct AmbientCollection {
tracks: Vec<AmbientItem>,
}
/// Configuration for a single music track in the soundtrack
#[derive(Debug, Deserialize)]
pub struct WindItem {
pub struct AmbientItem {
path: String,
/// Length of the track in seconds
length: f32,
tag: AmbientChannelTag,
}
pub struct WindMgr {
soundtrack: WindCollection,
pub struct AmbientMgr {
soundtrack: AmbientCollection,
began_playing: Instant,
next_track_change: f32,
volume: f32,
tree_multiplier: f32,
}
impl Default for WindMgr {
impl Default for AmbientMgr {
fn default() -> Self {
Self {
soundtrack: Self::load_soundtrack_items(),
@ -39,7 +43,7 @@ impl Default for WindMgr {
}
}
impl WindMgr {
impl AmbientMgr {
/// Checks whether the previous track has completed. If so, sends a
/// request to play the next (random) track
pub fn maintain(
@ -56,10 +60,14 @@ impl WindMgr {
let cam_alt = cam_pos.z;
let terrain_alt = Self::get_current_terrain_alt(client);
// 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 alt_multiplier = (cam_alt / 1200.0).abs();
// Tree density factors into wind volume. The more trees,
// the less wind
// Tree density factors into ambient volume. The more trees,
// the less ambient
let mut tree_multiplier = self.tree_multiplier;
let new_tree_multiplier = if (cam_alt - terrain_alt) < 150.0 {
1.0 - Self::get_current_tree_density(client)
@ -77,7 +85,7 @@ impl WindMgr {
let mut volume_multiplier = alt_multiplier * self.tree_multiplier;
// Checks if the camera is underwater to stop wind sounds
// Checks if the camera is underwater to stop ambient sounds
if state
.terrain()
.get((cam_pos).map(|e| e.floor() as i32))
@ -93,23 +101,31 @@ impl WindMgr {
let target_volume = volume_multiplier.max(0.0).min(1.0);
// Transitions the wind smoothly
self.volume = audio.get_wind_volume();
// Transitions the ambient sounds (more) smoothly
self.volume = audio.get_ambient_volume();
if self.volume < target_volume {
audio.set_wind_volume(self.volume + 0.001);
audio.set_ambient_volume(self.volume + 0.001);
} else if self.volume > target_volume {
audio.set_wind_volume(self.volume - 0.001);
audio.set_ambient_volume(self.volume - 0.001);
}
if self.began_playing.elapsed().as_secs_f32() > self.next_track_change {
//let game_time = (state.get_time_of_day() as u64 % 86400) as u32;
//let current_period_of_day = Self::get_current_day_period(game_time);
let track = &self.soundtrack.tracks[0];
self.began_playing = Instant::now();
self.next_track_change = track.length;
let track = &self
.soundtrack
.tracks
.iter()
.filter(|track| track.tag == AmbientChannelTag::Wind)
.next();
audio.play_wind(&track.path, volume_multiplier);
if let Some(track) = track {
self.began_playing = Instant::now();
self.next_track_change = track.length;
audio.play_ambient(AmbientChannelTag::Wind, &track.path, volume_multiplier);
}
}
}
}
@ -129,8 +145,8 @@ impl WindMgr {
}
}
fn load_soundtrack_items() -> WindCollection {
match assets::load_file("voxygen.audio.wind", &["ron"]) {
fn load_soundtrack_items() -> AmbientCollection {
match assets::load_file("voxygen.audio.ambient", &["ron"]) {
Ok(file) => match ron::de::from_reader(file) {
Ok(config) => config,
Err(error) => {
@ -139,7 +155,7 @@ impl WindMgr {
format!("{:#?}", error)
);
WindCollection::default()
AmbientCollection::default()
},
},
Err(error) => {
@ -148,7 +164,7 @@ impl WindMgr {
format!("{:#?}", error)
);
WindCollection::default()
AmbientCollection::default()
},
}
}

View File

@ -21,6 +21,7 @@ use crate::audio::{
Listener,
};
use rodio::{OutputStreamHandle, Sample, Sink, Source, SpatialSink};
use serde::Deserialize;
use tracing::warn;
use vek::*;
@ -31,6 +32,13 @@ enum ChannelState {
Stopped,
}
#[derive(PartialEq)]
pub enum ChannelKind {
Music(MusicChannelTag),
Sfx,
Ambient(AmbientChannelTag),
}
/// Each `MusicChannel` has a `MusicChannelTag` which help us determine when we
/// should transition between two types of in-game music. For example, we
/// transition between `TitleMusic` and `Exploration` when a player enters the
@ -153,25 +161,29 @@ impl MusicChannel {
}
}
/// A WindChannel uses a non-positional audio sink designed to play music which
/// is always heard at the player's position.
pub struct WindChannel {
/// AmbientChannelTags are used for non-positional sfx. Currently the only use
/// is for wind.
#[derive(Debug, PartialEq, Clone, Copy, Deserialize)]
pub enum AmbientChannelTag {
Wind,
}
/// A AmbientChannel uses a non-positional audio sink designed to play sounds
/// which are always heard at the camera's position.
pub struct AmbientChannel {
tag: AmbientChannelTag,
sink: Sink,
}
impl WindChannel {
pub fn set_volume(&mut self, volume: f32) { self.sink.set_volume(volume); }
pub fn get_volume(&mut self) -> f32 { self.sink.volume() }
pub fn new(stream: &OutputStreamHandle) -> Self {
impl AmbientChannel {
pub fn new(stream: &OutputStreamHandle, tag: AmbientChannelTag) -> Self {
let new_sink = Sink::try_new(stream);
match new_sink {
Ok(sink) => Self { sink },
Ok(sink) => Self { sink, tag },
Err(_) => {
warn!("Failed to create rodio sink. May not play wind sounds.");
Self {
sink: Sink::new_idle().0,
tag,
}
},
}
@ -186,6 +198,12 @@ impl WindChannel {
{
self.sink.append(source);
}
pub fn set_volume(&mut self, volume: f32) { self.sink.set_volume(volume); }
pub fn get_volume(&mut self) -> f32 { self.sink.volume() }
pub fn get_tag(&self) -> AmbientChannelTag { self.tag }
}
/// An SfxChannel uses a positional audio sink, and is designed for short-lived

View File

@ -1,13 +1,13 @@
//! Handles audio device detection and playback of sound effects and music
pub mod ambient;
pub mod channel;
pub mod fader;
pub mod music;
pub mod sfx;
pub mod soundcache;
pub mod wind;
use channel::{MusicChannel, MusicChannelTag, SfxChannel, WindChannel};
use channel::{AmbientChannel, AmbientChannelTag, MusicChannel, MusicChannelTag, SfxChannel};
use fader::Fader;
use soundcache::SoundCache;
use std::time::Duration;
@ -40,7 +40,7 @@ pub struct AudioFrontend {
sound_cache: SoundCache,
music_channels: Vec<MusicChannel>,
wind_channels: Vec<WindChannel>,
ambient_channels: Vec<AmbientChannel>,
sfx_channels: Vec<SfxChannel>,
sfx_volume: f32,
music_volume: f32,
@ -50,6 +50,7 @@ pub struct AudioFrontend {
impl AudioFrontend {
/// Construct with given device
pub fn new(/* dev: String, */ max_sfx_channels: usize) -> Self {
// Commented out until audio device switcher works
//let audio_device = get_device_raw(&dev);
//let device = match get_default_device() {
@ -63,10 +64,8 @@ impl AudioFrontend {
};
let mut sfx_channels = Vec::with_capacity(max_sfx_channels);
let mut wind_channels = Vec::new();
if let Some(audio_stream) = &audio_stream {
sfx_channels.resize_with(max_sfx_channels, || SfxChannel::new(audio_stream));
wind_channels.push(WindChannel::new(audio_stream));
};
Self {
@ -79,7 +78,7 @@ impl AudioFrontend {
sound_cache: SoundCache::default(),
music_channels: Vec::new(),
sfx_channels,
wind_channels,
ambient_channels: Vec::new(),
sfx_volume: 1.0,
music_volume: 1.0,
listener: Listener::default(),
@ -98,7 +97,7 @@ impl AudioFrontend {
sound_cache: SoundCache::default(),
music_channels: Vec::new(),
sfx_channels: Vec::new(),
wind_channels: Vec::new(),
ambient_channels: Vec::new(),
sfx_volume: 1.0,
music_volume: 1.0,
listener: Listener::default(),
@ -182,7 +181,7 @@ impl AudioFrontend {
/// Play (once) an sfx file by file path at the give position and volume
/// but with the sound passed through a low pass filter to simulate
/// underwater
/// being underwater
pub fn play_underwater_sfx(&mut self, sound: &str, pos: Vec3<f32>, vol: Option<f32>) {
if self.audio_stream.is_some() {
let sound = self
@ -194,15 +193,19 @@ impl AudioFrontend {
if let Some(channel) = self.get_sfx_channel() {
channel.set_pos(pos);
channel.update(&listener);
let sound = sound.convert_samples();
channel.play_with_low_pass_filter(sound);
channel.play_with_low_pass_filter(sound.convert_samples());
}
}
}
fn play_wind(&mut self, sound: &str, volume_multiplier: f32) {
fn play_ambient(
&mut self,
channel_tag: AmbientChannelTag,
sound: &str,
volume_multiplier: f32,
) {
if self.audio_stream.is_some() {
if let Some(channel) = self.get_wind_channel(volume_multiplier) {
if let Some(channel) = self.get_ambient_channel(channel_tag, volume_multiplier) {
let file = assets::load_file(&sound, &["ogg"]).expect("Failed to load sound");
let sound = Decoder::new(file).expect("Failed to decode sound");
@ -211,29 +214,40 @@ impl AudioFrontend {
}
}
fn get_wind_channel(&mut self, volume_multiplier: f32) -> Option<&mut WindChannel> {
if self.audio_stream.is_some() {
if let Some(channel) = self.wind_channels.iter_mut().last() {
channel.set_volume(self.sfx_volume * volume_multiplier);
return Some(channel);
fn get_ambient_channel(
&mut self,
channel_tag: AmbientChannelTag,
volume_multiplier: f32,
) -> Option<&mut AmbientChannel> {
if let Some(audio_stream) = &self.audio_stream {
if self.ambient_channels.is_empty() {
let mut ambient_channel = AmbientChannel::new(audio_stream, channel_tag);
ambient_channel.set_volume(self.sfx_volume * volume_multiplier);
self.ambient_channels.push(ambient_channel);
} else {
for channel in self.ambient_channels.iter_mut() {
if channel.get_tag() == channel_tag {
channel.set_volume(self.sfx_volume * volume_multiplier);
return Some(channel);
}
}
}
}
None
}
fn set_wind_volume(&mut self, volume_multiplier: f32) {
fn set_ambient_volume(&mut self, volume_multiplier: f32) {
if self.audio_stream.is_some() {
if let Some(channel) = self.wind_channels.iter_mut().last() {
if let Some(channel) = self.ambient_channels.iter_mut().last() {
channel.set_volume(self.sfx_volume * volume_multiplier);
}
}
}
fn get_wind_volume(&mut self) -> f32 {
fn get_ambient_volume(&mut self) -> f32 {
if self.audio_stream.is_some() {
if let Some(channel) = self.wind_channels.iter_mut().last() {
if let Some(channel) = self.ambient_channels.iter_mut().last() {
channel.get_volume() / self.sfx_volume
} else {
0.0
@ -244,14 +258,19 @@ impl AudioFrontend {
}
fn play_music(&mut self, sound: &str, channel_tag: MusicChannelTag) {
if let Some(channel) = self.get_music_channel(channel_tag) {
let file = assets::load_file(&sound, &["ogg"]).expect("Failed to load sound");
let sound = Decoder::new(file).expect("Failed to decode sound");
if self.music_enabled() {
if let Some(channel) = self.get_music_channel(channel_tag) {
let file = assets::load_file(&sound, &["ogg"]).expect("Failed to load sound");
let sound = Decoder::new(file).expect("Failed to decode sound");
channel.play(sound, channel_tag);
channel.play(sound, channel_tag);
}
}
}
/* These functions are saved for if we want music playback control at some
* point. They are not used currently but may be useful for later work.
*
fn fade_out_music(&mut self, channel_tag: MusicChannelTag) {
let music_volume = self.music_volume;
if let Some(channel) = self.get_music_channel(channel_tag) {
@ -271,6 +290,7 @@ impl AudioFrontend {
channel.stop(channel_tag);
}
}
*/
pub fn set_listener_pos(&mut self, pos: Vec3<f32>, ori: Vec3<f32>) {
self.listener.pos = pos;
@ -298,30 +318,6 @@ impl AudioFrontend {
}
}
pub fn play_exploration_music(&mut self, item: &str) {
if self.music_enabled() {
self.play_music(item, MusicChannelTag::Exploration)
}
}
pub fn fade_out_exploration_music(&mut self) {
if self.music_enabled() {
self.fade_out_music(MusicChannelTag::Exploration)
}
}
pub fn fade_in_exploration_music(&mut self) {
if self.music_enabled() {
self.fade_in_music(MusicChannelTag::Exploration)
}
}
pub fn stop_exploration_music(&mut self) {
if self.music_enabled() {
self.stop_music(MusicChannelTag::Exploration)
}
}
pub fn get_sfx_volume(&self) -> f32 { self.sfx_volume }
pub fn get_music_volume(&self) -> f32 { self.music_volume }

View File

@ -42,10 +42,10 @@
//! tracks
//! - If you are not the author of the track, ensure that the song's licensing
//! permits usage of the track for non-commercial use
use crate::audio::AudioFrontend;
use crate::audio::{AudioFrontend, MusicChannelTag};
use client::Client;
use common::{
assets,
assets, comp,
state::State,
terrain::{BiomeKind, SitesKind},
};
@ -134,9 +134,8 @@ impl MusicMgr {
//println!("alt: {}", current_chunk.meta().alt());
//println!("tree_density: {}",
// current_chunk.meta().tree_density());
//println!("cave_alt: {}", current_chunk.meta().cave_alt());
//if let Some(position) = client.current_position() {
// println!("player_pos: {:?}", position);
//if let Some(position) = client.current::<comp::Pos>() {
// player_alt = position.0.z;
//}
if audio.music_enabled()
@ -214,7 +213,7 @@ impl MusicMgr {
self.began_playing = Instant::now();
self.next_track_change = track.length + silence_between_tracks_seconds;
audio.play_exploration_music(&track.path);
audio.play_music(&track.path, MusicChannelTag::Exploration);
}
}
@ -235,16 +234,16 @@ impl MusicMgr {
fn get_current_site(client: &Client) -> SitesKind {
let mut player_alt = 0.0;
if let Some(position) = client.current_position() {
player_alt = position.z;
if let Some(position) = client.current::<comp::Pos>() {
player_alt = position.0.z;
}
let mut cave_alt = 0.0;
let mut contains_cave = false;
let mut terrain_alt = 0.0;
if let Some(chunk) = client.current_chunk() {
terrain_alt = chunk.meta().alt();
cave_alt = chunk.meta().cave_alt();
contains_cave = chunk.meta().contains_cave();
}
if player_alt < (terrain_alt - 20.0) && cave_alt != 0.0 {
if player_alt < (terrain_alt - 20.0) && contains_cave {
SitesKind::Cave
} else if player_alt < (terrain_alt - 20.0) {
SitesKind::Dungeon

View File

@ -14,7 +14,7 @@ pub use self::{
terrain::Terrain,
};
use crate::{
audio::{music::MusicMgr, sfx::SfxMgr, wind::WindMgr, AudioFrontend},
audio::{ambient::AmbientMgr, music::MusicMgr, sfx::SfxMgr, AudioFrontend},
render::{
create_clouds_mesh, create_pp_mesh, create_skybox_mesh, CloudsLocals, CloudsPipeline,
Consts, GlobalModel, Globals, Light, LodData, Model, PostProcessLocals,
@ -103,7 +103,7 @@ pub struct Scene {
figure_mgr: FigureMgr,
sfx_mgr: SfxMgr,
music_mgr: MusicMgr,
ambient_mgr: WindMgr,
ambient_mgr: AmbientMgr,
}
pub struct SceneData<'a> {
@ -309,7 +309,7 @@ impl Scene {
figure_mgr: FigureMgr::new(renderer),
sfx_mgr: SfxMgr::default(),
music_mgr: MusicMgr::default(),
ambient_mgr: WindMgr::default(),
ambient_mgr: AmbientMgr::default(),
}
}

View File

@ -654,6 +654,7 @@ pub enum AudioOutput {
// If this option is disabled, functions in the rodio
// library MUST NOT be called.
Off,
#[serde(other)]
Automatic,
}

View File

@ -160,7 +160,7 @@ impl World {
sim_chunk.get_biome(),
sim_chunk.alt,
sim_chunk.tree_density,
sim_chunk.cave.1.alt,
sim_chunk.cave.1.alt != 0.0,
sim_chunk.river.is_river(),
);