Revision 51:0997774f5fdc

View differences:

CepstralPitchTracker.cpp
23 23
*/
24 24

  
25 25
#include "CepstralPitchTracker.h"
26
#include "Cepstrum.h"
26 27
#include "MeanFilter.h"
27 28
#include "PeakInterpolator.h"
28 29

  
......
263 264
{
264 265
    FeatureSet fs;
265 266

  
266
    int bs = m_blockSize;
267
    int hs = m_blockSize/2 + 1;
268

  
269
    double *rawcep = new double[bs];
270
    double *io = new double[bs];
271
    double *logmag = new double[bs];
272

  
273
    // The "inverse symmetric" method. Seems to be the most reliable
274
        
275
    double magmean = 0.0;
276

  
277
    for (int i = 0; i < hs; ++i) {
278

  
279
	double power =
280
	    inputBuffers[0][i*2  ] * inputBuffers[0][i*2  ] +
281
	    inputBuffers[0][i*2+1] * inputBuffers[0][i*2+1];
282
	double mag = sqrt(power);
283

  
284
        magmean += mag;
285

  
286
	double lm = log(mag + 0.00000001);
287
	
288
	logmag[i] = lm;
289
	if (i > 0) logmag[bs - i] = lm;
290
    }
291

  
292
    magmean /= hs;
293
    double threshold = 0.1; // for magmean
294
    
295
    Vamp::FFT::inverse(bs, logmag, 0, rawcep, io);
296
    
297
    delete[] logmag;
298
    delete[] io;
267
    double *rawcep = new double[m_blockSize];
268
    double magmean = Cepstrum(m_blockSize).process(inputBuffers[0], rawcep);
299 269

  
300 270
    int n = m_bins;
301 271
    double *data = new double[n];
302
    MeanFilter(m_vflen).filterSubsequence(rawcep, data, m_blockSize, n, m_binFrom);
272
    MeanFilter(m_vflen).filterSubsequence
273
        (rawcep, data, m_blockSize, n, m_binFrom);
274

  
303 275
    delete[] rawcep;
304 276

  
305 277
    double maxval = 0.0;
......
332 304
    double peakfreq = m_inputSampleRate / (cimax + m_binFrom);
333 305

  
334 306
    double confidence = 0.0;
307
    double threshold = 0.1; // for magmean
308

  
335 309
    if (nextPeakVal != 0.0) {
336 310
        confidence = (maxval - nextPeakVal) * 10.0;
337 311
        if (magmean < threshold) confidence = 0.0;
Cepstrum.h
1
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
2
/*
3
    This file is Copyright (c) 2012 Chris Cannam
4
  
5
    Permission is hereby granted, free of charge, to any person
6
    obtaining a copy of this software and associated documentation
7
    files (the "Software"), to deal in the Software without
8
    restriction, including without limitation the rights to use, copy,
9
    modify, merge, publish, distribute, sublicense, and/or sell copies
10
    of the Software, and to permit persons to whom the Software is
11
    furnished to do so, subject to the following conditions:
12

  
13
    The above copyright notice and this permission notice shall be
14
    included in all copies or substantial portions of the Software.
15

  
16
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
20
    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
21
    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
*/
24

  
25
#ifndef _CEPSTRUM_H_
26
#define _CEPSTRUM_H_
27

  
28
#include "vamp-sdk/FFT.h"
29
#include <cmath>
30

  
31
#include <iostream>
32
#include <exception>
33

  
34
class Cepstrum
35
{
36
public:
37
    /**
38
     * Construct a cepstrum converter based on an n-point FFT.
39
     */
40
    Cepstrum(int n) : m_n(n) {
41
	if (n & (n-1)) {
42
	    throw "N must be a power of two";
43
	}
44
    }
45
    ~Cepstrum() { }
46
    
47
    /**
48
     * Convert the given frequency-domain data to the cepstral domain.
49
     *
50
     * The input must be in the format used for FrequencyDomain data
51
     * in the Vamp SDK: n/2+1 consecutive pairs of real and imaginary
52
     * component floats corresponding to bins 0..(n/2) of the FFT
53
     * output. Thus, n+2 values in total.
54
     *
55
     * The output consists of the raw cepstrum of length n.
56
     *
57
     * The cepstrum is calculated as the inverse FFT of a
58
     * synthetically symmetrical base-10 log magnitude spectrum.
59
     *
60
     * Returns the mean magnitude of the input spectrum.
61
     */
62
    double process(const float *in, double *out) {
63

  
64
	int hs = m_n/2 + 1;
65
	double *io = new double[m_n];
66
	double *logmag = new double[m_n];
67
	double epsilon = 1e-10;
68

  
69
	double magmean = 0.0;
70

  
71
	for (int i = 0; i < hs; ++i) {
72

  
73
	    double power = in[i*2] * in[i*2] + in[i*2+1] * in[i*2+1];
74
	    double mag = sqrt(power);
75
	    magmean += mag;
76

  
77
	    logmag[i] = log10(mag + epsilon);
78

  
79
	    if (i > 0) {
80
		// make the log magnitude spectrum symmetrical
81
		logmag[m_n - i] = logmag[i];
82
	    }
83
	}
84
	
85
	magmean /= hs;
86
	/*
87
	std::cerr << "logmags:" << std::endl;
88
	for (int i = 0; i < m_n; ++i) {
89
	    std::cerr << logmag[i] << " ";
90
	}
91
	std::cerr << std::endl;
92
	*/
93
	Vamp::FFT::inverse(m_n, logmag, 0, out, io);
94
    
95
	delete[] logmag;
96
	delete[] io;
97

  
98
	return magmean;
99
    }
100

  
101
private:
102
    int m_n;
103
};
104

  
105
#endif
Makefile.inc
24 24

  
25 25
PLUGIN_MAIN := libmain.cpp
26 26

  
27
TESTS := test/test-notehypothesis \
28
         test/test-meanfilter \
27
TESTS := test/test-meanfilter \
29 28
         test/test-fft \
30
         test/test-peakinterpolator
31

  
29
	 test/test-cepstrum \
30
         test/test-peakinterpolator \
31
	 test/test-notehypothesis
32
         
32 33
OBJECTS := $(SOURCES:.cpp=.o)
33 34
OBJECTS := $(OBJECTS:.c=.o)
34 35

  
......
46 47
test/test-meanfilter: test/TestMeanFilter.o $(OBJECTS)
47 48
	$(CXX) -o $@ $^ $(TEST_LDFLAGS)
48 49

  
50
test/test-cepstrum: test/TestCepstrum.o $(OBJECTS)
51
	$(CXX) -o $@ $^ $(TEST_LDFLAGS)
52

  
49 53
test/test-fft: test/TestFFT.o $(OBJECTS)
50 54
	$(CXX) -o $@ $^ $(TEST_LDFLAGS)
51 55

  
......
58 62
distclean:	clean
59 63
		rm -f $(PLUGIN) $(TESTS)
60 64

  
65
depend:
66
		makedepend -Y -fMakefile.inc *.cpp test/*.cpp *.h test/*.h
67

  
61 68
# DO NOT DELETE
62 69

  
63
CepstralPitchTracker.o: CepstralPitchTracker.h NoteHypothesis.h
70
CepstralPitchTracker.o: CepstralPitchTracker.h NoteHypothesis.h Cepstrum.h
71
CepstralPitchTracker.o: MeanFilter.h PeakInterpolator.h
64 72
libmain.o: CepstralPitchTracker.h NoteHypothesis.h
65 73
NoteHypothesis.o: NoteHypothesis.h
66 74
PeakInterpolator.o: PeakInterpolator.h
75
test/TestCepstrum.o: Cepstrum.h
76
test/TestMeanFilter.o: MeanFilter.h
67 77
test/TestNoteHypothesis.o: NoteHypothesis.h
68 78
test/TestPeakInterpolator.o: PeakInterpolator.h
79
CepstralPitchTracker.o: NoteHypothesis.h
test/TestCepstrum.cpp
1
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
2
/*
3
    This file is Copyright (c) 2012 Chris Cannam
4
  
5
    Permission is hereby granted, free of charge, to any person
6
    obtaining a copy of this software and associated documentation
7
    files (the "Software"), to deal in the Software without
8
    restriction, including without limitation the rights to use, copy,
9
    modify, merge, publish, distribute, sublicense, and/or sell copies
10
    of the Software, and to permit persons to whom the Software is
11
    furnished to do so, subject to the following conditions:
12

  
13
    The above copyright notice and this permission notice shall be
14
    included in all copies or substantial portions of the Software.
15

  
16
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
20
    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
21
    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
*/
24

  
25
#include "Cepstrum.h"
26

  
27
#define BOOST_TEST_DYN_LINK
28
#define BOOST_TEST_MAIN
29

  
30
#include <boost/test/unit_test.hpp>
31

  
32
BOOST_AUTO_TEST_SUITE(TestCepstrum)
33

  
34
BOOST_AUTO_TEST_CASE(cosine)
35
{
36
    // input format is re0, im0, re1, im1...
37
    float in[] = { 0,0, 10,0, 0,0 };
38
    double out[4];
39
    double mm = Cepstrum(4).process(in, out);
40
    BOOST_CHECK_SMALL(out[0] - (-4.5), 1e-10);
41
    BOOST_CHECK_EQUAL(out[1], 0);
42
    BOOST_CHECK_SMALL(out[2] - (-5.5), 1e-10);
43
    BOOST_CHECK_EQUAL(out[3], 0);
44
    BOOST_CHECK_EQUAL(mm, 10.0/3.0);
45
}
46

  
47
BOOST_AUTO_TEST_CASE(symmetry)
48
{
49
    // Cepstrum output bins 1..n-1 are symmetric about bin n/2
50
    float in[] = { 1,2,3,4,5,6,7,8,9,10 };
51
    double out[8];
52
    double mm = Cepstrum(8).process(in, out);
53
    BOOST_CHECK_SMALL(out[1] - out[7], 1e-14);
54
    BOOST_CHECK_SMALL(out[2] - out[6], 1e-14);
55
    BOOST_CHECK_SMALL(out[3] - out[5], 1e-14);
56
}
57

  
58
BOOST_AUTO_TEST_CASE(oneHarmonic)
59
{
60
    // input format is re0, im0, re1, im1, re2, im2
61
    // freq for bin i is i * samplerate / n
62
    // freqs:        0  sr/n  2sr/n  3sr/n  4sr/n  5sr/n  6sr/n  7sr/n  sr/2
63
    float in[] = { 0,0,  0,0,  10,0,   0,0,  10,0,   0,0,  10,0,   0,0,  0,0 };
64
    double out[16];
65
    double mm = Cepstrum(16).process(in, out);
66
    BOOST_CHECK_EQUAL(mm, 30.0/9.0);
67
    // peak is at 8
68
    BOOST_CHECK(out[8] > 0);
69
    // odd bins are all zero
70
    BOOST_CHECK_EQUAL(out[1], 0);
71
    BOOST_CHECK_EQUAL(out[3], 0);
72
    BOOST_CHECK_EQUAL(out[5], 0);
73
    BOOST_CHECK_EQUAL(out[7], 0);
74
    BOOST_CHECK_EQUAL(out[9], 0);
75
    BOOST_CHECK_EQUAL(out[11], 0);
76
    BOOST_CHECK_EQUAL(out[13], 0);
77
    BOOST_CHECK_EQUAL(out[15], 0);
78
    // the rest are negative
79
    BOOST_CHECK(out[0] < 0);
80
    BOOST_CHECK(out[2] < 0);
81
    BOOST_CHECK(out[4] < 0);
82
    BOOST_CHECK(out[6] < 0);
83
    BOOST_CHECK(out[10] < 0);
84
    BOOST_CHECK(out[12] < 0);
85
    BOOST_CHECK(out[14] < 0);
86
}
87

  
88
BOOST_AUTO_TEST_SUITE_END()
89

  
test/TestFFT.cpp
112 112
    COMPARE_ARRAY(back, in);
113 113
}
114 114

  
115
BOOST_AUTO_TEST_CASE(sineCosineDC)
116
{
117
    // Sine and cosine mixed
118
    double in[] = { 0.5, 1.0, -0.5, -1.0 };
119
    double re[4], im[4];
120
    Vamp::FFT::forward(4, in, 0, re, im);
121
    BOOST_CHECK_EQUAL(re[0], 0.0);
122
    BOOST_CHECK_CLOSE(re[1], 1.0, 1e-12);
123
    BOOST_CHECK_EQUAL(re[2], 0.0);
124
    BOOST_CHECK_EQUAL(im[0], 0.0);
125
    BOOST_CHECK_CLOSE(im[1], -2.0, 1e-12);
126
    BOOST_CHECK_EQUAL(im[2], 0.0);
127
    double back[4];
128
    double backim[4];
129
    Vamp::FFT::inverse(4, re, im, back, backim);
130
    COMPARE_ARRAY(back, in);
131
}
132

  
133 115
BOOST_AUTO_TEST_CASE(nyquist)
134 116
{
135 117
    double in[] = { 1, -1, 1, -1 };

Also available in: Unified diff