ref: 48386d443bf6e1e2828b9347fae7e08cdb745751
parent: 39067057c137c809b1ba1d58004e4863726f226c
author: Clownacy <Clownacy@users.noreply.github.com>
date: Mon Aug 31 12:07:25 EDT 2020
Add optional Lanczos filter to the audio mixer Should be higher-quality than the linear-interpolator, but also much slower, so it's disabled by default.
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -20,6 +20,7 @@
option(FIX_BUGS "Fix various bugs in the game" OFF)
option(DEBUG_SAVE "Re-enable the ability to drag-and-drop save files onto the window" OFF)
option(DOCONFIG "Compile a DoConfig clone tool - not useful for console ports" ON)
+option(LANCZOS_RESAMPLER "Use Lanczos filtering for audio resampling instead of linear-interpolation (Lanczos is more performance-intensive, but higher quality)" OFF)
set(BACKEND_RENDERER "SDLTexture" CACHE STRING "Which renderer the game should use: 'OpenGL3' for an OpenGL 3.2 renderer, 'OpenGLES2' for an OpenGL ES 2.0 renderer, 'SDLTexture' for SDL2's hardware-accelerated Texture API, 'SDLSurface' for SDL2's software-rendered Surface API, 'Wii U' for the Wii U's hardware-accelerated GX2 API, or 'Software' for a handwritten software renderer")
set(BACKEND_AUDIO "SDL2" CACHE STRING "Which audio backend the game should use: 'SDL2', 'miniaudio', 'WiiU-Hardware', 'WiiU-Software', or 'Null'")
@@ -266,6 +267,10 @@
if(DEBUG_SAVE)
target_compile_definitions(CSE2 PRIVATE DEBUG_SAVE)
+endif()
+
+if(LANCZOS_RESAMPLER)
+ target_compile_definitions(CSE2 PRIVATE LANCZOS_RESAMPLER)
endif()
if(PKG_CONFIG_STATIC_LIBS)
--- a/README.md
+++ b/README.md
@@ -95,6 +95,7 @@
`-DDEBUG_SAVE=ON` | Re-enable the ability to drag-and-drop save files onto the window
`-DDOCONFIG=OFF` | Disable compiling the DoConfig tool (it is not useful for console ports)
`-DDOCONFIG_LEGACY_OPENGL=ON` | Make DoConfig use OpenGL 2.1 instead of OpenGL 3.2 (useful for older/limited platforms)
+`-DLANCZOS_RESAMPLER=ON` | Use Lanczos filtering for audio resampling instead of linear-interpolation (Lanczos is more performance-intensive, but higher quality)
`-DBACKEND_RENDERER=OpenGL3` | Render with OpenGL 3.2 (hardware-accelerated)
`-DBACKEND_RENDERER=OpenGLES2` | Render with OpenGL ES 2.0 (hardware-accelerated)
`-DBACKEND_RENDERER=SDLTexture` | (Default) Render with SDL2's Texture API (hardware-accelerated) (note: requires `-DBACKEND_PLATFORM=SDL2`)
--- a/src/Backends/Audio/SoftwareMixer.cpp
+++ b/src/Backends/Audio/SoftwareMixer.cpp
@@ -51,7 +51,11 @@
if (sound == NULL)
return NULL;
- sound->samples = (signed char*)malloc(length + 1);
+#ifdef LANCZOS_RESAMPLER
+ sound->samples = (signed char*)malloc(length);
+#else
+ sound->samples = (signed char*)malloc(length + 1); // +1 for the linear-interpolator
+#endif
if (sound->samples == NULL)
{
@@ -96,7 +100,9 @@
sound->playing = true;
sound->looping = looping;
+#ifndef LANCZOS_RESAMPLER
sound->samples[sound->frames] = looping ? sound->samples[0] : 0; // For the linear interpolator
+#endif
}
void Mixer_StopSound(Mixer_Sound *sound)
@@ -143,15 +149,43 @@
for (size_t frames_done = 0; frames_done < frames_total; ++frames_done)
{
+ #ifdef LANCZOS_RESAMPLER
+ // Perform Lanczos resampling
+ const int kernel_radius = 2;
+
+ double accumulator = 0;
+
+ for (int i = -MIN(kernel_radius - 1, sound->position); i <= kernel_radius; ++i)
+ {
+ const signed char input_sample = sound->samples[(sound->position + i) % sound->frames];
+
+ const double kernel_input = ((double)sound->position_subsample / 0x10000) - i;
+
+ if (kernel_input == 0.0)
+ {
+ accumulator += input_sample;
+ }
+ else
+ {
+ const double nx = 3.14159265358979323846 * kernel_input;
+ const double nxa = nx / kernel_radius;
+
+ accumulator += input_sample * (sin(nx) / nx) * (sin(nxa) / nxa);
+ }
+ }
+
+ const short output_sample = (short)(accumulator * 0x100);
+ #else
// Perform linear interpolation
- const unsigned char subsample = sound->position_subsample >> 8;
+ const unsigned char interpolation_scale = sound->position_subsample >> 8;
- const short interpolated_sample = sound->samples[sound->position] * (0x100 - subsample)
- + sound->samples[sound->position + 1] * subsample;
+ const short output_sample = sound->samples[sound->position] * (0x100 - interpolation_scale)
+ + sound->samples[sound->position + 1] * interpolation_scale;
+ #endif
// Mix, and apply volume
- *stream_pointer++ += (interpolated_sample * sound->volume_l) >> 8;
- *stream_pointer++ += (interpolated_sample * sound->volume_r) >> 8;
+ *stream_pointer++ += (output_sample * sound->volume_l) >> 8;
+ *stream_pointer++ += (output_sample * sound->volume_r) >> 8;
// Increment sample
const unsigned long next_position_subsample = sound->position_subsample + sound->advance_delta;