Volume mixer settings with persistence to disk

This commit is contained in:
Rob Kelly 2024-12-09 02:57:08 -07:00
parent f75af2196d
commit 12fbbb82bc
8 changed files with 173 additions and 5 deletions

View File

@ -1,6 +1,7 @@
[gd_resource type="AudioBusLayout" format=3 uid="uid://cwxndi28bii8d"]
[resource]
bus/0/volume_db = 0.0
bus/1/name = &"SFX"
bus/1/solo = false
bus/1/mute = false

View File

@ -61,6 +61,7 @@ config/controls/camera/y_axis_acceleration=30.0
config/controls/camera/invert_pitch=false
config/accessibility/enable_screen_shake=true
config/accessibility/enable_hit_lag=true
audio/buses/override_bus_layout="user://audio_bus_layout.tres"
[global_group]

View File

@ -2,6 +2,7 @@ class_name GameSettingsType extends Node
## Container for project settings, for quick runtime access.
var settings_file: String
var audio_bus_file: String
var free_camera_speed: float
var x_sensitivity: float
@ -15,10 +16,13 @@ var enable_hit_lag: bool
func _init() -> void:
ProjectSettings.settings_changed.connect(_read_settings)
_read_settings()
_load_audio_bus_override()
func _read_settings() -> void:
settings_file = ProjectSettings.get_setting("application/config/project_settings_override")
audio_bus_file = ProjectSettings.get_setting("game/audio/buses/override_bus_layout")
free_camera_speed = ProjectSettings.get_setting("game/config/controls/camera/free_camera_speed")
x_sensitivity = ProjectSettings.get_setting("game/config/controls/camera/x_axis_sensitivity")
@ -31,3 +35,18 @@ func _read_settings() -> void:
"game/config/accessibility/enable_screen_shake"
)
enable_hit_lag = ProjectSettings.get_setting("game/config/accessibility/enable_hit_lag")
func _load_audio_bus_override() -> void:
# Load override audio bus file
if FileAccess.file_exists(audio_bus_file):
print_debug("Loading audio bus override from ", audio_bus_file)
var bus_layout: AudioBusLayout = load(audio_bus_file)
AudioServer.set_bus_layout(bus_layout)
func write() -> void:
print_debug("Writing settings to ", settings_file)
ProjectSettings.save_custom(settings_file)
print_debug("Writing audio bus levels to ", audio_bus_file)
ResourceSaver.save(AudioServer.generate_bus_layout(), audio_bus_file)

View File

@ -56,6 +56,12 @@ CancelButton/colors/font_outline_color = Color(0, 0, 0, 1)
CancelButton/constants/outline_size = 6
CancelButton/font_sizes/font_size = 24
CancelButton/fonts/font = ExtResource("2_5ty6u")
ChannelMixerIcon/base_type = &"Label"
ChannelMixerIcon/colors/font_color = Color(1, 1, 0.870588, 1)
ChannelMixerIcon/colors/font_outline_color = Color(0, 0, 0, 1)
ChannelMixerIcon/constants/outline_size = 5
ChannelMixerIcon/font_sizes/font_size = 24
ChannelMixerIcon/fonts/font = ExtResource("2_8kux8")
CheckerContainerEven/base_type = &"PanelContainer"
CheckerContainerEven/styles/panel = SubResource("StyleBoxFlat_rq1no")
CheckerContainerOdd/base_type = &"PanelContainer"
@ -75,10 +81,13 @@ ClubSelectLabelDisabled/colors/font_outline_color = Color(0.2, 0.2, 0.2, 1)
ClubSelectLabelDisabled/colors/font_shadow_color = Color(0.2, 0.2, 0.2, 1)
ClubSelectLabelDisabled/font_sizes/font_size = 84
ClubSelectLabelDisabled/fonts/font = ExtResource("1_3rv2b")
HeaderLarge/constants/outline_size = 8
HeaderLarge/font_sizes/font_size = 28
HeaderMedium/constants/outline_size = 6
HeaderMedium/font_sizes/font_size = 24
HeaderSmall/font_sizes/font_size = 20
HeaderXLarge/base_type = &"Label"
HeaderXLarge/constants/outline_size = 10
HeaderXLarge/font_sizes/font_size = 36
InputBindButton/base_type = &"UIButton"
InputBindButton/colors/font_color = Color(1, 1, 0.870588, 1)

View File

@ -2,11 +2,14 @@ extends MarginContainer
## Menu allowing the user to adjust game configuration.
const SETTINGS_GROUP := "Settings"
const VOLUME_GROUP := "VolumeSliders"
@onready var bus_mixer_list: HBoxContainer = %BusMixerList
@onready var control_binding_list: VBoxContainer = %ControlBindingList
func _ready() -> void:
populate_volume_mixers()
populate_control_bindings()
@ -16,12 +19,23 @@ func _get_settings_elements() -> Array[Setting]:
return elements
func _get_volume_sliders() -> Array[VolumeSlider]:
var elements: Array[VolumeSlider] = []
elements.assign(get_tree().get_nodes_in_group(VOLUME_GROUP))
return elements
func populate_control_bindings() -> void:
for action: StringName in InputMap.get_actions():
if not action.begins_with("ui_"):
control_binding_list.add_child(ControlBinding.create(action))
func populate_volume_mixers() -> void:
for idx: int in range(AudioServer.bus_count):
bus_mixer_list.add_child(VolumeSlider.create(idx))
## Close menu without applying settings.
func cancel() -> void:
queue_free()
@ -31,12 +45,13 @@ func cancel() -> void:
func apply() -> void:
for setting: Setting in _get_settings_elements():
setting.apply()
for mixer: VolumeSlider in _get_volume_sliders():
mixer.apply()
## Write all applied settings to disk.
func save_settings() -> void:
print_debug("Writing settings to ", Game.settings.settings_file)
ProjectSettings.save_custom(Game.settings.settings_file)
Game.settings.write()
BindingLoader.write()

