Audio pops and crackles with new audio mixer

We are noticing loud pops on our audio on many different sounds with the new audio mixer in our code project. Our code project is built off 4.16.3.

I can reproduce this in 4.17.1 in a straight blueprint project as well, using the same audio files and only once I have turned the EnableAudioMixer to true

The easiest way to hear these pops is to actually alt tab from the game/editor and you can hear the sounds making small pop sounds, even though all the audio should be at 0 volume now.

On some hardware/speakers it seems this pop is louder and thus a real problem for us. It is not feasable for us to now go back to the old mixer and we are looking to enter early access in the next few weeks.

Has there been any known cases of this bug and possible fixes previously.

I have a reproducible small project file here (4.17.1)

And here is a video showing the bug

Any assistance or guidance on this issues would be greatly appreciated.

Hi there,

Apologies for the delay, I’m crunching on audio engine work for 4.18, a lot of new stuff is coming out.

The issue your describing was recently discovered internally on our Unreal Tournament (which is using the audio mixer). The issue is due to uninitialized audio buffers getting sent out to the device due to the CPU being put into a lower-pri mode while the app is in the background. The fix is to make sure all audio buffers are zero’d up-front. I originally didn’t zero them as an optimization. Changing the audio buffers to zero-init before rendering fixed the issue on our end.

As a fix, look into making sure source buffers are zero’d in AudioMixerSourceManager.cpp before rendering. Look for “AddUninitialized” to various source buffer TArrays and change those to “AddZeroed”. Should do the trick.

Let me know if it doesn’t.

I can hear the same crackling at 0 volume in 4.16.3 but with the old audio mixer.
I also tried enabling the new audio mixer with and without your proposed fix applied but it didn’t make a difference.

I could only find one case of the source buffer being AddUnititialized in FMixerSourceManager::ComputeSourceBuffersForIdRange (bare in mind again I am running on 4.16.3).

Changing it to AddZeroed didnt solve the issue unfortunately. I saw two other AddUnititialized in the audio mixer folder source code and changed them too just in case but still didn’t fix our issue.

I checked the unreal tournament and unreal engine versions of the AudioMixerSourceManager.cpp to see if I could work out what had been done but they still had AddUnitialized.

Hi Ben,

I was able to repro a similar issue in Unreal Tournament (which uses the audio mixer) and I believe the issue is some thread timing between updating audio volume values and rendering audio sources. I have a complex change that is ensuring that all params are enqueued and dequeued at the same time, but a simpler fix is to actually query the FApp::GetVolumeMultiplier() value directly and simply applied an attenuation to the output. I refactored how the master volume attenuation works in the audio mixer for 4.18 so it’s a bit trickier to suggest a 4.17 fix.

The place to apply this patch is probably easiest in AudioMixer.cpp, in the function, IAudioMixerPlatformInterface::PerformFades(). This is the function where I smoothly fade in and fade out the audio mixer to avoid popping when opening or closing PIE or an application.

Here, what I would do is simply scale the Buffer there with the results of FApp::GetVolumeMultiplier() if it’s less than 1.0 and if it’s not in editor (for editor mode, FApp::GetVolumeMultiplier() is used to change volume of sounds in realtime-preview so if you didn’t check for this you’d break PIE editor audio).

Basically what this does is grab the current buffer, and scale it by the app volume. It’s likely going to be 0.0. This essentially ensures ALL audio is muted if the app is not in focus, assuming you are muting the app audio the way most UE4 games mute audio (i.e. resulting the FApp::GetVolumeMultiplier() returnign 0.0).

So I tried that and it seemed to indeed fix that case which is excellent. Thank you for that.

So now we are just left with the case where the AppVolume isn’t 0 but the sound class volume has been lowered (via the users options to turn down say the SFX). In this case you can hear the pops as clearly as before without any reduction to their volume.

I have uploaded a new video so you can see what I mean. In it the master volume is near 0, and you can hear the pops, then i set it back to an about 0.6 where the sound seems to mostly cover them and back low to hear them once more. This in our actual build, which is 4.16.3, but with the fix above applied (which stops all pops when no focus).

Any ideas how we can fix those?

Yep, not surprised.

So this is the root issue – something about setting individual volumes of sources to 0.0 is causing the problem. By muting the entire application output it bypasses the problem but doesn’t quite fix it.

Like I said, I also think it may be a timing issue.

The following changes seems to mitigate the issue UT, but not entirely. I can still barely hear tiny pops, but this might be worth trying to see if it helps.

In AudioMixerSourceManager.h, change the SourceCommandQueue TQueue into the following:

And then in AudioMixerSourceManager.cpp, in the update function (which is called from audio thread).

Then in the AudioMixerThreadCommand function and the PimpCommandQueue function, do the following changes:

The theory of the change is that there are some corner cases where commands can be enqueued from the audio thread (which set params on playing sources) while the command queue is getting pumped, so there can be individual audio mixer render frames where sources don’t have the proper values set, which causes discontinuities. This change basically creates 2 different command queues which swap out with each other after the render thread has consumed the previous audio render thread command queue.

As I said, testing with this change (and not the master attenuation application), I can hear a reduction in the issue, but I’m still hearing very quiet pops. I’m about to dig deep in this and add a bunch of custom instrumentation to validate expected state when the application is muted by FApp::GetVolumeMultiplier().

Alright - I tracked down the culprit!

