diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/audio_core/CMakeLists.txt | 6 | ||||
| -rw-r--r-- | src/audio_core/algorithm/filter.cpp | 79 | ||||
| -rw-r--r-- | src/audio_core/algorithm/filter.h | 62 |
3 files changed, 145 insertions, 2 deletions
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index ec71524a3..92322f59b 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt | |||
| @@ -1,4 +1,6 @@ | |||
| 1 | add_library(audio_core STATIC | 1 | add_library(audio_core STATIC |
| 2 | algorithm/filter.cpp | ||
| 3 | algorithm/filter.h | ||
| 2 | audio_out.cpp | 4 | audio_out.cpp |
| 3 | audio_out.h | 5 | audio_out.h |
| 4 | audio_renderer.cpp | 6 | audio_renderer.cpp |
| @@ -7,12 +9,12 @@ add_library(audio_core STATIC | |||
| 7 | codec.cpp | 9 | codec.cpp |
| 8 | codec.h | 10 | codec.h |
| 9 | null_sink.h | 11 | null_sink.h |
| 10 | stream.cpp | ||
| 11 | stream.h | ||
| 12 | sink.h | 12 | sink.h |
| 13 | sink_details.cpp | 13 | sink_details.cpp |
| 14 | sink_details.h | 14 | sink_details.h |
| 15 | sink_stream.h | 15 | sink_stream.h |
| 16 | stream.cpp | ||
| 17 | stream.h | ||
| 16 | 18 | ||
| 17 | $<$<BOOL:${ENABLE_CUBEB}>:cubeb_sink.cpp cubeb_sink.h> | 19 | $<$<BOOL:${ENABLE_CUBEB}>:cubeb_sink.cpp cubeb_sink.h> |
| 18 | ) | 20 | ) |
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 | ||