summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar MerryMage2018-08-12 19:32:16 +0100
committerGravatar MerryMage2018-08-13 11:26:50 +0100
commit56300f2928a4eb982cf686f9e920d6e1e3b59356 (patch)
tree7e4e84a417622be69ed6af3a716df88e9a2b9584 /src
parentMerge pull request #1028 from ogniK5377/aoa (diff)
downloadyuzu-56300f2928a4eb982cf686f9e920d6e1e3b59356.tar.gz
yuzu-56300f2928a4eb982cf686f9e920d6e1e3b59356.tar.xz
yuzu-56300f2928a4eb982cf686f9e920d6e1e3b59356.zip
audio_core: Implement low-pass filter
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/CMakeLists.txt6
-rw-r--r--src/audio_core/algorithm/filter.cpp79
-rw-r--r--src/audio_core/algorithm/filter.h62
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 @@
1add_library(audio_core STATIC 1add_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
14namespace AudioCore {
15
16Filter 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
32Filter::Filter() : Filter(1.0, 0.0, 0.0, 1.0, 0.0, 0.0) {}
33
34Filter::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
37void 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.
57static 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
62CascadingFilter 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
70CascadingFilter::CascadingFilter() = default;
71CascadingFilter::CascadingFilter(std::vector<Filter> filters) : filters(std::move(filters)) {}
72
73void 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
11namespace 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
18class Filter {
19public:
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
32private:
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.
44class CascadingFilter {
45public:
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
58private:
59 std::vector<Filter> filters;
60};
61
62} // namespace AudioCore