View File

@ -14,7 +14,7 @@ script = ExtResource("1_lbcn7")
[node name="TabContainer" type="TabContainer" parent="."]
layout_mode = 2
current_tab = 3
current_tab = 2
[node name="Game" type="MarginContainer" parent="TabContainer"]
visible = false
@ -196,7 +196,6 @@ theme_type_variation = &"SettingsListMargin"
layout_mode = 2
[node name="Audio" type="MarginContainer" parent="TabContainer"]
visible = false
layout_mode = 2
theme_type_variation = &"SettingsPageContainer"
metadata/_tab_index = 2
@ -229,10 +228,13 @@ size_flags_horizontal = 3
size_flags_vertical = 3
theme_type_variation = &"SettingsListMargin"
[node name="SettingsList" type="VBoxContainer" parent="TabContainer/Audio/VBoxContainer/ScrollContainer/MarginContainer"]
[node name="BusMixerList" type="HBoxContainer" parent="TabContainer/Audio/VBoxContainer/ScrollContainer/MarginContainer"]
unique_name_in_owner = true
layout_mode = 2
theme_override_constants/separation = 16
[node name="Controls" type="MarginContainer" parent="TabContainer"]
visible = false
layout_mode = 2
theme_type_variation = &"SettingsPageContainer"
metadata/_tab_index = 3

View File

@ -0,0 +1,70 @@
class_name VolumeSlider extends VBoxContainer
## Input element for adjusting the volume of a single bus.
const SCENE := preload("res://src/ui/menus/settings_menu/volume_slider/volume_slider.tscn")
const VOLUME_FACTOR := 50.0
@export var bus_idx: int
var value: float:
get:
return slider.value
set(v):
slider.set_value_no_signal(v)
spin_box.set_value_no_signal(v)
_set_bus_volume(v)
var _applied_value: float
@onready var bus_name: Label = %BusName
@onready var slider: VSlider = %Slider
@onready var spin_box: SpinBox = %SpinBox
@onready var audio_demo: AudioStreamPlayer = %AudioDemo
func _ready() -> void:
bus_name.text = AudioServer.get_bus_name(bus_idx)
audio_demo.bus = bus_name.text
_applied_value = _db_to_volume(AudioServer.get_bus_volume_db(bus_idx))
value = _applied_value
func _exit_tree() -> void:
# Revert volume to the value from the last time `apply` was called
_set_bus_volume(_applied_value)
func _db_to_volume(db: float) -> float:
return db_to_linear(db) * VOLUME_FACTOR
func _volume_to_db(volume: float) -> float:
return linear_to_db(volume / VOLUME_FACTOR)
func _on_component_value_changed(new_value: float) -> void:
value = new_value
func _set_bus_volume(new_value: float) -> void:
AudioServer.set_bus_volume_db(bus_idx, _volume_to_db(new_value))
func apply() -> void:
# Prevent value from being reverted during destructor.
_applied_value = value
func _on_slider_drag_ended(value_changed: bool) -> void:
if value_changed:
audio_demo.play()
func _on_spin_box_value_changed(_new_value: float) -> void:
audio_demo.play()
static func create(_bus_idx: int) -> VolumeSlider:
var instance: VolumeSlider = SCENE.instantiate()
instance.bus_idx = _bus_idx
return instance

View File

@ -0,0 +1,51 @@
[gd_scene load_steps=3 format=3 uid="uid://d2ya6ly01724l"]
[ext_resource type="Script" path="res://src/ui/menus/settings_menu/volume_slider/volume_slider.gd" id="1_rfe0k"]
[ext_resource type="AudioStream" uid="uid://cllhgumv1fkju" path="res://assets/sound/sfx/player/drive1.wav" id="2_822gu"]
[node name="VolumeSlider" type="VBoxContainer" groups=["VolumeSliders"]]
custom_minimum_size = Vector2(300, 0)
script = ExtResource("1_rfe0k")
[node name="NameContainer" type="HBoxContainer" parent="."]
layout_mode = 2
alignment = 1
[node name="Icon" type="Label" parent="NameContainer"]
layout_mode = 2
theme_type_variation = &"ChannelMixerIcon"
text = "🎧"
[node name="BusName" type="Label" parent="NameContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_vertical = 8
theme_type_variation = &"HeaderMedium"
text = "Channel"
[node name="Slider" type="VSlider" parent="."]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 1
size_flags_vertical = 3
step = 0.1
[node name="DigitalDisplay" type="HBoxContainer" parent="."]
layout_mode = 2
size_flags_horizontal = 4
[node name="SpinBox" type="SpinBox" parent="DigitalDisplay"]
unique_name_in_owner = true
layout_mode = 2
step = 0.1
alignment = 2
suffix = "%"
[node name="AudioDemo" type="AudioStreamPlayer" parent="."]
unique_name_in_owner = true
stream = ExtResource("2_822gu")
[connection signal="drag_ended" from="Slider" to="." method="_on_slider_drag_ended"]
[connection signal="value_changed" from="Slider" to="." method="_on_component_value_changed"]
[connection signal="value_changed" from="DigitalDisplay/SpinBox" to="." method="_on_component_value_changed"]
[connection signal="value_changed" from="DigitalDisplay/SpinBox" to="." method="_on_spin_box_value_changed"]