Old Title: Why do the Volume sliders reset after level transitions, but only visually?

When I change the Volume in One Level, the Sliders properly show the Volume of each bus (Left Image). But after I enter the next level, the Volume remains the same but the Sliders show Full Volume (Right Image).

How could I fix this visual disconnect? These are my Volume Changing Signals:

func _on_Sound_value_changed(value):
	AudioServer.set_bus_volume_db(
			AudioServer.get_bus_index("Sounds"),
			linear2db(value)
	)

func _on_Music_value_changed(value):
	AudioServer.set_bus_volume_db(
			AudioServer.get_bus_index("Music"),
			linear2db(value)
	)

And wheres the code handling the visible gui updating? Please show that too.

If you have none then you need to implement it. A function that is able set/update the sliders values.

Well, I've actually tried and failed that before. ??‍♀️

You probably need to set the sliders when the scene is shown. If you have the sliders and you know what bus to use, then the code to retrieve the value should look something like this:

func _ready():
	$SoundSlider.value = AudioServer.get_bus_volume_db(
		AudioServer.get_bus_index("Sounds")
	)
	$MusicSlider.value = AudioServer.get_bus_volume_db(
		AudioServer.get_bus_index("Music")
	)

Edit: Fixed formatting.

@TwistedTwigleg Thanks very much! Now I've gotten slightly further to fixing my problem. But it's not finished yet:

The Volume Sliders DO now show the accurate volume upon loading a new scene, but it's only because the Audio gets muted and the Sliders get reset to Minimum after every time a level gets loaded.

I still need the Volume to remain between Scene Loadings. But for that, I'll make a new Discussion if you aren't too fast to answer.

@Sosasees said: @TwistedTwigleg Thanks very much! Now I've gotten slightly further to fixing my problem. But it's not finished yet:

The Volume Sliders DO now show the accurate volume upon loading a new scene, but it's only because the Audio gets muted and the Sliders get reset to Minimum after every time a level gets loaded.

I still need the Volume to remain between Scene Loadings. But for that, I'll make a new Discussion if you aren't too fast to answer.

Sounds like something is changing the volume to the minimum. What does the script for loading/changing scenes look like? Also, are you setting the audio level anywhere else? Once the AudioServer has an audio bus volume set, it should retain it unless it is overridden.

Also: while I understand what you are saying, please don't make the speed of my reply decide whether you make a new discussion or not. Having the discussion here or making a new one is fine, though given it is related to this issue I think this discussion may be better, but regardless please do not make my hastiness or lack thereof be the deciding factor. Thanks :smile:

@TwistedTwigleg said: Sounds like something is changing the volume to the minimum. What does the script for loading/changing scenes look like? Also, are you setting the audio level anywhere else? Once the AudioServer has an audio bus volume set, it should retain it unless it is overridden. […]

As far as I'm aware, nothing is overriding the AudioBus Volumes. Before I followed the answer: the Sliders showed the Maximum Volume after each Loading, reguardless of the actual Volume. But the Volumes stayed the same between loadings. Only after I applied the answer, the Audio gets silenced after each Loading.

The're's no One Script for Loading a level.

This is how the Player Script resets the level:

[...]
			get_tree().reload_current_scene()
[...]

This is how the Exit Script loads the Next Level:

[...]
export (String, FILE, "*.tscn") var destination #The Next Level
[...}
			get_tree().change_scene(destination)

@TwistedTwigleg said: […] Also: while I understand what you are saying, please don't make the speed of my reply decide whether you make a new discussion or not. Having the discussion here or making a new one is fine, though given it is related to this issue I think this discussion may be better, but regardless please do not make my hastiness or lack thereof be the deciding factor. Thanks :smile:

This sounds like really good forum advice. I wanted to make it a new discussion if the reply didn't came already, because I was not sure if it was a good practice to ask related but different questions in the comments of the same question. Especially since the forum has a linear comment flow as suppose to starting mini-threads by replying to individual comments.

Hmm, so it doesn't seem to be caused by the scene changing. In the _ready function, maybe try adding the following print statement and code:

func _ready():
	var sounds_volume = AudioServer.get_bus_volume_db(AudioServer.get_bus_index("Sounds"))
	var music_volume = 	AudioServer.get_bus_volume_db(AudioServer.get_bus_index("Music"))
	
	print ("Sound volume is: ", sounds_volume)
	print ("Music volume is: ", music_volume)
	
	$SoundSlider.value = sounds_volume
	$MusicSlider.value = music_volume

