biquad 0.2__tar.gz → 0.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. biquad-0.4/PKG-INFO +99 -0
  2. biquad-0.4/README.md +70 -0
  3. {biquad-0.2 → biquad-0.4}/setup.cfg +4 -0
  4. {biquad-0.2 → biquad-0.4}/src/python/biquad/__init__.py +13 -1
  5. biquad-0.4/src/python/biquad/allpass.py +110 -0
  6. biquad-0.4/src/python/biquad/bandpass.py +119 -0
  7. biquad-0.4/src/python/biquad/biquad.py +270 -0
  8. biquad-0.4/src/python/biquad/highpass.py +110 -0
  9. biquad-0.4/src/python/biquad/highshelf.py +115 -0
  10. biquad-0.4/src/python/biquad/lowpass.py +110 -0
  11. biquad-0.4/src/python/biquad/lowshelf.py +115 -0
  12. biquad-0.4/src/python/biquad/notch.py +110 -0
  13. biquad-0.4/src/python/biquad/peak.py +113 -0
  14. biquad-0.4/src/python/biquad.egg-info/PKG-INFO +99 -0
  15. {biquad-0.2 → biquad-0.4}/src/python/biquad.egg-info/SOURCES.txt +0 -1
  16. biquad-0.4/src/python/biquad.egg-info/requires.txt +5 -0
  17. biquad-0.2/PKG-INFO +0 -48
  18. biquad-0.2/README.md +0 -20
  19. biquad-0.2/src/python/biquad/allpass.py +0 -70
  20. biquad-0.2/src/python/biquad/bandpass.py +0 -77
  21. biquad-0.2/src/python/biquad/biquad.py +0 -112
  22. biquad-0.2/src/python/biquad/highpass.py +0 -70
  23. biquad-0.2/src/python/biquad/highshelf.py +0 -77
  24. biquad-0.2/src/python/biquad/lowpass.py +0 -70
  25. biquad-0.2/src/python/biquad/lowshelf.py +0 -77
  26. biquad-0.2/src/python/biquad/notch.py +0 -70
  27. biquad-0.2/src/python/biquad/peak.py +0 -77
  28. biquad-0.2/src/python/biquad/plot.py +0 -88
  29. biquad-0.2/src/python/biquad.egg-info/PKG-INFO +0 -48
  30. biquad-0.2/src/python/biquad.egg-info/requires.txt +0 -2
  31. {biquad-0.2 → biquad-0.4}/LICENSE +0 -0
  32. {biquad-0.2 → biquad-0.4}/pyproject.toml +0 -0
  33. {biquad-0.2 → biquad-0.4}/src/python/biquad.egg-info/dependency_links.txt +0 -0
  34. {biquad-0.2 → biquad-0.4}/src/python/biquad.egg-info/top_level.txt +0 -0
