1
0
Fork 0
mirror of https://github.com/doukutsu-rs/doukutsu-rs synced 2025-10-31 13:25:47 +00:00

Added volume settings (#57)

This commit is contained in:
dawndus 2022-02-02 22:09:29 -05:00 committed by GitHub
parent 88fdb7b0ce
commit bd0762f812
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 161 additions and 27 deletions

View file

@ -1597,6 +1597,7 @@ impl EngineConstants {
self.tex_sizes.insert("Caret".to_owned(), (320, 320));
self.tex_sizes.insert("MyChar".to_owned(), (200, 384));
self.tex_sizes.insert("Npc/NpcRegu".to_owned(), (320, 410));
self.tex_sizes.insert("ui".to_owned(), (128, 32));
self.title.logo_rect = Rect { left: 0, top: 0, right: 214, bottom: 50 };
self.font_path = "csfont.fnt".to_owned();
self.font_scale = 0.5;
@ -1638,6 +1639,7 @@ impl EngineConstants {
self.supports_og_textures = true;
self.tex_sizes.insert("bkMoon".to_owned(), (427, 240));
self.tex_sizes.insert("bkFog".to_owned(), (427, 240));
self.tex_sizes.insert("ui".to_owned(), (128, 32));
self.title.logo_rect = Rect { left: 0, top: 0, right: 214, bottom: 62 };
self.inventory_dim_color = Color::from_rgba(0, 0, 32, 150);
self.textscript.encoding = TextScriptEncoding::UTF8;

View file

@ -1,8 +1,9 @@
use std::cell::Cell;
use crate::common::Rect;
use crate::common::{Color, Rect};
use crate::framework::context::Context;
use crate::framework::error::GameResult;
use crate::framework::graphics;
use crate::input::combined_menu_controller::CombinedMenuController;
use crate::shared_game_state::{MenuCharacter, SharedGameState};
@ -19,6 +20,7 @@ pub enum MenuEntry {
Toggle(String, bool),
Options(String, usize, Vec<String>),
DescriptiveOptions(String, usize, Vec<String>, Vec<String>),
OptionsBar(String, f32),
SaveData(MenuSaveInfo),
NewSave,
}
@ -33,6 +35,7 @@ impl MenuEntry {
MenuEntry::Toggle(_, _) => 16.0,
MenuEntry::Options(_, _, _) => 16.0,
MenuEntry::DescriptiveOptions(_, _, _, _) => 16.0,
MenuEntry::OptionsBar(_, _) => 16.0,
MenuEntry::SaveData(_) => 32.0,
MenuEntry::NewSave => 32.0,
}
@ -47,6 +50,7 @@ impl MenuEntry {
MenuEntry::Toggle(_, _) => true,
MenuEntry::Options(_, _, _) => true,
MenuEntry::DescriptiveOptions(_, _, _, _) => true,
MenuEntry::OptionsBar(_, _) => true,
MenuEntry::SaveData(_) => true,
MenuEntry::NewSave => true,
}
@ -57,8 +61,8 @@ pub enum MenuSelectionResult<'a> {
None,
Canceled,
Selected(usize, &'a mut MenuEntry),
Left(usize, &'a mut MenuEntry),
Right(usize, &'a mut MenuEntry),
Left(usize, &'a mut MenuEntry, i16),
Right(usize, &'a mut MenuEntry, i16),
}
pub struct Menu {
@ -352,6 +356,52 @@ impl Menu {
ctx,
)?;
}
MenuEntry::OptionsBar(name, percent) => {
state.font.draw_text(
name.chars(),
self.x as f32 + 20.0,
y,
&state.constants,
&mut state.texture_set,
ctx,
)?;
if state.constants.is_switch || state.constants.is_cs_plus {
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "ui")?;
let bar_width = if state.constants.is_switch { 81.0 } else { 109.0 };
let rect = Rect::new(0, 18, (bar_width - (bar_width * (1.0 - percent))) as u16, 32);
batch.add_rect(
(self.x + self.width as isize) as f32 - (bar_width + (2.0 * state.scale)),
y - (state.scale * 2.0),
&rect,
);
batch.draw(ctx)?;
} else {
let scale = state.scale;
let bar_rect = Rect::new_size(
((self.x + self.width as isize - 80) as f32 * scale) as isize,
(y * scale) as isize,
(75.0 * scale * percent) as isize,
(8.0 * scale) as isize,
);
graphics::draw_rect(
ctx,
Rect::new_size(
bar_rect.left + (2.0 * scale) as isize,
bar_rect.top + (2.0 * scale) as isize,
(75.0 * scale) as isize,
(8.0 * scale) as isize,
),
Color::new(0.0, 0.0, 0.0, 1.0),
)?;
graphics::draw_rect(ctx, bar_rect, Color::new(1.0, 1.0, 1.0, 1.0))?;
}
}
_ => {}
}
@ -416,21 +466,25 @@ impl Menu {
state.sound_manager.play_sfx(18);
return MenuSelectionResult::Selected(idx, entry);
}
MenuEntry::Options(_, _, _) if controller.trigger_left() => {
MenuEntry::Options(_, _, _) | MenuEntry::OptionsBar(_, _)
if self.selected == idx && controller.trigger_left() =>
{
state.sound_manager.play_sfx(1);
return MenuSelectionResult::Left(self.selected, entry);
return MenuSelectionResult::Left(self.selected, entry, -1);
}
MenuEntry::Options(_, _, _) if controller.trigger_right() => {
MenuEntry::Options(_, _, _) | MenuEntry::OptionsBar(_, _)
if self.selected == idx && controller.trigger_right() =>
{
state.sound_manager.play_sfx(1);
return MenuSelectionResult::Right(self.selected, entry);
return MenuSelectionResult::Right(self.selected, entry, 1);
}
MenuEntry::DescriptiveOptions(_, _, _, _) if controller.trigger_left() => {
MenuEntry::DescriptiveOptions(_, _, _, _) if self.selected == idx && controller.trigger_left() => {
state.sound_manager.play_sfx(1);
return MenuSelectionResult::Left(self.selected, entry);
return MenuSelectionResult::Left(self.selected, entry, -1);
}
MenuEntry::DescriptiveOptions(_, _, _, _) if controller.trigger_right() => {
MenuEntry::DescriptiveOptions(_, _, _, _) if self.selected == idx && controller.trigger_right() => {
state.sound_manager.play_sfx(1);
return MenuSelectionResult::Right(self.selected, entry);
return MenuSelectionResult::Right(self.selected, entry, 1);
}
_ => {}
}

View file

@ -1,8 +1,8 @@
use crate::framework::context::Context;
use crate::framework::error::GameResult;
use crate::input::combined_menu_controller::CombinedMenuController;
use crate::menu::{Menu, MenuSelectionResult};
use crate::menu::MenuEntry;
use crate::menu::{Menu, MenuSelectionResult};
use crate::shared_game_state::{SharedGameState, TimingMode};
use crate::sound::InterpolationMode;
@ -72,6 +72,9 @@ impl SettingsMenu {
self.main.push_entry(MenuEntry::Active("< Back".to_owned()));
self.sound.push_entry(MenuEntry::OptionsBar("Music Volume".to_owned(), state.settings.bgm_volume));
self.sound.push_entry(MenuEntry::OptionsBar("Effects Volume".to_owned(), state.settings.sfx_volume));
self.sound.push_entry(MenuEntry::DescriptiveOptions(
"BGM Interpolation:".to_owned(),
state.settings.organya_interpolation as usize,
@ -80,14 +83,14 @@ impl SettingsMenu {
"Linear".to_owned(),
"Cosine".to_owned(),
"Cubic".to_owned(),
"Polyphase".to_owned()
"Polyphase".to_owned(),
],
vec![
"(Fastest, lowest quality)".to_owned(),
"(Fast, similar to freeware on Vista+)".to_owned(),
"(Cosine interpolation)".to_owned(),
"(Cubic interpolation)".to_owned(),
"(Slowest, similar to freeware on XP)".to_owned()
"(Slowest, similar to freeware on XP)".to_owned(),
],
));
self.sound.push_entry(MenuEntry::DisabledWhite("".to_owned()));
@ -211,7 +214,25 @@ impl SettingsMenu {
_ => (),
},
CurrentMenu::SoundMenu => match self.sound.tick(controller, state) {
MenuSelectionResult::Selected(0, toggle) => {
MenuSelectionResult::Left(0, bgm, direction) | MenuSelectionResult::Right(0, bgm, direction) => {
if let MenuEntry::OptionsBar(_, value) = bgm {
*value = (*value + (direction as f32 * 0.1)).clamp(0.0, 1.0);
state.settings.bgm_volume = *value;
state.sound_manager.set_song_volume(*value);
let _ = state.settings.save(ctx);
}
}
MenuSelectionResult::Left(1, sfx, direction) | MenuSelectionResult::Right(1, sfx, direction) => {
if let MenuEntry::OptionsBar(_, value) = sfx {
*value = (*value + (direction as f32 * 0.1)).clamp(0.0, 1.0);
state.settings.sfx_volume = *value;
state.sound_manager.set_sfx_volume(*value);
let _ = state.settings.save(ctx);
}
}
MenuSelectionResult::Selected(2, toggle) => {
if let MenuEntry::DescriptiveOptions(_, value, _, _) = toggle {
let (new_mode, new_value) = match *value {
0 => (InterpolationMode::Linear, 1),
@ -228,11 +249,11 @@ impl SettingsMenu {
let _ = state.settings.save(ctx);
}
}
MenuSelectionResult::Selected(3, _) | MenuSelectionResult::Canceled => {
MenuSelectionResult::Selected(5, _) | MenuSelectionResult::Canceled => {
self.current = CurrentMenu::MainMenu
}
_ => (),
}
},
}
Ok(())
}

View file

@ -25,6 +25,10 @@ pub struct Settings {
pub motion_interpolation: bool,
pub touch_controls: bool,
pub soundtrack: String,
#[serde(default = "default_vol")]
pub bgm_volume: f32,
#[serde(default = "default_vol")]
pub sfx_volume: f32,
#[serde(default = "default_timing")]
pub timing_mode: TimingMode,
#[serde(default = "default_interpolation")]
@ -45,22 +49,35 @@ pub struct Settings {
pub fps_counter: bool,
}
fn default_true() -> bool { true }
fn default_true() -> bool {
true
}
#[inline(always)]
fn current_version() -> u32 { 4 }
fn current_version() -> u32 {
5
}
#[inline(always)]
fn default_timing() -> TimingMode { TimingMode::_50Hz }
fn default_timing() -> TimingMode {
TimingMode::_50Hz
}
#[inline(always)]
fn default_interpolation() -> InterpolationMode { InterpolationMode::Linear }
fn default_interpolation() -> InterpolationMode {
InterpolationMode::Linear
}
#[inline(always)]
fn default_speed() -> f64 {
1.0
}
#[inline(always)]
fn default_vol() -> f32 {
1.0
}
impl Settings {
pub fn load(ctx: &Context) -> GameResult<Settings> {
if let Ok(file) = user_open(ctx, "/settings.json") {
@ -86,6 +103,12 @@ impl Settings {
self.timing_mode = default_timing();
}
if self.version == 4 {
self.version = 5;
self.bgm_volume = default_vol();
self.sfx_volume = default_vol();
}
if self.version != initial_version {
log::info!("Upgraded configuration file from version {} to {}.", initial_version, self.version);
}
@ -125,6 +148,8 @@ impl Default for Settings {
motion_interpolation: true,
touch_controls: cfg!(target_os = "android"),
soundtrack: "".to_string(),
bgm_volume: 1.0,
sfx_volume: 1.0,
timing_mode: default_timing(),
organya_interpolation: InterpolationMode::Linear,
player1_key_map: p1_default_keymap(),

View file

@ -247,6 +247,9 @@ impl SharedGameState {
}
}
sound_manager.set_song_volume(settings.bgm_volume);
sound_manager.set_sfx_volume(settings.sfx_volume);
#[cfg(feature = "hooks")]
init_hooks();

View file

@ -4,16 +4,16 @@ use std::str::FromStr;
use std::sync::mpsc;
use std::sync::mpsc::{Receiver, Sender};
use cpal::Sample;
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
use cpal::Sample;
#[cfg(feature = "ogg-playback")]
use lewton::inside_ogg::OggStreamReader;
use num_traits::clamp;
use crate::engine_constants::EngineConstants;
use crate::framework::context::Context;
use crate::framework::error::{GameError, GameResult};
use crate::framework::error::GameError::{AudioError, InvalidValue};
use crate::framework::error::{GameError, GameResult};
use crate::framework::filesystem;
use crate::framework::filesystem::File;
use crate::settings::Settings;
@ -142,6 +142,20 @@ impl SoundManager {
let _ = self.tx.send(PlaybackMessage::SetOrgInterpolation(interpolation));
}
pub fn set_song_volume(&self, volume: f32) {
if self.no_audio {
return;
}
let _ = self.tx.send(PlaybackMessage::SetSongVolume(volume.powf(3.0)));
}
pub fn set_sfx_volume(&self, volume: f32) {
if self.no_audio {
return;
}
let _ = self.tx.send(PlaybackMessage::SetSampleVolume(volume.powf(3.0)));
}
pub fn play_song(
&mut self,
song_id: usize,
@ -403,6 +417,8 @@ pub(in crate::sound) enum PlaybackMessage {
LoopSampleFreq(u8, f32),
StopSample(u8),
SetSpeed(f32),
SetSongVolume(f32),
SetSampleVolume(f32),
SaveState,
RestoreState,
SetSampleParams(u8, PixToneParameters),
@ -464,6 +480,8 @@ where
let mut bgm_index = 0;
let mut pxt_index = 0;
let mut samples = 0;
let mut bgm_vol = 1.0_f32;
let mut sfx_vol = 1.0_f32;
pixtone.mix(&mut pxt_buf, sample_rate);
let err_fn = |err| eprintln!("an error occurred on stream: {}", err);
@ -547,6 +565,14 @@ where
ogg_engine.set_sample_rate((sample_rate / new_speed) as usize);
org_engine.set_sample_rate((sample_rate / new_speed) as usize);
}
Ok(PlaybackMessage::SetSongVolume(new_volume)) => {
assert!(bgm_vol >= 0.0);
bgm_vol = new_volume;
}
Ok(PlaybackMessage::SetSampleVolume(new_volume)) => {
assert!(sfx_vol >= 0.0);
sfx_vol = new_volume;
}
Ok(PlaybackMessage::SaveState) => {
saved_state = match state {
PlaybackState::Stopped => PlaybackStateType::None,
@ -647,13 +673,15 @@ where
if frame.len() >= 2 {
let sample_l = clamp(
(((bgm_sample_l ^ 0x8000) as i16) as isize) + (((pxt_sample ^ 0x8000) as i16) as isize),
(((bgm_sample_l ^ 0x8000) as i16) as f32 * bgm_vol) as isize
+ (((pxt_sample ^ 0x8000) as i16) as f32 * sfx_vol) as isize,
-0x7fff,
0x7fff,
) as u16
^ 0x8000;
let sample_r = clamp(
(((bgm_sample_r ^ 0x8000) as i16) as isize) + (((pxt_sample ^ 0x8000) as i16) as isize),
(((bgm_sample_r ^ 0x8000) as i16) as f32 * bgm_vol) as isize
+ (((pxt_sample ^ 0x8000) as i16) as f32 * sfx_vol) as isize,
-0x7fff,
0x7fff,
) as u16
@ -663,8 +691,9 @@ where
frame[1] = Sample::from::<u16>(&sample_r);
} else {
let sample = clamp(
((((bgm_sample_l ^ 0x8000) as i16) + ((bgm_sample_r ^ 0x8000) as i16)) / 2) as isize
+ (((pxt_sample ^ 0x8000) as i16) as isize),
((((bgm_sample_l ^ 0x8000) as i16) + ((bgm_sample_r ^ 0x8000) as i16)) as f32 * bgm_vol / 2.0)
as isize
+ (((pxt_sample ^ 0x8000) as i16) as f32 * sfx_vol) as isize,
-0x7fff,
0x7fff,
) as u16