mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Add support for fading sounds
This commit is contained in:
parent
50015c8b9a
commit
b9bcbeea60
68
voxygen/src/audio/fader.rs
Normal file
68
voxygen/src/audio/fader.rs
Normal file
@ -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
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,6 @@
|
|||||||
|
pub mod fader;
|
||||||
|
use fader::Fader;
|
||||||
|
|
||||||
use common::assets;
|
use common::assets;
|
||||||
use rodio::{Decoder, Device, Sink, SpatialSink};
|
use rodio::{Decoder, Device, Sink, SpatialSink};
|
||||||
|
|
||||||
@ -10,10 +13,64 @@ enum AudioType {
|
|||||||
Music,
|
Music,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Clone, Copy)]
|
||||||
|
enum ChannelState {
|
||||||
|
Playing,
|
||||||
|
Stopping,
|
||||||
|
Stopped,
|
||||||
|
}
|
||||||
|
|
||||||
struct Channel {
|
struct Channel {
|
||||||
id: usize,
|
id: usize,
|
||||||
sink: SpatialSink,
|
sink: SpatialSink,
|
||||||
audio_type: AudioType,
|
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 {
|
pub struct AudioFrontend {
|
||||||
@ -56,10 +113,11 @@ impl AudioFrontend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Maintain audio
|
/// Maintain audio
|
||||||
pub fn maintain(&mut self) {
|
pub fn maintain(&mut self, dt: f32) {
|
||||||
let mut stopped_channels = Vec::<usize>::new();
|
let mut stopped_channels = Vec::<usize>::new();
|
||||||
for (i, channel) in self.channels.iter().enumerate() {
|
for (i, channel) in self.channels.iter_mut().enumerate() {
|
||||||
if channel.sink.empty() {
|
channel.update(dt);
|
||||||
|
if channel.sink.empty() || channel.get_state() == ChannelState::Stopped {
|
||||||
stopped_channels.push(i);
|
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 sink = SpatialSink::new(device, [0.0, 0.0, 0.0], LEFT_EAR, RIGHT_EAR);
|
||||||
|
|
||||||
let file = assets::load_file(&sound, &["wav", "ogg"]).unwrap();
|
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);
|
sink.append(sound);
|
||||||
|
|
||||||
self.channels.push(Channel {
|
self.channels.push(Channel::music(id, sink));
|
||||||
id,
|
|
||||||
sink,
|
|
||||||
audio_type: AudioType::Music,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
id
|
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 {
|
pub fn get_sfx_volume(&self) -> f32 {
|
||||||
self.sfx_volume
|
self.sfx_volume
|
||||||
}
|
}
|
||||||
|
@ -58,8 +58,8 @@ impl GlobalState {
|
|||||||
self.window.needs_refresh_resize();
|
self.window.needs_refresh_resize();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn maintain(&mut self) {
|
pub fn maintain(&mut self, dt: f32) {
|
||||||
self.audio.maintain();
|
self.audio.maintain(dt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ impl PlayState for CharSelectionState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Maintain global state.
|
// Maintain global state.
|
||||||
global_state.maintain();
|
global_state.maintain(clock.get_last_delta().as_secs_f32());
|
||||||
|
|
||||||
// Maintain the scene.
|
// Maintain the scene.
|
||||||
self.scene.maintain(
|
self.scene.maintain(
|
||||||
|
@ -3,7 +3,7 @@ mod start_singleplayer;
|
|||||||
mod ui;
|
mod ui;
|
||||||
|
|
||||||
use super::char_selection::CharSelectionState;
|
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 client_init::{ClientInit, Error as InitError};
|
||||||
use common::{clock::Clock, comp};
|
use common::{clock::Clock, comp};
|
||||||
use log::warn;
|
use log::warn;
|
||||||
@ -34,7 +34,8 @@ impl PlayState for MainMenuState {
|
|||||||
// Used for client creation.
|
// Used for client creation.
|
||||||
let mut client_init: Option<ClientInit> = None;
|
let mut client_init: Option<ClientInit> = 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 {
|
loop {
|
||||||
// Handle window events.
|
// Handle window events.
|
||||||
@ -78,7 +79,7 @@ impl PlayState for MainMenuState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Maintain global_state
|
// Maintain global_state
|
||||||
global_state.maintain();
|
global_state.maintain(clock.get_last_delta().as_secs_f32());
|
||||||
|
|
||||||
// Maintain the UI.
|
// Maintain the UI.
|
||||||
for event in self.main_menu_ui.maintain(global_state) {
|
for event in self.main_menu_ui.maintain(global_state) {
|
||||||
|
@ -271,7 +271,7 @@ impl PlayState for SessionState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Maintain global state.
|
// 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.
|
// Extract HUD events ensuring the client borrow gets dropped.
|
||||||
let hud_events = self.hud.maintain(
|
let hud_events = self.hud.maintain(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user