diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/audio_core/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/audio_core/algorithm/interpolate.cpp | 71 | ||||
| -rw-r--r-- | src/audio_core/algorithm/interpolate.h | 43 | ||||
| -rw-r--r-- | src/audio_core/audio_renderer.cpp | 3 | ||||
| -rw-r--r-- | src/audio_core/audio_renderer.h | 2 |
5 files changed, 121 insertions, 0 deletions
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index 92322f59b..82e4850f7 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt | |||
| @@ -1,6 +1,8 @@ | |||
| 1 | add_library(audio_core STATIC | 1 | add_library(audio_core STATIC |
| 2 | algorithm/filter.cpp | 2 | algorithm/filter.cpp |
| 3 | algorithm/filter.h | 3 | algorithm/filter.h |
| 4 | algorithm/interpolate.cpp | ||
| 5 | algorithm/interpolate.h | ||
| 4 | audio_out.cpp | 6 | audio_out.cpp |
| 5 | audio_out.h | 7 | audio_out.h |
| 6 | audio_renderer.cpp | 8 | audio_renderer.cpp |
diff --git a/src/audio_core/algorithm/interpolate.cpp b/src/audio_core/algorithm/interpolate.cpp new file mode 100644 index 000000000..11459821f --- /dev/null +++ b/src/audio_core/algorithm/interpolate.cpp | |||
| @@ -0,0 +1,71 @@ | |||
| 1 | // Copyright 2018 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #define _USE_MATH_DEFINES | ||
| 6 | |||
| 7 | #include <algorithm> | ||
| 8 | #include <cmath> | ||
| 9 | #include <vector> | ||
| 10 | #include "audio_core/algorithm/interpolate.h" | ||
| 11 | #include "common/common_types.h" | ||
| 12 | #include "common/logging/log.h" | ||
| 13 | |||
| 14 | namespace AudioCore { | ||
| 15 | |||
| 16 | /// The Lanczos kernel | ||
| 17 | static double Lanczos(size_t a, double x) { | ||
| 18 | if (x == 0.0) | ||
| 19 | return 1.0; | ||
| 20 | const double px = M_PI * x; | ||
| 21 | return a * std::sin(px) * std::sin(px / a) / (px * px); | ||
| 22 | } | ||
| 23 | |||
| 24 | std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input, double ratio) { | ||
| 25 | if (input.size() < 2) | ||
| 26 | return {}; | ||
| 27 | |||
| 28 | if (ratio <= 0) { | ||
| 29 | LOG_CRITICAL(Audio, "Nonsensical interpolation ratio {}", ratio); | ||
| 30 | ratio = 1.0; | ||
| 31 | } | ||
| 32 | |||
| 33 | if (ratio != state.current_ratio) { | ||
| 34 | const double cutoff_frequency = std::min(0.5 / ratio, 0.5 * ratio); | ||
| 35 | state.nyquist = CascadingFilter::LowPass(std::clamp(cutoff_frequency, 0.0, 0.4), 3); | ||
| 36 | state.current_ratio = ratio; | ||
| 37 | } | ||
| 38 | state.nyquist.Process(input); | ||
| 39 | |||
| 40 | constexpr size_t taps = InterpolationState::lanczos_taps; | ||
| 41 | const size_t num_frames = input.size() / 2; | ||
| 42 | |||
| 43 | std::vector<s16> output; | ||
| 44 | output.reserve(static_cast<size_t>(input.size() / ratio + 4)); | ||
| 45 | |||
| 46 | double& pos = state.position; | ||
| 47 | auto& h = state.history; | ||
| 48 | for (size_t i = 0; i < num_frames; ++i) { | ||
| 49 | std::rotate(h.begin(), h.end() - 1, h.end()); | ||
| 50 | h[0][0] = input[i * 2 + 0]; | ||
| 51 | h[0][1] = input[i * 2 + 1]; | ||
| 52 | |||
| 53 | while (pos <= 1.0) { | ||
| 54 | double l = 0.0; | ||
| 55 | double r = 0.0; | ||
| 56 | for (size_t j = 0; j < h.size(); j++) { | ||
| 57 | l += Lanczos(taps, pos + j - taps + 1) * h[j][0]; | ||
| 58 | r += Lanczos(taps, pos + j - taps + 1) * h[j][1]; | ||
| 59 | } | ||
| 60 | output.emplace_back(static_cast<s16>(std::clamp(l, -32768.0, 32767.0))); | ||
| 61 | output.emplace_back(static_cast<s16>(std::clamp(r, -32768.0, 32767.0))); | ||
| 62 | |||
| 63 | pos += ratio; | ||
| 64 | } | ||
| 65 | pos -= 1.0; | ||
| 66 | } | ||
| 67 | |||
| 68 | return output; | ||
| 69 | } | ||
| 70 | |||
| 71 | } // namespace AudioCore | ||
diff --git a/src/audio_core/algorithm/interpolate.h b/src/audio_core/algorithm/interpolate.h new file mode 100644 index 000000000..c79c2eef4 --- /dev/null +++ b/src/audio_core/algorithm/interpolate.h | |||
| @@ -0,0 +1,43 @@ | |||
| 1 | // Copyright 2018 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | #include <vector> | ||
| 9 | #include "audio_core/algorithm/filter.h" | ||
| 10 | #include "common/common_types.h" | ||
| 11 | |||
| 12 | namespace AudioCore { | ||
| 13 | |||
| 14 | struct InterpolationState { | ||
| 15 | static constexpr size_t lanczos_taps = 4; | ||
| 16 | static constexpr size_t history_size = lanczos_taps * 2 - 1; | ||
| 17 | |||
| 18 | double current_ratio = 0.0; | ||
| 19 | CascadingFilter nyquist; | ||
| 20 | std::array<std::array<s16, 2>, history_size> history = {}; | ||
| 21 | double position = 0; | ||
| 22 | }; | ||
| 23 | |||
| 24 | /// Interpolates input signal to produce output signal. | ||
| 25 | /// @param input The signal to interpolate. | ||
| 26 | /// @param ratio Interpolation ratio. | ||
| 27 | /// ratio > 1.0 results in fewer output samples. | ||
| 28 | /// ratio < 1.0 results in more output samples. | ||
| 29 | /// @returns Output signal. | ||
| 30 | std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input, double ratio); | ||
| 31 | |||
| 32 | /// Interpolates input signal to produce output signal. | ||
| 33 | /// @param input The signal to interpolate. | ||
| 34 | /// @param input_rate The sample rate of input. | ||
| 35 | /// @param output_rate The desired sample rate of the output. | ||
| 36 | /// @returns Output signal. | ||
| 37 | inline std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input, | ||
| 38 | u32 input_rate, u32 output_rate) { | ||
| 39 | const double ratio = static_cast<double>(input_rate) / static_cast<double>(output_rate); | ||
| 40 | return Interpolate(state, std::move(input), ratio); | ||
| 41 | } | ||
| 42 | |||
| 43 | } // namespace AudioCore | ||
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp index 6ebed3fb0..7bff635b8 100644 --- a/src/audio_core/audio_renderer.cpp +++ b/src/audio_core/audio_renderer.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "audio_core/algorithm/interpolate.h" | ||
| 5 | #include "audio_core/audio_renderer.h" | 6 | #include "audio_core/audio_renderer.h" |
| 6 | #include "common/assert.h" | 7 | #include "common/assert.h" |
| 7 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| @@ -199,6 +200,8 @@ void AudioRenderer::VoiceState::RefreshBuffer() { | |||
| 199 | break; | 200 | break; |
| 200 | } | 201 | } |
| 201 | 202 | ||
| 203 | samples = Interpolate(interp_state, std::move(samples), Info().sample_rate, STREAM_SAMPLE_RATE); | ||
| 204 | |||
| 202 | is_refresh_pending = false; | 205 | is_refresh_pending = false; |
| 203 | } | 206 | } |
| 204 | 207 | ||
diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h index 13c5d0adc..eba67f28e 100644 --- a/src/audio_core/audio_renderer.h +++ b/src/audio_core/audio_renderer.h | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | #include <memory> | 8 | #include <memory> |
| 9 | #include <vector> | 9 | #include <vector> |
| 10 | 10 | ||
| 11 | #include "audio_core/algorithm/interpolate.h" | ||
| 11 | #include "audio_core/audio_out.h" | 12 | #include "audio_core/audio_out.h" |
| 12 | #include "audio_core/codec.h" | 13 | #include "audio_core/codec.h" |
| 13 | #include "audio_core/stream.h" | 14 | #include "audio_core/stream.h" |
| @@ -194,6 +195,7 @@ private: | |||
| 194 | size_t wave_index{}; | 195 | size_t wave_index{}; |
| 195 | size_t offset{}; | 196 | size_t offset{}; |
| 196 | Codec::ADPCMState adpcm_state{}; | 197 | Codec::ADPCMState adpcm_state{}; |
| 198 | InterpolationState interp_state{}; | ||
| 197 | std::vector<s16> samples; | 199 | std::vector<s16> samples; |
| 198 | VoiceOutStatus out_status{}; | 200 | VoiceOutStatus out_status{}; |
| 199 | VoiceInfo info{}; | 201 | VoiceInfo info{}; |