Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Audio stops working after scene reload. #28110

Closed
ca3games opened this issue Apr 17, 2019 · 28 comments · Fixed by #46202
Closed

Audio stops working after scene reload. #28110

ca3games opened this issue Apr 17, 2019 · 28 comments · Fixed by #46202

Comments

@ca3games
Copy link

Hi, for some reason in my project when I reload an scene, the audio stops working:

https://www.youtube.com/watch?v=5W4jNN3zqdI
Here's an example of the bug.

Not sure why it happens and if there's a fault of my code, since when I reset the game, the sound keeps working.

Wooble Booble.tar.gz
I add my project files so you can test the project.

@JFonS
Copy link
Contributor

JFonS commented Apr 17, 2019

Which Godot version are you using?

@JFonS JFonS added this to the 3.2 milestone Apr 17, 2019
@ca3games
Copy link
Author

Which Godot version are you using?

3.1 stable

@BridgetNichols
Copy link

I think I may have figured out what you did wrong. It's not an issue with the editor, but when you change scenes you have start the audio again. See here:

func ChangeScene():
	get_tree().change_scene("res://Scenes/Level.tscn")
	$Select.play()

That works for me, but if it doesn't work for you then tell me, and I can keep looking for solutions. In the future, it might also be a good idea to post bugs in your code on Godot's questions and answers forum as it is meant for these sort of questions. If their really is a bug in the editor, and you post to this forum you'll find out soon enough and you can open an issue here.

Hope this helps!

@ca3games
Copy link
Author

I think I may have figured out what you did wrong. It's not an issue with the editor, but when you change scenes you have start the audio again. See here:

func ChangeScene():
	get_tree().change_scene("res://Scenes/Level.tscn")
	$Select.play()

That works for me, but if it doesn't work for you then tell me, and I can keep looking for solutions. In the future, it might also be a good idea to post bugs in your code on Godot's questions and answers forum as it is meant for these sort of questions. If their really is a bug in the editor, and you post to this forum you'll find out soon enough and you can open an issue here.

Hope this helps!

yeah, I was guessing my issue lied because I stopped the audio when reloading the scene.
Thanks, I'll take into account your advice.

BTW How do I restart the audio engine?

@BridgetNichols
Copy link

All you have to do is add $Select.play() to your ChangeScene() function and it'll restart the audio

@ca3games
Copy link
Author

All you have to do is add $Select.play() to your ChangeScene() function and it'll restart the audio

Thanks, I guess I just need to restart the audio engine.
Closing my issue I guess.

@ca3games ca3games reopened this Apr 17, 2019
@ca3games
Copy link
Author

All you have to do is add $Select.play() to your ChangeScene() function and it'll restart the audio

For some reason it doesn't work, I'm sure my issues lies that the audio engine somehow get's paused when I paused the game at the end of the level transition.

@ca3games
Copy link
Author

I think I've found it.

func StartGame():
	$Select.play()
	#yield($Select, "finished")
	score = 0
	get_tree().change_scene("res://Scenes/Level.tscn")
	scoretween = find_node("Root/GUI/ScoreTween")
	scorelabel = find_node("Root/GUI/Score")

Somehow a node containing the sound and a yield condition to wait until the sound finishes before changing scenes, just may switch the scene and hung the audio engine somehow.

I believe this is what happens, maybe another dev could test my idea and see if is a real engine bug, like switching scenes with a yield condition make the audio to hung.

@ca3games
Copy link
Author

Ok, so I found the issue, I set the AudioStreamPlayer of the sounds to Pause mode Process and they worked fine, seems like when pausing a game tree and swapping scenes somehow makes the audio to hung.

At least that's what I'm able to understand what's happening.
Would be nice to see a real dev opinion.

@KoBeWi
Copy link
Member

KoBeWi commented Jul 6, 2019

Would be nice to see a real dev opinion.

Real dev here ( ͡° ͜ʖ ͡°)

This is a bug. I haven't managed to pinpoint it, but the direct cause seems to be that you are pausing the game inside level's ready. If you comment this line, the issue doesn't happen.