biquad-0.4/PKG-INFO ADDED
@@ -0,0 +1,99 @@
1
+ Metadata-Version: 2.1
2
+ Name: biquad
3
+ Version: 0.4
4
+ Summary: Collection of alterable digital biquad filters for dynamic audio effect creation
5
+ Home-page: https://github.com/jurihock/biquad
6
+ Author: Juergen Hock
7
+ Author-email: juergen.hock@jurihock.de
8
+ License: MIT
9
+ Keywords: digital,audio,signal,processing,dasp,dafx,effects,filter,equalizer,eq,biquad,frequency,phase,spectrum,algorithms,analysis,synthesis,c,cpp,python
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Intended Audience :: Education
12
+ Classifier: Intended Audience :: Other Audience
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: C
16
+ Classifier: Programming Language :: C++
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Topic :: Artistic Software
19
+ Classifier: Topic :: Education
20
+ Classifier: Topic :: Multimedia :: Sound/Audio
21
+ Classifier: Topic :: Multimedia :: Sound/Audio :: Analysis
22
+ Classifier: Topic :: Multimedia :: Sound/Audio :: Sound Synthesis
23
+ Classifier: Topic :: Scientific/Engineering
24
+ Classifier: Topic :: Software Development :: Libraries
25
+ Requires-Python: >=3
26
+ Description-Content-Type: text/markdown
27
+ Provides-Extra: plot
28
+ License-File: LICENSE
29
+
30
+ # Alterable biquad filters
31
+
32
+ ![language](https://img.shields.io/badge/languages-C%2B%2B%20Python-blue)
33
+ ![license](https://img.shields.io/github/license/jurihock/biquad?color=green)
34
+ ![pypi](https://img.shields.io/pypi/v/biquad?color=gold)
35
+
36
+ This is a collection of [digital biquad filters](https://webaudio.github.io/Audio-EQ-Cookbook/audio-eq-cookbook.html) whose parameters `f` (frequency in *Hz*), `g` (gain in *dB*) and `q` (quality) can be varied at runtime. Following [DF1](https://ccrma.stanford.edu/~jos/fp/Direct_Form_I.html) filter implementations are available:
37
+
38
+ - Allpass
39
+ - Bandpass
40
+ - Highpass
41
+ - Lowpass
42
+ - Highshelf
43
+ - Lowshelf
44
+ - Notch
45
+ - Peak
46
+
47
+ ## Basic usage
48
+
49
+ Filter with static configuration:
50
+
51
+ ```python
52
+ import biquad
53
+ import numpy as np
54
+
55
+ # load audio samples somehow
56
+ x, sr = np.zeros(...), 44100
57
+
58
+ # create a filter of your choice
59
+ f = biquad.bandpass(sr, f=sr/4, q=1)
60
+
61
+ # process all audio samples
62
+ y = f(x)
63
+ ```
64
+
65
+ Filter with dynamic configuration:
66
+
67
+ ```python
68
+ import biquad
69
+ import numpy as np
70
+
71
+ # load audio samples somehow
72
+ x, sr = np.zeros(...), 44100
73
+
74
+ # create a filter of your choice
75
+ f = biquad.bandpass(sr)
76
+
77
+ # create parameter modifications as you like
78
+ myf = np.linspace(1, sr/4, len(x))
79
+ myq = np.linspace(2, 1/2, len(x))
80
+
81
+ # process all audio samples
82
+ y = f(x, f=myf, q=myq)
83
+ ```
84
+
85
+ Keep in mind:
86
+
87
+ - All filters have a default value for the persistent parameters `g` and `q`, which is set in the particular `__init__` method.
88
+ - Parameter `f` must be set either in the `__init__` or in the `__call__` method.
89
+ - The optional instantaneous parameters `f`, `g` and `q`, if specified in the `__call__` method, override the persistent ones.
90
+
91
+ ## References
92
+
93
+ 1. <span id="1">[Cookbook formulae for audio EQ biquad filter coefficients](https://webaudio.github.io/Audio-EQ-Cookbook/audio-eq-cookbook.html) by Robert Bristow-Johnson</span>
94
+ 2. <span id="2">[Introduction to Digital Filters with Audio Applications](https://ccrma.stanford.edu/~jos/filters/filters.html) by Julius O. Smith III</span>
95
+
96
+ ## License
97
+
98
+ [github.com/jurihock/biquad](https://github.com/jurihock/biquad) is licensed under the terms of the MIT license.
99
+ For details please refer to the accompanying [LICENSE](https://github.com/jurihock/biquad/raw/main/LICENSE) file distributed with it.
biquad-0.4/README.md ADDED
@@ -0,0 +1,70 @@
1
+ # Alterable biquad filters
2
+
3
+ ![language](https://img.shields.io/badge/languages-C%2B%2B%20Python-blue)
4
+ ![license](https://img.shields.io/github/license/jurihock/biquad?color=green)
5
+ ![pypi](https://img.shields.io/pypi/v/biquad?color=gold)
6
+
7
+ This is a collection of [digital biquad filters](https://webaudio.github.io/Audio-EQ-Cookbook/audio-eq-cookbook.html) whose parameters `f` (frequency in *Hz*), `g` (gain in *dB*) and `q` (quality) can be varied at runtime. Following [DF1](https://ccrma.stanford.edu/~jos/fp/Direct_Form_I.html) filter implementations are available:
8
+
9
+ - Allpass
10
+ - Bandpass
11
+ - Highpass
12
+ - Lowpass
13
+ - Highshelf
14
+ - Lowshelf
15
+ - Notch
16
+ - Peak
17
+
18
+ ## Basic usage
19
+
20
+ Filter with static configuration:
21
+
22
+ ```python
23
+ import biquad
24
+ import numpy as np
25
+
26
+ # load audio samples somehow
27
+ x, sr = np.zeros(...), 44100
28
+
29
+ # create a filter of your choice
30
+ f = biquad.bandpass(sr, f=sr/4, q=1)
31
+
32
+ # process all audio samples
33
+ y = f(x)
34
+ ```
35
+
36
+ Filter with dynamic configuration:
37
+
38
+ ```python
39
+ import biquad
40
+ import numpy as np
41
+
42
+ # load audio samples somehow
43
+ x, sr = np.zeros(...), 44100
44
+
45
+ # create a filter of your choice
46
+ f = biquad.bandpass(sr)
47
+
48
+ # create parameter modifications as you like
49
+ myf = np.linspace(1, sr/4, len(x))
50
+ myq = np.linspace(2, 1/2, len(x))
51
+
52
+ # process all audio samples
53
+ y = f(x, f=myf, q=myq)
54
+ ```
55
+
56
+ Keep in mind:
57
+
58
+ - All filters have a default value for the persistent parameters `g` and `q`, which is set in the particular `__init__` method.
59
+ - Parameter `f` must be set either in the `__init__` or in the `__call__` method.
60
+ - The optional instantaneous parameters `f`, `g` and `q`, if specified in the `__call__` method, override the persistent ones.
61
+
62
+ ## References
63
+
64
+ 1. <span id="1">[Cookbook formulae for audio EQ biquad filter coefficients](https://webaudio.github.io/Audio-EQ-Cookbook/audio-eq-cookbook.html) by Robert Bristow-Johnson</span>
65
+ 2. <span id="2">[Introduction to Digital Filters with Audio Applications](https://ccrma.stanford.edu/~jos/filters/filters.html) by Julius O. Smith III</span>
66
+
67
+ ## License
68
+
69
+ [github.com/jurihock/biquad](https://github.com/jurihock/biquad) is licensed under the terms of the MIT license.
70
+ For details please refer to the accompanying [LICENSE](https://github.com/jurihock/biquad/raw/main/LICENSE) file distributed with it.
@@ -58,6 +58,10 @@ install_requires =
58
58
  numba
59
59
  numpy
60
60
 
61
+ [options.extras_require]
62
+ plot =
63
+ matplotlib
64
+
61
65
  [egg_info]
62
66
  tag_build =
63
67
  tag_date = 0
@@ -1,4 +1,4 @@
1
- __version__ = "0.2"
1
+ __version__ = "0.4"
2
2
 
3
3
 
4
4
  from .allpass import allpass
@@ -13,6 +13,18 @@ from .peak import peak
13
13
 
14
14
 
15
15
  def filter(name, sr, **kwargs):
16
+ """
17
+ Create a filter instance of the specified filter name.
18
+
19
+ Parameters
20
+ ----------
21
+ sr : int or float
22
+ Sample rate in hertz.
23
+ f : int or float, optional
24
+ Persistent filter frequency parameter in hertz.
25
+ q : int or float, optional
26
+ Persistent filter quality parameter.
27
+ """
16
28
 
17
29
  name = str(name).lower()
18
30
 
@@ -0,0 +1,110 @@
1
+ """
2
+ Copyright (c) 2023 Juergen Hock
3
+
4
+ SPDX-License-Identifier: MIT
5
+
6
+ Source: https://github.com/jurihock/biquad
7
+ """
8
+
9
+ from .biquad import biquad, __df1__, __gain__, __resize__
10
+
11
+ import numba
12
+ import numpy
13
+
14
+
15
+ class allpass(biquad):
16
+ """
17
+ Allpass filter (APF).
18
+ """
19
+
20
+ def __init__(self, sr, f=None, g=0, q=1):
21
+ """
22
+ Create a new filter instance.
23
+
24
+ Parameters
25
+ ----------
26
+ sr : int or float
27
+ Sample rate in hertz.
28
+ f : int or float, optional
29
+ Persistent filter frequency parameter in hertz.
30
+ g : int or float, optional
31
+ Persistent filter gain parameter in decibel.
32
+ q : int or float, optional
33
+ Persistent filter quality parameter.
34
+ """
35
+
36
+ super().__init__(sr=sr, f=f, g=g, q=q)
37
+
38
+ self.__call__(0) # warmup numba
39
+
40
+ def __call__(self, x, f=None, g=None, q=None):
41
+ """
42
+ Process single or multiple contiguous signal values at once.
43
+
44
+ Parameters
45
+ ----------
46
+ x : scalar or array like
47
+ Filter input data.
48
+ f : scalar or array like, optional
49
+ Instantaneous filter frequency parameter in hertz.
50
+ g : scalar or array like, optional
51
+ Instantaneous filter gain parameter in decibel.
52
+ q : scalar or array like, optional
53
+ Instantaneous filter quality parameter.
54
+
55
+ Returns
56
+ -------
57
+ y : scalar or ndarray
58
+ Filter output data of the same shape and dtype as the input x.
59
+ """
60
+
61
+ scalar = numpy.isscalar(x)
62
+
63
+ ba = self.ba
64
+ xy = self.xy
65
+
66
+ x = numpy.atleast_1d(x)
67
+ y = numpy.zeros(x.shape, x.dtype)
68
+
69
+ f = __resize__(self.f if f is None else f, x.shape)
70
+ g = __resize__(self.g if g is None else __gain__(g), x.shape)
71
+ q = __resize__(self.q if q is None else q, x.shape)
72
+
73
+ sr = self.sr
74
+
75
+ self.__filter__(ba, xy, x, y, f, g, q, sr)
76
+
77
+ self.f = f[-1]
78
+ self.g = g[-1]
79
+ self.q = q[-1]
80
+
81
+ return y[0] if scalar else y
82
+
83
+ @staticmethod
84
+ @numba.jit(nopython=True, fastmath=True)
85
+ def __filter__(ba, xy, x, y, f, g, q, sr):
86
+
87
+ rs = 2 * numpy.pi / sr
88
+
89
+ for i in range(x.size):
90
+
91
+ w = f[i] * rs
92
+
93
+ cosw = numpy.cos(w)
94
+ sinw = numpy.sin(w)
95
+
96
+ alpha = sinw / (+2 * q[i])
97
+ beta = cosw * (-2)
98
+
99
+ # update b
100
+ ba[0, 0] = 1 - alpha
101
+ ba[0, 1] = beta
102
+ ba[0, 2] = 1 + alpha
103
+
104
+ # update a
105
+ ba[1, 0] = 1 + alpha
106
+ ba[1, 1] = beta
107
+ ba[1, 2] = 1 - alpha
108
+
109
+ # update y
110
+ __df1__(g[i], ba, xy, x, y, i)
@@ -0,0 +1,119 @@
1
+ """
2
+ Copyright (c) 2023 Juergen Hock
3
+
4
+ SPDX-License-Identifier: MIT
5
+
6
+ Source: https://github.com/jurihock/biquad
7
+ """
8
+
9
+ from .biquad import biquad, __df1__, __gain__, __resize__
10
+
11
+ import numba
12
+ import numpy
13
+
14
+
15
+ class bandpass(biquad):
16
+ """
17
+ Bandpass filter (BPF).
18
+ """
19
+
20
+ def __init__(self, sr, f=None, g=0, q=0.7071, *, mode='peak'):
21
+ """
22
+ Create a new filter instance.
23
+
24
+ Parameters
25
+ ----------
26
+ sr : int or float
27
+ Sample rate in hertz.
28
+ f : int or float, optional
29
+ Persistent filter frequency parameter in hertz.
30
+ g : int or float, optional
31
+ Persistent filter gain parameter in decibel.
32
+ q : int or float, optional
33
+ Persistent filter quality parameter.
34
+ mode : str, peak or skirt, optional
35
+ Choice between constant 0 dB peak gain or constant skirt gain.
36
+ """
37
+
38
+ super().__init__(sr=sr, f=f, g=g, q=q)
39
+
40
+ assert mode in ['peak', 'skirt']
41
+
42
+ self.mode = mode
43
+
44
+ self.__call__(0) # warmup numba
45
+
46
+ def __call__(self, x, f=None, g=None, q=None):
47
+ """
48
+ Process single or multiple contiguous signal values at once.
49
+
50
+ Parameters
51
+ ----------
52
+ x : scalar or array like
53
+ Filter input data.
54
+ f : scalar or array like, optional
55
+ Instantaneous filter frequency parameter in hertz.
56
+ g : scalar or array like, optional
57
+ Instantaneous filter gain parameter in decibel.
58
+ q : scalar or array like, optional
59
+ Instantaneous filter quality parameter.
60
+
61
+ Returns
62
+ -------
63
+ y : scalar or ndarray
64
+ Filter output data of the same shape and dtype as the input x.
65
+ """
66
+
67
+ scalar = numpy.isscalar(x)
68
+
69
+ ba = self.ba
70
+ xy = self.xy
71
+
72
+ x = numpy.atleast_1d(x)
73
+ y = numpy.zeros(x.shape, x.dtype)
74
+
75
+ f = __resize__(self.f if f is None else f, x.shape)
76
+ g = __resize__(self.g if g is None else __gain__(g), x.shape)
77
+ q = __resize__(self.q if q is None else q, x.shape)
78
+
79
+ sr = self.sr
80
+ mode = self.mode
81
+
82
+ self.__filter__(ba, xy, x, y, f, g, q, sr, mode)
83
+
84
+ self.f = f[-1]
85
+ self.g = g[-1]
86
+ self.q = q[-1]
87
+
88
+ return y[0] if scalar else y
89
+
90
+ @staticmethod
91
+ @numba.jit(nopython=True, fastmath=True)
92
+ def __filter__(ba, xy, x, y, f, g, q, sr, mode):
93
+
94
+ rs = 2 * numpy.pi / sr
95
+ skirt = mode == 'skirt'
96
+
97
+ for i in range(x.size):
98
+
99
+ w = f[i] * rs
100
+
101
+ cosw = numpy.cos(w)
102
+ sinw = numpy.sin(w)
103
+
104
+ alpha = sinw / (+2 * q[i])
105
+ beta = cosw * (-2)
106
+ gamma = sinw / (+2) if skirt else alpha
107
+
108
+ # update b
109
+ ba[0, 0] = +gamma
110
+ ba[0, 1] = 0
111
+ ba[0, 2] = -gamma
112
+
113
+ # update a
114
+ ba[1, 0] = 1 + alpha
115
+ ba[1, 1] = beta
116
+ ba[1, 2] = 1 - alpha
117
+
118
+ # update y
119
+ __df1__(g[i], ba, xy, x, y, i)
@@ -0,0 +1,270 @@
1
+ """
2
+ Copyright (c) 2023 Juergen Hock
3
+
4
+ SPDX-License-Identifier: MIT
5
+
6
+ Source: https://github.com/jurihock/biquad
7
+ """
8
+
9
+ import numba
10
+ import numpy
11
+
12
+
13
+ @numba.jit(nopython=True, fastmath=True)
14
+ def __df1__(g, ba, xy, x, y, i):
15
+ """
16
+ Compute filter output y[i] based on filter input x[i]
17
+ as well as specified filter gain g, coeffs ba and delay line xy,
18
+ according to the Direct Form 1.
19
+ """
20
+
21
+ # roll x
22
+ xy[0, 2] = xy[0, 1]
23
+ xy[0, 1] = xy[0, 0]
24
+
25
+ # roll y
26
+ xy[1, 2] = xy[1, 1]
27
+ xy[1, 1] = xy[1, 0]
28
+
29
+ # update x
30
+ xy[0, 0] = x[i]
31
+
32
+ # compute intermediate results b*x and a*y
33
+ bx = ba[0, 0] * xy[0, 0] + ba[0, 1] * xy[0, 1] + ba[0, 2] * xy[0, 2]
34
+ ay = ba[1, 1] * xy[1, 1] + ba[1, 2] * xy[1, 2]
35
+
36
+ # update y
37
+ xy[1, 0] = (bx * g - ay) / ba[1, 0]
38
+
39
+ # return y
40
+ y[i] = xy[1, 0]
41
+
42
+
43
+ def __gain__(x, divisor=20):
44
+ """
45
+ Convert the specified decibel gain to the linear gain.
46
+ """
47
+
48
+ if numpy.isscalar(x):
49
+
50
+ y = x / divisor
51
+ y = 10 ** y
52
+
53
+ else:
54
+
55
+ y = numpy.atleast_1d(x) / divisor
56
+ numpy.power(10, y, out=y)
57
+
58
+ return y
59
+
60
+
61
+ def __resize__(x, shape):
62
+ """
63
+ Resize the specified value to the ndarray of desired shape.
64
+ """
65
+
66
+ if numpy.isscalar(x):
67
+
68
+ y = numpy.full(shape, x)
69
+
70
+ else:
71
+
72
+ x = numpy.atleast_1d(x)
73
+ y = numpy.resize(x, shape)
74
+
75
+ return y
76
+
77
+
78
+ class biquad:
79
+ """
80
+ Biquad filter base class.
81
+ """
82
+
83
+ ba = numpy.array([[1, 0, 0], [1, 0, 0]], float)
84
+ """
85
+ Biquad filter coefficient matrix of shape (2, 3) excl. gain factor:
86
+ - ba[0] holds b coefficients
87
+ - ba[1] holds a coefficients
88
+ """
89
+
90
+ xy = numpy.array([[0, 0, 0], [0, 0, 0]], float)
91
+ """
92
+ Biquad filter delay line matrix of shape (2, 3):
93
+ - xy[0] holds input values
94
+ - xy[1] holds output values
95
+ """
96
+
97
+ def __init__(self, sr, f=None, g=None, q=None):
98
+ """
99
+ Create a new filter instance.
100
+
101
+ Parameters
102
+ ----------
103
+ sr : int or float
104
+ Sample rate in hertz.
105
+ f : int or float, optional
106
+ Persistent filter frequency parameter in hertz.
107
+ g : int or float, optional
108
+ Persistent filter gain parameter in decibel.
109
+ q : int or float, optional
110
+ Persistent filter quality parameter.
111
+ """
112
+
113
+ assert (sr is not None) and (numpy.isscalar(sr) and numpy.isreal(sr))
114
+ assert (f is None) or (numpy.isscalar(f) and numpy.isreal(f))
115
+ assert (g is None) or (numpy.isscalar(g) and numpy.isreal(g))
116
+ assert (q is None) or (numpy.isscalar(q) and numpy.isreal(q))
117
+
118
+ self.sr = sr
119
+ self.f = (sr / 4) if f is None else f
120
+ self.g = __gain__(0 if g is None else g)
121
+ self.q = 1 if q is None else q
122
+
123
+ # warmup numba
124
+ g = self.g
125
+ ba = self.ba
126
+ xy = self.xy
127
+ x = numpy.zeros(1, float)
128
+ y = numpy.zeros(x.shape, x.dtype)
129
+ __df1__(g, ba, xy, x, y, 0)
130
+
131
+ def __call__(self, x, f=None, g=None, q=None):
132
+ """
133
+ Process single or multiple contiguous signal values at once.
134
+
135
+ Parameters
136
+ ----------
137
+ x : scalar or array like
138
+ Filter input data.
139
+ f : scalar or array like, optional
140
+ Instantaneous filter frequency parameter in hertz.
141
+ g : scalar or array like, optional
142
+ Instantaneous filter gain parameter in decibel.
143
+ q : scalar or array like, optional
144
+ Instantaneous filter quality parameter.
145
+
146
+ Returns
147
+ -------
148
+ y : scalar or ndarray
149
+ Filter output data of the same shape and dtype as the input x.
150
+ """
151
+
152
+ scalar = numpy.isscalar(x)
153
+
154
+ ba = self.ba
155
+ xy = self.xy
156
+
157
+ x = numpy.atleast_1d(x)
158
+ y = numpy.zeros(x.shape, x.dtype)
159
+
160
+ f = __resize__(self.f if f is None else f, x.shape)
161
+ g = __resize__(self.g if g is None else __gain__(g), x.shape)
162
+ q = __resize__(self.q if q is None else q, x.shape)
163
+
164
+ self.__filter__(ba, xy, x, y, g)
165
+
166
+ self.f = f[-1]
167
+ self.g = g[-1]
168
+ self.q = q[-1]
169
+
170
+ return y[0] if scalar else y
171
+
172
+ @staticmethod
173
+ @numba.jit(nopython=True, fastmath=True)
174
+ def __filter__(ba, xy, x, y, g):
175
+
176
+ for i in range(x.size):
177
+
178
+ __df1__(g[i], ba, xy, x, y, i)
179
+
180
+ def response(self, *, norm=False, log=False):
181
+ """
182
+ Returns frequency and phase response of the transfer function given by the ba coefficients.
183
+
184
+ Parameters
185
+ ----------
186
+ norm : bool, optional
187
+ Option whether to normalize the output frequency response.
188
+ log : bool, optional
189
+ Option whether to express the output frequency values logarithmically.
190
+
191
+ Returns
192
+ -------
193
+ w : array
194
+ Corresponding frequency values.
195
+ h : array
196
+ Complex filter response values.
197
+
198
+ See also
199
+ --------
200
+ scipy.signal.freqz
201
+ """
202
+
203
+ (b, a), sr = self.ba, self.sr
204
+
205
+ b *= self.g
206
+
207
+ n = int(sr / 2)
208
+
209
+ # compute frequencies from 0 to pi or sr/2 but excluding the Nyquist frequency
210
+ w = numpy.linspace(0, numpy.pi, n, endpoint=False) \
211
+ if not log else \
212
+ numpy.logspace(numpy.log10(1), numpy.log10(numpy.pi), n, endpoint=False, base=10)
213
+
214
+ # compute the z-domain transfer function
215
+ z = numpy.exp(-1j * w)
216
+ x = numpy.polynomial.polynomial.polyval(z, a, tensor=False)
217
+ y = numpy.polynomial.polynomial.polyval(z, b, tensor=False)
218
+ h = y / x
219
+
220
+ # normalize frequency amplitudes
221
+ h /= len(h) if norm else 1
222
+
223
+ # normalize frequency values according to sr
224
+ w = (w * sr) / (2 * numpy.pi)
225
+
226
+ return w, h
227
+
228
+ def plot(self, *, log=False):
229
+ """
230
+ Creates filter frequency and phase response plot.
231
+
232
+ Parameters
233
+ ----------
234
+ log : bool, optional
235
+ Option whether to express frequency values logarithmically.
236
+ """
237
+
238
+ import matplotlib.pyplot as pyplot
239
+
240
+ w, h = self.response()
241
+
242
+ with numpy.errstate(divide='ignore', invalid='ignore'):
243
+ habs = 20 * numpy.log10(numpy.abs(h))
244
+
245
+ harg = numpy.angle(h)
246
+
247
+ pyplot.plot(w, habs, color='b', alpha=0.9)
248
+ axis1 = pyplot.gca()
249
+ axis2 = axis1.twinx()
250
+ axis2.plot(w, harg, color='g', alpha=0.9)
251
+
252
+ axis1.set_xlabel('Hz')
253
+ axis1.set_ylabel('dB', color='b')
254
+ axis2.set_ylabel('rad', color='g')
255
+
256
+ habsmin = numpy.min(habs[numpy.isfinite(habs)])
257
+ habsmax = numpy.max(habs[numpy.isfinite(habs)])
258
+
259
+ habsmin = numpy.minimum(habsmin, -100)
260
+ habsmax = numpy.maximum(habsmax, +10)
261
+
262
+ hargmin = -numpy.pi
263
+ hargmax = +numpy.pi
264
+
265
+ axis1.set_ylim((habsmin * 1.1, habsmax * 1.1))
266
+ axis2.set_ylim((hargmin * 1.1, hargmax * 1.1))
267
+
268
+ if log: axis1.set_xscale('log')
269
+
270
+ return pyplot