|
7 | 7 |
|
8 | 8 | Code based on https://webaudio.github.io/Audio-EQ-Cookbook/audio-eq-cookbook.html |
9 | 9 | Alternatively you can use scipy.signal.butter, which should yield the same results. |
| 10 | +
|
| 11 | +https://en.wikipedia.org/wiki/Butterworth_filter |
| 12 | +
|
| 13 | +Notation used throughout this module (from the RBJ Audio EQ Cookbook): |
| 14 | + w0 -- normalised angular frequency, ``2 * pi * frequency / samplerate`` |
| 15 | + alpha -- bandwidth parameter, ``sin(w0) / (2 * q_factor)`` |
| 16 | + b0..b2 -- feed-forward (numerator) coefficients of the biquad |
| 17 | + a0..a2 -- feed-back (denominator) coefficients of the biquad |
| 18 | +The a/b coefficient names match ``IIRFilter.set_coefficients`` and the standard |
| 19 | +biquad transfer function, so they are kept consistent across every filter here. |
10 | 20 | """ |
11 | 21 |
|
12 | 22 |
|
@@ -232,3 +242,81 @@ def make_highshelf( |
232 | 242 | filt = IIRFilter(2) |
233 | 243 | filt.set_coefficients([a0, a1, a2], [b0, b1, b2]) |
234 | 244 | return filt |
| 245 | + |
| 246 | + |
| 247 | +def make_notch( |
| 248 | + frequency: int, |
| 249 | + samplerate: int, |
| 250 | + q_factor: float = 1 / sqrt(2), |
| 251 | +) -> IIRFilter: |
| 252 | + """ |
| 253 | + Creates a notch (band-reject) filter that strongly attenuates a narrow band |
| 254 | + of frequencies around ``frequency`` while leaving the rest of the spectrum |
| 255 | + unchanged. It is the complement of the band-pass filter and is commonly used |
| 256 | + to remove a single tone such as 50/60 Hz mains hum. |
| 257 | +
|
| 258 | + https://en.wikipedia.org/wiki/Band-stop_filter |
| 259 | +
|
| 260 | + >>> filter = make_notch(1000, 48000) |
| 261 | + >>> filter.a_coeffs + filter.b_coeffs # doctest: +NORMALIZE_WHITESPACE |
| 262 | + [1.0922959556412573, -1.9828897227476208, 0.9077040443587427, 1.0, |
| 263 | + -1.9828897227476208, 1.0] |
| 264 | + """ |
| 265 | + w0 = tau * frequency / samplerate # centre frequency, in radians/sample |
| 266 | + _sin = sin(w0) |
| 267 | + _cos = cos(w0) |
| 268 | + alpha = _sin / (2 * q_factor) # controls how narrow the rejected band is |
| 269 | + |
| 270 | + # Feed-forward: a pair of zeros placed exactly on the notch frequency, so |
| 271 | + # that frequency is fully cancelled while the rest of the spectrum passes. |
| 272 | + b0 = 1.0 |
| 273 | + b1 = -2 * _cos |
| 274 | + b2 = 1.0 |
| 275 | + |
| 276 | + # Feed-back: matching poles just inside the unit circle keep the notch |
| 277 | + # narrow and the surrounding gain flat. |
| 278 | + a0 = 1 + alpha |
| 279 | + a1 = -2 * _cos |
| 280 | + a2 = 1 - alpha |
| 281 | + |
| 282 | + filt = IIRFilter(2) |
| 283 | + filt.set_coefficients([a0, a1, a2], [b0, b1, b2]) |
| 284 | + return filt |
| 285 | + |
| 286 | + |
| 287 | +def make_bandpass_peak( |
| 288 | + frequency: int, |
| 289 | + samplerate: int, |
| 290 | + q_factor: float = 1 / sqrt(2), |
| 291 | +) -> IIRFilter: |
| 292 | + """ |
| 293 | + Creates a band-pass filter with constant 0 dB peak gain. |
| 294 | +
|
| 295 | + Unlike :func:`make_bandpass`, whose skirt (edge) gain is held constant so the |
| 296 | + peak gain grows with ``q_factor``, this variant normalises the response so |
| 297 | + the peak always reaches 0 dB regardless of the chosen ``q_factor``. Both |
| 298 | + forms come from the RBJ Audio EQ Cookbook. |
| 299 | +
|
| 300 | + https://en.wikipedia.org/wiki/Band-pass_filter |
| 301 | +
|
| 302 | + >>> filter = make_bandpass_peak(1000, 48000) |
| 303 | + >>> filter.a_coeffs + filter.b_coeffs # doctest: +NORMALIZE_WHITESPACE |
| 304 | + [1.0922959556412573, -1.9828897227476208, 0.9077040443587427, |
| 305 | + 0.09229595564125725, 0, -0.09229595564125725] |
| 306 | + """ |
| 307 | + w0 = tau * frequency / samplerate |
| 308 | + _sin = sin(w0) |
| 309 | + _cos = cos(w0) |
| 310 | + alpha = _sin / (2 * q_factor) |
| 311 | + |
| 312 | + b0 = alpha |
| 313 | + b1 = 0 |
| 314 | + b2 = -alpha |
| 315 | + |
| 316 | + a0 = 1 + alpha |
| 317 | + a1 = -2 * _cos |
| 318 | + a2 = 1 - alpha |
| 319 | + |
| 320 | + filt = IIRFilter(2) |
| 321 | + filt.set_coefficients([a0, a1, a2], [b0, b1, b2]) |
| 322 | + return filt |
0 commit comments