But what's actually weird, it randomly works. When I run the game, it almost always hangs all the audio after pressing start (and by hangs audio, I mean completely no sound, nothing). However, it seems to work 1/20 of the times and also it's always ok when I double-press start. So yeah, this shouldn't happen.

@ruki3
Copy link

ruki3 commented Nov 6, 2019

This still keeps happening, but in my case changing the scene actually removes all audio nodes from the scene.

@akien-mga akien-mga removed this from the 3.2 milestone Jan 15, 2020
@KoBeWi
Copy link
Member

KoBeWi commented Dec 2, 2020

Still valid in 3.2.4 beta3

@jjmontesl
Copy link

jjmontesl commented Jan 7, 2021

Hello. Any news on this? I'm impacted by a similar behavior.

In my case I'm pretty sure it's not related to Pause. It happens both on Linux and Windows. For some users it happens 90% of the time, for others just 5% of the time. I am using Tweening and modifying the master bus from a function called from Tween.

Sometimes I can hear a split second of the audio buffer when going back to my menu. Some other times the audio is restored after unloading and loading several nodes. Difficullt to troubleshoot with so many things going on but I'm trying to research this. Any guidance or hint is very welcome.

@Calinou
Copy link
Member

Calinou commented Jan 7, 2021

@jjmontesl Does it work better if you remove the Tween for audio and use another way to change the volume (such as AnimationPlayer or setting it directly via code)?

You can check how I fade the music in https://github.com/Calinou/escape-space.

@jjmontesl
Copy link

jjmontesl commented Jan 10, 2021

Hello. Apparently no, my tweening is not involved. I am trying to research this as thoroughly as I can.

So I am observing something in line with the bug description:

  1. sound has stopped after some node swapping heavy lifting (but will work again if I repeat this reload process again)
  2. I pause the SceneTree. Nodes that are marked as "pause_mode = process" start playing.
  3. I unpause. All nodes stop playing.

Audio nodes that are "pause_mode = inherit" and were part of the tree at the moment the "issue" happens stop working. But wil work if I use the Remote editor to set their "pause mode = process" and play them (as long as the SceneTree is paused). So the behavior is really astonishing: nodes in "inherit" mode don't work at all, and nodes in "process" mode work only when the scene is paused.

I am experiencing this on Windows and on Linux with both ALSA and PulseAudio drivers.

This happens with at least AudioStreamPlayer and AudioStreamPlayer2D. OGG and WAV. Looping and non looping streams.

Also, I suspect race conditions. I can reproduce this every 1 in 20 tries, but some for some of my users appears to happen up to 50% of the time.

