diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a6138d5..b8182f71 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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(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'") @@ -268,6 +269,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) target_link_options(CSE2 PRIVATE "-static") endif() diff --git a/README.md b/README.md index 3d950158..b6f12388 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,7 @@ Name | Function `-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`) diff --git a/src/Backends/Audio/SoftwareMixer.cpp b/src/Backends/Audio/SoftwareMixer.cpp index 0c65ce86..3e2303f3 100644 --- a/src/Backends/Audio/SoftwareMixer.cpp +++ b/src/Backends/Audio/SoftwareMixer.cpp @@ -51,7 +51,11 @@ Mixer_Sound* Mixer_CreateSound(unsigned int frequency, const unsigned char *samp 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 @@ void Mixer_PlaySound(Mixer_Sound *sound, bool looping) 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 @@ ATTRIBUTE_HOT void Mixer_MixSounds(long *stream, size_t frames_total) for (size_t frames_done = 0; frames_done < frames_total; ++frames_done) { - // Perform linear interpolation - const unsigned char subsample = sound->position_subsample >> 8; + #ifdef LANCZOS_RESAMPLER + // Perform Lanczos resampling + const int kernel_radius = 2; - const short interpolated_sample = sound->samples[sound->position] * (0x100 - subsample) - + sound->samples[sound->position + 1] * subsample; + 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 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 - *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;