-
Notifications
You must be signed in to change notification settings - Fork 37
/
Copy pathfilters.py
195 lines (144 loc) · 5.92 KB
/
filters.py
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
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''Filter construction and loading.
--------------------------------
`resampy` provides two pre-computed resampling filters which are tuned for either
high-quality or fast calculation:
- `kaiser_best` : 64 zero-crossings, a Kaiser window with beta=14.769656459379492,
and a roll-off frequency of Nyquist * 0.9475937167399596.
- `kaiser_fast` : 16 zero-crossings, a Kaiser window with beta=8.555504641634386,
and a roll-off frequency of Nyquist * 0.85.
These filters can be used by calling `resample` as follows:
>>> resampy.resample(x, sr_orig, sr_new, filter='kaiser_best') # High-quality
>>> resampy.resample(x, sr_orig, sr_new, filter='kaiser_fast') # Fast calculation
It is also possible to construct custom filters as follows:
>>> resampy.resample(x, sr_orig, sr_new, filter='sinc_window',
... **kwargs)
where ``**kwargs`` are additional parameters to `resampy.filters.sinc_window`_.
'''
import scipy.signal
import numpy as np
import os
import pkg_resources
import six
import sys
FILTER_FUNCTIONS = ['sinc_window']
__all__ = ['get_filter'] + FILTER_FUNCTIONS
def sinc_window(num_zeros=64, precision=9, window=None, rolloff=0.945):
'''Construct a windowed sinc interpolation filter
Parameters
----------
num_zeros : int > 0
The number of zero-crossings to retain in the sinc filter
precision : int > 0
The number of filter coefficients to retain for each zero-crossing
window : callable
The window function. By default, uses Blackman-Harris.
rolloff : float > 0
The roll-off frequency (as a fraction of nyquist)
Returns
-------
interp_window: np.ndarray [shape=(num_zeros * num_table + 1)]
The interpolation window (right-hand side)
num_bits: int
The number of bits of precision to use in the filter table
rolloff : float > 0
The roll-off frequency of the filter, as a fraction of Nyquist
Raises
------
TypeError
if `window` is not callable or `None`
ValueError
if `num_zeros < 1`, `precision < 1`,
or `rolloff` is outside the range `(0, 1]`.
Examples
--------
>>> # A filter with 10 zero-crossings, 32 samples per crossing, and a
... # Hann window for tapering.
>>> halfwin, prec, rolloff = resampy.filters.sinc_window(num_zeros=10, precision=5,
... window=scipy.signal.hann)
>>> halfwin
array([ 9.450e-01, 9.436e-01, ..., -7.455e-07, -0.000e+00])
>>> prec
32
>>> rolloff
0.945
>>> # Or using sinc-window filter construction directly in resample
>>> y = resampy.resample(x, sr_orig, sr_new, filter='sinc_window',
... num_zeros=10, precision=5,
... window=scipy.signal.hann)
'''
if window is None:
window = scipy.signal.blackmanharris
elif not six.callable(window):
raise TypeError('window must be callable, not type(window)={}'.format(type(window)))
if not 0 < rolloff <= 1:
raise ValueError('Invalid roll-off: rolloff={}'.format(rolloff))
if num_zeros < 1:
raise ValueError('Invalid num_zeros: num_zeros={}'.format(num_zeros))
if precision < 0:
raise ValueError('Invalid precision: precision={}'.format(precision))
# Generate the right-wing of the sinc
num_bits = 2**precision
n = num_bits * num_zeros
sinc_win = rolloff * np.sinc(rolloff * np.linspace(0, num_zeros, num=n + 1,
endpoint=True))
# Build the window function and cut off the left half
taper = window(2 * n + 1)[n:]
interp_win = (taper * sinc_win)
return interp_win, num_bits, rolloff
def get_filter(name_or_function, **kwargs):
'''Retrieve a window given its name or function handle.
Parameters
----------
name_or_function : str or callable
If a function, returns `name_or_function(**kwargs)`.
If a string, and it matches the name of one of the defined
filter functions, the corresponding function is called with `**kwargs`.
If a string, and it matches the name of a pre-computed filter,
the corresponding filter is retrieved, and kwargs is ignored.
Valid pre-computed filter names are:
- 'kaiser_fast'
- 'kaiser_best'
Returns
-------
half_window : np.ndarray
The right wing of the interpolation filter
precision : int > 0
The number of samples between zero-crossings of the filter
rolloff : float > 0
The roll-off frequency of the filter as a fraction of Nyquist
Raises
------
NotImplementedError
If `name_or_function` cannot be found as a filter.
'''
if name_or_function in FILTER_FUNCTIONS:
return getattr(sys.modules[__name__], name_or_function)(**kwargs)
elif six.callable(name_or_function):
return name_or_function(**kwargs)
else:
try:
return load_filter(name_or_function)
except (IOError, ValueError):
raise NotImplementedError('Cannot load filter definition for '
'{}'.format(name_or_function))
def load_filter(filter_name):
'''Retrieve a pre-computed filter.
Parameters
----------
filter_name : str
The key of the filter, e.g., 'kaiser_fast'
Returns
-------
half_window : np.ndarray
The right wing of the interpolation filter
precision : int > 0
The number of samples between zero-crossings of the fitler
rolloff : float > 0
The roll-off frequency of the filter, as a fraction of Nyquist
'''
fname = os.path.join('data',
os.path.extsep.join([filter_name, 'npz']))
data = np.load(pkg_resources.resource_filename(__name__, fname))
return data['half_window'], data['precision'], data['rolloff']