Audio backend work

This commit is contained in:
DaforLynx
2025-04-23 11:13:46 -07:00
parent 87a602b373
commit fa1bcff179
13 changed files with 271 additions and 85 deletions

View File

@ -32,6 +32,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Adding a space after chat mode commands changes chat modes.
- New and more granular options for the `/kit` command.
- Beds now regen health when mounted.
- Setting dropdown for SFX channel capacity.
### Changed
@ -90,6 +91,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Made it easier to interact with sprites on moving airships.
- Fixed mismatches in some armour item drop models.
- Gnarling Chieftain's totem summons now face their target.
- Game should no longer crash when encountering bad audio device configuration.
- Audio backend tries to switch to a lower samplerate when detecting an abnormally high one.
- Fixed music tracks playing straight after one another sometimes.
## [0.17.0] - 2024-12-28

34
Cargo.lock generated
View File

@ -517,7 +517,7 @@ dependencies = [
"bitflags 2.8.0",
"cexpr",
"clang-sys",
"itertools 0.13.0",
"itertools 0.10.5",
"proc-macro2 1.0.93",
"quote 1.0.38",
"regex",
@ -724,7 +724,7 @@ dependencies = [
"cap-primitives",
"cap-std",
"io-lifetimes",
"windows-sys 0.59.0",
"windows-sys 0.52.0",
]
[[package]]
@ -752,7 +752,7 @@ dependencies = [
"ipnet",
"maybe-owned",
"rustix",
"windows-sys 0.59.0",
"windows-sys 0.52.0",
"winx",
]
@ -2124,7 +2124,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
dependencies = [
"libc",
"windows-sys 0.59.0",
"windows-sys 0.52.0",
]
[[package]]
@ -2693,9 +2693,9 @@ dependencies = [
[[package]]
name = "glam"
version = "0.29.0"
version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c28091a37a5d09b555cb6628fd954da299b536433834f5b8e59eba78e0cbbf8a"
checksum = "bf3aa70d918d2b234126ff4f850f628f172542bf0603ded26b8ee36e5e22d5f9"
dependencies = [
"mint",
]
@ -3801,13 +3801,12 @@ dependencies = [
[[package]]
name = "kira"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15d97c4d549cb864dbd2eb22391231d785a25ec908268603b70abeba5845fd66"
version = "0.10.5"
source = "git+https://github.com/DaforLynx/kira.git?tag=v0.10.5-with-samplerate-selection#854d0e8c63230045c5bc464376fa1caed73eb441"
dependencies = [
"atomic-arena",
"cpal",
"glam 0.29.0",
"glam 0.30.1",
"mint",
"paste",
"rtrb",
@ -5484,7 +5483,7 @@ dependencies = [
"once_cell",
"socket2",
"tracing",
"windows-sys 0.59.0",
"windows-sys 0.52.0",
]
[[package]]
@ -5941,7 +5940,7 @@ dependencies = [
"libc",
"linux-raw-sys",
"once_cell",
"windows-sys 0.59.0",
"windows-sys 0.52.0",
]
[[package]]
@ -6698,7 +6697,7 @@ dependencies = [
"cfg-if 1.0.0",
"libc",
"psm",
"windows-sys 0.59.0",
"windows-sys 0.52.0",
]
[[package]]
@ -7383,9 +7382,9 @@ dependencies = [
[[package]]
name = "triple_buffer"
version = "8.0.0"
version = "8.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e66931c8eca6381f0d34656a9341f09bd462010488c1a3bc0acd3f2d08dffce"
checksum = "de7a7d39da903eaef0d0fd14aae8c8c36cdd7dc1d5a251f88c84b676e8dc0a14"
dependencies = [
"crossbeam-utils 0.8.21",
]
@ -7414,7 +7413,7 @@ version = "1.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675"
dependencies = [
"cfg-if 1.0.0",
"cfg-if 0.1.10",
"rand",
"static_assertions",
]
@ -8032,6 +8031,7 @@ dependencies = [
"clap",
"conrod_core",
"conrod_winit",
"cpal",
"criterion",
"crossbeam-channel",
"crossbeam-utils 0.8.21",
@ -9085,7 +9085,7 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [
"windows-sys 0.59.0",
"windows-sys 0.48.0",
]
[[package]]

View File

@ -41,7 +41,7 @@
],
sites: [Void],
music_state: Activity(Explore),
artist: ("Stone Music Productions", "https://www.stonemusicproductions.com/"),
artist: ("Stone Music Productions", "https://soundcloud.com/stone_music_productions/"),
)),
Individual((
title: "A Peaceful Moment",
@ -568,7 +568,7 @@
Settlement(SavannahTown),
],
music_state: Activity(Explore),
artist: ("Stone Music Productions", "https://www.stonemusicproductions.com/"),
artist: ("Stone Music Productions", "https://soundcloud.com/stone_music_productions/"),
)),
Individual((
title: "Life of a Background Mob",

View File

@ -137,6 +137,7 @@ hud-settings-ambience_volume = Ambience Volume
hud-settings-combat_music = Combat Music
hud-settings-music_spacing = Music Spacing
hud-settings-audio_device = Audio Device
hud-settings-sfx_channels = SFX Channel Capacity
hud-settings-reset_sound = Reset to Defaults
hud-settings-english_fallback = Display English for missing translations
hud-settings-language_send_to_server = Send the configured language to servers (for localizing rules and motd messages)

View File

@ -129,7 +129,7 @@ dot_vox = "5.1"
guillotiere = "0.6.2"
hashbrown = { workspace = true }
image = { workspace = true, features = ["ico"] }
kira = { version = "0.10.3", default-features = false, features = [
kira = { git="https://github.com/DaforLynx/kira.git", tag="v0.10.5-with-samplerate-selection", default-features = false, features = [
"cpal",
"symphonia",
"ogg",
@ -159,6 +159,7 @@ open = "5.3.2"
# Discord RPC
discord-sdk = { version = "0.4", optional = true }
enum-map = { workspace = true }
cpal = "0.15.3"
[target.'cfg(target_os = "macos")'.dependencies]
dispatch = "0.2"

View File

@ -12,9 +12,16 @@ use channel::{
AmbienceChannel, AmbienceChannelTag, LoopPoint, MusicChannel, MusicChannelTag, SfxChannel,
UiChannel,
};
use cpal::{
Device,
traits::{DeviceTrait, HostTrait},
};
use kira::{
AudioManager, AudioManagerSettings, Decibels, Tween, Value,
backend::{self, DefaultBackend},
backend::{
self,
cpal::{CpalBackend, CpalBackendSettings},
},
clock::{ClockHandle, ClockSpeed, ClockTime},
effect::filter::{FilterBuilder, FilterHandle},
listener::ListenerHandle,
@ -24,7 +31,8 @@ use music::MusicTransitionManifest;
use sfx::{SfxEvent, SfxTriggerItem};
use soundcache::load_ogg;
use std::{collections::VecDeque, time::Duration};
use tracing::{debug, error, warn};
use strum::Display;
use tracing::{debug, error, info, warn};
use common::{
assets::{AssetExt, AssetHandle},
@ -71,6 +79,32 @@ struct Tracks {
ambience: TrackHandle,
}
#[derive(Clone, Copy, Debug, Display)]
pub enum SfxChannelSettings {
Low,
Medium,
High,
}
impl SfxChannelSettings {
pub fn from_str_slice(str: &str) -> Self {
match str {
"Low" => SfxChannelSettings::Low,
"Medium" => SfxChannelSettings::Medium,
"High" => SfxChannelSettings::High,
_ => SfxChannelSettings::Medium,
}
}
pub fn to_usize(&self) -> usize {
match self {
SfxChannelSettings::Low => 16,
SfxChannelSettings::Medium => 32,
SfxChannelSettings::High => 64,
}
}
}
struct Effects {
sfx: FilterHandle,
ambience: FilterHandle,
@ -154,7 +188,7 @@ struct AudioFrontendInner {
}
enum AudioCreationError {
Manager(<DefaultBackend as backend::Backend>::Error),
Manager(<CpalBackend as backend::Backend>::Error),
Clock(kira::ResourceLimitReached),
Track(kira::ResourceLimitReached),
Listener(kira::ResourceLimitReached),
@ -165,12 +199,20 @@ impl AudioFrontendInner {
num_sfx_channels: usize,
num_ui_channels: usize,
buffer_size: usize,
device: Option<Device>,
sample_rate: Option<u32>,
) -> Result<Self, AudioCreationError> {
let manager_settings = AudioManagerSettings {
internal_buffer_size: buffer_size,
let backend_settings = CpalBackendSettings {
device,
sample_rate,
..Default::default()
};
let mut manager = AudioManager::<DefaultBackend>::new(manager_settings)
let manager_settings = AudioManagerSettings {
internal_buffer_size: buffer_size,
backend_settings,
..Default::default()
};
let mut manager = AudioManager::<CpalBackend>::new(manager_settings)
.map_err(AudioCreationError::Manager)?;
let mut clock = manager
@ -301,38 +343,73 @@ impl AudioFrontend {
combat_music_enabled: bool,
buffer_size: usize,
) -> Self {
let inner = AudioFrontendInner::new(num_sfx_channels, num_ui_channels, buffer_size)
.inspect_err(|err| match err {
AudioCreationError::Manager(e) => {
#[cfg(unix)]
error!(
?e,
"failed to construct audio frontend manager. Is `pulseaudio-alsa` \
installed?"
let mut device = cpal::default_host().default_output_device();
let mut samplerate = None;
if let Some(device) = device.as_mut() {
if let Ok(default_output_config) = device.default_output_config() {
// Max samplerate
info!(
"Current default samplerate: {:?}",
default_output_config.sample_rate().0
);
if default_output_config.sample_rate().0 > 48000 {
warn!(
"Current default samplerate is higher than 48000; attempting to lower \
samplerate to 44100"
);
#[cfg(not(unix))]
error!(?e, "failed to construct audio frontend manager.");
},
AudioCreationError::Clock(e) => {
error!(?e, "Failed to construct audio frontend clock.")
},
AudioCreationError::Track(e) => {
error!(?e, "Failed to construct audio frontend track.")
},
AudioCreationError::Listener(e) => {
error!(?e, "Failed to construct audio frontend listener.")
},
})
.ok();
samplerate = Some(44100)
}
}
}
let inner = AudioFrontendInner::new(
num_sfx_channels,
num_ui_channels,
buffer_size,
device,
samplerate,
)
.inspect_err(|err| match err {
AudioCreationError::Manager(e) => {
#[cfg(unix)]
error!(
?e,
"failed to construct audio frontend manager. Is `pulseaudio-alsa` installed?"
);
#[cfg(not(unix))]
error!(?e, "failed to construct audio frontend manager.");
},
AudioCreationError::Clock(e) => {
error!(?e, "Failed to construct audio frontend clock.")
},
AudioCreationError::Track(e) => {
error!(?e, "Failed to construct audio frontend track.")
},
AudioCreationError::Listener(e) => {
error!(?e, "Failed to construct audio frontend listener.")
},
})
.ok();
Self {
inner,
volumes: Volumes::default(),
music_spacing: 1.0,
mtm: AssetExt::load_expect("voxygen.audio.music_transition_manifest"),
subtitles: VecDeque::new(),
subtitles_enabled: subtitles,
combat_music_enabled,
if let Some(inner) = inner {
Self {
inner: Some(inner),
volumes: Volumes::default(),
music_spacing: 1.0,
mtm: AssetExt::load_expect("voxygen.audio.music_transition_manifest"),
subtitles: VecDeque::new(),
subtitles_enabled: subtitles,
combat_music_enabled,
}
} else {
Self {
inner: None,
volumes: Volumes::default(),
music_spacing: 1.0,
mtm: AssetExt::load_expect("voxygen.audio.music_transition_manifest"),
subtitles: VecDeque::new(),
subtitles_enabled: subtitles,
combat_music_enabled,
}
}
}
@ -369,6 +446,14 @@ impl AudioFrontend {
.unwrap_or_default()
}
pub fn get_cpu_usage(&mut self) -> f32 {
if let Some(inner) = self.inner.as_mut() {
inner.manager.backend_mut().pop_cpu_usage().unwrap_or(0.0)
} else {
0.0
}
}
/// Play a music file with the given tag. Pass in the length of the track in
/// seconds.
fn play_music(&mut self, sound: &str, channel_tag: MusicChannelTag, length: f32) {
@ -651,6 +736,9 @@ impl AudioFrontend {
}
}
/// Retrieves the current setting for master volume
pub fn get_master_volume(&self) -> f32 { self.volumes.master }
/// Retrieves the current setting for music volume
pub fn get_music_volume(&self) -> f32 { self.volumes.music }
@ -747,6 +835,21 @@ impl AudioFrontend {
}
}
pub fn set_num_sfx_channels(&mut self, channels: usize) {
if let Some(inner) = self.inner.as_mut() {
inner.channels.sfx = Vec::new();
for _ in 0..channels {
if let Ok(channel) =
SfxChannel::new(&mut inner.tracks.sfx, inner.listener.handle.id())
{
inner.channels.sfx.push(channel);
} else {
warn!("Cannot create sfx channel")
}
}
}
}
pub fn get_num_music_channels(&self) -> usize {
self.inner
.as_ref()

View File

@ -149,6 +149,8 @@ pub struct MusicMgr {
began_playing: Option<ClockTime>,
/// Instant at which the current track should stop
song_end: Option<ClockTime>,
/// Currently staying silent for gap between tracks
is_gap: bool,
/// Time until the next track should be played after a track ends
gap_length: f32,
/// Time remaining for gap
@ -212,6 +214,7 @@ impl MusicMgr {
soundtrack: Self::load_soundtrack_items(calendar),
began_playing: None,
song_end: None,
is_gap: true,
gap_length: 0.0,
gap_time: -1.0,
last_track: String::from("None"),
@ -301,7 +304,7 @@ impl MusicMgr {
let began_playing = *self.began_playing.get_or_insert(now);
let last_interrupt_attempt = *self.last_interrupt_attempt.get_or_insert(now);
let song_end = *self.song_end.get_or_insert(now);
let mut time_since_began_playing = time_f64(now) - time_f64(began_playing);
let time_since_began_playing = time_f64(now) - time_f64(began_playing);
// TODO: Instead of a constant tick, make this a timer that starts only when
// combat might end, providing a proper "buffer".
@ -327,7 +330,6 @@ impl MusicMgr {
> time_f64(song_end) - time_f64(began_playing) // Amount of time between when the song ends and when it began playing
|| interrupt)
{
time_since_began_playing = time_f64(now) - time_f64(began_playing);
if time_since_began_playing > self.track_length as f64
&& self.last_activity
!= MusicState::Activity(MusicActivity::Combat(CombatIntensity::High))
@ -355,7 +357,7 @@ impl MusicMgr {
)
{
// If current state is Explore, insert a gap now.
if self.gap_time == 0.0 {
if !self.is_gap {
self.gap_length = self.generate_silence_between_tracks(
audio.music_spacing,
client,
@ -364,9 +366,10 @@ impl MusicMgr {
);
self.gap_time = self.gap_length as f64;
self.song_end = audio.get_clock_time();
self.is_gap = true
} else if self.gap_time < 0.0 {
// Gap time is up, play a track
// Hack to make combat situations not cancel explore music
// Hack to make combat situations not cancel explore music for now
if music_state
== MusicState::Transition(
MusicActivity::Explore,
@ -381,6 +384,7 @@ impl MusicMgr {
self.last_activity = next_activity;
self.gap_time = 0.0;
self.gap_length = 0.0;
self.is_gap = false;
}
}
} else if music_state
@ -410,7 +414,9 @@ impl MusicMgr {
if time_since_began_playing > self.track_length as f64 {
// Time remaining = Max time - (current time - time song ended)
self.gap_time = (self.gap_length as f64) - (time_f64(now) - time_f64(song_end));
if self.is_gap {
self.gap_time = (self.gap_length as f64) - (time_f64(now) - time_f64(song_end));
}
}
}
@ -513,7 +519,6 @@ impl MusicMgr {
if let Ok(track) = new_maybe_track {
let now = audio.get_clock_time().unwrap();
// println!("Now playing {:?}", track.title);
self.last_track = String::from(&track.title);
self.began_playing = Some(now);
self.song_end = Some(ClockTime::from_ticks_f64(
@ -631,9 +636,15 @@ impl MusicMgr {
pub fn current_artist(&self) -> String { self.current_artist.clone() }
pub fn reset_track(&mut self) {
pub fn reset_track(&mut self, audio: &mut AudioFrontend) {
self.current_artist = String::from("None");
self.current_track = String::from("None");
self.gap_length = 1.0;
self.gap_time = 1.0;
self.is_gap = true;
self.track_length = 0.0;
self.began_playing = audio.get_clock_time();
self.song_end = audio.get_clock_time();
}
/// Loads default soundtrack if no events are active. Otherwise, attempts to

View File

@ -672,6 +672,7 @@ pub struct DebugInfo {
pub current_track: String,
pub current_artist: String,
pub active_channels: ActiveChannels,
pub audio_cpu_usage: f32,
}
pub struct HudInfo<'a> {
@ -2882,11 +2883,12 @@ impl Hud {
.font_size(self.fonts.cyri.scale(14))
.set(self.ids.song_info, ui_widgets);
Text::new(&format!(
"Active channels: M{}, A{}, S{}, U{}",
"Active channels: M{}, A{}, S{}, U{}, CPU: {:2.0}%",
debug_info.active_channels.music,
debug_info.active_channels.ambience,
debug_info.active_channels.sfx,
debug_info.active_channels.ui,
debug_info.audio_cpu_usage * 100.0,
))
.color(TEXT_COLOR)
.down_from(self.ids.song_info, V_PAD)

View File

@ -2,14 +2,15 @@ use super::{RESET_BUTTONS_HEIGHT, RESET_BUTTONS_WIDTH};
use crate::{
GlobalState,
hud::{TEXT_COLOR, TEXT_COLOR_GREY, img_ids::Imgs},
session::settings_change::{Audio as AudioChange, Audio::*},
audio::SfxChannelSettings,
hud::{MENU_BG, TEXT_COLOR, TEXT_COLOR_GREY, img_ids::Imgs},
session::settings_change::Audio::{self as AudioChange, *},
ui::{ImageSlider, ToggleButton, fonts::Fonts},
};
use conrod_core::{
Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon, color,
position::{Align, Relative},
widget::{self, Button, Rectangle, Scrollbar, Text},
widget::{self, Button, DropDownList, Rectangle, Scrollbar, Text},
widget_ids,
};
use i18n::Localization;
@ -42,11 +43,13 @@ widget_ids! {
music_spacing_text,
music_spacing_slider,
music_spacing_number,
//audio_device_list,
//audio_device_text,
reset_sound_button,
combat_music_toggle_label,
combat_music_toggle_button,
sfx_channels_label,
sfx_channels_list,
// audio_device_list,
// audio_device_text,
}
}
@ -433,6 +436,62 @@ impl Widget for Sound<'_> {
.color(TEXT_COLOR)
.set(state.ids.music_spacing_number, ui);
// Num Sfx Channels
// --------------------------------------------
Text::new(&self.localized_strings.get_msg("hud-settings-sfx_channels"))
.down_from(state.ids.music_spacing_number, 10.0)
.x_align_to(state.ids.music_spacing_text, Align::Start)
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
.color(TEXT_COLOR)
.set(state.ids.sfx_channels_label, ui);
let current: Option<usize> = match self.global_state.settings.audio.num_sfx_channels {
16 => Some(0),
32 => Some(1),
64 => Some(2),
_ => None,
};
let num_sfx_setting_list = [
SfxChannelSettings::Low.to_string(),
SfxChannelSettings::Medium.to_string(),
SfxChannelSettings::High.to_string(),
];
let num_sfx_setting_list_shown = vec![
format!(
"{} ({})",
&SfxChannelSettings::Low.to_string(),
&SfxChannelSettings::Low.to_usize().to_string()
),
format!(
"{} ({})",
&SfxChannelSettings::Medium.to_string(),
&SfxChannelSettings::Medium.to_usize().to_string()
),
format!(
"{} ({})",
&SfxChannelSettings::High.to_string(),
&SfxChannelSettings::High.to_usize().to_string()
),
];
if let Some(clicked) = DropDownList::new(&num_sfx_setting_list_shown, current)
.w_h(150.0, 22.0)
.color(MENU_BG)
.label_color(TEXT_COLOR)
.label_font_id(self.fonts.cyri.conrod_id)
.down_from(state.ids.sfx_channels_label, 10.0)
.set(state.ids.sfx_channels_list, ui)
{
let new_val = &num_sfx_setting_list[clicked];
events.push(SetNumSfxChannels(SfxChannelSettings::from_str_slice(
new_val,
)));
}
// Combat music toggle
// let audio = &self.global_state.audio;
@ -459,18 +518,17 @@ impl Widget for Sound<'_> {
// Audio Device Selector
// --------------------------------------------
// let device = &self.global_state.audio.device;
//let device_list = &self.global_state.audio.device_list;
//Text::new(self.localized_strings.get("hud.settings.audio_device"
// )) .down_from(state.ids.sfx_volume_slider, 10.0)
// .font_size(self.fonts.cyri.scale(14))
// .font_id(self.fonts.cyri.conrod_id)
// .color(TEXT_COLOR)
// .set(state.ids.audio_device_text, ui);
// let device = &self.global_state.audio.current_device;
// let device_list = self.global_state.audio.get_device_list();
// Text::new(&self.localized_strings.get_msg("hud.settings.audio_device"))
// .down_from(state.ids.music_spacing_number, 10.0)
// .font_size(self.fonts.cyri.scale(14))
// .font_id(self.fonts.cyri.conrod_id)
// .color(TEXT_COLOR)
// .set(state.ids.audio_device_text, ui);
//// Get which device is currently selected
//let selected = device_list.iter().position(|x|
// x.contains(device));
// // Get which device is currently selected
// let selected = device_list.iter().position(|d| d == device);
//if let Some(clicked) = DropDownList::new(&device_list, selected)
// .w_h(400.0, 22.0)
@ -489,7 +547,7 @@ impl Widget for Sound<'_> {
.w_h(RESET_BUTTONS_WIDTH, RESET_BUTTONS_HEIGHT)
.hover_image(self.imgs.button_hover)
.press_image(self.imgs.button_press)
.down_from(state.ids.combat_music_toggle_button, 12.0)
.down_from(state.ids.sfx_channels_list, 12.0)
.x_align_to(state.ids.ambience_volume_text, Align::Start)
.label(&self.localized_strings.get_msg("hud-settings-reset_sound"))
.label_font_size(self.fonts.cyri.scale(14))

View File

@ -162,7 +162,6 @@ fn main() {
false, // We're disabling combat music for now
settings.audio.buffer_size,
),
// AudioOutput::Device(ref dev) => Some(dev.clone()),
};
audio.set_master_volume(settings.audio.master_volume.get_checked());

View File

@ -424,7 +424,7 @@ impl SessionState {
client::Event::CharacterEdited(_) => {},
client::Event::CharacterError(_) => {},
client::Event::CharacterJoined(_) => {
self.scene.music_mgr.reset_track();
self.scene.music_mgr.reset_track(&mut global_state.audio);
},
client::Event::MapMarker(event) => {
self.hud.show.update_map_markers(event);
@ -1640,6 +1640,7 @@ impl PlayState for SessionState {
current_track: self.scene.music_mgr().current_track(),
current_artist: self.scene.music_mgr().current_artist(),
active_channels: global_state.audio.get_num_active_channels(),
audio_cpu_usage: global_state.audio.get_cpu_usage(),
}
});

View File

@ -1,6 +1,7 @@
use super::SessionState;
use crate::{
GlobalState,
audio::SfxChannelSettings,
controller::ControllerSettings,
game_input::GameInput,
hud::{
@ -31,7 +32,7 @@ pub enum Audio {
MuteAmbienceVolume(bool),
AdjustMusicSpacing(f32),
ToggleCombatMusic(bool),
//ChangeAudioDevice(String),
SetNumSfxChannels(SfxChannelSettings),
ResetAudioSettings,
}
#[derive(Clone)]
@ -300,6 +301,11 @@ impl SettingsChange {
Audio::ToggleCombatMusic(combat_music_enabled) => {
global_state.audio.combat_music_enabled = combat_music_enabled
},
Audio::SetNumSfxChannels(level) => {
let num = SfxChannelSettings::to_usize(&level);
global_state.audio.set_num_sfx_channels(num);
settings.audio.num_sfx_channels = num;
},
Audio::ResetAudioSettings => {
settings.audio = AudioSettings::default();

View File

@ -65,7 +65,7 @@ impl Default for AudioSettings {
music_volume: AudioVolume::new(0.5, false),
sfx_volume: AudioVolume::new(0.8, false),
ambience_volume: AudioVolume::new(0.8, false),
num_sfx_channels: 48,
num_sfx_channels: 32,
num_ui_channels: 16,
music_spacing: 1.0,
subtitles: false,