Getting merge ready minus device picker

This commit is contained in:
jiminycrick 2020-11-08 23:05:07 -08:00
parent 2d088faea4
commit d47e0bbb73
14 changed files with 124 additions and 71 deletions

View File

@ -280,25 +280,25 @@
),
//
// Fire Rod / Regeneration Staff
// Fire Staff
//
Wield(Staff): (
files: [
"voxygen.audio.sfx.weapon.staff_out",
"voxygen.audio.sfx.weapon.sword_out",
],
threshold: 0.5,
),
Unwield(Staff): (
files: [
"voxygen.audio.sfx.weapon.staff_in",
"voxygen.audio.sfx.weapon.sword_in",
],
threshold: 0.5,
),
Attack(BasicMelee, Staff): (
Attack(BasicBeam, Staff): (
files: [
"voxygen.audio.sfx.abilities.swing",
"voxygen.audio.sfx.abilities.flame_thrower",
],
threshold: 0.8,
threshold: 0.2,
),
Attack(BasicRanged, Staff): (
files: [

BIN
assets/voxygen/audio/sfx/abilities/flame_thrower.wav (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -112,7 +112,7 @@
title: "Limits",
path: "voxygen.audio.soundtrack.limits",
length: 203.0,
timing: Some(Night),
timing: None,
biomes: [],
site: Some(Dungeon),
artist: "badbbad",

View File

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

View File

@ -21,6 +21,7 @@ use crate::audio::{
Listener,
};
use rodio::{OutputStreamHandle, Sample, Sink, Source, SpatialSink};
use tracing::warn;
use vek::*;
#[derive(PartialEq, Clone, Copy)]
@ -54,11 +55,23 @@ pub struct MusicChannel {
impl MusicChannel {
pub fn new(stream: &OutputStreamHandle) -> Self {
Self {
sink: Sink::try_new(stream).unwrap(),
tag: MusicChannelTag::TitleMusic,
state: ChannelState::Stopped,
fader: Fader::default(),
let new_sink = Sink::try_new(stream);
match new_sink {
Ok(sink) => Self {
sink,
tag: MusicChannelTag::TitleMusic,
state: ChannelState::Stopped,
fader: Fader::default(),
},
Err(_) => {
warn!("Failed to create a rodio sink. May not play sounds.");
Self {
sink: Sink::new_idle().0,
tag: MusicChannelTag::TitleMusic,
state: ChannelState::Stopped,
fader: Fader::default(),
}
},
}
}
@ -140,6 +153,8 @@ 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 {
sink: Sink,
}
@ -147,11 +162,18 @@ pub struct WindChannel {
impl WindChannel {
pub fn set_volume(&mut self, volume: f32) { self.sink.set_volume(volume); }
pub fn volume(&mut self) -> f32 { self.sink.volume() }
pub fn get_volume(&mut self) -> f32 { self.sink.volume() }
pub fn new(stream: &OutputStreamHandle) -> Self {
Self {
sink: Sink::try_new(stream).unwrap(),
let new_sink = Sink::try_new(stream);
match new_sink {
Ok(sink) => Self { sink },
Err(_) => {
warn!("Failed to create rodio sink. May not play wind sounds.");
Self {
sink: Sink::new_idle().0,
}
},
}
}
@ -195,6 +217,8 @@ impl SfxChannel {
self.sink.append(source);
}
/// Same as SfxChannel::play but with the source passed through
/// a low pass filter at 300 Hz
pub fn play_with_low_pass_filter<S>(&mut self, source: S)
where
S: Sized + Send + 'static,

View File

@ -51,31 +51,24 @@ impl AudioFrontend {
/// Construct with given device
pub fn new(dev: String, max_sfx_channels: usize) -> Self {
let audio_device = get_device_raw(&dev);
let device = match get_default_device() {
Some(d) => d,
None => "".to_string(),
};
//if let Some(this_device) = device {
//let (stream, audio_stream) = match get_stream(&device.clone().unwrap()) {
// Ok(s) => (Some(s.0), Some(s.1)),
// Err(_) => (None, None),
//};
//} else {
let (stream, audio_stream) = match get_default_stream() {
Ok(s) => (Some(s.0), Some(s.1)),
Err(_) => (None, None),
//let (stream, audio_stream) = match get_default_stream() {
let (stream, audio_stream) = if get_device_raw(&dev).is_some() {
match get_stream(&get_device_raw(&dev).unwrap()) {
Ok(s) => (Some(s.0), Some(s.1)),
Err(_) => (None, None),
}
} else {
match get_default_stream() {
Ok(s) => (Some(s.0), Some(s.1)),
Err(_) => (None, None),
}
};
//}
//let (stream, audio_stream) = match &device {
// Some(dev) => match get_stream(&dev) {
// Ok(s) => (Some(s.0), Some(s.1)),
// Err(_) => (None, None),
// },
// None => match get_default_stream() {
// Ok(s) => (Some(s.0), Some(s.1)),
// Err(_) => (None, None),
// },
//};
let mut sfx_channels = Vec::with_capacity(max_sfx_channels);
let mut wind_channels = Vec::new();
@ -127,6 +120,7 @@ impl AudioFrontend {
}
}
/// Retrive an empty sfx channel from the list
fn get_sfx_channel(&mut self) -> Option<&mut SfxChannel> {
if self.audio_stream.is_some() {
if let Some(channel) = self.sfx_channels.iter_mut().find(|c| c.is_done()) {
@ -246,7 +240,7 @@ impl AudioFrontend {
fn get_wind_volume(&mut self) -> f32 {
if self.audio_stream.is_some() {
if let Some(channel) = self.wind_channels.iter_mut().find(|_c| true) {
channel.volume() / self.sfx_volume
channel.get_volume() / self.sfx_volume
} else {
0.0
}

View File

@ -50,24 +50,31 @@ use serde::Deserialize;
use std::time::Instant;
use tracing::warn;
// TODO These should eventually not be constants if we have seasons
const DAY_START_SECONDS: u32 = 28800; // 8:00
const DAY_END_SECONDS: u32 = 70200; // 19:30
/// Collection of all the tracks
#[derive(Debug, Default, Deserialize)]
struct SoundtrackCollection {
/// List of tracks
tracks: Vec<SoundtrackItem>,
}
/// Configuration for a single music track in the soundtrack
#[derive(Debug, Deserialize)]
pub struct SoundtrackItem {
/// Song title
title: String,
/// File path to asset
path: String,
/// Length of the track in seconds
length: f32,
/// Whether this track should play during day or night
timing: Option<DayPeriod>,
/// What biomes this track should play in with chance of play
biomes: Vec<(BiomeKind, u8)>,
/// Whether this track should play in a specific site
site: Option<SitesKind>,
}
@ -91,17 +98,19 @@ enum PlayState {
/// Provides methods to control music playback
pub struct MusicMgr {
/// Collection of all the tracks
soundtrack: SoundtrackCollection,
/// Instant at which the current track began playing
began_playing: Instant,
/// Time until the next track should be played
next_track_change: f32,
/// The title of the last track played. Used to prevent a track
/// being played twice in a row
last_track: String,
}
impl MusicMgr {
#[allow(clippy::new_without_default)] // TODO: Pending review in #587
pub fn new() -> Self {
impl Default for MusicMgr {
fn default() -> Self {
Self {
soundtrack: Self::load_soundtrack_items(),
began_playing: Instant::now(),
@ -109,7 +118,9 @@ impl MusicMgr {
last_track: String::from("None"),
}
}
}
impl MusicMgr {
/// 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) {
@ -149,15 +160,18 @@ impl MusicMgr {
}
fn play_random_track(&mut self, audio: &mut AudioFrontend, state: &State, client: &Client) {
let silence_between_tracks_seconds: f32 = thread_rng().gen_range(30.0, 60.0);
let mut rng = thread_rng();
// Adds a bit of randomness between plays
let silence_between_tracks_seconds: f32 = rng.gen_range(30.0, 60.0);
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 current_biome = Self::get_current_biome(client);
let current_site = Self::get_current_site(client);
let mut rng = thread_rng();
let maybe_track = self
// Filters out tracks not matching the timing, site, and biome
let maybe_tracks = self
.soundtrack
.tracks
.iter()
@ -167,10 +181,10 @@ impl MusicMgr {
Some(period_of_day) => period_of_day == &current_period_of_day,
None => true,
}
})
.filter(|track| match &track.site {
Some(site) => site == &current_site,
None => true,
&& match &track.site {
Some(site) => site == &current_site,
None => true,
}
})
.filter(|track| {
let mut result = false;
@ -187,7 +201,9 @@ impl MusicMgr {
})
.collect::<Vec<&SoundtrackItem>>();
let new_maybe_track = maybe_track.choose_weighted(&mut rng, |track| {
// Randomly selects a track from the remaining tracks weighted based
// on the biome
let new_maybe_track = maybe_tracks.choose_weighted(&mut rng, |track| {
let mut chance = 0;
if track.biomes.len() > 0 {
for biome in track.biomes.iter() {
@ -196,6 +212,9 @@ impl MusicMgr {
}
}
} else {
// If no biome is listed, the song is still added to the
// rotation to allow for site specific songs to play
// in any biome
chance = 1;
}
chance
@ -228,17 +247,17 @@ 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[2];
player_alt = position.z;
}
let mut cave_alt = 0.0;
let mut alt = 0.0;
let mut terrain_alt = 0.0;
if let Some(chunk) = client.current_chunk() {
alt = chunk.meta().alt();
terrain_alt = chunk.meta().alt();
cave_alt = chunk.meta().cave_alt();
}
if player_alt < (alt - 20.0) && cave_alt != 0.0 {
if player_alt < (terrain_alt - 20.0) && cave_alt != 0.0 {
SitesKind::Cave
} else if player_alt < (alt - 20.0) {
} else if player_alt < (terrain_alt - 20.0) {
SitesKind::Dungeon
} else {
SitesKind::None

View File

@ -1,5 +1,5 @@
/// EventMapper::Block watches the sound emitting blocks in the same
/// chunk as the player and emits ambient sfx
/// EventMapper::Block watches the sound emitting blocks within
/// chunk range of the player and emits ambient sfx
use crate::{
audio::sfx::{SfxEvent, SfxEventItem, SfxTriggerItem, SfxTriggers, SFX_DIST_LIMIT_SQR},
scene::{terrain::BlocksOfInterest, Camera, Terrain},
@ -54,6 +54,7 @@ impl EventMapper for BlockEventMapper {
let focus_off = camera.get_focus_pos().map(f32::trunc);
let cam_pos = camera.dependents().cam_pos + focus_off;
// Get the player position and chunk
let player_pos = state
.read_component_copied::<Pos>(player_entity)
.unwrap_or_default();
@ -137,7 +138,6 @@ impl EventMapper for BlockEventMapper {
range: 1,
sfx: SfxEvent::Bees,
volume: 1.0,
//cond: |_| true,
cond: |st| st.get_day_period().is_light(),
},
];
@ -147,8 +147,8 @@ impl EventMapper for BlockEventMapper {
// If the timing condition is false, continue
if !(sounds.cond)(state) {
continue;
// If the player is underground, continue
} else if player_pos.0.z < (terrain_alt - 20.0) {
// If the player is far enough underground, continue
} else if player_pos.0.z < (terrain_alt - 30.0) {
continue;
// Hack to reduce the number of birdcalls (too many leaf blocks)
} else if (sounds.sfx == SfxEvent::Birdcall || sounds.sfx == SfxEvent::Owl)
@ -171,6 +171,7 @@ impl EventMapper for BlockEventMapper {
// Iterate through each individual block
for block in blocks {
// Hack to reduce the number of bird sounds (too many leaf blocks)
if (sounds.sfx == SfxEvent::Birdcall || sounds.sfx == SfxEvent::Owl)
&& thread_rng().gen_bool(0.999)
{
@ -189,9 +190,9 @@ impl EventMapper for BlockEventMapper {
Some(block_pos),
Some(sounds.volume),
));
state.time = Instant::now();
state.event = sounds.sfx.clone();
}
state.time = Instant::now();
state.event = sounds.sfx.clone();
}
}
});
@ -213,7 +214,7 @@ impl BlockEventMapper {
) -> bool {
if let Some((event, item)) = sfx_trigger_item {
if &previous_state.event == event {
previous_state.time.elapsed().as_secs_f64() >= item.threshold
previous_state.time.elapsed().as_secs_f32() >= item.threshold
} else {
true
}

View File

@ -121,7 +121,7 @@ impl CampfireEventMapper {
) -> bool {
if let Some((event, item)) = sfx_trigger_item {
if &previous_state.event == event {
previous_state.time.elapsed().as_secs_f64() >= item.threshold
previous_state.time.elapsed().as_secs_f32() >= item.threshold
} else {
true
}

View File

@ -121,7 +121,7 @@ impl CombatEventMapper {
) -> bool {
if let Some((event, item)) = sfx_trigger_item {
if &previous_state.event == event {
previous_state.time.elapsed().as_secs_f64() >= item.threshold
previous_state.time.elapsed().as_secs_f32() >= item.threshold
} else {
true
}

View File

@ -159,7 +159,7 @@ impl MovementEventMapper {
) -> bool {
if let Some((event, item)) = sfx_trigger_item {
if &previous_state.event == event {
previous_state.time.elapsed().as_secs_f64() >= item.threshold
previous_state.time.elapsed().as_secs_f32() >= item.threshold
} else {
true
}

View File

@ -116,8 +116,11 @@ use vek::*;
const SFX_DIST_LIMIT_SQR: f32 = 20000.0;
pub struct SfxEventItem {
/// The SFX event that triggers this sound
pub sfx: SfxEvent,
/// The position at which the sound should play
pub pos: Option<Vec3<f32>>,
/// The volume to play the sound at
pub vol: Option<f32>,
}
@ -179,6 +182,7 @@ pub enum SfxInventoryEvent {
Swapped,
}
// TODO Move to a separate event mapper?
impl From<&InventoryUpdateEvent> for SfxEvent {
fn from(value: &InventoryUpdateEvent) -> Self {
match value {
@ -209,8 +213,10 @@ impl From<&InventoryUpdateEvent> for SfxEvent {
#[derive(Deserialize)]
pub struct SfxTriggerItem {
/// A list of SFX filepaths for this event
pub files: Vec<String>,
pub threshold: f64,
/// The time to wait before repeating this SfxEvent
pub threshold: f32,
}
#[derive(Deserialize, Default)]
@ -247,13 +253,16 @@ impl SfxMgr {
terrain: &Terrain<TerrainChunk>,
client: &Client,
) {
// Checks if the SFX volume is set to zero or audio is disabled
// This prevents us from running all the following code unnecessarily
if !audio.sfx_enabled() {
return;
}
let ecs = state.ecs();
let focus_off = camera.get_focus_pos().map(f32::trunc);
// This checks to see if the camera is underwater. If it is,
// we pass all sfx through a low pass filter
let focus_off = camera.get_focus_pos().map(f32::trunc);
let underwater = state
.terrain()
.get((camera.dependents().cam_pos + focus_off).map(|e| e.floor() as i32))
@ -262,6 +271,8 @@ impl SfxMgr {
== BlockKind::Water;
let cam_pos = camera.dependents().cam_pos + focus_off;
// Sets the listener position to the camera position facing the
// same direction as the camera
audio.set_listener_pos(cam_pos, camera.dependents().cam_dir);
// TODO: replace; deprecated in favor of outcomes
@ -294,6 +305,7 @@ impl SfxMgr {
.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]
},

View File

@ -69,6 +69,8 @@ impl WindMgr {
let alt_multiplier = (player_alt / 1200.0).abs();
// Tree density factors into wind volume. The more trees,
// the less wind
let mut tree_multiplier = self.tree_multiplier;
let new_tree_multiplier = if (player_alt - terrain_alt) < 150.0 {
1.0 - Self::get_current_tree_density(client)
@ -76,6 +78,7 @@ impl WindMgr {
1.0
};
// Smooths tree_multiplier transitions between chunks
if tree_multiplier < new_tree_multiplier {
tree_multiplier += 0.001;
} else if tree_multiplier > new_tree_multiplier {
@ -83,7 +86,6 @@ impl WindMgr {
}
self.tree_multiplier = tree_multiplier;
println!("tree multiplier: {}", tree_multiplier);
let mut volume_multiplier = alt_multiplier * self.tree_multiplier;
let focus_off = camera.get_focus_pos().map(f32::trunc);
@ -108,9 +110,8 @@ impl WindMgr {
}
let target_volume = volume_multiplier;
println!("target vol: {}", volume_multiplier);
println!("current_wind_volume: {}", self.volume);
// Transitions the wind smoothly
self.volume = audio.get_wind_volume();
if self.volume < target_volume {
audio.set_wind_volume(self.volume + 0.001);

View File

@ -308,7 +308,7 @@ impl Scene {
particle_mgr: ParticleMgr::new(renderer),
figure_mgr: FigureMgr::new(renderer),
sfx_mgr: SfxMgr::new(),
music_mgr: MusicMgr::new(),
music_mgr: MusicMgr::default(),
ambient_mgr: WindMgr::new(),
}
}