-
Notifications
You must be signed in to change notification settings - Fork 19
Expand file tree
/
Copy pathRecursiveLinearFilter.h
More file actions
205 lines (177 loc) · 5.11 KB
/
RecursiveLinearFilter.h
File metadata and controls
205 lines (177 loc) · 5.11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
//
// RecursiveLinearFilter.h
//
//
// Created by Steven Atkinson on 12/28/22.
//
// Recursive linear filters (LPF, HPF, Peaking, Shelving)
#pragma once
#include "dsp.h"
#include <cmath> // pow, sin
#include <vector>
#define MATH_PI 3.14159265358979323846
// TODO refactor base DSP into a common abstraction.
namespace recursive_linear_filter
{
class Base : public dsp::DSP
{
public:
Base(const size_t inputDegree, const size_t outputDegree);
DSP_SAMPLE** Process(DSP_SAMPLE** inputs, const size_t numChannels, const size_t numFrames) override;
protected:
// Methods
size_t _GetInputDegree() const { return this->mInputCoefficients.size(); };
size_t _GetOutputDegree() const { return this->mOutputCoefficients.size(); };
// Additionally prepares mInputHistory and mOutputHistory.
void _PrepareBuffers(const size_t numChannels, const size_t numFrames) override;
// Coefficients for the DSP filter
// [0] is for the current sample
// [1] is for the previous
// [2] before that
// (mOutputCoefficients[0] should always be zero. It'll never be used.)
std::vector<double> mInputCoefficients;
std::vector<double> mOutputCoefficients;
// Arrays holding the history on which the filter depends recursively.
// First index is channel
// Second index, [0] is the current input/output, [1] is the previous, [2] is
// before that, etc.
std::vector<std::vector<DSP_SAMPLE>> mInputHistory;
std::vector<std::vector<DSP_SAMPLE>> mOutputHistory;
// Indices for history.
// Designates which index is currently "0". Use modulus to wrap around.
long mInputStart;
long mOutputStart;
};
class LevelParams : public dsp::Params
{
public:
LevelParams(const double gain)
: Params()
, mGain(gain) {};
double GetGain() const { return this->mGain; };
private:
// The gain (multiplicative, i.e. not dB)
double mGain;
};
class Level : public Base
{
public:
Level()
: Base(1, 0) {};
// Invalid usage: require a pointer to recursive_linear_filter::Params so
// that SetCoefficients() is defined.
void SetParams(const LevelParams& params) { this->mInputCoefficients[0] = params.GetGain(); };
;
};
// The same 3 params (frequency, quality, gain) describe a bunch of filters.
// (Low shelf, high shelf, peaking)
class BiquadParams : public dsp::Params
{
public:
BiquadParams(const double sampleRate, const double frequency, const double quality, const double gainDB)
: dsp::Params()
, mFrequency(frequency)
, mGainDB(gainDB)
, mQuality(quality)
, mSampleRate(sampleRate) {};
// Parameters defined in
// https://webaudio.github.io/Audio-EQ-Cookbook/audio-eq-cookbook.html
double GetA() const { return pow(10.0, this->mGainDB / 40.0); };
double GetOmega0() const { return 2.0 * MATH_PI * this->mFrequency / this->mSampleRate; };
double GetAlpha(const double omega_0) const { return sin(omega_0) / (2.0 * this->mQuality); };
double GetCosW(const double omega_0) const { return cos(omega_0); };
private:
double mFrequency;
double mGainDB;
double mQuality;
double mSampleRate;
};
class Biquad : public Base
{
public:
Biquad()
: Base(3, 3) {};
virtual void SetParams(const BiquadParams& params) = 0;
protected:
void _AssignCoefficients(const double a0, const double a1, const double a2, const double b0, const double b1,
const double b2);
};
class LowShelf : public Biquad
{
public:
void SetParams(const BiquadParams& params) override;
};
class Peaking : public Biquad
{
public:
void SetParams(const BiquadParams& params) override;
};
class HighShelf : public Biquad
{
public:
void SetParams(const BiquadParams& params) override;
};
// HPF only has one param: frequency
// TODO LPF (alpha calculation is different though)
class HighPassParams : public dsp::Params
{
public:
HighPassParams(const double sampleRate, const double frequency)
: dsp::Params()
, mFrequency(frequency)
, mSampleRate(sampleRate) {};
double GetAlpha() const
{
const double c = 2.0 * MATH_PI * mFrequency / mSampleRate;
return 1.0 / (c + 1.0);
};
private:
double mFrequency;
double mSampleRate;
};
class HighPass : public Base
{
public:
HighPass()
: Base(2, 2) {};
void SetParams(const HighPassParams& params)
{
const double alpha = params.GetAlpha();
// y[i] = alpha * y[i-1] + alpha * (x[i]-x[i-1])
mInputCoefficients[0] = alpha;
mInputCoefficients[1] = -alpha;
mOutputCoefficients[0] = 0.0;
mOutputCoefficients[1] = alpha;
}
};
class LowPassParams : public dsp::Params
{
public:
LowPassParams(const double sampleRate, const double frequency)
: dsp::Params()
, mFrequency(frequency)
, mSampleRate(sampleRate) {};
double GetAlpha() const
{
const double c = 2.0 * MATH_PI * mFrequency / mSampleRate;
return c / (c + 1.0);
};
private:
double mFrequency;
double mSampleRate;
};
class LowPass : public Base
{
public:
LowPass()
: Base(1, 2) {};
void SetParams(const LowPassParams& params)
{
const double alpha = params.GetAlpha();
// y[i] = alpha * x[i] + (1-alpha) * y[i-1]
mInputCoefficients[0] = alpha;
mOutputCoefficients[0] = 0.0;
mOutputCoefficients[1] = 1.0 - alpha;
}
};
}; // namespace recursive_linear_filter