Skip to content

Conversation

@SternXD
Copy link
Contributor

@SternXD SternXD commented Nov 21, 2025

Description of Changes

Fixes #12700

  • Added an audio capture volume slider
  • Made a new AudioCaptureVolume setting stored in GS config, defaulting to 80%.
  • Applied the volume multiplier when feeding samples to the FFmpeg encoder

Originally I was going to add libavfilter but I couldn't justify adding that library for one feature so that's why I did this the way I did.

Rationale behind Changes

Giving users a volume control makes built-in recording behave more like standard capture tools where mic/system levels can be balanced before hitting "Record."

Suggested Testing Steps

  • Start a capture with default settings, then inspect the resulting video to confirm the recorded audio is quieter than before.
  • Change the slider to e.g. 50% and 100%, capture again, and confirm the output volume scales accordingly for the video capture

Did you use AI to help find, test, or implement this issue or feature?

No.

@JordanTheToaster JordanTheToaster added this to the Release 2.6 milestone Nov 25, 2025
if (volume_scale != 1.0f)
{
float sample = static_cast<float>(src) * volume_scale;
if (sample > 32767.0f)
Copy link
Contributor

@TheTechnician27 TheTechnician27 Nov 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you wanted to, in theory, you could get away with:

for (u32 j = 0; j < this_batch; j++)
{
	const s16 scaled_input = volume_scale == 1.0f ? *input :
		static_cast<16>(std::clamp(static_cast<float>(*input) * volume_scale, -32768.0f, 32767.0f));

    std::memcpy(output, &scaled_input, sizeof(s16));

	input += AUDIO_CHANNELS;
	output += s_audio_frame_bps;
}

else if that ternary is too gross:

for (u32 j = 0; j < this_batch; j++)
{
	if (volume_scale != 1.0f)
	{
		const s16 scaled_input = static_cast<16>(
			std::clamp(static_cast<float>(*input) * volume_scale, -32768.0f, 32767.0f)
		std::memcpy(output, &scaled_input, sizeof(s16));
	}
	else
	{
		std::memcpy(output, input, sizeof(s16));
	}

	input += AUDIO_CHANNELS;
	output += s_audio_frame_bps;
}

(In the second one, you could eliminate the else by just having scaled_input be the name for both branches, but that seems more confusing. Like so:)

for (u32 j = 0; j < this_batch; j++)
{
	s16 scaled_input = *input;
	if (volume_scale != 1.0f)
	{
		scaled_input = static_cast<16>(
			std::clamp(static_cast<float>(scaled_input) * volume_scale, -32768.0f, 32767.0f)
	}
	std::memcpy(output, &scaled_input, sizeof(s16));

	input += AUDIO_CHANNELS;
	output += s_audio_frame_bps;
}

TL;DR is that using std::clamp() like you do later on would cut down on verbosity here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright I think I made it a lot better

@SternXD SternXD force-pushed the recorder-volume branch 3 times, most recently from 285f5f9 to 90e6dce Compare November 28, 2025 15:02
if (volume_scale != 1.0f)
{
// Apply volume to source samples before conversion
volume_buffer.resize(this_batch * AUDIO_CHANNELS);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For something that's probably going to be running quite frequently, this seems like something you would want to call volume_buffer.reserve(n) for rather than volume_buffer.resize(n). The latter will zero-initialize the values for, in this case, no good reason. (You can also safely declare it inside the volume_scale != 1.0f condition, since it's not used anywhere else.)

I don't assume you're allowed to overwrite the original s_audio_buffer while doing this, as that would arguably be the most efficient way.

Copy link
Member

@chaoticgd chaoticgd Nov 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not familiar with the code, but I just want to flag up that you need to be really careful with std::vector::reserve since it defeats std::vectors default behaviour of doubling (or similar) the size of the memory buffer every time it needs to grow. Hence it makes it have to reallocate every single time, which can kill performance.

Copy link
Member

@chaoticgd chaoticgd Nov 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reading more of the code, it looks like it won't be a problem in this case since it's only called once.

Copy link
Contributor

@TheTechnician27 TheTechnician27 Nov 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not familiar with the code, but I just want to flag up that you need to be really careful with std::vector::reserve since it defeats std::vectors default behaviour of doubling (or similar) the size of the memory buffer every time it needs to grow. Hence it makes it have to reallocate every single time, which can kill performance.

In this case, the vec is being initialized and then resized to exactly the size it needs to be once per call of ProcessAudioPackets() should this branch be taken. So it should only ever grow one time, and thus it should be the same or faster in all cases.

Edit: Honestly, it's functionally an array, whose allocation might be more efficient.

Copy link
Member

@chaoticgd chaoticgd Nov 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea I missed that it was being reallocated for every loop iteration the first time I looked.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alright so I just pushed changes that should resolve some of the suggestions like the don't clamp mention in the discord for example

@kamfretoz
Copy link
Contributor

Latest changes seems to cause crackling on the audio.

Crash.Twinsanity_SLUS-20909_20251201215551.mp4

My capture settings:
image

@SternXD
Copy link
Contributor Author

SternXD commented Dec 1, 2025

Latest changes seems to cause crackling on the audio.

Crash.Twinsanity_SLUS-20909_20251201215551.mp4
My capture settings: image

Oh yeah, yikes let me go ahead and try to fix that.

@F0bes
Copy link
Member

F0bes commented Dec 5, 2025

If the PR is not in a reviewable state please draft it. I just spent time looking at the code to find that it needs to be changed 💔

@F0bes F0bes marked this pull request as draft December 5, 2025 15:54
@F0bes F0bes modified the milestones: Release 2.6, Release 2.8 Dec 5, 2025
@SternXD
Copy link
Contributor Author

SternXD commented Dec 5, 2025

If the PR is not in a reviewable state please draft it. I just spent time looking at the code to find that it needs to be changed 💔

Sowy I forgor

@SternXD
Copy link
Contributor Author

SternXD commented Jan 5, 2026

Rebased

So I don't forget this is what I need to fix:

  • Fix crackling when turning the volume up and down while recording

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature Request]: Volume setting for recordings

6 participants