Skip to content

Clipping when multiple sounds are playing #3449

@Lt-knb

Description

@Lt-knb

In EasyRPG, there's clipping when total_volume is greater than 1.0.
In RPG_RT, sound is limited to 0 dB.

.Flow has a lot of clipping, but you can notice it in Yume Nikki too, and Test Game 2000 (in Music Playing).
I only tested those, but in theory, it should affect all games.
Reproducing it is very easy.

This is caused by Decode in audio_generic.cpp:
It applies compression with a soft knee at -2 dB while converting to 16 bit, which causes distortion as if it's clipping.
RPG_RT limits to 0 dB by scaling samples uniformly.

Basically, replace this:

		if (total_volume > 1.0) {
			float threshold = 0.8f;
			for (unsigned i = 0; i < (unsigned)(samples_per_frame * 2); i++) {
				float sample = mixer_buffer[i];
				float sign = (sample < 0) ? -1.0 : 1.0;
				sample /= sign;
				//dynamic range compression
				if (sample > threshold) {
					sample_buffer[i] = sign * 32768.0 * (threshold + (1.0 - threshold) * (sample - threshold) / (total_volume - threshold));
				} else {
					sample_buffer[i] = sign * sample * 32768.0;
				}
			}
		} else {
			//No dynamic range compression necessary
			for (unsigned i = 0; i < (unsigned)(samples_per_frame * 2); i++) {
				sample_buffer[i] = mixer_buffer[i] * 32768.0;
			}
		}

With this:

	float max_sample = 0.0f;
	unsigned num_samples = samples_per_frame * 2;
	for (unsigned i = 0; i < num_samples; i++) {
		float abs_sample = std::abs(mixer_buffer[i]);
		if (abs_sample > max_sample) max_sample = abs_sample;
	}
	float scale = (max_sample > 1.0f) ? (1.0f / max_sample) : 1.0f;
	for (unsigned i = 0; i < num_samples; i++) {
		float sample = mixer_buffer[i] * scale;
		sample_buffer[i] = static_cast<int16_t>(sample * 32768.0f);
	}

Which also makes total_volume unnecessary

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions