2020-06-14 16:56:32 +00:00
|
|
|
//! Handles audio device detection and playback of sound effects and music
|
|
|
|
|
2019-08-31 09:06:24 +00:00
|
|
|
pub mod channel;
|
2019-09-05 09:11:18 +00:00
|
|
|
pub mod fader;
|
2020-02-03 11:55:32 +00:00
|
|
|
pub mod music;
|
2019-11-23 08:26:39 +00:00
|
|
|
pub mod sfx;
|
2019-09-01 23:00:12 +00:00
|
|
|
pub mod soundcache;
|
2019-11-23 08:26:39 +00:00
|
|
|
|
2020-02-15 21:30:44 +00:00
|
|
|
use channel::{MusicChannel, MusicChannelTag, SfxChannel};
|
2019-09-05 09:11:18 +00:00
|
|
|
use fader::Fader;
|
2019-09-01 23:00:12 +00:00
|
|
|
use soundcache::SoundCache;
|
2020-06-21 10:22:26 +00:00
|
|
|
use tracing::warn;
|
2019-08-31 08:30:23 +00:00
|
|
|
|
2019-08-31 06:37:09 +00:00
|
|
|
use common::assets;
|
2019-10-06 09:18:23 +00:00
|
|
|
use cpal::traits::DeviceTrait;
|
2020-02-23 01:26:51 +00:00
|
|
|
use rodio::{source::Source, Decoder, Device};
|
2019-09-01 03:18:03 +00:00
|
|
|
use vek::*;
|
2019-08-31 06:37:09 +00:00
|
|
|
|
2020-07-31 22:25:23 +00:00
|
|
|
#[derive(Default, Clone)]
|
|
|
|
pub struct Listener {
|
|
|
|
pos: Vec3<f32>,
|
|
|
|
ori: Vec3<f32>,
|
|
|
|
|
|
|
|
ear_left_rpos: Vec3<f32>,
|
|
|
|
ear_right_rpos: Vec3<f32>,
|
|
|
|
}
|
2019-05-11 20:39:46 +00:00
|
|
|
|
2020-06-14 16:56:32 +00:00
|
|
|
/// Holds information about the system audio devices and internal channels used
|
|
|
|
/// for sfx and music playback. An instance of `AudioFrontend` is used by
|
|
|
|
/// Voxygen's [`GlobalState`](../struct.GlobalState.html#structfield.audio) to
|
|
|
|
/// provide access to devices and playback control in-game
|
2019-05-11 20:39:46 +00:00
|
|
|
pub struct AudioFrontend {
|
2019-08-31 06:37:09 +00:00
|
|
|
pub device: String,
|
|
|
|
pub device_list: Vec<String>,
|
|
|
|
audio_device: Option<Device>,
|
2019-09-01 23:00:12 +00:00
|
|
|
sound_cache: SoundCache,
|
2019-08-31 06:37:09 +00:00
|
|
|
|
2020-02-15 21:30:44 +00:00
|
|
|
music_channels: Vec<MusicChannel>,
|
|
|
|
sfx_channels: Vec<SfxChannel>,
|
2019-08-31 06:37:09 +00:00
|
|
|
|
|
|
|
sfx_volume: f32,
|
|
|
|
music_volume: f32,
|
2019-09-01 21:40:54 +00:00
|
|
|
|
2020-07-31 22:25:23 +00:00
|
|
|
listener: Listener,
|
2019-05-11 20:39:46 +00:00
|
|
|
}
|
|
|
|
|
2020-08-17 09:08:11 +00:00
|
|
|
#[allow(clippy::same_item_push)] // TODO: Pending review in #587
|
2019-05-11 20:39:46 +00:00
|
|
|
impl AudioFrontend {
|
2019-08-31 06:37:09 +00:00
|
|
|
/// Construct with given device
|
2020-06-10 19:47:36 +00:00
|
|
|
#[allow(clippy::redundant_clone)] // TODO: Pending review in #587
|
2020-02-15 21:30:44 +00:00
|
|
|
pub fn new(device: String, max_sfx_channels: usize) -> Self {
|
|
|
|
let mut sfx_channels = Vec::with_capacity(max_sfx_channels);
|
2019-09-05 09:03:24 +00:00
|
|
|
let audio_device = get_device_raw(&device);
|
2020-02-15 21:30:44 +00:00
|
|
|
|
2019-09-05 09:03:24 +00:00
|
|
|
if let Some(audio_device) = &audio_device {
|
2020-02-15 21:30:44 +00:00
|
|
|
for _ in 0..max_sfx_channels {
|
|
|
|
sfx_channels.push(SfxChannel::new(&audio_device));
|
2019-09-05 09:03:24 +00:00
|
|
|
}
|
|
|
|
}
|
2020-02-15 21:30:44 +00:00
|
|
|
|
2019-08-31 06:37:09 +00:00
|
|
|
Self {
|
|
|
|
device: device.clone(),
|
|
|
|
device_list: list_devices(),
|
2019-09-05 09:03:24 +00:00
|
|
|
audio_device,
|
2020-06-11 12:53:24 +00:00
|
|
|
sound_cache: SoundCache::default(),
|
2020-02-15 21:30:44 +00:00
|
|
|
music_channels: Vec::new(),
|
|
|
|
sfx_channels,
|
2019-08-31 06:37:09 +00:00
|
|
|
sfx_volume: 1.0,
|
|
|
|
music_volume: 1.0,
|
2020-07-31 22:25:23 +00:00
|
|
|
|
|
|
|
listener: Listener::default(),
|
2019-08-31 06:37:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Construct in `no-audio` mode for debugging
|
|
|
|
pub fn no_audio() -> Self {
|
2019-07-02 04:10:55 +00:00
|
|
|
Self {
|
2019-08-31 06:37:09 +00:00
|
|
|
device: "none".to_string(),
|
2019-09-07 09:46:21 +00:00
|
|
|
device_list: Vec::new(),
|
2019-08-31 06:37:09 +00:00
|
|
|
audio_device: None,
|
2020-06-11 12:53:24 +00:00
|
|
|
sound_cache: SoundCache::default(),
|
2020-02-15 21:30:44 +00:00
|
|
|
music_channels: Vec::new(),
|
|
|
|
sfx_channels: Vec::new(),
|
2019-08-31 06:37:09 +00:00
|
|
|
sfx_volume: 1.0,
|
|
|
|
music_volume: 1.0,
|
2020-07-31 22:25:23 +00:00
|
|
|
listener: Listener::default(),
|
2019-06-28 17:05:20 +00:00
|
|
|
}
|
2019-05-18 19:28:12 +00:00
|
|
|
}
|
|
|
|
|
2020-02-15 21:30:44 +00:00
|
|
|
/// Drop any unused music channels, and update their faders
|
2019-08-31 08:30:23 +00:00
|
|
|
pub fn maintain(&mut self, dt: f32) {
|
2020-02-15 21:30:44 +00:00
|
|
|
self.music_channels.retain(|c| !c.is_done());
|
|
|
|
|
|
|
|
for channel in self.music_channels.iter_mut() {
|
|
|
|
channel.maintain(dt);
|
2019-08-31 06:37:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-15 21:30:44 +00:00
|
|
|
fn get_sfx_channel(&mut self) -> Option<&mut SfxChannel> {
|
|
|
|
if self.audio_device.is_some() {
|
|
|
|
if let Some(channel) = self.sfx_channels.iter_mut().find(|c| c.is_done()) {
|
|
|
|
channel.set_volume(self.sfx_volume);
|
|
|
|
|
|
|
|
return Some(channel);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Retrieve a music channel from the channel list. This inspects the
|
|
|
|
/// MusicChannelTag to determine whether we are transitioning between
|
|
|
|
/// music types and acts accordingly. For example transitioning between
|
|
|
|
/// `TitleMusic` and `Exploration` should fade out the title channel and
|
|
|
|
/// fade in a new `Exploration` channel.
|
|
|
|
fn get_music_channel(
|
2020-02-03 11:55:32 +00:00
|
|
|
&mut self,
|
2020-02-15 21:30:44 +00:00
|
|
|
next_channel_tag: MusicChannelTag,
|
|
|
|
) -> Option<&mut MusicChannel> {
|
|
|
|
if let Some(audio_device) = &self.audio_device {
|
|
|
|
if self.music_channels.is_empty() {
|
|
|
|
let mut next_music_channel = MusicChannel::new(&audio_device);
|
|
|
|
next_music_channel.set_volume(self.music_volume);
|
|
|
|
|
|
|
|
self.music_channels.push(next_music_channel);
|
|
|
|
} else {
|
|
|
|
let existing_channel = self.music_channels.last_mut()?;
|
|
|
|
|
|
|
|
if existing_channel.get_tag() != next_channel_tag {
|
|
|
|
// Fade the existing channel out. It will be removed when the fade completes.
|
|
|
|
existing_channel.set_fader(Fader::fade_out(2.0, self.music_volume));
|
|
|
|
|
|
|
|
let mut next_music_channel = MusicChannel::new(&audio_device);
|
|
|
|
|
|
|
|
next_music_channel.set_fader(Fader::fade_in(12.0, self.music_volume));
|
|
|
|
|
|
|
|
self.music_channels.push(next_music_channel);
|
|
|
|
}
|
|
|
|
}
|
2019-12-10 14:02:51 +00:00
|
|
|
}
|
2020-02-15 21:30:44 +00:00
|
|
|
|
|
|
|
self.music_channels.last_mut()
|
2019-12-10 14:02:51 +00:00
|
|
|
}
|
|
|
|
|
2020-06-14 16:56:32 +00:00
|
|
|
/// Play (once) an sfx file by file path at the give position and volume
|
2020-02-23 01:26:51 +00:00
|
|
|
pub fn play_sfx(&mut self, sound: &str, pos: Vec3<f32>, vol: Option<f32>) {
|
2019-12-10 14:02:51 +00:00
|
|
|
if self.audio_device.is_some() {
|
2020-02-23 01:26:51 +00:00
|
|
|
let sound = self
|
|
|
|
.sound_cache
|
|
|
|
.load_sound(sound)
|
|
|
|
.amplify(vol.unwrap_or(1.0));
|
2019-08-31 21:39:40 +00:00
|
|
|
|
2020-07-31 22:25:23 +00:00
|
|
|
let listener = self.listener.clone();
|
2020-02-15 21:30:44 +00:00
|
|
|
if let Some(channel) = self.get_sfx_channel() {
|
2020-07-31 22:25:23 +00:00
|
|
|
channel.set_pos(pos);
|
|
|
|
channel.update(&listener);
|
2019-09-05 09:03:24 +00:00
|
|
|
channel.play(sound);
|
2019-12-10 14:02:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-15 21:30:44 +00:00
|
|
|
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");
|
2019-12-10 14:02:51 +00:00
|
|
|
|
2020-02-15 21:30:44 +00:00
|
|
|
channel.play(sound, channel_tag);
|
2019-08-31 21:39:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-31 22:25:23 +00:00
|
|
|
pub fn set_listener_pos(&mut self, pos: Vec3<f32>, ori: Vec3<f32>) {
|
|
|
|
self.listener.pos = pos;
|
|
|
|
self.listener.ori = ori.normalized();
|
2019-09-01 21:40:54 +00:00
|
|
|
|
|
|
|
let up = Vec3::new(0.0, 0.0, 1.0);
|
2020-07-31 22:25:23 +00:00
|
|
|
self.listener.ear_left_rpos = up.cross(self.listener.ori).normalized();
|
|
|
|
self.listener.ear_right_rpos = -up.cross(self.listener.ori).normalized();
|
2019-09-01 03:18:03 +00:00
|
|
|
|
2020-02-15 21:30:44 +00:00
|
|
|
for channel in self.sfx_channels.iter_mut() {
|
|
|
|
if !channel.is_done() {
|
2020-07-31 22:25:23 +00:00
|
|
|
channel.update(&self.listener);
|
2019-09-01 03:18:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-14 16:56:32 +00:00
|
|
|
/// Switches the playing music to the title music, which is pinned to a
|
|
|
|
/// specific sound file (veloren_title_tune.ogg)
|
2020-02-15 21:30:44 +00:00
|
|
|
pub fn play_title_music(&mut self) {
|
2020-02-03 11:55:32 +00:00
|
|
|
if self.music_enabled() {
|
|
|
|
self.play_music(
|
|
|
|
"voxygen.audio.soundtrack.veloren_title_tune",
|
2020-02-15 21:30:44 +00:00
|
|
|
MusicChannelTag::TitleMusic,
|
2020-02-03 11:55:32 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-15 21:30:44 +00:00
|
|
|
pub fn play_exploration_music(&mut self, item: &str) {
|
|
|
|
if self.music_enabled() {
|
|
|
|
self.play_music(item, MusicChannelTag::Exploration)
|
2019-08-31 08:30:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-01 20:39:39 +00:00
|
|
|
pub fn get_sfx_volume(&self) -> f32 { self.sfx_volume }
|
2019-08-31 06:37:09 +00:00
|
|
|
|
2020-02-01 20:39:39 +00:00
|
|
|
pub fn get_music_volume(&self) -> f32 { self.music_volume }
|
2019-08-31 06:37:09 +00:00
|
|
|
|
2020-02-03 11:55:32 +00:00
|
|
|
pub fn sfx_enabled(&self) -> bool { self.sfx_volume > 0.0 }
|
|
|
|
|
|
|
|
pub fn music_enabled(&self) -> bool { self.music_volume > 0.0 }
|
|
|
|
|
2019-09-24 16:25:24 +00:00
|
|
|
pub fn set_sfx_volume(&mut self, sfx_volume: f32) {
|
|
|
|
self.sfx_volume = sfx_volume;
|
2019-08-31 06:37:09 +00:00
|
|
|
|
2020-02-15 21:30:44 +00:00
|
|
|
for channel in self.sfx_channels.iter_mut() {
|
|
|
|
channel.set_volume(sfx_volume);
|
2019-05-20 16:54:54 +00:00
|
|
|
}
|
|
|
|
}
|
2019-08-31 06:37:09 +00:00
|
|
|
|
2019-09-24 16:25:24 +00:00
|
|
|
pub fn set_music_volume(&mut self, music_volume: f32) {
|
|
|
|
self.music_volume = music_volume;
|
2019-08-31 06:37:09 +00:00
|
|
|
|
2020-02-15 21:30:44 +00:00
|
|
|
for channel in self.music_channels.iter_mut() {
|
|
|
|
channel.set_volume(music_volume);
|
2019-08-31 06:37:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-01 03:18:03 +00:00
|
|
|
// TODO: figure out how badly this will break things when it is called
|
2019-08-31 06:37:09 +00:00
|
|
|
pub fn set_device(&mut self, name: String) {
|
|
|
|
self.device = name.clone();
|
2019-09-05 09:03:24 +00:00
|
|
|
self.audio_device = get_device_raw(&name);
|
2019-08-31 06:37:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the default audio device.
|
|
|
|
/// Does not return rodio Device struct in case our audio backend changes.
|
2020-03-28 13:12:35 +00:00
|
|
|
pub fn get_default_device() -> Option<String> {
|
2020-06-07 15:00:31 +00:00
|
|
|
match rodio::default_output_device() {
|
|
|
|
Some(x) => Some(x.name().ok()?),
|
|
|
|
None => None,
|
|
|
|
}
|
2019-08-31 06:37:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns a vec of the audio devices available.
|
|
|
|
/// Does not return rodio Device struct in case our audio backend changes.
|
|
|
|
pub fn list_devices() -> Vec<String> {
|
2019-10-06 09:18:23 +00:00
|
|
|
list_devices_raw()
|
|
|
|
.iter()
|
2020-06-07 15:00:31 +00:00
|
|
|
.map(|x| x.name().unwrap())
|
2019-10-06 09:18:23 +00:00
|
|
|
.collect()
|
2019-08-31 06:37:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns vec of devices
|
|
|
|
fn list_devices_raw() -> Vec<Device> {
|
2020-06-07 15:00:31 +00:00
|
|
|
match rodio::output_devices() {
|
|
|
|
Ok(devices) => {
|
|
|
|
// Filter out any devices that the name isn't available for
|
|
|
|
devices.filter(|d| d.name().is_ok()).collect()
|
|
|
|
},
|
|
|
|
Err(_) => {
|
2020-06-21 10:22:26 +00:00
|
|
|
warn!("Failed to enumerate audio output devices, audio will not be available");
|
2020-06-07 15:00:31 +00:00
|
|
|
Vec::new()
|
|
|
|
},
|
|
|
|
}
|
2019-08-31 06:37:09 +00:00
|
|
|
}
|
|
|
|
|
2019-09-05 09:03:24 +00:00
|
|
|
fn get_device_raw(device: &str) -> Option<Device> {
|
2020-06-07 15:00:31 +00:00
|
|
|
list_devices_raw()
|
|
|
|
.into_iter()
|
|
|
|
.find(|d| d.name().unwrap() == device)
|
2019-05-11 20:39:46 +00:00
|
|
|
}
|