Then you can see if it is the slider value assignment causing the issue, or whether the value being retrieved by the audio server is causing the issue. That should, hopefully, help pin down the issue.

@TwistedTwigleg said: […] Then you can see if it is the slider value assignment causing the issue, or whether the value being retrieved by the audio server is causing the issue. That should, hopefully, help pin down the issue.

I basically already had this code: The only difference is, that the _ready(): print() was missing.

extends VBoxContainer

func _ready():
	print(AudioServer.get_bus_volume_db(AudioServer.get_bus_index("Sounds")),
	", ", AudioServer.get_bus_volume_db(AudioServer.get_bus_index("Music"))
	)

func _on_Sound_value_changed(value):
	AudioServer.set_bus_volume_db(
			AudioServer.get_bus_index("Sounds"),
			linear2db(value) )

func _on_Sounds_ready():
	$Sounds/Slider.value = AudioServer.get_bus_volume_db(
		AudioServer.get_bus_index("Sounds") )
	pass


func _on_Music_value_changed(value):
	AudioServer.set_bus_volume_db(
			AudioServer.get_bus_index("Music"),
			linear2db(value) )

func _on_Music_ready():
	$Music/Slider.value = AudioServer.get_bus_volume_db(
		AudioServer.get_bus_index("Music") )
	pass

After every Loading, the Console displays One more line of -inf, -inf, which corresponds to the Volumes muting to -Infinity after every Loading.

Maybe remove the use of linear2db when setting the volume in the AudioServer and see if that fixes it? From what I can tell, linear2db is for converting a normalized float into a db range, as seen in this Godot engine blog post (which granted, is on the older side).

@TwistedTwigleg If I remove linear2db, the Volume is permanantly set to Full Volume

Now I wanted to try saving and retrieving the Volume Slider Values to a ConfigFile, but I failed to understand how it works.

@Sosasees said: @TwistedTwigleg If I remove linear2db, the Volume is permanantly set to Full Volume

Hmm, at least we know that is not the issue.

Now I wanted to try saving and retrieving the Volume Slider Values to a ConfigFile, but I failed to understand how it works.

The ConfigFile is really interesting and powerful, but unfortunately there is really not a lot of material covering it. That said, something like this should work for saving and loading the audio:

extends VBoxContainer

const AUDIO_FILEPATH = "user://audio_settings.cfg"
var config_file

func _ready():
	config_file = ConfigFile.new()
	var error = config_file.load(AUDIO_FILEPATH)
	if err != OK:
		print ("Audio configuration file not found!")
	else:
		print ("Audio configuration file loaded")

func _on_Sound_value_changed(value):
	AudioServer.set_bus_volume_db(
		AudioServer.get_bus_index("Sounds"),
		linear2db(value) )
	# Save the setting and file
	config_file.set_value("audio", "sound_volume", linear2db(value))
	config_file.save(AUDIO_FILEPATH)
	

