Cannot get sound to play through level loads

I’m trying to play a sound that persists through a level load, but the sound is always terminated. I am not using level streaming. Instead, my GameInstance BP is using the OpenLevel node to switch levels.

Specifically, when the game starts up, Map “Select” is loaded. The player makes some selections, and when ready, selects a menu item to start the game. The GameInstance BP starts playing a sound that I want to persist well into the next map. GameInstance then uses OpenLevel to load the “Game” map. This is when the sound cuts out.

I’m using 4.12.5. My sound is a member of the Music sound group, which is flagged with bAlwaysDecompressOnLoad=true in BaseEngine.ini. I’ve also tried making the sound a member of the UI sound group. And I’ve tried checking and unchecking the “Is UISound” checkbox in the sound class. I’ve also tried playing the sound via PlaySoundAtLocation, PlaySound2D and SpawnSound2D. In all cases, the sound is terminated when OpenLevel is used.

I feel there must be a solution to this - I can’t be the only developer who needs to play music/sound across level loads. But I just can’t seem to find a solution.

You are correct that sounds tied to a level are stopped when that sound ends. You can carry a sound across levels using the bIgnoreForFlushing boolean on FActiveSound/UAudioComponent. The boolean is not exposed to any of the Blueprint play APIs, you need to set it up in native at this time. Keep in mind that if you are using a component for this (which I would probably suggest) it should not be owned by an Actor as otherwise the component will be destroyed as well.

Here is a snippet that shows how one of our internal games creates its cross level music component:

	// Note this audio component is not attached to a world because it needs to persist between some level transitions
	WorldMusicComponent = FAudioDevice::CreateComponent(WorldMusicCueObject, nullptr, nullptr, false, false);
	if ( WorldMusicComponent )
	{
		WorldMusicComponent->bAllowSpatialization = false;
		WorldMusicComponent->bIsUISound = true;
		WorldMusicComponent->bIgnoreForFlushing = true;
	}

bIsUISound is a poorly named variable (from the UE3 days) that indicates that the sound should play while the game is in a paused state

Thanks for the help, Marc. I’ve coded this up, but am having problems. Referencing your code snippet, if I pass in nullptr for the UWorld parameter to FAudioDevice::CreateComponent(), and then PIE, no sound is heard until I press the editor’s Stop button to stop the PIE session. At that point, the sound starts playing.

If I pass in a valid UWorld, the sound plays during PIE just fine. But of course, it stops upon OpenLevel.

Not sure if it matters, but I wrote this function as part of a C++ BP Function Library.

Yeah, that makes sense as when World is null it will be picking the main audio device, rather than the one for the PIE world. This is irrelevant in a standalone game as there aren’t multiple audio devices floating around.

I’m a bit surprised that the sound is stopping on level transition if bIgnoreForFlushing is set to true when you do specify a World. I would suggest taking a look a FAudioDevice::Flush and see if you can identify why the bIgnoreForFlushing is not being respected.

Hey Marc,

I tried a standalone game as well as a packaged game, and the null-world version works in both cases, which is great. However, playing the game from the editor window is still troublesome because the sound doesn’t play until I Stop the game, and then it starts playing and plays forever while I’m editing my project. I need to exit and re-launch the editor to get the sound to stop.

Can you think of a way where this just always works, regardless of which way I run the game?

Regarding FAudioDevice::Flush and bIgnoreForFlushing, I confirmed that bIgnoreForFlushing is true from within Flush, but the sound still terminates if a world is specified. In fact, even when I skip the entire Flush function, the sound is still stopped. I used stat sounds and see “sound waves: 1” change to “sound waves: 0” when the next level is loaded.

To get it work in PIE today, we’d need to add the AudioDevice to use as an optional parameter to the CreateComponent function and then instead of specifying the World, pass in its AudioDevice and use that rather than picking one based on World/nullptr using the Main AudioDevice (which is what is assigning it to the editor level rather than PIE device). Since there is already a mess of optional parameters I’d probably change to a new function that takes a parameter block to clean up all the optional junk.

That said, I’m really confused as to why the sound is getting picked up during the flush. I’ll try to set aside some time to see if I can repro and track it down, but if you wanted to get ahead of that you could throw a breakpoint in FActiveSound::Stop and see what path is picking it up.

Hey Marc, couple of updates:


With your help, I was able to get things working by doing the following:

  1. Added an optional parameter to FAudioDevice::CreateComponent called AudioDeviceOverride, which defaults to nullptr.

  2. Added a new data member to UAudioComponent, called AudioDeviceOverride, and set it to nullptr in the constructor.

  3. In the block of text at the top of FAudioDevice::CreateComponent where AudioDevice is set, I first check for whether AudioDeviceOverride is null. If so, it uses the current code path in setting AudioDevice. Otherwise, AudioDevice is set to AudioDeviceOverride.

  4. Also in FAudioDevice::CreateComponent, I set AudioComponent->AudioDeviceOveride to the AudioDeviceOverride parameter.

  5. In UAudioComponent::GetAudioDevice(), I check whether AudioDeviceOverride is null. If so, I use the current code path in setting AudioDevice. Otherwise, AudioDevice is set to AudioDeviceOverride.

  6. Finally, this is how I obtain the audio device to use, when creating the component:

Aside from my not checking for a null ThisWorld yet, is this the right way of getting the audio device? I did test it in PIE, VR Preview, New Editor Window, Standalone Game, and in a Shipping build with and without VR, and it all seems to work in my simple test case. I’m not quite sure how the WorldContextObject parameter is being filled out when I call this function from a BP, but it apparently is.


From a BP point of view, it would be useful if the SpawnSound2D node offered a “Play across level loads” checkbox that worked well in and out of the editor. It would still have to return an Audio Component (which could be saved to GameInstance) so that the sound could be stopped or faded out once the new level’s been loaded.


As for the sound stopping when a world is provided, I put a breakpoint in FActiveSound::Stop and it hits because FAudioDevice::AddSoundToStop had been called previously. Here’s the call stack for when AddSoundToStop hits:

I’m not super familiar with the engine code yet - am I obtaining the world pointer correctly?

Here’s how I’m calling that C++ function from within the level BP:

That callstack makes sense now. Basically the component created for the cross-world music is not referenced by anything. Probably something like your GameInstance needs to be keeping a reference to this component.

Getting the audio device looks right.

I will put in a feature request for making the creation of a cross-level friendly sound easier.

I will put in a feature request for making the creation of a cross-level friendly sound easier.

Thanks! Should be helpful to anyone wanting to play a sound across a level load :slight_smile:

As for my solution, I mentioned that I’m storing a pointer to the audio device in UAudioComponent. Is there any chance that the pointer will become stale, meaning the audio device that I’m pointing to is destroyed and released from memory at run-time after I’ve stored a pointer to it?

I wouldn’t expect that to be unsafe. The only time an audio device will be torn down is when a PIE session is finished. The component should be going away as part of that same cleanup, so I can’t think how you’d end up in an unsafe situation.