It’s due to an optimization I checked in for 4.16 for parameter updates. The problem is that the parameter interpolators were getting previous lifetime values and lerping from THOSE values to target values rather than properly initializing at the target value if it was the first value set. So when muting, it was lerping from non-zero values to zero values in one audio callback buffer.

The fix is super simple.

In AudioMixerSourceManager.h, in the FSourceParam class, add the following changes:

Then in AudioMixerSourceManager.cpp, switch these functions from Reset() to Init() inside FMixerSourceManager::ReleaseSource():

212813-source-param2.png

This will make sure when a source begins to play, the param interpolators will instantly set their values to the target value vs lerping to the target value.

This is a great bug find and I’m going to try and get this into the next 4.17.2 hotfix as this is going to cause issues with source param lerping in general (will cause unintended pitch-shifting, LPF lerping, etc).

Thank you so much for bringing it to my attention and I apologize for the inconvenience!

Great to hear! Looking at the same code in our 4.16, I haven’t been able to successfully apply the same fix/

4.16 has:

FSourceParam::FSourceParam(float InNumInterpFrames)
		: StartValue(0.0f)
		, EndValue(0.0f)
		, CurrentValue(0.0f)
		, NumInterpFrames(InNumInterpFrames)
		, NumInterpFramesInverse(0.0f)
		, Frame(0.0f)
		, bIsInit(true)
		, bIsDone(false)
	{
		NumInterpFramesInverse = (NumInterpFrames == 0.0f ? 0.0f : (1.0f / NumInterpFrames));
	}

	void FSourceParam::Reset()
	{
		bIsInit = true;
	}

	void FSourceParam::SetValue(float InValue)
	{
		if (bIsInit || NumInterpFrames == 0.0f)
		{
			bIsInit = false;
			CurrentValue = InValue;
			StartValue = InValue;
			EndValue = InValue;
			Frame = NumInterpFrames;
			bIsDone = true;
		}
		else
		{
			StartValue = CurrentValue;
			EndValue = InValue;
			Frame = 0.0f;
			bIsDone = false;
		}
	}

	float FSourceParam::Update()
	{
		if (!bIsDone)
		{
			float Alpha = Frame * NumInterpFramesInverse;
			if (Alpha >= 1.0f)
			{
				bIsDone = true;
				CurrentValue = EndValue;
			}
			else
			{
				CurrentValue = FMath::Lerp(StartValue, EndValue, Alpha);
			}

			Frame += 1.0f;
		}

		return CurrentValue;
	}

	float FSourceParam::GetValue() const
	{
		return CurrentValue;
	}

The original 4.16 code seems more closer to what you changed it to, as it sets bIsDone to true if it is an init and thus doesn’t do the lerp. I did try and match it more to 4.17 but it didnt seem to fix the issue at all. I imagine it might be that something else in 4.17 helps fix this issue. I need look at getting 4.17 merged, but that is a few day process for us and as we launching next monday on early access, I wont have time to do that just yet. I will let you know how I go there.

Ok We have now updated to 4.17 for our game itself (4.17.2) which does contain the code you have in your last answer. But I still get pops in my audio.

I also added the changes you recommend to the source manager in those 3 images without any luck. Here is a video of the game in action in 4.17.2 with the additional source manager changes you suggested.

Any ideas of what this could be? Currently I have been forced to disable the ability to change the volume in game for our players.

I adjusted the test case in my 4.17.2 from Epic Launcher, and again could hear the pops (after a few loops). Which is good because at least it is being consistent. I then tried 4.18 with the same project, and this time there were no pops. So something in 4.18 might very well of fixed it.

Of course updating to 4.18 for us is not an easy process with multiple platforms and plugins to consider, as well as being careful to fit within our release schedule with our small team. So I wont have a definite answer on if 4.18 does fix it for us for probably another month or two. But does that give you an idea of what might be the fix or particular change we need?

I definitely know the issue is due to “slicing” on parameters between source instance lifetimes (sources are pooled). Conceptually, a given source has a bit of the previous lifetimes value and it “lerps” to the new value rather than instantly setting the parameter to it’s initial value. This causes pops for a bit after muting the audio as sources before the mute were non-zero and are lerping to muting.

It’s hard for me to know if you’ve got the fix properly integrated but that’s definitely the bug and it’s definitely fixed for 4.18.

I’d run the audio mixer with optimization disabled (Set OptimizeCode = CodeOptimization.Never; in AudioMixer.build.cs), then set some breakpoints (or add debug logging) to the code which is setting initial volume parameters, see what’s going on. Make sure that the parameter lerping object gets its initial value set immediately to the value.

Hi there, we are also hearing the crackling in 4.18 and 4.18.1.

The above code for FSourceParam doesn’t exist at all and it looks like the code in AudioMixerSourceManager has been streamlined.

Is this currently being investigated further? Is there currently a workaround?

Alright, I’ll see if I can look into ASAP. Thanks.

We are now upgrading to 4.18.1 and for us at least all of the pops and crackles that we ran into have now gone. We can set the main volume to 0 and no sounds play, no corruption occurs in the audio, so thank you heaps Minus_Kelvin for all of that.

Sorry that isn’t of much help ZeJudge, the code above was for 4.17, which as you have seen has changed a lot for 4.18. Are you getting the sounds like in the videos I posted?

Hey! I know this is quite old thread already, but I’m using Unreal 4 version 4.23.1 and I’m having this same issue. When audio volumes are 0 or really close to 0, I’m hearing some crack and pop sounds mostly from UI elements like hovering buttons or clicking. Those older solutions won’t work because all those classes mentioned have changed so much. What can I do?