diff --git a/CMakeLists.txt b/CMakeLists.txt index 7bd7edf13f..e0cecc6866 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,6 +86,7 @@ option(USE_SYSTEM_SDL2 "Use the system SDL2 lib (instead of the bundled one)" OF option(USE_SYSTEM_BOOST "Use the system Boost libs (instead of the bundled ones)" OFF) option(USE_SYSTEM_OPENSSL "Use the system OpenSSL libs (instead of the bundled LibreSSL)" OFF) option(USE_SYSTEM_LIBUSB "Use the system libusb (instead of the bundled libusb)" OFF) +option(USE_SYSTEM_SOUNDTOUCH "Use the system SoundTouch (instead of the bundled one)" OFF) if (CITRA_USE_PRECOMPILED_HEADERS) message(STATUS "Using Precompiled Headers.") @@ -389,6 +390,14 @@ if (ENABLE_LIBUSB AND USE_SYSTEM_LIBUSB) find_package(LibUSB) endif() +if (USE_SYSTEM_SOUNDTOUCH) + include(FindPkgConfig) + find_package(SoundTouch REQUIRED) + add_library(SoundTouch INTERFACE) + target_link_libraries(SoundTouch INTERFACE "${SOUNDTOUCH_LIBRARIES}") + target_include_directories(SoundTouch INTERFACE "${SOUNDTOUCH_INCLUDE_DIRS}") +endif() + add_subdirectory(src) add_subdirectory(dist/installer) diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 7777a6125f..f11cb29962 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -122,10 +122,13 @@ add_subdirectory(open_source_archives) add_subdirectory(library-headers EXCLUDE_FROM_ALL) # SoundTouch -set(INTEGER_SAMPLES ON CACHE BOOL "") -set(SOUNDSTRETCH OFF CACHE BOOL "") -set(SOUNDTOUCH_DLL OFF CACHE BOOL "") -add_subdirectory(soundtouch EXCLUDE_FROM_ALL) +if(NOT USE_SYSTEM_SOUNDTOUCH) + set(INTEGER_SAMPLES ON CACHE BOOL "") + set(SOUNDSTRETCH OFF CACHE BOOL "") + set(SOUNDTOUCH_DLL OFF CACHE BOOL "") + add_subdirectory(soundtouch EXCLUDE_FROM_ALL) + target_compile_definitions(SoundTouch PUBLIC SOUNDTOUCH_INTEGER_SAMPLES) +endif() # sirit add_subdirectory(sirit EXCLUDE_FROM_ALL) diff --git a/externals/cmake-modules/FindSoundTouch.cmake b/externals/cmake-modules/FindSoundTouch.cmake new file mode 100644 index 0000000000..69792f7382 --- /dev/null +++ b/externals/cmake-modules/FindSoundTouch.cmake @@ -0,0 +1,27 @@ +if(NOT SOUNDTOUCH_FOUND) + pkg_check_modules(SOUNDTOUCH_TMP soundtouch) + + find_path(SOUNDTOUCH_INCLUDE_DIRS NAMES SoundTouch.h + PATHS + ${SOUNDTOUCH_TMP_INCLUDE_DIRS} + /usr/include/soundtouch + /usr/include + /usr/local/include/soundtouch + /usr/local/include + ) + + find_library(SOUNDTOUCH_LIBRARIES NAMES SoundTouch + PATHS + ${SOUNDTOUCH_TMP_LIBRARY_DIRS} + /usr/lib + /usr/local/lib + ) + + if(SOUNDTOUCH_INCLUDE_DIRS AND SOUNDTOUCH_LIBRARIES) + set(SOUNDTOUCH_FOUND TRUE CACHE INTERNAL "SoundTouch found") + message(STATUS "Found SoundTouch: ${SOUNDTOUCH_INCLUDE_DIRS}, ${SOUNDTOUCH_LIBRARIES}") + else() + set(SOUNDTOUCH_FOUND FALSE CACHE INTERNAL "SoundTouch found") + message(STATUS "SoundTouch not found.") + endif() +endif() diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index 003ebf181c..46967735d7 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt @@ -49,7 +49,6 @@ create_target_directory_groups(audio_core) target_link_libraries(audio_core PUBLIC citra_common citra_core) target_link_libraries(audio_core PRIVATE SoundTouch teakra) -add_definitions(-DSOUNDTOUCH_INTEGER_SAMPLES) if(ENABLE_MF) target_sources(audio_core PRIVATE diff --git a/src/audio_core/time_stretch.cpp b/src/audio_core/time_stretch.cpp index 4e50b9fa99..ce89088743 100644 --- a/src/audio_core/time_stretch.cpp +++ b/src/audio_core/time_stretch.cpp @@ -5,7 +5,10 @@ #include #include #include +#include #include +#include +#include #include #include "audio_core/audio_types.h" #include "audio_core/time_stretch.h" @@ -62,8 +65,44 @@ std::size_t TimeStretcher::Process(const s16* in, std::size_t num_in, s16* out, LOG_TRACE(Audio, "{:5}/{:5} ratio:{:0.6f} backlog:{:0.6f}", num_in, num_out, stretch_ratio, backlog_fullness); - sound_touch->putSamples(in, static_cast(num_in)); - return sound_touch->receiveSamples(out, static_cast(num_out)); + if constexpr (std::is_floating_point()) { + // The SoundTouch library on most systems expects float samples + // use this vector to store input if soundtouch::SAMPLETYPE is a float + std::vector float_in(2 * num_in); + std::vector float_out(2 * num_out); + + for (std::size_t i = 0; i < (2 * num_in); i++) { + // Conventional integer PCM uses a range of -32768 to 32767, + // but float samples use -1 to 1 + // As a result we need to scale sample values during conversion + const float temp = static_cast(in[i]) / std::numeric_limits::max(); + float_in[i] = static_cast(temp); + } + + sound_touch->putSamples(float_in.data(), static_cast(num_in)); + + const std::size_t samples_received = + sound_touch->receiveSamples(float_out.data(), static_cast(num_out)); + + // Converting output samples back to shorts so we can use them + for (std::size_t i = 0; i < (2 * num_out); i++) { + const s16 temp = static_cast(float_out[i] * std::numeric_limits::max()); + out[i] = temp; + } + + return samples_received; + } else if (std::is_same()) { + // Use reinterpret_cast to workaround compile error when SAMPLETYPE is float. + sound_touch->putSamples(reinterpret_cast(in), + static_cast(num_in)); + return sound_touch->receiveSamples(reinterpret_cast(out), + static_cast(num_out)); + } else { + static_assert(std::is_floating_point() || + std::is_same()); + UNREACHABLE_MSG("Invalid SAMPLETYPE {}", typeid(soundtouch::SAMPLETYPE).name()); + return 0; + } } void TimeStretcher::Clear() {