From 447cfec19fa4fd5267f82eb50700d2ab68510b66 Mon Sep 17 00:00:00 2001 From: Louis Pearson Date: Sat, 31 Aug 2019 02:30:23 -0600 Subject: [PATCH] Add support for fading sounds --- voxygen/src/audio/fader.rs | 68 ++++++++++++++++++++++ voxygen/src/audio/mod.rs | 79 +++++++++++++++++++++++--- voxygen/src/main.rs | 4 +- voxygen/src/menu/char_selection/mod.rs | 2 +- voxygen/src/menu/main/mod.rs | 7 ++- voxygen/src/session.rs | 2 +- 6 files changed, 146 insertions(+), 16 deletions(-) create mode 100644 voxygen/src/audio/fader.rs diff --git a/voxygen/src/audio/fader.rs b/voxygen/src/audio/fader.rs new file mode 100644 index 0000000000..25c064365d --- /dev/null +++ b/voxygen/src/audio/fader.rs @@ -0,0 +1,68 @@ + +#[derive(PartialEq, Clone, Copy)] +pub struct Fader { + length: f32, + running_time: f32, + volume_from: f32, + volume_to: f32, + is_running: bool +} + +fn lerp(t: f32, a: f32, b: f32) -> f32 { + (1.0 - t) * a + t * b +} + +impl Fader { + pub fn fade(time: f32, volume_from: f32, volume_to: f32) -> Self { + Self { + length: time, + running_time: 0.0, + volume_from, + volume_to, + is_running: true, + } + } + + pub fn fade_in(time: f32) -> Self { + Self { + length: time, + running_time: 0.0, + volume_from: 0.0, + volume_to: 1.0, + is_running: true, + } + } + + pub fn fade_out(time: f32) -> Self { + Self { + length: time, + running_time: 0.0, + volume_from: 1.0, + volume_to: 0.0, + is_running: true, + } + } + + pub fn update(&mut self, dt: f32) { + if self.is_running { + self.running_time = self.running_time + dt; + if self.running_time >= self.length { + self.running_time = self.length; + self.is_running = false; + } + } + } + + pub fn get_volume(&self) -> f32 { + lerp(self.running_time / self.length, self.volume_from, self.volume_to) + } + + // TODO: Remove + pub fn get_running_time(&self) -> f32 { + self.running_time + } + + pub fn is_finished(&self) -> bool { + self.running_time >= self.length || !self.is_running + } +} diff --git a/voxygen/src/audio/mod.rs b/voxygen/src/audio/mod.rs index b4c6c6c6ed..947954146c 100644 --- a/voxygen/src/audio/mod.rs +++ b/voxygen/src/audio/mod.rs @@ -1,3 +1,6 @@ +pub mod fader; +use fader::Fader; + use common::assets; use rodio::{Decoder, Device, Sink, SpatialSink}; @@ -10,10 +13,64 @@ enum AudioType { Music, } +#[derive(PartialEq, Clone, Copy)] +enum ChannelState { + Playing, + Stopping, + Stopped, +} + struct Channel { id: usize, sink: SpatialSink, audio_type: AudioType, + state: ChannelState, + fader: Fader, +} + +impl Channel { + pub fn music(id: usize, sink: SpatialSink) -> Self { + Self { + id, + sink, + audio_type: AudioType::Music, + state: ChannelState::Playing, + fader: Fader::fade_in(0.25), + } + } + + pub fn sfx(id: usize, sink: SpatialSink) -> Self { + Self { + id, + sink, + audio_type: AudioType::Sfx, + state: ChannelState::Playing, + fader: Fader::fade_in(0.0), + } + } + + pub fn stop(&mut self, fader: Fader) { + self.state = ChannelState::Stopping; + self.fader = fader; + } + + pub fn get_state(&self) -> ChannelState { + self.state + } + + pub fn update(&mut self, dt: f32) { + match self.state { + ChannelState::Playing => {}, + ChannelState::Stopping => { + self.fader.update(dt); + self.sink.set_volume(self.fader.get_volume()); + if self.fader.is_finished() { + self.state = ChannelState::Stopped; + } + }, + ChannelState::Stopped => {}, + } + } } pub struct AudioFrontend { @@ -56,10 +113,11 @@ impl AudioFrontend { } /// Maintain audio - pub fn maintain(&mut self) { + pub fn maintain(&mut self, dt: f32) { let mut stopped_channels = Vec::::new(); - for (i, channel) in self.channels.iter().enumerate() { - if channel.sink.empty() { + for (i, channel) in self.channels.iter_mut().enumerate() { + channel.update(dt); + if channel.sink.empty() || channel.get_state() == ChannelState::Stopped { stopped_channels.push(i); } } @@ -80,20 +138,23 @@ impl AudioFrontend { let sink = SpatialSink::new(device, [0.0, 0.0, 0.0], LEFT_EAR, RIGHT_EAR); let file = assets::load_file(&sound, &["wav", "ogg"]).unwrap(); - let sound = rodio::Decoder::new(file).unwrap(); + let sound = Decoder::new(file).unwrap(); sink.append(sound); - self.channels.push(Channel { - id, - sink, - audio_type: AudioType::Music, - }); + self.channels.push(Channel::music(id, sink)); } id } + pub fn stop_channel(&mut self, channel_id: usize, fader: Fader) { + let index = self.channels.iter().position(|c| c.id == channel_id); + if let Some(index) = index { + self.channels[index].stop(fader); + } + } + pub fn get_sfx_volume(&self) -> f32 { self.sfx_volume } diff --git a/voxygen/src/main.rs b/voxygen/src/main.rs index 949f1d91d2..49d6c7ac7d 100644 --- a/voxygen/src/main.rs +++ b/voxygen/src/main.rs @@ -58,8 +58,8 @@ impl GlobalState { self.window.needs_refresh_resize(); } - pub fn maintain(&mut self) { - self.audio.maintain(); + pub fn maintain(&mut self, dt: f32) { + self.audio.maintain(dt); } } diff --git a/voxygen/src/menu/char_selection/mod.rs b/voxygen/src/menu/char_selection/mod.rs index 5ddfb528ec..ac5010747f 100644 --- a/voxygen/src/menu/char_selection/mod.rs +++ b/voxygen/src/menu/char_selection/mod.rs @@ -78,7 +78,7 @@ impl PlayState for CharSelectionState { } // Maintain global state. - global_state.maintain(); + global_state.maintain(clock.get_last_delta().as_secs_f32()); // Maintain the scene. self.scene.maintain( diff --git a/voxygen/src/menu/main/mod.rs b/voxygen/src/menu/main/mod.rs index facfae1020..68f503fa9d 100644 --- a/voxygen/src/menu/main/mod.rs +++ b/voxygen/src/menu/main/mod.rs @@ -3,7 +3,7 @@ mod start_singleplayer; mod ui; use super::char_selection::CharSelectionState; -use crate::{window::Event, Direction, GlobalState, PlayState, PlayStateResult}; +use crate::{audio::fader::Fader, window::Event, Direction, GlobalState, PlayState, PlayStateResult}; use client_init::{ClientInit, Error as InitError}; use common::{clock::Clock, comp}; use log::warn; @@ -34,7 +34,8 @@ impl PlayState for MainMenuState { // Used for client creation. let mut client_init: Option = None; - global_state.audio.play_sound("voxygen.audio.soundtrack.veloren_title_tune-3".to_string()); + let music = global_state.audio.play_sound("voxygen.audio.soundtrack.veloren_title_tune-3".to_string()); + global_state.audio.stop_channel(music, Fader::fade_out(10.0)); loop { // Handle window events. @@ -78,7 +79,7 @@ impl PlayState for MainMenuState { } // Maintain global_state - global_state.maintain(); + global_state.maintain(clock.get_last_delta().as_secs_f32()); // Maintain the UI. for event in self.main_menu_ui.maintain(global_state) { diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index e60550cc7f..a3192f51b3 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -271,7 +271,7 @@ impl PlayState for SessionState { } // Maintain global state. - global_state.maintain(); + global_state.maintain(clock.get_last_delta().as_secs_f32()); // Extract HUD events ensuring the client borrow gets dropped. let hud_events = self.hud.maintain(