If I do again some more heavy lifting with nodes, the "lock" disappears and all sound works as expected. A few reloads later it is triggered again. (I speak of "node heavy lifting" because I don't use ChangeScene, but remove and add a lot of nodes to the tree, but this seems enough to trigger this issue).

I have observed that grabbing screenshots also triggers this issue (again, randomly). A small lag is appreciated whenever I take a screenshot, but sometimes this provokes this bug and audio stops working (again, except nodes that keep running once I pause the scene tree).

I have been adding logging to source code and looking into audio system pause/unpause, callbacks setup, mixing, locks, streams and related stuff but I'm a newcomer to this codebase and I'm coming to a stall (also, I'm not really sure what the best approach for debugging this would be, can I attach a debugger to the running Godot game and somewhjat debug the audio thread?). I guess I'll add some counters (logging mixing information its too much).

I have checked both from C# and through logging statements in Godot source that, when this happens, the AudioStreamPlayers involved are indeed "playing" and not "stream_paused". Their playback position keeps moving as expected. The bus channels involved are active, and they go inactive if sound would not be being sent to them, as expected (though I cannot hear it in any case of course). The AudioStreamPlayback objects are active as expected. In other words, I can't find anything unexpected in the objects internal state :/.

I have also tried blindly removing all fade in/out operations from Godot audio system, with no changes, so those seem not to be involved.

So at this point I haven't had much success. It could be anything: audio locking/threading issues, wrong buffer operations, wrong logic, wrong object references, race conditions, and of course my own code doing something weird (although at this point I think there's something on Godot side)...

All advice or insight is welcome, as I happen to be in a rush to resolve this. Cheers!

@jjmontesl
Copy link

I have found this reference: https://www.reddit.com/r/godot/comments/g7n71v/audio_limitations/

@jjmontesl
Copy link

jjmontesl commented Feb 7, 2021

For the record: I have read about other projects (published on Itch) where this is happening. I also have recently published a project which is impacted by this.

@jjmontesl
Copy link

jjmontesl commented Feb 7, 2021

Hello. I'm using the project uploaded to this Issue and I have added logging statements to Godot source and I have found the following:

Mixing into bus Song: /root/Root/Music Energy: 6.618641 TargetBuffer: True
Sending into bus Song -> Master: Energy: -nan TargetBuffer: True
Sending into bus Ball -> Master: Energy: 0 TargetBuffer: True

When the problem is triggered (still, not consistently), some of the buffers are being added NaN values.

@jjmontesl
Copy link

I have found that bus effects are involved, as they are driving one of the channels energy to -NaN:

Mixing into bus Song: /root/Root/Music Energy: 0 + 6.857935 Buffer: 8ca26b0 -> 67d6ec0
Effects Energy: 6.857935 -> -nan
Sending bus Song -> Master: Energy: 0 + -nan Buffer: 5238800 -> 523a880

@jjmontesl
Copy link

jjmontesl commented Feb 7, 2021

Some findings:

The problem is not triggered if no effects are used in any bus (or if they are bypassed -disabled-).

On the other hand, the following code, placed in audio_server.cpp, works around this issue, although when triggered it prevents audio effects from working:

		//process effects
		if (!bus->bypass) {
			for (int j = 0; j < bus->effects.size(); j++) {

				if (!bus->effects[j].enabled)
					continue;

				// Check if any of the processed effect samples are NaN, and if so skip the effect.
				float sanityCheck = 0.0f;
				for (int k = 0; k < bus->channels.size(); k++) {
					const AudioFrame *buf = bus->channels.write[k].buffer.ptr();
					for (uint32_t jj = 0; jj < buffer_size; jj++) {
						sanityCheck += (buf[jj].l + buf[jj].r);	
					}
				}
				if (sanityCheck != sanityCheck) {
					continue;
				}

We are still researching what is the root cause of the NaNs in the audio bus in the first place. Some questions are:

  • Why is this a race condition? Why does pausing the game before scene load cause the problem more often?
  • Are NaNs pumped by AudioStreamPlayer at some point, and then maintained and propagated by effects?
  • Are EffectInstances reset at some point? why does the problem get often resolved when reloading another scene if the effects are already poisoned?

@jjmontesl
Copy link

jjmontesl commented Feb 12, 2021

After some more research I have traced every place where audio is mixed between buffers. NaNs are pumped at several places, and once they appear in a buffer they quickly propagate (especially through audio filters and AudioStreamPlayer2D internal mixing).

I have managed to workaround this for Bus Effects and AudioStreamPlayer by checking every mix (this is expensive), but I still have troubles with AudioStreamPlayer2D (I haven't looked into 3D). My current tentative workaround code is here: jjmontesl@dea4a16 . It works almost but the 2D audio nodes sometimes trigger the fail condition and, in this case, my workaround is adding a lot of noise instead of blanking the channel (to me this may be a signal of buffer overlap or incorrect swap or buffer chunk list corruption within AudioStreamPlayer2D, in addition to or as part of the root cause of all this).

I still don't know the real root cause. I'm exploring:

  • thread_get_channel_mix_buffer() and related: I will try to add more debugging here, but I see places where these are called outside the audio thread
  • notifications (in particular, the "active", "stream_paused", "used", "fadein/out" flags might be being subject to race conditions, and these could cause mixing to wrong buffers)
  • buffer swap (I'm ignoring these for now, but might be involved)
  • set_stream() operations: I'm suspicious about these being involved in the race conditions, so I'm trying to understand these too
  • uninitialized buffers: I believe this is directly related to the root cause of the NaN, as my workaround detects corrupt buffers and clears them, and then the node works as expected from there on (until scene is reloaded or a new failure is triggered).

In my code I'm using free() at some points, and I pause during large node tree replacement operations. That helps causing the failure but not in a deterministic way. But I'm prettty sure that sometimes just playing a sound or approaching an Audio2D node will cause it.

I can also see that virtually all nodes introduce NaNs at some points, and buses always pump some NaNs into the mix at program startup, but for most people seems they often go unadverted. Filters will propagate the NaNs which is why using them seems to cause this more often, but they are not the root cause.

@jjmontesl
Copy link

jjmontesl commented Feb 12, 2021

Another note: at least some of the NaNs introduced by the AudioStreamPlayer2D were due to my Node2D having a Transform with NaN values. This was used to calculate distance fading which in turn filled audio buffers with NaNs. If somebody else is impacted by this you may want to check that your AudioStreamPlayer2D Transform values are correct (at all times).

This is not the only cause of the issue as the reproduction project (attached to this issue) does not have AudioStreamPlayer2D nodes.

This can be easily checked for in AudioStreamPlayer2D. I will send a PR with this fix (separately).

@jjmontesl
Copy link

jjmontesl commented Feb 12, 2021

Commit jjmontesl@dea4a16 is my tentative workaround. It solves all audio issues in my codebase and in the reproduction project above.

It possibly may still fail for AudioStreamPlayer3D nodes and other audio sources like video.

Imo it's not a suitable solution, however, as it is expensive and does not tackle the root cause of the problem. But if it were to be used, at a very minimum, some unnecesary checks in the workaround could be avoided.

@jjmontesl
Copy link

jjmontesl commented Feb 15, 2021

Possibly related: #22016 #25087 #30827 #23544 #40630 (if not strictly related, all should possibly be tackled jointly).

@ellenhp
Copy link
Contributor

ellenhp commented Feb 15, 2021

I've been unable to reproduce the original issue in that example project, but I did come up with a tweak to it that allows me to reproduce an audio pop very easily. To your point @jjmontesl I think there might be a few underlying issues that are manifesting in lots of unique and hard to reproduce ways. Do you have a minimal repro project that works in 4.0?

@jjmontesl
Copy link

jjmontesl commented Feb 16, 2021

Do you have a minimal repro project that works in 4.0?

No, I work in stable. I've been told there are no changes in the audio system for 4.0.

Also, that you cannot reproduce this issue with this project is not surprising. This issue seems not consistent. However, with the example project, I and iFire are able to reproduce it 50% of the runs. In my project (different codebase), the error happens only 1/20 or 1/40 (I need to load a scene a lot of times before it's triggered). It's also possible that you don't realize it's failing; there are severall sounds at play in that project: background music, ball bounces, enemy "crash"... sometimes you stop hearing some of them but not all. I'm using 3.2.3-stable.

I have been inspecting the audio system throroughly and I have some ideas about what to look for, which I stated above. I don't think there are "a few" underlying issues. I think we have mostly one problem with a race condition returning uninitialized buffers. There may be other issues but that's the main blocking one (imo).

@ellenhp
Copy link
Contributor

ellenhp commented Feb 16, 2021

I fixed one race condition in #46078 (it's very hacky right now, but I'll tidy it up soon). I'd consider making the same changes in 3.2 and seeing if that fixes your issue? It definitely helps with my pops and I suspect the pops are due to unintialized values as well.

@jjmontesl
Copy link

jjmontesl commented Feb 16, 2021

At a quick glance, I don't think your fix is usable as a final fix (same as my workaround). It may work but note that the p_buffer, if I understand correctly, may be shared (several streams may need to mix onto the same player buffer) or is otherwise provided externally, so it should have been already initialized at that point (I'm specifically looking at https://github.com/godotengine/godot/blob/master/servers/audio_server.cpp#L349, and how used/active flags are maintained, and related places).

But I agree, for what I've seen, that your fix will help with clicks and pops. Note that my workaround already fixes all my sound issues: what we need is to find why buffers are being used prior to their initialization (if such is the case). Why do I mean with this? that I think that we both are fixing "sympthoms", not "root causes".

I have downloaded your test project, I'll compile myself a 4.0 and test your changes and my changes on both projects.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants