Skip to content

Commit b5fa495

Browse files
Sebastian BöckSebastian Böck
authored andcommitted
docstring, formatting and Numpy 1.8 compatibility fixes
1 parent 6b70da4 commit b5fa495

2 files changed

Lines changed: 82 additions & 84 deletions

File tree

madmom/audio/cepstrogram.py

Lines changed: 60 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
from __future__ import absolute_import, division, print_function
1111

1212
import inspect
13-
import math
1413
from functools import partial
1514

1615
import numpy as np
@@ -125,9 +124,9 @@ def process(self, data, **kwargs):
125124
MFCC_NORM_FILTERS = True
126125
MFCC_MUL = 1.
127126
MFCC_ADD = np.spacing(1)
128-
MFCC_DCT_NORM = "ortho"
127+
MFCC_DCT_NORM = 'ortho'
129128
MFCC_DELTA_FILTER = np.linspace(4, -4, 9) / 60
130-
MFCC_DELTADELTA_FILTER = np.linspace(1, -1, 3) / 2
129+
MFCC_DELTA_DELTA_FILTER = np.linspace(1, -1, 3) / 2
131130

132131

133132
class MFCC(Cepstrogram):
@@ -159,7 +158,7 @@ class MFCC(Cepstrogram):
159158
Add this value before taking the logarithm of the magnitudes.
160159
dct_norm : {'ortho', None}, optional
161160
Normalization mode (see scipy.fftpack.dct). Default is 'ortho'.
162-
kwargs : dict
161+
kwargs : dict, optional
163162
If no :class:`.audio.spectrogram.Spectrogram` instance was given, one
164163
is instantiated and these keyword arguments are passed.
165164
@@ -234,7 +233,7 @@ def __new__(cls, spectrogram, filterbank=MelFilterbank,
234233
@staticmethod
235234
def calc_deltas(data, delta_filter):
236235
"""
237-
Applies the given filter to the data after automatically padding by
236+
Apply the given filter to the data after automatically padding by
238237
replicating the first and last frame. The length of the padding is
239238
calculated via ceil(len(delta_filter)).
240239
@@ -245,106 +244,111 @@ def calc_deltas(data, delta_filter):
245244
Parameters
246245
----------
247246
data: numpy array
248-
containing the data to process
247+
Data to process, i.e. MFCCs or deltas thereof.
249248
delta_filter: numpy array
250-
the filter used for convolution
249+
Filter used for convolution.
251250
252251
Returns
253252
-------
254253
deltas: numpy array
255-
containing the deltas, has the same shape as data
254+
Deltas of `data`, same shape as `data`.
255+
256256
"""
257-
# prepare vectorized convolve function
258-
# (requires transposed matrices in our use case)
259-
vconv = np.vectorize(partial(np.convolve, mode="same"),
260-
signature='(n),(m)->(k)')
261257
# pad data by replicating the first and the last frame
262-
k = int(math.ceil(len(delta_filter) / 2))
263-
padded = np.vstack((np.array([data[0], ] * k),
264-
data,
258+
k = int(np.ceil(len(delta_filter) / 2))
259+
padded = np.vstack((np.array([data[0], ] * k), data,
265260
np.array([data[-1], ] * k)))
266261
# calculate the deltas for each coefficient
267-
deltas = vconv(padded.transpose(), delta_filter)
268-
return deltas.transpose()[k:-k]
262+
deltas = []
263+
for band in padded.T:
264+
deltas.append(np.convolve(band, delta_filter, 'same'))
265+
# return deltas (first/last k frames truncated)
266+
return np.vstack(deltas).T[k:-k]
269267

270268
@lazyprop
271269
def deltas(self, delta_filter=MFCC_DELTA_FILTER):
272270
"""
273-
Return the derivative of this MFCC's coefficients by convolving with
274-
a filter. Accessing this property corresponds to the function call
275-
``MFCC.calc_deltas(self, delta_filter)``. However, using this property,
276-
the result is calculated only once and cached for later access.
277-
See ``@lazyprop``for further details.
271+
First order derivative of the MFCCs.
278272
279273
Parameters
280274
----------
281275
delta_filter: numpy array, optional
282-
the filter used for convolution, defaults to MFCC_DELTA_FILTER
276+
Filter to calculate the derivative of the MFCCs.
283277
284278
Returns
285279
-------
286280
deltas: numpy array
287-
containing the deltas, has the same shape as self
281+
Deltas of the MFCCs, same shape as MFCCs.
282+
283+
Notes
284+
-----
285+
Accessing this property corresponds to the function call
286+
``MFCC.calc_deltas(mfccs, delta_filter)``, with results being cached.
287+
288288
"""
289289
return MFCC.calc_deltas(self, delta_filter)
290290

291291
@lazyprop
292-
def deltadeltas(self, deltadelta_filter=MFCC_DELTADELTA_FILTER):
292+
def delta_deltas(self, delta_delta_filter=MFCC_DELTA_DELTA_FILTER):
293293
"""
294-
Return the second order derivative of this MFCC's coefficients by
295-
convolving with a filter. Accessing this property corresponds to the
296-
function call ``MFCC.calc_deltas(self, deltadelta_filter)``. However,
297-
using this property, the result is calculated only once and cached
298-
for later access. See ``@lazyprop``for further details.
294+
Second order derivatives of the MFCCs.
299295
300296
Parameters
301297
----------
302-
delta_filter: numpy array, optional
303-
the filter used for convolution, defaults to MFCC_DELTA_FILTER
298+
delta_delta_filter: numpy array, optional
299+
Filter to calculate the derivative of the derivative.
304300
305301
Returns
306302
-------
307303
deltas: numpy array
308-
containing the deltas, has the same shape as self
304+
Delta deltas of the MFCCs, same shape as MFCCs.
305+
306+
Notes
307+
-----
308+
Accessing this property corresponds to the function call
309+
``MFCC.calc_deltas(deltas, delta_delta_filter)``, with results being
310+
cached.
311+
309312
"""
310-
return MFCC.calc_deltas(self.deltas, deltadelta_filter)
313+
return MFCC.calc_deltas(self.deltas, delta_delta_filter)
311314

312315
def calc_voicebox_deltas(self, delta_filter=MFCC_DELTA_FILTER,
313-
ddelta_filter=MFCC_DELTADELTA_FILTER):
316+
delta_delta_filter=MFCC_DELTA_DELTA_FILTER):
314317
"""
315-
Method to calculate deltas and deltadeltas the way it is done in the
316-
voicebox MatLab toolbox.
317-
318-
see http://www.ee.ic.ac.uk/hp/staff/dmb/voicebox/voicebox.html
318+
Calculates deltas and delta deltas the way it is done in the voicebox
319+
MatLab toolbox [1]_.
319320
320321
Parameters
321322
----------
322323
delta_filter : numpy array
323-
filter to calculate the derivative of this MFCC's data
324-
ddelta_filter : numpy array
325-
filter to calculate the derivative of the derivative
324+
Filter to calculate the derivative of the MFCCs.
325+
delta_delta_filter : numpy array
326+
Filter to calculate the derivative of the derivative.
326327
327328
Returns
328329
-------
329-
[self, deltas, deltadeltas] : numpy array, shape (|frames|, |bands|*3)
330-
a horizontally stacked np array consisting of the MFCC coefficients
331-
its derivative and the derivative of second order
330+
[mfcc, delta, delta_delta] : numpy array, shape (num_frames, bands * 3)
331+
Horizontally stacked array consisting of the MFCC coefficients,
332+
their first and second order derivatives.
333+
334+
References
335+
----------
336+
.. [1] http://www.ee.ic.ac.uk/hp/staff/dmb/voicebox/voicebox.html
337+
332338
"""
333339
padded_input = np.vstack(
334340
(np.array([self[0], ] * 5), self, np.array([self[-1], ] * 5)))
335341
deltashape = tuple(reversed(padded_input.shape))
336342
flat_input = padded_input.transpose().flatten()
337-
338-
deltas = np.convolve(flat_input, delta_filter, mode="same") \
339-
.reshape(deltashape).T[4:-4, ]
343+
deltas = np.convolve(flat_input, delta_filter, mode='same')
344+
deltas = deltas.reshape(deltashape).T[4:-4, ]
340345
deltadeltashape = tuple(reversed(deltas.shape))
341346
flat_deltas = deltas.transpose().flatten()
342347
deltas = deltas[1:-1, ]
343-
344-
deltadeltas = np.convolve(flat_deltas, ddelta_filter, mode="same") \
345-
.reshape(deltadeltashape).T[1:-1, ]
346-
347-
return np.hstack((self, deltas, deltadeltas))
348+
delta_deltas = np.convolve(flat_deltas, delta_delta_filter,
349+
mode='same')
350+
delta_deltas = delta_deltas.reshape(deltadeltashape).T[1:-1, ]
351+
return np.hstack((self, deltas, delta_deltas))
348352

349353
def __array_finalize__(self, obj):
350354
if obj is None:
@@ -358,9 +362,8 @@ def __array_finalize__(self, obj):
358362

359363
class MFCCProcessor(Processor):
360364
"""
361-
MFCCProcessor is CepstrogramProcessor which filters the magnitude
362-
spectrogram of the spectrogram with a Mel filterbank, takes the logarithm
363-
and performs a discrete cosine transform afterwards.
365+
MFCCProcessor filters the magnitude spectrogram with a Mel filterbank,
366+
takes the logarithm and performs a discrete cosine transform afterwards.
364367
365368
Parameters
366369
----------
@@ -377,8 +380,6 @@ class MFCCProcessor(Processor):
377380
logarithm.
378381
add : float, optional
379382
Add this value before taking the logarithm of the magnitudes.
380-
transform : numpy ufunc
381-
Transformation applied to the Mel filtered spectrogram.
382383
383384
"""
384385

@@ -403,7 +404,7 @@ def process(self, data, **kwargs):
403404
----------
404405
data : numpy array
405406
Data to be processed (a spectrogram).
406-
kwargs : dict
407+
kwargs : dict, optional
407408
Keyword arguments passed to :class:`MFCC`.
408409
409410
Returns
Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -17,59 +17,56 @@
1717
from . import AUDIO_PATH
1818

1919
sample_file = pj(AUDIO_PATH, 'sample.wav')
20-
sample_file_22050 = pj(AUDIO_PATH, 'sample_22050.wav')
2120

2221

2322
class TestMFCCClass(unittest.TestCase):
23+
24+
def setUp(self):
25+
self.mfcc = MFCC(sample_file)
26+
2427
def test_types(self):
25-
result = MFCC(sample_file)
26-
self.assertIsInstance(result, MFCC)
27-
self.assertIsInstance(result, Cepstrogram)
28+
self.assertIsInstance(self.mfcc, MFCC)
29+
self.assertIsInstance(self.mfcc, Cepstrogram)
2830
# attributes
29-
self.assertIsInstance(result.filterbank, MelFilterbank)
31+
self.assertIsInstance(self.mfcc.filterbank, MelFilterbank)
3032
# properties
31-
self.assertIsInstance(result.deltas, np.ndarray)
32-
self.assertIsInstance(result.deltadeltas, np.ndarray)
33-
self.assertIsInstance(result.num_bins, int)
34-
self.assertIsInstance(result.num_frames, int)
33+
self.assertIsInstance(self.mfcc.deltas, np.ndarray)
34+
self.assertIsInstance(self.mfcc.delta_deltas, np.ndarray)
35+
self.assertIsInstance(self.mfcc.num_bins, int)
36+
self.assertIsInstance(self.mfcc.num_frames, int)
3537
# wrong filterbank type
3638
with self.assertRaises(TypeError):
3739
FilteredSpectrogram(sample_file, filterbank='bla')
3840

3941
def test_values(self):
40-
# from file
41-
result = MFCC(sample_file)
4242
allclose = partial(np.allclose, rtol=1.e-3, atol=1.e-5)
43-
self.assertTrue(allclose(result[0, :6],
43+
# values
44+
self.assertTrue(allclose(self.mfcc[0, :6],
4445
[-3.61102366, 6.81075716, 2.55457568,
4546
1.88377929, 1.04133379, 0.6382336]))
46-
self.assertTrue(allclose(result[0, -6:],
47+
self.assertTrue(allclose(self.mfcc[0, -6:],
4748
[-0.20386486, -0.18468723, -0.00233107,
4849
0.20703268, 0.21419463, 0.00598407]))
4950
# attributes
50-
self.assertTrue(result.shape == (281, 30))
51-
51+
self.assertTrue(self.mfcc.shape == (281, 30))
5252
# properties
53-
self.assertEqual(result.num_bins, 30)
54-
self.assertEqual(result.num_frames, 281)
53+
self.assertEqual(self.mfcc.num_bins, 30)
54+
self.assertEqual(self.mfcc.num_frames, 281)
5555

5656
def test_deltas(self):
57-
# from file
58-
result = MFCC(sample_file)
5957
allclose = partial(np.allclose, rtol=1.e-2, atol=1.e-4)
60-
6158
# don't compare first element because it is dependent on the
6259
# padding used for filtering
63-
self.assertTrue(allclose(result.deltas[1, :6],
60+
self.assertTrue(allclose(self.mfcc.deltas[1, :6],
6461
[-0.02286286, -0.11329014, 0.05381977,
6562
0.10438456, 0.04268386, -0.06839912]))
66-
self.assertTrue(allclose(result.deltas[1, -6:],
63+
self.assertTrue(allclose(self.mfcc.deltas[1, -6:],
6764
[-0.03156065, -0.019716, -0.03417692,
6865
-0.07768068, -0.05539324, -0.02616282]))
69-
70-
self.assertTrue(allclose(result.deltadeltas[1, :6],
66+
# delta deltas
67+
self.assertTrue(allclose(self.mfcc.delta_deltas[1, :6],
7168
[-0.00804922, -0.009922, -0.00454391,
7269
0.0038989, 0.00254525, 0.0120557]))
73-
self.assertTrue(allclose(result.deltadeltas[1, -6:],
70+
self.assertTrue(allclose(self.mfcc.delta_deltas[1, -6:],
7471
[0.0072148, 0.00094424, 0.00029913,
7572
0.00530994, 0.00184207, -0.00276511]))

0 commit comments

Comments
 (0)