func _on_Sounds_ready():
	if (config_file.has_section_key("audio", "sound_volume"):
		var volume = config_file.get_value("audio", "sound_volume")
		$Sounds/Slider.value  = volume
	else:
		$Sounds/Slider.value = AudioServer.get_bus_volume_db(
			AudioServer.get_bus_index("Sounds") )

func _on_Music_value_changed(value):
	AudioServer.set_bus_volume_db(
		AudioServer.get_bus_index("Music"),
		linear2db(value) )
	# Save the setting and file
	config_file.set_value("audio", "music_volume", linear2db(value))
	config_file.save(AUDIO_FILEPATH)

func _on_Music_ready():
	if (config_file.has_section_key("audio", "music_volume"):
		var volume = config_file.get_value("audio", "music_volume")
		$Sounds/Slider.value  = volume
	else:
		$Music/Slider.value = AudioServer.get_bus_volume_db(
			AudioServer.get_bus_index("Music") )
	pass

Ideally you'd only save changes once you are about to change scenes, but the code above should at least give an idea about how to go about using a ConfigFile.

ConfigFile.get_value() accepts an optional 3rd default parameter which will be used if the value can't be found in the file. You can use this to avoid littering your code with if ConfigFile.has_section_key(...) calls.

@TwistedTwigleg said: […] The ConfigFile is really interesting and powerful, but unfortunately there is really not a lot of material covering it. That said, something like this should work for saving and loading the audio:

	extends VBoxContainer
	[...]
		pass

This is way too much at once! Could I please get the changes explained step-by-step?

Could I please get the changes explained step-by-step?

Sure, though I'm just going to explain each chunk instead of step-by-step, as it will be faster and easier to write, and it shouldn't impact the explanation any:

extends VBoxContainer

const AUDIO_FILEPATH = "user://audio_settings.cfg"
var config_file

AUDIO_FILEPATH is the filepath we are going to save the config file at. The user:// directory is a special Godot directory that maps to a location on the file system that can be read and written to by Godot. There's a page on the documentation if you are curious about where this file goes, which I can link to later if desired. config_file is just a class variable that we'll be using to hold the reference to the ConfigFile, nothing fancy.

func _ready():
	config_file = ConfigFile.new()
	var error = config_file.load(AUDIO_FILEPATH)
	if error != OK:
		print ("Audio configuration file not found!")
	else:
		print ("Audio configuration file loaded")

First, this function makes a new ConfigFile and assigns it to the config_file variable. Then, we try to load the file at AUDIO_FILEPATH, which is a location where the file will be stored. We assign the returned value of the load function to a variable called error. Then we check to see if error is NOT equal to OK, and this will occur if the file is not found, meaning no configuration has been saved yet. If error is equal to OK, then that means we have successfully loaded the config file.


func _on_Sound_value_changed(value):
	AudioServer.set_bus_volume_db(
		AudioServer.get_bus_index("Sounds"),
		linear2db(value) )
	# Save the setting and file
	config_file.set_value("audio", "sound_volume", linear2db(value))
	config_file.save(AUDIO_FILEPATH)

Nothing fancy here until the bit after the comment. What we are doing here is storing the new sound volume in the config file, in the section audio with the key sound_volume. This means we can later access this value using the same section and key combo. Finally, we tell the config file to save itself so when we need to load it later, we have up-to-date values.

func _on_Sounds_ready():
	if (config_file.has_section_key("audio", "sound_volume"):
		var volume = config_file.get_value("audio", "sound_volume")
		$Sounds/Slider.value  = volume
	else:
		$Sounds/Slider.value = AudioServer.get_bus_volume_db(
			AudioServer.get_bus_index("Sounds") )
	
	# New version based on Calinou's suggestion (remove version above if using new version)
	# var volume = config_file.get_value("audio", "sound_volume", AudioServer.get_bus_volume_db(AudioServer.get_bus_index("Sounds")))
	# $Sounds/Slider.value  = volume

First, we check to see if there is a value stored in the section audio with the key sound_volume. If there is, then we get that value from config_file using the get_value function and assign it to a function variable called volume. We then assign the value of the slider to this value. If there is NOT a value stored in the section audio with the key sound_volume, then we simply get the volume from the AudioServer like normal.

However, as @Calinou mentioned, we can simplify this code by taking advantage of the get_value function. The third, optional, argument in the get_value function will be returned if the key does not exist in the config_file. So, we can make this third argument the AudioServer's sound instead, allowing us to use just two lines of code. If the value exists in the config_file, then the new version will return it, while if it does not, the new version will simply return the third argument instead.

func _on_Music_value_changed(value):
	AudioServer.set_bus_volume_db(
		AudioServer.get_bus_index("Music"),
		linear2db(value) )
	# Save the setting and file
	config_file.set_value("audio", "music_volume", linear2db(value))
	config_file.save(AUDIO_FILEPATH)

Nothing fancy here until the bit after the comment, and its the same as the _on_Sound_value_changed function, just for music.


func _on_Music_ready():
	if (config_file.has_section_key("audio", "music_volume"):
		var volume = config_file.get_value("audio", "music_volume")
		$Music/Slider.value  = volume
	else:
		$Music/Slider.value = AudioServer.get_bus_volume_db(
			AudioServer.get_bus_index("Music") )
	pass
	
	# New version based on Calinou's suggestion (remove version above if using new version)
	# var volume = config_file.get_value("audio", "music_volume", AudioServer.get_bus_volume_db(AudioServer.get_bus_index("Music")))
	# $Music/Slider.value  = volume

Same thing here: It's the same code as _on_Sound_ready, it's just for music instead of sound. As I mentioned in the note above, you can simplify the code.


Hopefully that helps explain things a bit!

@TwistedTwigleg @Calinou In both instances of the line if (config_file.has_section_key( [...] )):, I get an error message upon running the game:

Invalid call. Nonexistent function 'has_section_key' in base 'Nil'.

The error message is saying that the config_file variable has not been assigned to anything, that it's nill. Did you include the code in the _ready function?

@TwistegTwigleg Yes, this is the _ready() code:

func _ready():
	configFile = ConfigFile.new()
	var error = configFile.load(configPath)
	if error != OK:
		print ("Audio configuration file not found!")
	else:
		print ("Audio configuration file loaded")