diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/audio_core/CMakeLists.txt | 8 | ||||
| -rw-r--r-- | src/audio_core/algorithm/filter.cpp | 79 | ||||
| -rw-r--r-- | src/audio_core/algorithm/filter.h | 62 | ||||
| -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 | 5 | ||||
| -rw-r--r-- | src/audio_core/audio_renderer.h | 2 | ||||
| -rw-r--r-- | src/core/arm/dynarmic/arm_dynarmic.cpp | 4 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/maxwell_to_gl.h | 1 |
9 files changed, 269 insertions, 6 deletions
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index ec71524a3..82e4850f7 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt | |||
| @@ -1,4 +1,8 @@ | |||
| 1 | add_library(audio_core STATIC | 1 | add_library(audio_core STATIC |
| 2 | algorithm/filter.cpp | ||
| 3 | algorithm/filter.h | ||
| 4 | algorithm/interpolate.cpp | ||
| 5 | algorithm/interpolate.h | ||
| 2 | audio_out.cpp | 6 | audio_out.cpp |
| 3 | audio_out.h | 7 | audio_out.h |
| 4 | audio_renderer.cpp | 8 | audio_renderer.cpp |
| @@ -7,12 +11,12 @@ add_library(audio_core STATIC | |||
| 7 | codec.cpp | 11 | codec.cpp |
| 8 | codec.h | 12 | codec.h |
| 9 | null_sink.h | 13 | null_sink.h |
| 10 | stream.cpp | ||
| 11 | stream.h | ||
| 12 | sink.h | 14 | sink.h |
| 13 | sink_details.cpp | 15 | sink_details.cpp |
| 14 | sink_details.h | 16 | sink_details.h |
| 15 | sink_stream.h | 17 | sink_stream.h |
| 18 | stream.cpp | ||
| 19 | stream.h | ||
| 16 | 20 | ||
| 17 | $<$<BOOL:${ENABLE_CUBEB}>:cubeb_sink.cpp cubeb_sink.h> | 21 | $<$<BOOL:${ENABLE_CUBEB}>:cubeb_sink.cpp cubeb_sink.h> |
| 18 | ) | 22 | ) |
diff --git a/src/audio_core/algorithm/filter.cpp b/src/audio_core/algorithm/filter.cpp new file mode 100644 index 000000000..403b8503f --- /dev/null +++ b/src/audio_core/algorithm/filter.cpp | |||
| @@ -0,0 +1,79 @@ | |||
| 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 <array> | ||
| 9 | #include <cmath> | ||
| 10 | #include <vector> | ||
| 11 | #include "audio_core/algorithm/filter.h" | ||
| 12 | #include "common/common_types.h" | ||
| 13 | |||
| 14 | namespace AudioCore { | ||
| 15 | |||
| 16 | Filter Filter::LowPass(double cutoff, double Q) { | ||
| 17 | const double w0 = 2.0 * M_PI * cutoff; | ||
| 18 | const double sin_w0 = std::sin(w0); | ||
| 19 | const double cos_w0 = std::cos(w0); | ||
| 20 | const double alpha = sin_w0 / (2 * Q); | ||
| 21 | |||
| 22 | const double a0 = 1 + alpha; | ||
| 23 | const double a1 = -2.0 * cos_w0; | ||
| 24 | const double a2 = 1 - alpha; | ||
| 25 | const double b0 = 0.5 * (1 - cos_w0); | ||
| 26 | const double b1 = 1.0 * (1 - cos_w0); | ||
| 27 | const double b2 = 0.5 * (1 - cos_w0); | ||
| 28 | |||
| 29 | return {a0, a1, a2, b0, b1, b2}; | ||
| 30 | } | ||
| 31 | |||
| 32 | Filter::Filter() : Filter(1.0, 0.0, 0.0, 1.0, 0.0, 0.0) {} | ||
| 33 | |||
| 34 | Filter::Filter(double a0, double a1, double a2, double b0, double b1, double b2) | ||
| 35 | : a1(a1 / a0), a2(a2 / a0), b0(b0 / a0), b1(b1 / a0), b2(b2 / a0) {} | ||
| 36 | |||
| 37 | void Filter::Process(std::vector<s16>& signal) { | ||
| 38 | const size_t num_frames = signal.size() / 2; | ||
| 39 | for (size_t i = 0; i < num_frames; i++) { | ||
| 40 | std::rotate(in.begin(), in.end() - 1, in.end()); | ||
| 41 | std::rotate(out.begin(), out.end() - 1, out.end()); | ||
| 42 | |||
| 43 | for (size_t ch = 0; ch < channel_count; ch++) { | ||
| 44 | in[0][ch] = signal[i * channel_count + ch]; | ||
| 45 | |||
| 46 | out[0][ch] = b0 * in[0][ch] + b1 * in[1][ch] + b2 * in[2][ch] - a1 * out[1][ch] - | ||
| 47 | a2 * out[2][ch]; | ||
| 48 | |||
| 49 | signal[i * 2 + ch] = std::clamp(out[0][ch], -32768.0, 32767.0); | ||
| 50 | } | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | /// Calculates the appropriate Q for each biquad in a cascading filter. | ||
| 55 | /// @param total_count The total number of biquads to be cascaded. | ||
| 56 | /// @param index 0-index of the biquad to calculate the Q value for. | ||
| 57 | static double CascadingBiquadQ(size_t total_count, size_t index) { | ||
| 58 | const double pole = M_PI * (2 * index + 1) / (4.0 * total_count); | ||
| 59 | return 1.0 / (2.0 * std::cos(pole)); | ||
| 60 | } | ||
| 61 | |||
| 62 | CascadingFilter CascadingFilter::LowPass(double cutoff, size_t cascade_size) { | ||
| 63 | std::vector<Filter> cascade(cascade_size); | ||
| 64 | for (size_t i = 0; i < cascade_size; i++) { | ||
| 65 | cascade[i] = Filter::LowPass(cutoff, CascadingBiquadQ(cascade_size, i)); | ||
| 66 | } | ||
| 67 | return CascadingFilter{std::move(cascade)}; | ||
| 68 | } | ||
| 69 | |||
| 70 | CascadingFilter::CascadingFilter() = default; | ||
| 71 | CascadingFilter::CascadingFilter(std::vector<Filter> filters) : filters(std::move(filters)) {} | ||
| 72 | |||
| 73 | void CascadingFilter::Process(std::vector<s16>& signal) { | ||
| 74 | for (auto& filter : filters) { | ||
| 75 | filter.Process(signal); | ||
| 76 | } | ||
| 77 | } | ||
| 78 | |||
| 79 | } // namespace AudioCore | ||
diff --git a/src/audio_core/algorithm/filter.h b/src/audio_core/algorithm/filter.h new file mode 100644 index 000000000..a41beef98 --- /dev/null +++ b/src/audio_core/algorithm/filter.h | |||
| @@ -0,0 +1,62 @@ | |||
| 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 "common/common_types.h" | ||
| 10 | |||
| 11 | namespace AudioCore { | ||
| 12 | |||
| 13 | /// Digital biquad filter: | ||
| 14 | /// | ||
| 15 | /// b0 + b1 z^-1 + b2 z^-2 | ||
| 16 | /// H(z) = ------------------------ | ||
| 17 | /// a0 + a1 z^-1 + b2 z^-2 | ||
| 18 | class Filter { | ||
| 19 | public: | ||
| 20 | /// Creates a low-pass filter. | ||
| 21 | /// @param cutoff Determines the cutoff frequency. A value from 0.0 to 1.0. | ||
| 22 | /// @param Q Determines the quality factor of this filter. | ||
| 23 | static Filter LowPass(double cutoff, double Q = 0.7071); | ||
| 24 | |||
| 25 | /// Passthrough filter. | ||
| 26 | Filter(); | ||
| 27 | |||
| 28 | Filter(double a0, double a1, double a2, double b0, double b1, double b2); | ||
| 29 | |||
| 30 | void Process(std::vector<s16>& signal); | ||
| 31 | |||
| 32 | private: | ||
| 33 | static constexpr size_t channel_count = 2; | ||
| 34 | |||
| 35 | /// Coefficients are in normalized form (a0 = 1.0). | ||
| 36 | double a1, a2, b0, b1, b2; | ||
| 37 | /// Input History | ||
| 38 | std::array<std::array<double, channel_count>, 3> in; | ||
| 39 | /// Output History | ||
| 40 | std::array<std::array<double, channel_count>, 3> out; | ||
| 41 | }; | ||
| 42 | |||
| 43 | /// Cascade filters to build up higher-order filters from lower-order ones. | ||
| 44 | class CascadingFilter { | ||
| 45 | public: | ||
| 46 | /// Creates a cascading low-pass filter. | ||
| 47 | /// @param cutoff Determines the cutoff frequency. A value from 0.0 to 1.0. | ||
| 48 | /// @param cascade_size Number of biquads in cascade. | ||
| 49 | static CascadingFilter LowPass(double cutoff, size_t cascade_size); | ||
| 50 | |||
| 51 | /// Passthrough. | ||
| 52 | CascadingFilter(); | ||
| 53 | |||
| 54 | explicit CascadingFilter(std::vector<Filter> filters); | ||
| 55 | |||
| 56 | void Process(std::vector<s16>& signal); | ||
| 57 | |||
| 58 | private: | ||
| 59 | std::vector<Filter> filters; | ||
| 60 | }; | ||
| 61 | |||
| 62 | } // namespace AudioCore | ||
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..397b107f5 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 | ||
| @@ -224,7 +227,7 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) { | |||
| 224 | break; | 227 | break; |
| 225 | } | 228 | } |
| 226 | 229 | ||
| 227 | samples_remaining -= samples.size(); | 230 | samples_remaining -= samples.size() / stream->GetNumChannels(); |
| 228 | 231 | ||
| 229 | for (const auto& sample : samples) { | 232 | for (const auto& sample : samples) { |
| 230 | const s32 buffer_sample{buffer[offset]}; | 233 | const s32 buffer_sample{buffer[offset]}; |
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{}; |
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp index 0996f129c..20e5200a8 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic.cpp | |||
| @@ -243,9 +243,7 @@ void ARM_Dynarmic::LoadContext(const ThreadContext& ctx) { | |||
| 243 | } | 243 | } |
| 244 | 244 | ||
| 245 | void ARM_Dynarmic::PrepareReschedule() { | 245 | void ARM_Dynarmic::PrepareReschedule() { |
| 246 | if (jit->IsExecuting()) { | 246 | jit->HaltExecution(); |
| 247 | jit->HaltExecution(); | ||
| 248 | } | ||
| 249 | } | 247 | } |
| 250 | 248 | ||
| 251 | void ARM_Dynarmic::ClearInstructionCache() { | 249 | void ARM_Dynarmic::ClearInstructionCache() { |
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h index 679e5ceb2..83ea0cfc0 100644 --- a/src/video_core/renderer_opengl/maxwell_to_gl.h +++ b/src/video_core/renderer_opengl/maxwell_to_gl.h | |||
| @@ -27,6 +27,7 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) { | |||
| 27 | case Maxwell::VertexAttribute::Type::UnsignedNorm: { | 27 | case Maxwell::VertexAttribute::Type::UnsignedNorm: { |
| 28 | 28 | ||
| 29 | switch (attrib.size) { | 29 | switch (attrib.size) { |
| 30 | case Maxwell::VertexAttribute::Size::Size_8: | ||
| 30 | case Maxwell::VertexAttribute::Size::Size_8_8: | 31 | case Maxwell::VertexAttribute::Size::Size_8_8: |
| 31 | case Maxwell::VertexAttribute::Size::Size_8_8_8_8: | 32 | case Maxwell::VertexAttribute::Size::Size_8_8_8_8: |
| 32 | return GL_UNSIGNED_BYTE; | 33 | return GL_UNSIGNED_BYTE; |