1 /*
2   dsp/RBJ.h
3 
4 	Copyright
5 		1998 Robert Bristow-Johnson
6 		2004-10 Tim Goetze <tim@quitte.de>
7 
8 	biquad prototypes according to the eq cookbook. easy-to-use, nice,
9 	predictable filters. thanks rbj!
10 */
11 /*
12 	This program is free software; you can redistribute it and/or
13 	modify it under the terms of the GNU General Public License
14 	as published by the Free Software Foundation; either version 2
15 	of the License, or (at your option) any later version.
16 
17 	This program is distributed in the hope that it will be useful,
18 	but WITHOUT ANY WARRANTY; without even the implied warranty of
19 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 	GNU General Public License for more details.
21 
22 	You should have received a copy of the GNU General Public License
23 	along with this program; if not, write to the Free Software
24 	Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
25 	02111-1307, USA or point your web browser to http://www.gnu.org.
26 */
27 
28 #ifndef _DSP_RBJ_H_
29 #define _DSP_RBJ_H_
30 
31 #include "BiQuad.h"
32 
33 namespace DSP {
34 namespace RBJ {
35 
36 /* base class, prepares common parameters */
37 class RBJ
38 {
39 	public:
40 		double Q, alpha, sin, cos;
41 		double a[3], b[3];
42 
43 	public:
RBJ(double f,double _Q)44 		RBJ (double f, double _Q)
45 			{
46 				Q = _Q;
47 
48 				double w = 2 * M_PI * f;
49 
50 				sin = ::sin (w);
51 				cos = ::cos (w);
52 
53 				alpha = sin / (2 * Q);
54 			}
55 
56 		/* templated so we can set double and float coefficients from the same
57 		 * piece of code */
58 		template <class T>
make_direct_I(T * ca,T * cb)59 		void make_direct_I (T * ca, T * cb)
60 			{
61 				double a0i = 1 / a[0];
62 
63 				ca[0] = b[0] * a0i;
64 				ca[1] = b[1] * a0i;
65 				ca[2] = b[2] * a0i;
66 
67 				/* our bi-quad implementation /adds/ b[i] * y[i] so we need to
68 				 * toggle the sign for the b[] coefficients.
69 				 */
70 				cb[0] = 0;
71 				cb[1] = -a[1] * a0i;
72 				cb[2] = -a[2] * a0i;
73 			}
74 };
75 
76 /* now the individual prototypes.
77  * set-up is not optimal, i.e. does a lot of operations twice for readability.
78  */
79 class LP
80 : public RBJ
81 {
82 	public:
LP(double f,double Q,BiQuad & bq)83 		LP (double f, double Q, BiQuad & bq) : RBJ (f, Q)
84 			{ ab (bq.a, bq.b); }
85 
86 		template <class T>
LP(double f,double Q,T * ca,T * cb)87 		LP (double f, double Q, T * ca, T * cb) : RBJ (f, Q)
88 			{ ab (ca, cb); }
89 
90 		template <class T>
ab(T * ca,T * cb)91 		void ab (T * ca, T * cb)
92 			{
93 				b[0] = (1 - cos) * .5;
94 				b[1] = (1 - cos);
95 				b[2] = (1 - cos) * .5;
96 
97 				a[0] = 1 + alpha;
98 				a[1] = -2 * cos;
99 				a[2] = 1 - alpha;
100 
101 				make_direct_I (ca, cb);
102 			}
103 };
104 
105 class BP
106 : public RBJ
107 {
108 	public:
BP(double f,double Q,BiQuad & bq)109 		BP (double f, double Q, BiQuad & bq) : RBJ (f, Q)
110 			{ ab (bq.a, bq.b); }
111 
112 		template <class T>
BP(double f,double Q,T * ca,T * cb)113 		BP (double f, double Q, T * ca, T * cb) : RBJ (f, Q)
114 			{ ab (ca, cb); }
115 
116 		template <class T>
ab(T * ca,T * cb)117 		void ab (T * ca, T * cb)
118 			{
119 				b[0] = Q * alpha;
120 				b[1] = 0;
121 				b[2] = -Q * alpha;
122 
123 				a[0] = 1 + alpha;
124 				a[1] = -2 * cos;
125 				a[2] = 1 - alpha;
126 
127 				make_direct_I (ca, cb);
128 			}
129 };
130 
131 class HP
132 : public RBJ
133 {
134 	public:
HP(double f,double Q,BiQuad & bq)135 		HP (double f, double Q, BiQuad & bq) : RBJ (f, Q)
136 			{ ab (bq.a, bq.b); }
137 
138 		template <class T>
HP(double f,double Q,T * ca,T * cb)139 		HP (double f, double Q, T * ca, T * cb) : RBJ (f, Q)
140 			{ ab (ca, cb); }
141 
142 		template <class T>
ab(T * ca,T * cb)143 		void ab (T * ca, T * cb)
144 			{
145 				b[0] =  (1 + cos) * .5;
146 				b[1] = -(1 + cos);
147 				b[2] =  (1 + cos) * .5;
148 
149 				a[0] = 1 + alpha;
150 				a[1] = -2 * cos;
151 				a[2] = 1 - alpha;
152 
153 				make_direct_I (ca, cb);
154 			}
155 };
156 
157 class Notch
158 : public RBJ
159 {
160 	public:
Notch(double f,double Q,BiQuad & bq)161 		Notch (double f, double Q, BiQuad & bq) : RBJ (f, Q)
162 			{ ab (bq.a, bq.b); }
163 
164 		template <class T>
Notch(double f,double Q,T * ca,T * cb)165 		Notch (double f, double Q, T * ca, T * cb) : RBJ (f, Q)
166 			{ ab (ca, cb); }
167 
168 		template <class T>
ab(T * ca,T * cb)169 		void ab (T * ca, T * cb)
170 			{
171 				b[0] = 1;
172 				b[1] = -2 * cos;
173 				b[2] = 1;
174 
175 				a[0] = 1 + alpha;
176 				a[1] = -2 * cos;
177 				a[2] = 1 - alpha;
178 
179 				make_direct_I (ca, cb);
180 			}
181 };
182 
183 /* shelving and peaking dept. ////////////////////////////////////////////// */
184 
185 class PeakShelve
186 : public RBJ
187 {
188 	public:
189 		double A, beta;
190 
191 	public:
PeakShelve(double f,double Q,double dB)192 		PeakShelve (double f, double Q, double dB)
193 			: RBJ (f, Q)
194 			{
195 				A = pow (10, dB * .025);
196 				double S = Q; /* slope */
197 				beta = sqrt ((A * A + 1) / S - (A - 1) * (A - 1));
198 			}
199 };
200 
201 class LoShelve
202 : public PeakShelve
203 {
204 	public:
LoShelve(double f,double Q,double dB,BiQuad & bq)205 		LoShelve (double f, double Q, double dB, BiQuad & bq)
206 			: PeakShelve (f, Q, dB)
207 			{ ab (bq.a, bq.b); }
208 
209 		template <class T>
LoShelve(double f,double Q,double dB,T * ca,T * cb)210 		LoShelve (double f, double Q, double dB, T * ca, T * cb)
211 			: PeakShelve (f, Q, dB)
212 			{ ab (ca, cb); }
213 
214 		template <class T>
ab(T * ca,T * cb)215 		void ab (T * ca, T * cb)
216 			{
217 				double Ap1 = A + 1, Am1 = A - 1;
218 				double beta_sin = beta * sin;
219 
220 				b[0] =     A * (Ap1 - Am1 * cos + beta_sin);
221 				b[1] = 2 * A * (Am1 - Ap1 * cos);
222 				b[2] =     A * (Ap1 - Am1 * cos - beta_sin);
223 
224 				a[0] =          Ap1 + Am1 * cos + beta_sin;
225 				a[1] = -2 *    (Am1 + Ap1 * cos);
226 				a[2] =          Ap1 + Am1 * cos - beta_sin;
227 
228 				make_direct_I (ca, cb);
229 			}
230 };
231 
232 class PeakingEQ
233 : public PeakShelve
234 {
235 	public:
PeakingEQ(double f,double Q,double dB,BiQuad & bq)236 		PeakingEQ (double f, double Q, double dB, BiQuad & bq)
237 			: PeakShelve (f, Q, dB)
238 			{ ab (bq.a, bq.b); }
239 
240 		template <class T>
PeakingEQ(double f,double Q,double dB,T * ca,T * cb)241 		PeakingEQ (double f, double Q, double dB, T * ca, T * cb)
242 			: PeakShelve (f, Q, dB)
243 			{ ab (ca, cb); }
244 
245 		template <class T>
ab(T * ca,T * cb)246 		void ab (T * ca, T * cb)
247 			{
248 				b[0] = 1 + alpha * A;
249 				b[1] = -2 * cos;
250 				b[2] = 1 - alpha * A;
251 
252 				a[0] = 1 + alpha / A;
253 				a[1] = -2 * cos;
254 				a[2] = 1 - alpha / A;
255 
256 				make_direct_I (ca, cb);
257 			}
258 };
259 
260 class HiShelve
261 : public PeakShelve
262 {
263 	public:
HiShelve(double f,double Q,double dB,BiQuad & bq)264 		HiShelve (double f, double Q, double dB, BiQuad & bq)
265 			: PeakShelve (f, Q, dB)
266 			{ ab (bq.a, bq.b); }
267 
268 		template <class T>
HiShelve(double f,double Q,double dB,T * ca,T * cb)269 		HiShelve (double f, double Q, double dB, T * ca, T * cb)
270 			: PeakShelve (f, Q, dB)
271 			{ ab (ca, cb); }
272 
273 		template <class T>
ab(T * ca,T * cb)274 		void ab (T * ca, T * cb)
275 			{
276 				double Ap1 = A + 1, Am1 = A - 1;
277 				double beta_sin = beta * sin;
278 
279 				b[0] =      A * (Ap1 + Am1 * cos + beta_sin);
280 				b[1] = -2 * A * (Am1 + Ap1 * cos);
281 				b[2] =      A * (Ap1 + Am1 * cos - beta_sin);
282 
283 				a[0] =           Ap1 - Am1 * cos + beta_sin;
284 				a[1] =  2 *     (Am1 - Ap1 * cos);
285 				a[2] =           Ap1 - Am1 * cos - beta_sin;
286 
287 				make_direct_I (ca, cb);
288 			}
289 };
290 
291 } /* ~namespace RBJ */
292 } /* ~namespace DSP */
293 
294 #endif /* _DSP_RBJ_H_ */
295