Prompt48 commited on
Commit
8f03048
·
verified ·
1 Parent(s): af25e42

Upload edit\Qwen3-TTS-test\.venv\Lib\site-packages\librosa\filters.py with huggingface_hub

Browse files
edit//Qwen3-TTS-test//.venv//Lib//site-packages//librosa//filters.py ADDED
@@ -0,0 +1,1669 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Filters
5
+ =======
6
+
7
+ Filter bank construction
8
+ ------------------------
9
+ .. autosummary::
10
+ :toctree: generated/
11
+
12
+ mel
13
+ chroma
14
+ wavelet
15
+ semitone_filterbank
16
+
17
+ Window functions
18
+ ----------------
19
+ .. autosummary::
20
+ :toctree: generated/
21
+
22
+ window_bandwidth
23
+ get_window
24
+
25
+ Miscellaneous
26
+ -------------
27
+ .. autosummary::
28
+ :toctree: generated/
29
+
30
+ wavelet_lengths
31
+ cq_to_chroma
32
+ mr_frequencies
33
+ window_sumsquare
34
+ diagonal_filter
35
+
36
+ Deprecated
37
+ ----------
38
+ .. autosummary::
39
+ :toctree: generated/
40
+
41
+ constant_q
42
+ constant_q_lengths
43
+
44
+ """
45
+ import warnings
46
+
47
+ import numpy as np
48
+ import scipy
49
+ import scipy.signal
50
+ import scipy.ndimage
51
+
52
+ from numba import jit
53
+
54
+ from ._cache import cache
55
+ from . import util
56
+ from .util.exceptions import ParameterError
57
+ from .util.decorators import deprecated
58
+
59
+ from .core.convert import note_to_hz, hz_to_midi, midi_to_hz, hz_to_octs
60
+ from .core.convert import fft_frequencies, mel_frequencies
61
+ from numpy.typing import ArrayLike, DTypeLike
62
+ from typing import Any, List, Optional, Tuple, Union
63
+ from typing_extensions import Literal
64
+ from ._typing import _WindowSpec, _FloatLike_co
65
+
66
+ __all__ = [
67
+ "mel",
68
+ "chroma",
69
+ "constant_q",
70
+ "constant_q_lengths",
71
+ "cq_to_chroma",
72
+ "window_bandwidth",
73
+ "get_window",
74
+ "mr_frequencies",
75
+ "semitone_filterbank",
76
+ "window_sumsquare",
77
+ "diagonal_filter",
78
+ "wavelet",
79
+ "wavelet_lengths",
80
+ ]
81
+
82
+ # Dictionary of window function bandwidths
83
+
84
+ WINDOW_BANDWIDTHS = {
85
+ "bart": 1.3334961334912805,
86
+ "barthann": 1.4560255965133932,
87
+ "bartlett": 1.3334961334912805,
88
+ "bkh": 2.0045975283585014,
89
+ "black": 1.7269681554262326,
90
+ "blackharr": 2.0045975283585014,
91
+ "blackman": 1.7269681554262326,
92
+ "blackmanharris": 2.0045975283585014,
93
+ "blk": 1.7269681554262326,
94
+ "bman": 1.7859588613860062,
95
+ "bmn": 1.7859588613860062,
96
+ "bohman": 1.7859588613860062,
97
+ "box": 1.0,
98
+ "boxcar": 1.0,
99
+ "brt": 1.3334961334912805,
100
+ "brthan": 1.4560255965133932,
101
+ "bth": 1.4560255965133932,
102
+ "cosine": 1.2337005350199792,
103
+ "flat": 2.7762255046484143,
104
+ "flattop": 2.7762255046484143,
105
+ "flt": 2.7762255046484143,
106
+ "halfcosine": 1.2337005350199792,
107
+ "ham": 1.3629455320350348,
108
+ "hamm": 1.3629455320350348,
109
+ "hamming": 1.3629455320350348,
110
+ "han": 1.50018310546875,
111
+ "hann": 1.50018310546875,
112
+ "nut": 1.9763500280946082,
113
+ "nutl": 1.9763500280946082,
114
+ "nuttall": 1.9763500280946082,
115
+ "ones": 1.0,
116
+ "par": 1.9174603174603191,
117
+ "parz": 1.9174603174603191,
118
+ "parzen": 1.9174603174603191,
119
+ "rect": 1.0,
120
+ "rectangular": 1.0,
121
+ "tri": 1.3331706523555851,
122
+ "triang": 1.3331706523555851,
123
+ "triangle": 1.3331706523555851,
124
+ }
125
+
126
+
127
+ @cache(level=10)
128
+ def mel(
129
+ *,
130
+ sr: float,
131
+ n_fft: int,
132
+ n_mels: int = 128,
133
+ fmin: float = 0.0,
134
+ fmax: Optional[float] = None,
135
+ htk: bool = False,
136
+ norm: Optional[Union[Literal["slaney"], float]] = "slaney",
137
+ dtype: DTypeLike = np.float32,
138
+ ) -> np.ndarray:
139
+ """Create a Mel filter-bank.
140
+
141
+ This produces a linear transformation matrix to project
142
+ FFT bins onto Mel-frequency bins.
143
+
144
+ Parameters
145
+ ----------
146
+ sr : number > 0 [scalar]
147
+ sampling rate of the incoming signal
148
+
149
+ n_fft : int > 0 [scalar]
150
+ number of FFT components
151
+
152
+ n_mels : int > 0 [scalar]
153
+ number of Mel bands to generate
154
+
155
+ fmin : float >= 0 [scalar]
156
+ lowest frequency (in Hz)
157
+
158
+ fmax : float >= 0 [scalar]
159
+ highest frequency (in Hz).
160
+ If `None`, use ``fmax = sr / 2.0``
161
+
162
+ htk : bool [scalar]
163
+ use HTK formula instead of Slaney
164
+
165
+ norm : {None, 'slaney', or number} [scalar]
166
+ If 'slaney', divide the triangular mel weights by the width of the mel band
167
+ (area normalization).
168
+
169
+ If numeric, use `librosa.util.normalize` to normalize each filter by to unit l_p norm.
170
+ See `librosa.util.normalize` for a full description of supported norm values
171
+ (including `+-np.inf`).
172
+
173
+ Otherwise, leave all the triangles aiming for a peak value of 1.0
174
+
175
+ dtype : np.dtype
176
+ The data type of the output basis.
177
+ By default, uses 32-bit (single-precision) floating point.
178
+
179
+ Returns
180
+ -------
181
+ M : np.ndarray [shape=(n_mels, 1 + n_fft/2)]
182
+ Mel transform matrix
183
+
184
+ See Also
185
+ --------
186
+ librosa.util.normalize
187
+
188
+ Notes
189
+ -----
190
+ This function caches at level 10.
191
+
192
+ Examples
193
+ --------
194
+ >>> melfb = librosa.filters.mel(sr=22050, n_fft=2048)
195
+ >>> melfb
196
+ array([[ 0. , 0.016, ..., 0. , 0. ],
197
+ [ 0. , 0. , ..., 0. , 0. ],
198
+ ...,
199
+ [ 0. , 0. , ..., 0. , 0. ],
200
+ [ 0. , 0. , ..., 0. , 0. ]])
201
+
202
+ Clip the maximum frequency to 8KHz
203
+
204
+ >>> librosa.filters.mel(sr=22050, n_fft=2048, fmax=8000)
205
+ array([[ 0. , 0.02, ..., 0. , 0. ],
206
+ [ 0. , 0. , ..., 0. , 0. ],
207
+ ...,
208
+ [ 0. , 0. , ..., 0. , 0. ],
209
+ [ 0. , 0. , ..., 0. , 0. ]])
210
+
211
+ >>> import matplotlib.pyplot as plt
212
+ >>> fig, ax = plt.subplots()
213
+ >>> img = librosa.display.specshow(melfb, x_axis='linear', ax=ax)
214
+ >>> ax.set(ylabel='Mel filter', title='Mel filter bank')
215
+ >>> fig.colorbar(img, ax=ax)
216
+ """
217
+ if fmax is None:
218
+ fmax = float(sr) / 2
219
+
220
+ # Initialize the weights
221
+ n_mels = int(n_mels)
222
+ weights = np.zeros((n_mels, int(1 + n_fft // 2)), dtype=dtype)
223
+
224
+ # Center freqs of each FFT bin
225
+ fftfreqs = fft_frequencies(sr=sr, n_fft=n_fft)
226
+
227
+ # 'Center freqs' of mel bands - uniformly spaced between limits
228
+ mel_f = mel_frequencies(n_mels + 2, fmin=fmin, fmax=fmax, htk=htk)
229
+
230
+ fdiff = np.diff(mel_f)
231
+ ramps = np.subtract.outer(mel_f, fftfreqs)
232
+
233
+ for i in range(n_mels):
234
+ # lower and upper slopes for all bins
235
+ lower = -ramps[i] / fdiff[i]
236
+ upper = ramps[i + 2] / fdiff[i + 1]
237
+
238
+ # .. then intersect them with each other and zero
239
+ weights[i] = np.maximum(0, np.minimum(lower, upper))
240
+
241
+ if isinstance(norm, str):
242
+ if norm == "slaney":
243
+ # Slaney-style mel is scaled to be approx constant energy per channel
244
+ enorm = 2.0 / (mel_f[2 : n_mels + 2] - mel_f[:n_mels])
245
+ weights *= enorm[:, np.newaxis]
246
+ else:
247
+ raise ParameterError(f"Unsupported norm={norm}")
248
+ else:
249
+ weights = util.normalize(weights, norm=norm, axis=-1)
250
+
251
+ # Only check weights if f_mel[0] is positive
252
+ if not np.all((mel_f[:-2] == 0) | (weights.max(axis=1) > 0)):
253
+ # This means we have an empty channel somewhere
254
+ warnings.warn(
255
+ "Empty filters detected in mel frequency basis. "
256
+ "Some channels will produce empty responses. "
257
+ "Try increasing your sampling rate (and fmax) or "
258
+ "reducing n_mels.",
259
+ stacklevel=2,
260
+ )
261
+
262
+ return weights
263
+
264
+
265
+ @cache(level=10)
266
+ def chroma(
267
+ *,
268
+ sr: float,
269
+ n_fft: int,
270
+ n_chroma: int = 12,
271
+ tuning: float = 0.0,
272
+ ctroct: float = 5.0,
273
+ octwidth: Union[float, None] = 2,
274
+ norm: Optional[float] = 2,
275
+ base_c: bool = True,
276
+ dtype: DTypeLike = np.float32,
277
+ ) -> np.ndarray:
278
+ """Create a chroma filter bank.
279
+
280
+ This creates a linear transformation matrix to project
281
+ FFT bins onto chroma bins (i.e. pitch classes).
282
+
283
+ Parameters
284
+ ----------
285
+ sr : number > 0 [scalar]
286
+ audio sampling rate
287
+
288
+ n_fft : int > 0 [scalar]
289
+ number of FFT bins
290
+
291
+ n_chroma : int > 0 [scalar]
292
+ number of chroma bins
293
+
294
+ tuning : float
295
+ Tuning deviation from A440 in fractions of a chroma bin.
296
+
297
+ ctroct : float > 0 [scalar]
298
+
299
+ octwidth : float > 0 or None [scalar]
300
+ ``ctroct`` and ``octwidth`` specify a dominance window:
301
+ a Gaussian weighting centered on ``ctroct`` (in octs, A0 = 27.5Hz)
302
+ and with a gaussian half-width of ``octwidth``.
303
+
304
+ Set ``octwidth`` to `None` to use a flat weighting.
305
+
306
+ norm : float > 0 or np.inf
307
+ Normalization factor for each filter
308
+
309
+ base_c : bool
310
+ If True, the filter bank will start at 'C'.
311
+ If False, the filter bank will start at 'A'.
312
+
313
+ dtype : np.dtype
314
+ The data type of the output basis.
315
+ By default, uses 32-bit (single-precision) floating point.
316
+
317
+ Returns
318
+ -------
319
+ wts : ndarray [shape=(n_chroma, 1 + n_fft / 2)]
320
+ Chroma filter matrix
321
+
322
+ See Also
323
+ --------
324
+ librosa.util.normalize
325
+ librosa.feature.chroma_stft
326
+
327
+ Notes
328
+ -----
329
+ This function caches at level 10.
330
+
331
+ Examples
332
+ --------
333
+ Build a simple chroma filter bank
334
+
335
+ >>> chromafb = librosa.filters.chroma(sr=22050, n_fft=4096)
336
+ array([[ 1.689e-05, 3.024e-04, ..., 4.639e-17, 5.327e-17],
337
+ [ 1.716e-05, 2.652e-04, ..., 2.674e-25, 3.176e-25],
338
+ ...,
339
+ [ 1.578e-05, 3.619e-04, ..., 8.577e-06, 9.205e-06],
340
+ [ 1.643e-05, 3.355e-04, ..., 1.474e-10, 1.636e-10]])
341
+
342
+ Use quarter-tones instead of semitones
343
+
344
+ >>> librosa.filters.chroma(sr=22050, n_fft=4096, n_chroma=24)
345
+ array([[ 1.194e-05, 2.138e-04, ..., 6.297e-64, 1.115e-63],
346
+ [ 1.206e-05, 2.009e-04, ..., 1.546e-79, 2.929e-79],
347
+ ...,
348
+ [ 1.162e-05, 2.372e-04, ..., 6.417e-38, 9.923e-38],
349
+ [ 1.180e-05, 2.260e-04, ..., 4.697e-50, 7.772e-50]])
350
+
351
+ Equally weight all octaves
352
+
353
+ >>> librosa.filters.chroma(sr=22050, n_fft=4096, octwidth=None)
354
+ array([[ 3.036e-01, 2.604e-01, ..., 2.445e-16, 2.809e-16],
355
+ [ 3.084e-01, 2.283e-01, ..., 1.409e-24, 1.675e-24],
356
+ ...,
357
+ [ 2.836e-01, 3.116e-01, ..., 4.520e-05, 4.854e-05],
358
+ [ 2.953e-01, 2.888e-01, ..., 7.768e-10, 8.629e-10]])
359
+
360
+ >>> import matplotlib.pyplot as plt
361
+ >>> fig, ax = plt.subplots()
362
+ >>> img = librosa.display.specshow(chromafb, x_axis='linear', ax=ax)
363
+ >>> ax.set(ylabel='Chroma filter', title='Chroma filter bank')
364
+ >>> fig.colorbar(img, ax=ax)
365
+ """
366
+ wts = np.zeros((n_chroma, n_fft))
367
+
368
+ # Get the FFT bins, not counting the DC component
369
+ frequencies = np.linspace(0, sr, n_fft, endpoint=False)[1:]
370
+
371
+ frqbins = n_chroma * hz_to_octs(
372
+ frequencies, tuning=tuning, bins_per_octave=n_chroma
373
+ )
374
+
375
+ # make up a value for the 0 Hz bin = 1.5 octaves below bin 1
376
+ # (so chroma is 50% rotated from bin 1, and bin width is broad)
377
+ frqbins = np.concatenate(([frqbins[0] - 1.5 * n_chroma], frqbins))
378
+
379
+ binwidthbins = np.concatenate((np.maximum(frqbins[1:] - frqbins[:-1], 1.0), [1]))
380
+
381
+ D = np.subtract.outer(frqbins, np.arange(0, n_chroma, dtype="d")).T
382
+
383
+ n_chroma2 = np.round(float(n_chroma) / 2)
384
+
385
+ # Project into range -n_chroma/2 .. n_chroma/2
386
+ # add on fixed offset of 10*n_chroma to ensure all values passed to
387
+ # rem are positive
388
+ D = np.remainder(D + n_chroma2 + 10 * n_chroma, n_chroma) - n_chroma2
389
+
390
+ # Gaussian bumps - 2*D to make them narrower
391
+ wts = np.exp(-0.5 * (2 * D / np.tile(binwidthbins, (n_chroma, 1))) ** 2)
392
+
393
+ # normalize each column
394
+ wts = util.normalize(wts, norm=norm, axis=0)
395
+
396
+ # Maybe apply scaling for fft bins
397
+ if octwidth is not None:
398
+ wts *= np.tile(
399
+ np.exp(-0.5 * (((frqbins / n_chroma - ctroct) / octwidth) ** 2)),
400
+ (n_chroma, 1),
401
+ )
402
+
403
+ if base_c:
404
+ wts = np.roll(wts, -3 * (n_chroma // 12), axis=0)
405
+
406
+ # remove aliasing columns, copy to ensure row-contiguity
407
+ return np.ascontiguousarray(wts[:, : int(1 + n_fft / 2)], dtype=dtype)
408
+
409
+
410
+ def __float_window(window_spec):
411
+ """Decorate a window function to support fractional input lengths.
412
+
413
+ This function guarantees that for fractional ``x``, the following hold:
414
+
415
+ 1. ``__float_window(window_function)(x)`` has length ``np.ceil(x)``
416
+ 2. all values from ``np.floor(x)`` are set to 0.
417
+
418
+ For integer-valued ``x``, there should be no change in behavior.
419
+ """
420
+ def _wrap(n, *args, **kwargs):
421
+ """Wrap the window"""
422
+ n_min, n_max = int(np.floor(n)), int(np.ceil(n))
423
+
424
+ window = get_window(window_spec, n_min)
425
+
426
+ if len(window) < n_max:
427
+ window = np.pad(window, [(0, n_max - len(window))], mode="constant")
428
+
429
+ window[n_min:] = 0.0
430
+
431
+ return window
432
+
433
+ return _wrap
434
+
435
+
436
+ @deprecated(version="0.9.0", version_removed="1.0")
437
+ def constant_q(
438
+ *,
439
+ sr: float,
440
+ fmin: Optional[_FloatLike_co] = None,
441
+ n_bins: int = 84,
442
+ bins_per_octave: int = 12,
443
+ window: _WindowSpec = "hann",
444
+ filter_scale: float = 1,
445
+ pad_fft: bool = True,
446
+ norm: Optional[float] = 1,
447
+ dtype: DTypeLike = np.complex64,
448
+ gamma: float = 0,
449
+ **kwargs: Any,
450
+ ) -> Tuple[np.ndarray, np.ndarray]:
451
+ r"""Construct a constant-Q basis.
452
+
453
+ This function constructs a filter bank similar to Morlet wavelets,
454
+ where complex exponentials are windowed to different lengths
455
+ such that the number of cycles remains fixed for all frequencies.
456
+
457
+ By default, a Hann window (rather than the Gaussian window of Morlet wavelets)
458
+ is used, but this can be controlled by the ``window`` parameter.
459
+
460
+ Frequencies are spaced geometrically, increasing by a factor of
461
+ ``(2**(1./bins_per_octave))`` at each successive band.
462
+
463
+ .. warning:: This function is deprecated as of v0.9 and will be removed in 1.0.
464
+ See `librosa.filters.wavelet`.
465
+
466
+ Parameters
467
+ ----------
468
+ sr : number > 0 [scalar]
469
+ Audio sampling rate
470
+
471
+ fmin : float > 0 [scalar]
472
+ Minimum frequency bin. Defaults to `C1 ~= 32.70`
473
+
474
+ n_bins : int > 0 [scalar]
475
+ Number of frequencies. Defaults to 7 octaves (84 bins).
476
+
477
+ bins_per_octave : int > 0 [scalar]
478
+ Number of bins per octave
479
+
480
+ window : string, tuple, number, or function
481
+ Windowing function to apply to filters.
482
+
483
+ filter_scale : float > 0 [scalar]
484
+ Scale of filter windows.
485
+ Small values (<1) use shorter windows for higher temporal resolution.
486
+
487
+ pad_fft : boolean
488
+ Center-pad all filters up to the nearest integral power of 2.
489
+
490
+ By default, padding is done with zeros, but this can be overridden
491
+ by setting the ``mode=`` field in *kwargs*.
492
+
493
+ norm : {inf, -inf, 0, float > 0}
494
+ Type of norm to use for basis function normalization.
495
+ See librosa.util.normalize
496
+
497
+ gamma : number >= 0
498
+ Bandwidth offset for variable-Q transforms.
499
+ ``gamma=0`` produces a constant-Q filterbank.
500
+
501
+ dtype : np.dtype
502
+ The data type of the output basis.
503
+ By default, uses 64-bit (single precision) complex floating point.
504
+
505
+ **kwargs : additional keyword arguments
506
+ Arguments to `np.pad()` when ``pad==True``.
507
+
508
+ Returns
509
+ -------
510
+ filters : np.ndarray, ``len(filters) == n_bins``
511
+ ``filters[i]`` is ``i``\ th time-domain CQT basis filter
512
+ lengths : np.ndarray, ``len(lengths) == n_bins``
513
+ The (fractional) length of each filter
514
+
515
+ Notes
516
+ -----
517
+ This function caches at level 10.
518
+
519
+ See Also
520
+ --------
521
+ wavelet
522
+ constant_q_lengths
523
+ librosa.cqt
524
+ librosa.vqt
525
+ librosa.util.normalize
526
+
527
+ Examples
528
+ --------
529
+ Use a shorter window for each filter
530
+
531
+ >>> basis, lengths = librosa.filters.constant_q(sr=22050, filter_scale=0.5)
532
+
533
+ Plot one octave of filters in time and frequency
534
+
535
+ >>> import matplotlib.pyplot as plt
536
+ >>> basis, lengths = librosa.filters.constant_q(sr=22050)
537
+ >>> fig, ax = plt.subplots(nrows=2, figsize=(10, 6))
538
+ >>> notes = librosa.midi_to_note(np.arange(24, 24 + len(basis)))
539
+ >>> for i, (f, n) in enumerate(zip(basis, notes[:12])):
540
+ ... f_scale = librosa.util.normalize(f) / 2
541
+ ... ax[0].plot(i + f_scale.real)
542
+ ... ax[0].plot(i + f_scale.imag, linestyle=':')
543
+ >>> ax[0].set(yticks=np.arange(len(notes[:12])), yticklabels=notes[:12],
544
+ ... ylabel='CQ filters',
545
+ ... title='CQ filters (one octave, time domain)',
546
+ ... xlabel='Time (samples at 22050 Hz)')
547
+ >>> ax[0].legend(['Real', 'Imaginary'])
548
+ >>> F = np.abs(np.fft.fftn(basis, axes=[-1]))
549
+ >>> # Keep only the positive frequencies
550
+ >>> F = F[:, :(1 + F.shape[1] // 2)]
551
+ >>> librosa.display.specshow(F, x_axis='linear', y_axis='cqt_note', ax=ax[1])
552
+ >>> ax[1].set(ylabel='CQ filters', title='CQ filter magnitudes (frequency domain)')
553
+ """
554
+ if fmin is None:
555
+ fmin = note_to_hz("C1")
556
+
557
+ # Pass-through parameters to get the filter lengths
558
+ lengths = constant_q_lengths(
559
+ sr=sr,
560
+ fmin=fmin,
561
+ n_bins=n_bins,
562
+ bins_per_octave=bins_per_octave,
563
+ window=window,
564
+ filter_scale=filter_scale,
565
+ gamma=gamma,
566
+ )
567
+
568
+ freqs = fmin * (2.0 ** (np.arange(n_bins, dtype=float) / bins_per_octave))
569
+
570
+ # Build the filters
571
+ filters = []
572
+ for ilen, freq in zip(lengths, freqs):
573
+ # Build the filter: note, length will be ceil(ilen)
574
+ sig = util.phasor(
575
+ np.arange(-ilen // 2, ilen // 2, dtype=float) * 2 * np.pi * freq / sr
576
+ )
577
+
578
+ # Apply the windowing function
579
+ sig = sig * __float_window(window)(len(sig))
580
+
581
+ # Normalize
582
+ sig = util.normalize(sig, norm=norm)
583
+
584
+ filters.append(sig)
585
+
586
+ # Pad and stack
587
+ max_len = max(lengths)
588
+ if pad_fft:
589
+ max_len = int(2.0 ** (np.ceil(np.log2(max_len))))
590
+ else:
591
+ max_len = int(np.ceil(max_len))
592
+
593
+ filters = np.asarray(
594
+ [util.pad_center(filt, size=max_len, **kwargs) for filt in filters], dtype=dtype
595
+ )
596
+
597
+ return filters, np.asarray(lengths)
598
+
599
+
600
+ @deprecated(version="0.9.0", version_removed="1.0")
601
+ @cache(level=10)
602
+ def constant_q_lengths(
603
+ *,
604
+ sr: float,
605
+ fmin: _FloatLike_co,
606
+ n_bins: int = 84,
607
+ bins_per_octave: int = 12,
608
+ window: _WindowSpec = "hann",
609
+ filter_scale: float = 1,
610
+ gamma: float = 0,
611
+ ) -> np.ndarray:
612
+ r"""Return length of each filter in a constant-Q basis.
613
+
614
+ .. warning:: This function is deprecated as of v0.9 and will be removed in 1.0.
615
+ See `librosa.filters.wavelet_lengths`.
616
+
617
+ Parameters
618
+ ----------
619
+ sr : number > 0 [scalar]
620
+ Audio sampling rate
621
+ fmin : float > 0 [scalar]
622
+ Minimum frequency bin.
623
+ n_bins : int > 0 [scalar]
624
+ Number of frequencies. Defaults to 7 octaves (84 bins).
625
+ bins_per_octave : int > 0 [scalar]
626
+ Number of bins per octave
627
+ window : str or callable
628
+ Window function to use on filters
629
+ filter_scale : float > 0 [scalar]
630
+ Resolution of filter windows. Larger values use longer windows.
631
+ gamma : number >= 0
632
+ Bandwidth offset for variable-Q transforms.
633
+ ``gamma=0`` produces a constant-Q filterbank.
634
+
635
+ Returns
636
+ -------
637
+ lengths : np.ndarray
638
+ The length of each filter.
639
+
640
+ Notes
641
+ -----
642
+ This function caches at level 10.
643
+
644
+ See Also
645
+ --------
646
+ wavelet_lengths
647
+ """
648
+ if fmin <= 0:
649
+ raise ParameterError("fmin must be strictly positive")
650
+
651
+ if bins_per_octave <= 0:
652
+ raise ParameterError("bins_per_octave must be positive")
653
+
654
+ if filter_scale <= 0:
655
+ raise ParameterError("filter_scale must be positive")
656
+
657
+ if n_bins <= 0 or not isinstance(n_bins, (int, np.integer)):
658
+ raise ParameterError("n_bins must be a positive integer")
659
+
660
+ # Compute the frequencies
661
+ freq = fmin * (2.0 ** (np.arange(n_bins, dtype=float) / bins_per_octave))
662
+
663
+ # Q should be capitalized here, so we suppress the name warning
664
+ # pylint: disable=invalid-name
665
+ #
666
+ # Balance filter bandwidths
667
+ alpha = (2.0 ** (2 / bins_per_octave) - 1) / (2.0 ** (2 / bins_per_octave) + 1)
668
+ Q = float(filter_scale) / alpha
669
+
670
+ if max(freq * (1 + 0.5 * window_bandwidth(window) / Q)) > sr / 2.0:
671
+ raise ParameterError(
672
+ f"Maximum filter frequency={max(freq):.2f} would exceed Nyquist={sr/2}"
673
+ )
674
+
675
+ # Convert frequencies to filter lengths
676
+ lengths: np.ndarray = Q * sr / (freq + gamma / alpha)
677
+
678
+ return lengths
679
+
680
+
681
+ @cache(level=10)
682
+ def wavelet_lengths(
683
+ *,
684
+ freqs: ArrayLike,
685
+ sr: float = 22050,
686
+ window: _WindowSpec = "hann",
687
+ filter_scale: float = 1,
688
+ gamma: Optional[float] = 0,
689
+ alpha: Optional[Union[float, np.ndarray]] = None,
690
+ ) -> Tuple[np.ndarray, float]:
691
+ """Return length of each filter in a wavelet basis.
692
+
693
+ Parameters
694
+ ----------
695
+ freqs : np.ndarray (positive)
696
+ Center frequencies of the filters (in Hz).
697
+ Must be in ascending order.
698
+
699
+ sr : number > 0 [scalar]
700
+ Audio sampling rate
701
+
702
+ window : str or callable
703
+ Window function to use on filters
704
+
705
+ filter_scale : float > 0 [scalar]
706
+ Resolution of filter windows. Larger values use longer windows.
707
+
708
+ gamma : number >= 0 [scalar, optional]
709
+ Bandwidth offset for determining filter lengths, as used in
710
+ Variable-Q transforms.
711
+
712
+ Bandwidth for the k'th filter is determined by::
713
+
714
+ B[k] = alpha[k] * freqs[k] + gamma
715
+
716
+ ``alpha[k]`` is twice the relative difference between ``freqs[k+1]`` and ``freqs[k-1]``::
717
+
718
+ alpha[k] = (freqs[k+1]-freqs[k-1]) / (freqs[k+1]+freqs[k-1])
719
+
720
+ If ``freqs`` follows a geometric progression (as in CQT and VQT), the vector
721
+ ``alpha`` is constant and such that::
722
+
723
+ (1 + alpha) * freqs[k-1] = (1 - alpha) * freqs[k+1]
724
+
725
+ Furthermore, if ``gamma=0`` (default), ``alpha`` is such that even-``k`` and
726
+ odd-``k`` filters are interleaved::
727
+
728
+ freqs[k-1] + B[k-1] = freqs[k+1] - B[k+1]
729
+
730
+ If ``gamma=None`` is specified, then ``gamma`` is computed such
731
+ that each filter has bandwidth proportional to the equivalent
732
+ rectangular bandwidth (ERB) at frequency ``freqs[k]``::
733
+
734
+ gamma[k] = 24.7 * alpha[k] / 0.108
735
+
736
+ as derived by [#]_.
737
+
738
+ .. [#] Glasberg, Brian R., and Brian CJ Moore.
739
+ "Derivation of auditory filter shapes from notched-noise data."
740
+ Hearing research 47.1-2 (1990): 103-138.
741
+
742
+ alpha : number > 0 [optional]
743
+ Optional pre-computed relative bandwidth parameter.
744
+ Note that this must be provided if ``len(freqs)==1`` because bandwidth cannot be
745
+ inferred from a single frequency.
746
+ Otherwise, if left unspecified, it will be automatically derived by the rules
747
+ specified above.
748
+
749
+ Returns
750
+ -------
751
+ lengths : np.ndarray
752
+ The length of each filter.
753
+ f_cutoff : float
754
+ The lowest frequency at which all filters' main lobes have decayed by
755
+ at least 3dB.
756
+
757
+ This second output serves in cqt and vqt to ensure that all wavelet
758
+ bands remain below the Nyquist frequency.
759
+
760
+ Notes
761
+ -----
762
+ This function caches at level 10.
763
+
764
+ Raises
765
+ ------
766
+ ParameterError
767
+ - If ``filter_scale`` is not strictly positive
768
+
769
+ - If ``gamma`` is a negative number
770
+
771
+ - If any frequencies are <= 0
772
+
773
+ - If the frequency array is not sorted in ascending order
774
+ """
775
+ freqs = np.asarray(freqs)
776
+ if filter_scale <= 0:
777
+ raise ParameterError(f"filter_scale={filter_scale} must be positive")
778
+
779
+ if gamma is not None and gamma < 0:
780
+ raise ParameterError(f"gamma={gamma} must be non-negative")
781
+
782
+ if np.any(freqs <= 0):
783
+ raise ParameterError("frequencies must be strictly positive")
784
+
785
+ if len(freqs) > 1 and np.any(freqs[:-1] > freqs[1:]):
786
+ raise ParameterError(
787
+ f"Frequency array={freqs} must be in strictly ascending order"
788
+ )
789
+
790
+ if alpha is None:
791
+ alpha = _relative_bandwidth(freqs=freqs)
792
+ else:
793
+ alpha = np.asarray(alpha)
794
+
795
+ gamma_: Union[_FloatLike_co, np.ndarray]
796
+ if gamma is None:
797
+ gamma_ = alpha * 24.7 / 0.108
798
+ else:
799
+ gamma_ = gamma
800
+ # Q should be capitalized here, so we suppress the name warning
801
+ # pylint: disable=invalid-name
802
+ Q = float(filter_scale) / alpha
803
+
804
+ # How far up does our highest frequency reach?
805
+ f_cutoff = max(freqs * (1 + 0.5 * window_bandwidth(window) / Q) + 0.5 * gamma_)
806
+
807
+ # Convert frequencies to filter lengths
808
+ lengths = Q * sr / (freqs + gamma_ / alpha)
809
+
810
+ return lengths, f_cutoff
811
+
812
+
813
+ def _relative_bandwidth(*, freqs: np.ndarray) -> np.ndarray:
814
+ """Compute the relative bandwidth for each of a set of specified frequencies.
815
+
816
+ This function is used as a helper in wavelet basis construction.
817
+
818
+ Parameters
819
+ ----------
820
+ freqs : np.ndarray
821
+ The array of frequencies
822
+
823
+ Returns
824
+ -------
825
+ alpha : np.ndarray
826
+ Relative bandwidth
827
+ """
828
+ if len(freqs) <= 1:
829
+ raise ParameterError(f"2 or more frequencies are required to compute bandwidths. Given freqs={freqs}")
830
+
831
+ # Approximate the local octave resolution around each frequency
832
+ bpo = np.empty_like(freqs)
833
+ logf = np.log2(freqs)
834
+ # Reflect at the lowest and highest frequencies
835
+ bpo[0] = 1 / (logf[1] - logf[0])
836
+ bpo[-1] = 1 / (logf[-1] - logf[-2])
837
+
838
+ # For everything else, do a centered difference
839
+ bpo[1:-1] = 2 / (logf[2:] - logf[:-2])
840
+
841
+ # Compute relative bandwidths
842
+ alpha = (2.0 ** (2 / bpo) - 1) / (2.0 ** (2 / bpo) + 1)
843
+ return alpha
844
+
845
+
846
+ @cache(level=10)
847
+ def wavelet(
848
+ *,
849
+ freqs: np.ndarray,
850
+ sr: float = 22050,
851
+ window: _WindowSpec = "hann",
852
+ filter_scale: float = 1,
853
+ pad_fft: bool = True,
854
+ norm: Optional[float] = 1,
855
+ dtype: DTypeLike = np.complex64,
856
+ gamma: float = 0,
857
+ alpha: Optional[float] = None,
858
+ **kwargs: Any,
859
+ ) -> Tuple[np.ndarray, np.ndarray]:
860
+ """Construct a wavelet basis using windowed complex sinusoids.
861
+
862
+ This function constructs a wavelet filterbank at a specified set of center
863
+ frequencies.
864
+
865
+ Parameters
866
+ ----------
867
+ freqs : np.ndarray (positive)
868
+ Center frequencies of the filters (in Hz).
869
+ Must be in ascending order.
870
+
871
+ sr : number > 0 [scalar]
872
+ Audio sampling rate
873
+
874
+ window : string, tuple, number, or function
875
+ Windowing function to apply to filters.
876
+
877
+ filter_scale : float > 0 [scalar]
878
+ Scale of filter windows.
879
+ Small values (<1) use shorter windows for higher temporal resolution.
880
+
881
+ pad_fft : boolean
882
+ Center-pad all filters up to the nearest integral power of 2.
883
+
884
+ By default, padding is done with zeros, but this can be overridden
885
+ by setting the ``mode=`` field in *kwargs*.
886
+
887
+ norm : {inf, -inf, 0, float > 0}
888
+ Type of norm to use for basis function normalization.
889
+ See librosa.util.normalize
890
+
891
+ gamma : number >= 0
892
+ Bandwidth offset for variable-Q transforms.
893
+
894
+ dtype : np.dtype
895
+ The data type of the output basis.
896
+ By default, uses 64-bit (single precision) complex floating point.
897
+
898
+ alpha : number > 0 [optional]
899
+ Optional pre-computed relative bandwidth parameter.
900
+ Note that this must be provided if ``len(freqs)==1`` because bandwidth cannot be
901
+ inferred from a single frequency.
902
+ Otherwise, if left unspecified, it will be automatically derived by the rules
903
+ specified above.
904
+
905
+ **kwargs : additional keyword arguments
906
+ Arguments to `np.pad()` when ``pad==True``.
907
+
908
+ Returns
909
+ -------
910
+ filters : np.ndarray, ``len(filters) == n_bins``
911
+ each ``filters[i]`` is a (complex) time-domain filter
912
+ lengths : np.ndarray, ``len(lengths) == n_bins``
913
+ The (fractional) length of each filter in samples
914
+
915
+ Notes
916
+ -----
917
+ This function caches at level 10.
918
+
919
+ See Also
920
+ --------
921
+ wavelet_lengths
922
+ librosa.cqt
923
+ librosa.vqt
924
+ librosa.util.normalize
925
+
926
+ Examples
927
+ --------
928
+ Create a constant-Q basis
929
+
930
+ >>> freqs = librosa.cqt_frequencies(n_bins=84, fmin=librosa.note_to_hz('C1'))
931
+ >>> basis, lengths = librosa.filters.wavelet(freqs=freqs, sr=22050)
932
+
933
+ Plot one octave of filters in time and frequency
934
+
935
+ >>> import matplotlib.pyplot as plt
936
+ >>> basis, lengths = librosa.filters.wavelet(freqs=freqs, sr=22050)
937
+ >>> fig, ax = plt.subplots(nrows=2, figsize=(10, 6))
938
+ >>> notes = librosa.midi_to_note(np.arange(24, 24 + len(basis)))
939
+ >>> for i, (f, n) in enumerate(zip(basis, notes[:12])):
940
+ ... f_scale = librosa.util.normalize(f) / 2
941
+ ... ax[0].plot(i + f_scale.real)
942
+ ... ax[0].plot(i + f_scale.imag, linestyle=':')
943
+ >>> ax[0].set(yticks=np.arange(len(notes[:12])), yticklabels=notes[:12],
944
+ ... ylabel='CQ filters',
945
+ ... title='CQ filters (one octave, time domain)',
946
+ ... xlabel='Time (samples at 22050 Hz)')
947
+ >>> ax[0].legend(['Real', 'Imaginary'])
948
+ >>> F = np.abs(np.fft.fftn(basis, axes=[-1]))
949
+ >>> # Keep only the positive frequencies
950
+ >>> F = F[:, :(1 + F.shape[1] // 2)]
951
+ >>> librosa.display.specshow(F, x_axis='linear', y_axis='cqt_note', ax=ax[1])
952
+ >>> ax[1].set(ylabel='CQ filters', title='CQ filter magnitudes (frequency domain)')
953
+ """
954
+ # Pass-through parameters to get the filter lengths
955
+ lengths, _ = wavelet_lengths(
956
+ freqs=freqs,
957
+ sr=sr,
958
+ window=window,
959
+ filter_scale=filter_scale,
960
+ gamma=gamma,
961
+ alpha=alpha,
962
+ )
963
+
964
+ # Build the filters
965
+ filters = []
966
+ for ilen, freq in zip(lengths, freqs):
967
+ # Build the filter: note, length will be ceil(ilen)
968
+ sig = util.phasor(
969
+ np.arange(-ilen // 2, ilen // 2, dtype=float) * 2 * np.pi * freq / sr
970
+ )
971
+
972
+ # Apply the windowing function
973
+ sig *= __float_window(window)(len(sig))
974
+
975
+ # Normalize
976
+ sig = util.normalize(sig, norm=norm)
977
+
978
+ filters.append(sig)
979
+
980
+ # Pad and stack
981
+ max_len = max(lengths)
982
+ if pad_fft:
983
+ max_len = int(2.0 ** (np.ceil(np.log2(max_len))))
984
+ else:
985
+ max_len = int(np.ceil(max_len))
986
+
987
+ filters = np.asarray(
988
+ [util.pad_center(filt, size=max_len, **kwargs) for filt in filters], dtype=dtype
989
+ )
990
+
991
+ return filters, lengths
992
+
993
+
994
+ @cache(level=10)
995
+ def cq_to_chroma(
996
+ n_input: int,
997
+ *,
998
+ bins_per_octave: int = 12,
999
+ n_chroma: int = 12,
1000
+ fmin: Optional[_FloatLike_co] = None,
1001
+ window: Optional[np.ndarray] = None,
1002
+ base_c: bool = True,
1003
+ dtype: DTypeLike = np.float32,
1004
+ ) -> np.ndarray:
1005
+ """Construct a linear transformation matrix to map Constant-Q bins
1006
+ onto chroma bins (i.e., pitch classes).
1007
+
1008
+ Parameters
1009
+ ----------
1010
+ n_input : int > 0 [scalar]
1011
+ Number of input components (CQT bins)
1012
+ bins_per_octave : int > 0 [scalar]
1013
+ How many bins per octave in the CQT
1014
+ n_chroma : int > 0 [scalar]
1015
+ Number of output bins (per octave) in the chroma
1016
+ fmin : None or float > 0
1017
+ Center frequency of the first constant-Q channel.
1018
+ Default: 'C1' ~= 32.7 Hz
1019
+ window : None or np.ndarray
1020
+ If provided, the cq_to_chroma filter bank will be
1021
+ convolved with ``window``.
1022
+ base_c : bool
1023
+ If True, the first chroma bin will start at 'C'
1024
+ If False, the first chroma bin will start at 'A'
1025
+ dtype : np.dtype
1026
+ The data type of the output basis.
1027
+ By default, uses 32-bit (single-precision) floating point.
1028
+
1029
+ Returns
1030
+ -------
1031
+ cq_to_chroma : np.ndarray [shape=(n_chroma, n_input)]
1032
+ Transformation matrix: ``Chroma = np.dot(cq_to_chroma, CQT)``
1033
+
1034
+ Raises
1035
+ ------
1036
+ ParameterError
1037
+ If ``n_input`` is not an integer multiple of ``n_chroma``
1038
+
1039
+ Notes
1040
+ -----
1041
+ This function caches at level 10.
1042
+
1043
+ Examples
1044
+ --------
1045
+ Get a CQT, and wrap bins to chroma
1046
+
1047
+ >>> y, sr = librosa.load(librosa.ex('trumpet'))
1048
+ >>> CQT = np.abs(librosa.cqt(y, sr=sr))
1049
+ >>> chroma_map = librosa.filters.cq_to_chroma(CQT.shape[0])
1050
+ >>> chromagram = chroma_map.dot(CQT)
1051
+ >>> # Max-normalize each time step
1052
+ >>> chromagram = librosa.util.normalize(chromagram, axis=0)
1053
+
1054
+ >>> import matplotlib.pyplot as plt
1055
+ >>> fig, ax = plt.subplots(nrows=3, sharex=True)
1056
+ >>> imgcq = librosa.display.specshow(librosa.amplitude_to_db(CQT,
1057
+ ... ref=np.max),
1058
+ ... y_axis='cqt_note', x_axis='time',
1059
+ ... ax=ax[0])
1060
+ >>> ax[0].set(title='CQT Power')
1061
+ >>> ax[0].label_outer()
1062
+ >>> librosa.display.specshow(chromagram, y_axis='chroma', x_axis='time',
1063
+ ... ax=ax[1])
1064
+ >>> ax[1].set(title='Chroma (wrapped CQT)')
1065
+ >>> ax[1].label_outer()
1066
+ >>> chroma = librosa.feature.chroma_stft(y=y, sr=sr)
1067
+ >>> imgchroma = librosa.display.specshow(chroma, y_axis='chroma', x_axis='time', ax=ax[2])
1068
+ >>> ax[2].set(title='librosa.feature.chroma_stft')
1069
+ """
1070
+ # How many fractional bins are we merging?
1071
+ n_merge = float(bins_per_octave) / n_chroma
1072
+
1073
+ fmin_: _FloatLike_co
1074
+ if fmin is None:
1075
+ fmin_ = note_to_hz("C1")
1076
+ else:
1077
+ fmin_ = fmin
1078
+
1079
+ if np.mod(n_merge, 1) != 0:
1080
+ raise ParameterError(
1081
+ "Incompatible CQ merge: "
1082
+ "input bins must be an "
1083
+ "integer multiple of output bins."
1084
+ )
1085
+
1086
+ # Tile the identity to merge fractional bins
1087
+ cq_to_ch = np.repeat(np.eye(n_chroma), int(n_merge), axis=1)
1088
+
1089
+ # Roll it left to center on the target bin
1090
+ cq_to_ch = np.roll(cq_to_ch, -int(n_merge // 2), axis=1)
1091
+
1092
+ # How many octaves are we repeating?
1093
+ n_octaves = np.ceil(float(n_input) / bins_per_octave)
1094
+
1095
+ # Repeat and trim
1096
+ cq_to_ch = np.tile(cq_to_ch, int(n_octaves))[:, :n_input]
1097
+
1098
+ # What's the note number of the first bin in the CQT?
1099
+ # midi uses 12 bins per octave here
1100
+ midi_0 = np.mod(hz_to_midi(fmin_), 12)
1101
+
1102
+ if base_c:
1103
+ # rotate to C
1104
+ roll = midi_0
1105
+ else:
1106
+ # rotate to A
1107
+ roll = midi_0 - 9
1108
+
1109
+ # Adjust the roll in terms of how many chroma we want out
1110
+ # We need to be careful with rounding here
1111
+ roll = int(np.round(roll * (n_chroma / 12.0)))
1112
+
1113
+ # Apply the roll
1114
+ cq_to_ch = np.roll(cq_to_ch, roll, axis=0).astype(dtype)
1115
+
1116
+ if window is not None:
1117
+ cq_to_ch = scipy.signal.convolve(cq_to_ch, np.atleast_2d(window), mode="same")
1118
+
1119
+ return cq_to_ch
1120
+
1121
+
1122
+ @cache(level=10)
1123
+ def window_bandwidth(window: _WindowSpec, n: int = 1000) -> float:
1124
+ """Get the equivalent noise bandwidth (ENBW) of a window function.
1125
+
1126
+ The ENBW of a window is defined by [#]_ (equation 11) as the normalized
1127
+ ratio of the sum of squares to the square of sums::
1128
+
1129
+ enbw = n * sum(window**2) / sum(window)**2
1130
+
1131
+ .. [#] Harris, F. J.
1132
+ "On the use of windows for harmonic analysis with the discrete Fourier transform."
1133
+ Proceedings of the IEEE, 66(1), 51-83. 1978.
1134
+
1135
+ Parameters
1136
+ ----------
1137
+ window : callable or string
1138
+ A window function, or the name of a window function,
1139
+ e.g.: `scipy.signal.hann` or `'boxcar'`
1140
+ n : int > 0
1141
+ The number of coefficients to use in estimating the
1142
+ window bandwidth
1143
+
1144
+ Returns
1145
+ -------
1146
+ bandwidth : float
1147
+ The equivalent noise bandwidth (in FFT bins) of the
1148
+ given window function
1149
+
1150
+ Notes
1151
+ -----
1152
+ This function caches at level 10.
1153
+
1154
+ See Also
1155
+ --------
1156
+ get_window
1157
+ """
1158
+ if hasattr(window, "__name__"):
1159
+ key = window.__name__
1160
+ else:
1161
+ key = window
1162
+
1163
+ if key not in WINDOW_BANDWIDTHS:
1164
+ win = get_window(window, n)
1165
+ WINDOW_BANDWIDTHS[key] = (
1166
+ n * np.sum(win**2) / (np.sum(win) ** 2 + util.tiny(win))
1167
+ )
1168
+
1169
+ return WINDOW_BANDWIDTHS[key]
1170
+
1171
+
1172
+ @cache(level=10)
1173
+ def get_window(
1174
+ window: _WindowSpec,
1175
+ Nx: int,
1176
+ *,
1177
+ fftbins: Optional[bool] = True,
1178
+ ) -> np.ndarray:
1179
+ """Compute a window function.
1180
+
1181
+ This is a wrapper for `scipy.signal.get_window` that additionally
1182
+ supports callable or pre-computed windows.
1183
+
1184
+ Parameters
1185
+ ----------
1186
+ window : string, tuple, number, callable, or list-like
1187
+ The window specification:
1188
+
1189
+ - If string, it's the name of the window function (e.g., `'hann'`)
1190
+ - If tuple, it's the name of the window function and any parameters
1191
+ (e.g., `('kaiser', 4.0)`)
1192
+ - If numeric, it is treated as the beta parameter of the `'kaiser'`
1193
+ window, as in `scipy.signal.get_window`.
1194
+ - If callable, it's a function that accepts one integer argument
1195
+ (the window length)
1196
+ - If list-like, it's a pre-computed window of the correct length `Nx`
1197
+
1198
+ Nx : int > 0
1199
+ The length of the window
1200
+
1201
+ fftbins : bool, optional
1202
+ If True (default), create a periodic window for use with FFT
1203
+ If False, create a symmetric window for filter design applications.
1204
+
1205
+ Returns
1206
+ -------
1207
+ get_window : np.ndarray
1208
+ A window of length `Nx` and type `window`
1209
+
1210
+ See Also
1211
+ --------
1212
+ scipy.signal.get_window
1213
+
1214
+ Notes
1215
+ -----
1216
+ This function caches at level 10.
1217
+
1218
+ Raises
1219
+ ------
1220
+ ParameterError
1221
+ If `window` is supplied as a vector of length != `n_fft`,
1222
+ or is otherwise mis-specified.
1223
+ """
1224
+ if callable(window):
1225
+ return window(Nx)
1226
+
1227
+ elif isinstance(window, (str, tuple)) or np.isscalar(window):
1228
+ # TODO: if we add custom window functions in librosa, call them here
1229
+
1230
+ win: np.ndarray = scipy.signal.get_window(window, Nx, fftbins=fftbins)
1231
+ return win
1232
+
1233
+ elif isinstance(window, (np.ndarray, list)):
1234
+ if len(window) == Nx:
1235
+ return np.asarray(window)
1236
+
1237
+ raise ParameterError(f"Window size mismatch: {len(window):d} != {Nx:d}")
1238
+ else:
1239
+ raise ParameterError(f"Invalid window specification: {window!r}")
1240
+
1241
+
1242
+ @cache(level=10)
1243
+ def _multirate_fb(
1244
+ center_freqs: Optional[np.ndarray] = None,
1245
+ sample_rates: Optional[np.ndarray] = None,
1246
+ Q: float = 25.0,
1247
+ passband_ripple: float = 1,
1248
+ stopband_attenuation: float = 50,
1249
+ ftype: str = "ellip",
1250
+ flayout: str = "sos",
1251
+ ) -> Tuple[List[Any], np.ndarray]:
1252
+ r"""Construct a multirate filterbank.
1253
+
1254
+ A filter bank consists of multiple band-pass filters which divide the input signal
1255
+ into subbands. In the case of a multirate filter bank, the band-pass filters
1256
+ operate with resampled versions of the input signal, e.g. to keep the length
1257
+ of a filter constant while shifting its center frequency.
1258
+
1259
+ This implementation uses `scipy.signal.iirdesign` to design the filters.
1260
+
1261
+ Parameters
1262
+ ----------
1263
+ center_freqs : np.ndarray [shape=(n,), dtype=float]
1264
+ Center frequencies of the filter kernels.
1265
+ Also defines the number of filters in the filterbank.
1266
+
1267
+ sample_rates : np.ndarray [shape=(n,), dtype=float]
1268
+ Samplerate for each filter (used for multirate filterbank).
1269
+
1270
+ Q : float
1271
+ Q factor (influences the filter bandwidth).
1272
+
1273
+ passband_ripple : float
1274
+ The maximum loss in the passband (dB)
1275
+ See `scipy.signal.iirdesign` for details.
1276
+
1277
+ stopband_attenuation : float
1278
+ The minimum attenuation in the stopband (dB)
1279
+ See `scipy.signal.iirdesign` for details.
1280
+
1281
+ ftype : str
1282
+ The type of IIR filter to design
1283
+ See `scipy.signal.iirdesign` for details.
1284
+
1285
+ flayout : string
1286
+ Valid `output` argument for `scipy.signal.iirdesign`.
1287
+
1288
+ - If `ba`, returns numerators/denominators of the transfer functions,
1289
+ used for filtering with `scipy.signal.filtfilt`.
1290
+ Can be unstable for high-order filters.
1291
+
1292
+ - If `sos`, returns a series of second-order filters,
1293
+ used for filtering with `scipy.signal.sosfiltfilt`.
1294
+ Minimizes numerical precision errors for high-order filters, but is slower.
1295
+
1296
+ - If `zpk`, returns zeros, poles, and system gains of the transfer functions.
1297
+
1298
+ Returns
1299
+ -------
1300
+ filterbank : list [shape=(n,), dtype=float]
1301
+ Each list entry comprises the filter coefficients for a single filter.
1302
+ sample_rates : np.ndarray [shape=(n,), dtype=float]
1303
+ Samplerate for each filter.
1304
+
1305
+ Notes
1306
+ -----
1307
+ This function caches at level 10.
1308
+
1309
+ See Also
1310
+ --------
1311
+ scipy.signal.iirdesign
1312
+
1313
+ Raises
1314
+ ------
1315
+ ParameterError
1316
+ If ``center_freqs`` is ``None``.
1317
+ If ``sample_rates`` is ``None``.
1318
+ If ``center_freqs.shape`` does not match ``sample_rates.shape``.
1319
+ """
1320
+ if center_freqs is None:
1321
+ raise ParameterError("center_freqs must be provided.")
1322
+
1323
+ if sample_rates is None:
1324
+ raise ParameterError("sample_rates must be provided.")
1325
+
1326
+ if center_freqs.shape != sample_rates.shape:
1327
+ raise ParameterError(
1328
+ "Number of provided center_freqs and sample_rates must be equal."
1329
+ )
1330
+
1331
+ nyquist = 0.5 * sample_rates
1332
+ filter_bandwidths = center_freqs / float(Q)
1333
+
1334
+ filterbank = []
1335
+
1336
+ for cur_center_freq, cur_nyquist, cur_bw in zip(
1337
+ center_freqs, nyquist, filter_bandwidths
1338
+ ):
1339
+ passband_freqs = [
1340
+ cur_center_freq - 0.5 * cur_bw,
1341
+ cur_center_freq + 0.5 * cur_bw,
1342
+ ] / cur_nyquist
1343
+ stopband_freqs = [
1344
+ cur_center_freq - cur_bw,
1345
+ cur_center_freq + cur_bw,
1346
+ ] / cur_nyquist
1347
+
1348
+ cur_filter = scipy.signal.iirdesign(
1349
+ passband_freqs,
1350
+ stopband_freqs,
1351
+ passband_ripple,
1352
+ stopband_attenuation,
1353
+ analog=False,
1354
+ ftype=ftype,
1355
+ output=flayout,
1356
+ )
1357
+
1358
+ filterbank.append(cur_filter)
1359
+
1360
+ return filterbank, sample_rates
1361
+
1362
+
1363
+ @cache(level=10)
1364
+ def mr_frequencies(tuning: float) -> Tuple[np.ndarray, np.ndarray]:
1365
+ r"""Generate center frequencies and sample rate pairs.
1366
+
1367
+ This function will return center frequency and corresponding sample rates
1368
+ to obtain similar pitch filterbank settings as described in [#]_.
1369
+ Instead of starting with MIDI pitch `A0`, we start with `C0`.
1370
+
1371
+ .. [#] Müller, Meinard.
1372
+ "Information Retrieval for Music and Motion."
1373
+ Springer Verlag. 2007.
1374
+
1375
+ Parameters
1376
+ ----------
1377
+ tuning : float [scalar]
1378
+ Tuning deviation from A440, measure as a fraction of the equally
1379
+ tempered semitone (1/12 of an octave).
1380
+
1381
+ Returns
1382
+ -------
1383
+ center_freqs : np.ndarray [shape=(n,), dtype=float]
1384
+ Center frequencies of the filter kernels.
1385
+ Also defines the number of filters in the filterbank.
1386
+ sample_rates : np.ndarray [shape=(n,), dtype=float]
1387
+ Sample rate for each filter, used for multirate filterbank.
1388
+
1389
+ Notes
1390
+ -----
1391
+ This function caches at level 10.
1392
+
1393
+ See Also
1394
+ --------
1395
+ librosa.filters.semitone_filterbank
1396
+ """
1397
+ center_freqs = midi_to_hz(np.arange(24 + tuning, 109 + tuning))
1398
+
1399
+ sample_rates = np.asarray(
1400
+ len(np.arange(0, 36))
1401
+ * [
1402
+ 882.0,
1403
+ ]
1404
+ + len(np.arange(36, 70))
1405
+ * [
1406
+ 4410.0,
1407
+ ]
1408
+ + len(np.arange(70, 85))
1409
+ * [
1410
+ 22050.0,
1411
+ ]
1412
+ )
1413
+
1414
+ return center_freqs, sample_rates
1415
+
1416
+
1417
+ def semitone_filterbank(
1418
+ *,
1419
+ center_freqs: Optional[np.ndarray] = None,
1420
+ tuning: float = 0.0,
1421
+ sample_rates: Optional[np.ndarray] = None,
1422
+ flayout: str = "ba",
1423
+ **kwargs: Any,
1424
+ ) -> Tuple[List[Any], np.ndarray]:
1425
+ r"""Construct a multi-rate bank of infinite-impulse response (IIR)
1426
+ band-pass filters at user-defined center frequencies and sample rates.
1427
+
1428
+ By default, these center frequencies are set equal to the 88 fundamental
1429
+ frequencies of the grand piano keyboard, according to a pitch tuning standard
1430
+ of A440, that is, note A above middle C set to 440 Hz. The center frequencies
1431
+ are tuned to the twelve-tone equal temperament, which means that they grow
1432
+ exponentially at a rate of 2**(1/12), that is, twelve notes per octave.
1433
+
1434
+ The A440 tuning can be changed by the user while keeping twelve-tone equal
1435
+ temperament. While A440 is currently the international standard in the music
1436
+ industry (ISO 16), some orchestras tune to A441-A445, whereas baroque musicians
1437
+ tune to A415.
1438
+
1439
+ See [#]_ for details.
1440
+
1441
+ .. [#] Müller, Meinard.
1442
+ "Information Retrieval for Music and Motion."
1443
+ Springer Verlag. 2007.
1444
+
1445
+ Parameters
1446
+ ----------
1447
+ center_freqs : np.ndarray [shape=(n,), dtype=float]
1448
+ Center frequencies of the filter kernels.
1449
+ Also defines the number of filters in the filterbank.
1450
+ tuning : float [scalar]
1451
+ Tuning deviation from A440 as a fraction of a semitone (1/12 of an octave
1452
+ in equal temperament).
1453
+ sample_rates : np.ndarray [shape=(n,), dtype=float]
1454
+ Sample rates of each filter in the multirate filterbank.
1455
+ flayout : string
1456
+ - If `ba`, the standard difference equation is used for filtering with `scipy.signal.filtfilt`.
1457
+ Can be unstable for high-order filters.
1458
+ - If `sos`, a series of second-order filters is used for filtering with `scipy.signal.sosfiltfilt`.
1459
+ Minimizes numerical precision errors for high-order filters, but is slower.
1460
+ **kwargs : additional keyword arguments
1461
+ Additional arguments to the private function `_multirate_fb()`.
1462
+
1463
+ Returns
1464
+ -------
1465
+ filterbank : list [shape=(n,), dtype=float]
1466
+ Each list entry contains the filter coefficients for a single filter.
1467
+ fb_sample_rates : np.ndarray [shape=(n,), dtype=float]
1468
+ Sample rate for each filter.
1469
+
1470
+ See Also
1471
+ --------
1472
+ librosa.cqt
1473
+ librosa.iirt
1474
+ librosa.filters.mr_frequencies
1475
+ scipy.signal.iirdesign
1476
+
1477
+ Examples
1478
+ --------
1479
+ >>> import matplotlib.pyplot as plt
1480
+ >>> import numpy as np
1481
+ >>> import scipy.signal
1482
+ >>> semitone_filterbank, sample_rates = librosa.filters.semitone_filterbank(
1483
+ ... center_freqs=librosa.midi_to_hz(np.arange(60, 72)),
1484
+ ... sample_rates=np.repeat(4410.0, 12),
1485
+ ... flayout='sos'
1486
+ ... )
1487
+ >>> magnitudes = []
1488
+ >>> for cur_sr, cur_filter in zip(sample_rates, semitone_filterbank):
1489
+ ... w, h = scipy.signal.sosfreqz(cur_filter,fs=cur_sr, worN=1025)
1490
+ ... magnitudes.append(20 * np.log10(np.abs(h)))
1491
+ >>> fig, ax = plt.subplots(figsize=(12,6))
1492
+ >>> img = librosa.display.specshow(
1493
+ ... np.array(magnitudes),
1494
+ ... x_axis="hz",
1495
+ ... sr=4410,
1496
+ ... y_coords=librosa.midi_to_hz(np.arange(60, 72)),
1497
+ ... vmin=-60,
1498
+ ... vmax=3,
1499
+ ... ax=ax
1500
+ ... )
1501
+ >>> fig.colorbar(img, ax=ax, format="%+2.f dB", label="Magnitude (dB)")
1502
+ >>> ax.set(
1503
+ ... xlim=[200, 600],
1504
+ ... yticks=librosa.midi_to_hz(np.arange(60, 72)),
1505
+ ... title='Magnitude Responses of the Pitch Filterbank',
1506
+ ... xlabel='Frequency (Hz)',
1507
+ ... ylabel='Semitone filter center frequency (Hz)'
1508
+ ... )
1509
+ """
1510
+ if (center_freqs is None) and (sample_rates is None):
1511
+ center_freqs, sample_rates = mr_frequencies(tuning)
1512
+
1513
+ filterbank, fb_sample_rates = _multirate_fb(
1514
+ center_freqs=center_freqs, sample_rates=sample_rates, flayout=flayout, **kwargs
1515
+ )
1516
+
1517
+ return filterbank, fb_sample_rates
1518
+
1519
+
1520
+ @jit(nopython=True, cache=True)
1521
+ def __window_ss_fill(x, win_sq, n_frames, hop_length): # pragma: no cover
1522
+ """Compute the sum-square envelope of a window."""
1523
+ n = len(x)
1524
+ n_fft = len(win_sq)
1525
+ for i in range(n_frames):
1526
+ sample = i * hop_length
1527
+ x[sample : min(n, sample + n_fft)] += win_sq[: max(0, min(n_fft, n - sample))]
1528
+
1529
+
1530
+ def window_sumsquare(
1531
+ *,
1532
+ window: _WindowSpec,
1533
+ n_frames: int,
1534
+ hop_length: int = 512,
1535
+ win_length: Optional[int] = None,
1536
+ n_fft: int = 2048,
1537
+ dtype: DTypeLike = np.float32,
1538
+ norm: Optional[float] = None,
1539
+ ) -> np.ndarray:
1540
+ """Compute the sum-square envelope of a window function at a given hop length.
1541
+
1542
+ This is used to estimate modulation effects induced by windowing observations
1543
+ in short-time Fourier transforms.
1544
+
1545
+ Parameters
1546
+ ----------
1547
+ window : string, tuple, number, callable, or list-like
1548
+ Window specification, as in `get_window`
1549
+ n_frames : int > 0
1550
+ The number of analysis frames
1551
+ hop_length : int > 0
1552
+ The number of samples to advance between frames
1553
+ win_length : [optional]
1554
+ The length of the window function. By default, this matches ``n_fft``.
1555
+ n_fft : int > 0
1556
+ The length of each analysis frame.
1557
+ dtype : np.dtype
1558
+ The data type of the output
1559
+ norm : {np.inf, -np.inf, 0, float > 0, None}
1560
+ Normalization mode used in window construction.
1561
+ Note that this does not affect the squaring operation.
1562
+
1563
+ Returns
1564
+ -------
1565
+ wss : np.ndarray, shape=``(n_fft + hop_length * (n_frames - 1))``
1566
+ The sum-squared envelope of the window function
1567
+
1568
+ Examples
1569
+ --------
1570
+ For a fixed frame length (2048), compare modulation effects for a Hann window
1571
+ at different hop lengths:
1572
+
1573
+ >>> n_frames = 50
1574
+ >>> wss_256 = librosa.filters.window_sumsquare(window='hann', n_frames=n_frames, hop_length=256)
1575
+ >>> wss_512 = librosa.filters.window_sumsquare(window='hann', n_frames=n_frames, hop_length=512)
1576
+ >>> wss_1024 = librosa.filters.window_sumsquare(window='hann', n_frames=n_frames, hop_length=1024)
1577
+
1578
+ >>> import matplotlib.pyplot as plt
1579
+ >>> fig, ax = plt.subplots(nrows=3, sharey=True)
1580
+ >>> ax[0].plot(wss_256)
1581
+ >>> ax[0].set(title='hop_length=256')
1582
+ >>> ax[1].plot(wss_512)
1583
+ >>> ax[1].set(title='hop_length=512')
1584
+ >>> ax[2].plot(wss_1024)
1585
+ >>> ax[2].set(title='hop_length=1024')
1586
+ """
1587
+ if win_length is None:
1588
+ win_length = n_fft
1589
+
1590
+ n = n_fft + hop_length * (n_frames - 1)
1591
+ x = np.zeros(n, dtype=dtype)
1592
+
1593
+ # Compute the squared window at the desired length
1594
+ win_sq = get_window(window, win_length)
1595
+ win_sq = util.normalize(win_sq, norm=norm) ** 2
1596
+ win_sq = util.pad_center(win_sq, size=n_fft)
1597
+
1598
+ # Fill the envelope
1599
+ __window_ss_fill(x, win_sq, n_frames, hop_length)
1600
+
1601
+ return x
1602
+
1603
+
1604
+ @cache(level=10)
1605
+ def diagonal_filter(
1606
+ window: _WindowSpec,
1607
+ n: int,
1608
+ *,
1609
+ slope: float = 1.0,
1610
+ angle: Optional[float] = None,
1611
+ zero_mean: bool = False,
1612
+ ) -> np.ndarray:
1613
+ """Build a two-dimensional diagonal filter.
1614
+
1615
+ This is primarily used for smoothing recurrence or self-similarity matrices.
1616
+
1617
+ Parameters
1618
+ ----------
1619
+ window : string, tuple, number, callable, or list-like
1620
+ The window function to use for the filter.
1621
+
1622
+ See `get_window` for details.
1623
+
1624
+ Note that the window used here should be non-negative.
1625
+
1626
+ n : int > 0
1627
+ the length of the filter
1628
+
1629
+ slope : float
1630
+ The slope of the diagonal filter to produce
1631
+
1632
+ angle : float or None
1633
+ If given, the slope parameter is ignored,
1634
+ and angle directly sets the orientation of the filter (in radians).
1635
+ Otherwise, angle is inferred as `arctan(slope)`.
1636
+
1637
+ zero_mean : bool
1638
+ If True, a zero-mean filter is used.
1639
+ Otherwise, a non-negative averaging filter is used.
1640
+
1641
+ This should be enabled if you want to enhance paths and suppress
1642
+ blocks.
1643
+
1644
+ Returns
1645
+ -------
1646
+ kernel : np.ndarray, shape=[(m, m)]
1647
+ The 2-dimensional filter kernel
1648
+
1649
+ Notes
1650
+ -----
1651
+ This function caches at level 10.
1652
+ """
1653
+ if angle is None:
1654
+ angle = np.arctan(slope)
1655
+
1656
+ win = np.diag(get_window(window, n, fftbins=False))
1657
+
1658
+ if not np.isclose(angle, np.pi / 4):
1659
+ win = scipy.ndimage.rotate(
1660
+ win, 45 - angle * 180 / np.pi, order=5, prefilter=False
1661
+ )
1662
+
1663
+ np.clip(win, 0, None, out=win)
1664
+ win /= win.sum()
1665
+
1666
+ if zero_mean:
1667
+ win -= win.mean()
1668
+
1669
+ return win