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.
This commit is contained in:
Clownacy 2020-08-31 16:07:25 +01:00
parent 39067057c1
commit 48386d443b
3 changed files with 47 additions and 7 deletions

View file

@ -20,6 +20,7 @@ option(JAPANESE "Enable the Japanese-language build (instead of the unofficial A
option(FIX_BUGS "Fix various bugs in the game" OFF) 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(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(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_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'") set(BACKEND_AUDIO "SDL2" CACHE STRING "Which audio backend the game should use: 'SDL2', 'miniaudio', 'WiiU-Hardware', 'WiiU-Software', or 'Null'")
@ -268,6 +269,10 @@ if(DEBUG_SAVE)
target_compile_definitions(CSE2 PRIVATE DEBUG_SAVE) target_compile_definitions(CSE2 PRIVATE DEBUG_SAVE)
endif() endif()
if(LANCZOS_RESAMPLER)
target_compile_definitions(CSE2 PRIVATE LANCZOS_RESAMPLER)
endif()
if(PKG_CONFIG_STATIC_LIBS) if(PKG_CONFIG_STATIC_LIBS)
target_link_options(CSE2 PRIVATE "-static") target_link_options(CSE2 PRIVATE "-static")
endif() endif()

View file

@ -95,6 +95,7 @@ Name | Function
`-DDEBUG_SAVE=ON` | Re-enable the ability to drag-and-drop save files onto the window `-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=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) `-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=OpenGL3` | Render with OpenGL 3.2 (hardware-accelerated)
`-DBACKEND_RENDERER=OpenGLES2` | Render with OpenGL ES 2.0 (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`) `-DBACKEND_RENDERER=SDLTexture` | (Default) Render with SDL2's Texture API (hardware-accelerated) (note: requires `-DBACKEND_PLATFORM=SDL2`)

View file

@ -51,7 +51,11 @@ Mixer_Sound* Mixer_CreateSound(unsigned int frequency, const unsigned char *samp
if (sound == NULL) if (sound == NULL)
return 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) if (sound->samples == NULL)
{ {
@ -96,7 +100,9 @@ void Mixer_PlaySound(Mixer_Sound *sound, bool looping)
sound->playing = true; sound->playing = true;
sound->looping = looping; sound->looping = looping;
#ifndef LANCZOS_RESAMPLER
sound->samples[sound->frames] = looping ? sound->samples[0] : 0; // For the linear interpolator sound->samples[sound->frames] = looping ? sound->samples[0] : 0; // For the linear interpolator
#endif
} }
void Mixer_StopSound(Mixer_Sound *sound) void Mixer_StopSound(Mixer_Sound *sound)
@ -143,15 +149,43 @@ ATTRIBUTE_HOT void Mixer_MixSounds(long *stream, size_t frames_total)
for (size_t frames_done = 0; frames_done < frames_total; ++frames_done) for (size_t frames_done = 0; frames_done < frames_total; ++frames_done)
{ {
// Perform linear interpolation #ifdef LANCZOS_RESAMPLER
const unsigned char subsample = sound->position_subsample >> 8; // Perform Lanczos resampling
const int kernel_radius = 2;
const short interpolated_sample = sound->samples[sound->position] * (0x100 - subsample) double accumulator = 0;
+ sound->samples[sound->position + 1] * subsample;
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 interpolation_scale = sound->position_subsample >> 8;
const short output_sample = sound->samples[sound->position] * (0x100 - interpolation_scale)
+ sound->samples[sound->position + 1] * interpolation_scale;
#endif
// Mix, and apply volume // Mix, and apply volume
*stream_pointer++ += (interpolated_sample * sound->volume_l) >> 8; *stream_pointer++ += (output_sample * sound->volume_l) >> 8;
*stream_pointer++ += (interpolated_sample * sound->volume_r) >> 8; *stream_pointer++ += (output_sample * sound->volume_r) >> 8;
// Increment sample // Increment sample
const unsigned long next_position_subsample = sound->position_subsample + sound->advance_delta; const unsigned long next_position_subsample = sound->position_subsample + sound->advance_delta;