To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

Statistics Download as Zip
| Branch: | Tag: | Revision:

root / CepstrumPitchTracker.cpp @ 5:383c5b497f4a

History | View | Annotate | Download (9.79 KB)

1
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
2
/*
3
    Permission is hereby granted, free of charge, to any person
4
    obtaining a copy of this software and associated documentation
5
    files (the "Software"), to deal in the Software without
6
    restriction, including without limitation the rights to use, copy,
7
    modify, merge, publish, distribute, sublicense, and/or sell copies
8
    of the Software, and to permit persons to whom the Software is
9
    furnished to do so, subject to the following conditions:
10

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

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

    
23
#include "CepstrumPitchTracker.h"
24

    
25
#include <vector>
26
#include <algorithm>
27

    
28
#include <cstdio>
29
#include <cmath>
30
#include <complex>
31

    
32
using std::string;
33

    
34
CepstrumPitchTracker::CepstrumPitchTracker(float inputSampleRate) :
35
    Plugin(inputSampleRate),
36
    m_channels(0),
37
    m_stepSize(256),
38
    m_blockSize(1024),
39
    m_fmin(50),
40
    m_fmax(1000),
41
    m_histlen(1),
42
    m_vflen(3),
43
    m_binFrom(0),
44
    m_binTo(0),
45
    m_bins(0),
46
    m_history(0)
47
{
48
}
49

    
50
CepstrumPitchTracker::~CepstrumPitchTracker()
51
{
52
    if (m_history) {
53
        for (int i = 0; i < m_histlen; ++i) {
54
            delete[] m_history[i];
55
        }
56
        delete[] m_history;
57
    }
58
}
59

    
60
string
61
CepstrumPitchTracker::getIdentifier() const
62
{
63
    return "cepstrum-pitch";
64
}
65

    
66
string
67
CepstrumPitchTracker::getName() const
68
{
69
    return "Cepstrum Pitch Tracker";
70
}
71

    
72
string
73
CepstrumPitchTracker::getDescription() const
74
{
75
    return "Estimate f0 of monophonic material using a cepstrum method.";
76
}
77

    
78
string
79
CepstrumPitchTracker::getMaker() const
80
{
81
    return "Chris Cannam";
82
}
83

    
84
int
85
CepstrumPitchTracker::getPluginVersion() const
86
{
87
    // Increment this each time you release a version that behaves
88
    // differently from the previous one
89
    return 1;
90
}
91

    
92
string
93
CepstrumPitchTracker::getCopyright() const
94
{
95
    return "Freely redistributable (BSD license)";
96
}
97

    
98
CepstrumPitchTracker::InputDomain
99
CepstrumPitchTracker::getInputDomain() const
100
{
101
    return FrequencyDomain;
102
}
103

    
104
size_t
105
CepstrumPitchTracker::getPreferredBlockSize() const
106
{
107
    return 1024;
108
}
109

    
110
size_t 
111
CepstrumPitchTracker::getPreferredStepSize() const
112
{
113
    return 256;
114
}
115

    
116
size_t
117
CepstrumPitchTracker::getMinChannelCount() const
118
{
119
    return 1;
120
}
121

    
122
size_t
123
CepstrumPitchTracker::getMaxChannelCount() const
124
{
125
    return 1;
126
}
127

    
128
CepstrumPitchTracker::ParameterList
129
CepstrumPitchTracker::getParameterDescriptors() const
130
{
131
    ParameterList list;
132
    return list;
133
}
134

    
135
float
136
CepstrumPitchTracker::getParameter(string identifier) const
137
{
138
    return 0.f;
139
}
140

    
141
void
142
CepstrumPitchTracker::setParameter(string identifier, float value) 
143
{
144
}
145

    
146
CepstrumPitchTracker::ProgramList
147
CepstrumPitchTracker::getPrograms() const
148
{
149
    ProgramList list;
150
    return list;
151
}
152

    
153
string
154
CepstrumPitchTracker::getCurrentProgram() const
155
{
156
    return ""; // no programs
157
}
158

    
159
void
160
CepstrumPitchTracker::selectProgram(string name)
161
{
162
}
163

    
164
CepstrumPitchTracker::OutputList
165
CepstrumPitchTracker::getOutputDescriptors() const
166
{
167
    OutputList outputs;
168

    
169
    int n = 0;
170

    
171
    OutputDescriptor d;
172

    
173
    d.identifier = "f0";
174
    d.name = "Estimated f0";
175
    d.description = "Estimated fundamental frequency";
176
    d.unit = "Hz";
177
    d.hasFixedBinCount = true;
178
    d.binCount = 1;
179
    d.hasKnownExtents = true;
180
    d.minValue = m_fmin;
181
    d.maxValue = m_fmax;
182
    d.isQuantized = false;
183
    d.sampleType = OutputDescriptor::FixedSampleRate;
184
    d.sampleRate = (m_inputSampleRate / m_stepSize);
185
    d.hasDuration = false;
186
    outputs.push_back(d);
187

    
188
    return outputs;
189
}
190

    
191
bool
192
CepstrumPitchTracker::initialise(size_t channels, size_t stepSize, size_t blockSize)
193
{
194
    if (channels < getMinChannelCount() ||
195
        channels > getMaxChannelCount()) return false;
196

    
197
//    std::cerr << "CepstrumPitchTracker::initialise: channels = " << channels
198
//              << ", stepSize = " << stepSize << ", blockSize = " << blockSize
199
//              << std::endl;
200

    
201
    m_channels = channels;
202
    m_stepSize = stepSize;
203
    m_blockSize = blockSize;
204

    
205
    m_binFrom = int(m_inputSampleRate / m_fmax);
206
    m_binTo = int(m_inputSampleRate / m_fmin); 
207

    
208
    if (m_binTo >= (int)m_blockSize / 2) {
209
        m_binTo = m_blockSize / 2 - 1;
210
    }
211

    
212
    m_bins = (m_binTo - m_binFrom) + 1;
213

    
214
    m_history = new double *[m_histlen];
215
    for (int i = 0; i < m_histlen; ++i) {
216
        m_history[i] = new double[m_bins];
217
    }
218

    
219
    reset();
220

    
221
    return true;
222
}
223

    
224
void
225
CepstrumPitchTracker::reset()
226
{
227
    for (int i = 0; i < m_histlen; ++i) {
228
        for (int j = 0; j < m_bins; ++j) {
229
            m_history[i][j] = 0.0;
230
        }
231
    }
232
}
233

    
234
void
235
CepstrumPitchTracker::filter(const double *cep, double *result)
236
{
237
    int hix = m_histlen - 1; // current history index
238

    
239
    // roll back the history
240
    if (m_histlen > 1) {
241
        double *oldest = m_history[0];
242
        for (int i = 1; i < m_histlen; ++i) {
243
            m_history[i-1] = m_history[i];
244
        }
245
        // and stick this back in the newest spot, to recycle
246
        m_history[hix] = oldest;
247
    }
248

    
249
    for (int i = 0; i < m_bins; ++i) {
250
        double v = 0;
251
        int n = 0;
252
        // average according to the vertical filter length
253
        for (int j = -m_vflen/2; j <= m_vflen/2; ++j) {
254
            int ix = i + m_binFrom + j;
255
            if (ix >= 0 && ix < m_blockSize) {
256
                v += cep[ix];
257
                ++n;
258
            }
259
        }
260
        m_history[hix][i] = v / n;
261
    }
262

    
263
    for (int i = 0; i < m_bins; ++i) {
264
        double mean = 0.0;
265
        for (int j = 0; j < m_histlen; ++j) {
266
            mean += m_history[j][i];
267
        }
268
        mean /= m_histlen;
269
        result[i] = mean;
270
    }
271
}
272

    
273
CepstrumPitchTracker::FeatureSet
274
CepstrumPitchTracker::process(const float *const *inputBuffers, Vamp::RealTime timestamp)
275
{
276
    FeatureSet fs;
277

    
278
    int bs = m_blockSize;
279
    int hs = m_blockSize/2 + 1;
280

    
281
    double *rawcep = new double[bs];
282
    double *io = new double[bs];
283
    double *logmag = new double[bs];
284

    
285
    // The "inverse symmetric" method. Seems to be the most reliable
286
        
287
    for (int i = 0; i < hs; ++i) {
288

    
289
        double power =
290
            inputBuffers[0][i*2  ] * inputBuffers[0][i*2  ] +
291
            inputBuffers[0][i*2+1] * inputBuffers[0][i*2+1];
292
        double mag = sqrt(power);
293
        
294
        double lm = log(mag + 0.00000001);
295
        
296
        logmag[i] = lm;
297
        if (i > 0) logmag[bs - i] = lm;
298
    }
299

    
300
    fft(bs, true, logmag, 0, rawcep, io);
301
    
302
    delete[] logmag;
303
    delete[] io;
304

    
305
    int n = m_bins;
306
    double *data = new double[n];
307
    filter(rawcep, data);
308
    delete[] rawcep;
309

    
310
    double maxval = 0.0;
311
    int maxbin = 0;
312
    double abstot = 0.0;
313

    
314
    for (int i = 0; i < n; ++i) {
315
        if (data[i] > maxval) {
316
            maxval = data[i];
317
            maxbin = i;
318
        }
319
        abstot += fabs(data[i]);
320
    }
321

    
322
    double aroundPeak = 0.0;
323
    double peakProportion = 0.0;
324
    if (maxval > 0.0) {
325
        aroundPeak += fabs(maxval);
326
        int i = maxbin - 1;
327
        while (i > 0 && data[i] <= data[i+1]) {
328
            aroundPeak += fabs(data[i]);
329
            --i;
330
        }
331
        i = maxbin + 1;
332
        while (i < n && data[i] <= data[i-1]) {
333
            aroundPeak += fabs(data[i]);
334
            ++i;
335
        }
336
    }
337
    peakProportion = aroundPeak / abstot;
338

    
339
//    std::cerr << "peakProportion = " << peakProportion << std::endl;
340
//    std::cerr << "peak = " << m_inputSampleRate / (maxbin + m_binFrom) << std::endl;
341
//    std::cerr << "bins = " << m_bins << std::endl;
342

    
343
    if (peakProportion >= (0.00006 * m_bins)) {
344
        Feature f;
345
        f.hasTimestamp = true;
346
        f.timestamp = timestamp;
347
        f.values.push_back(m_inputSampleRate / (maxbin + m_binFrom));
348
        fs[0].push_back(f);
349
    }
350

    
351
    delete[] data;
352
    return fs;
353
}
354

    
355
CepstrumPitchTracker::FeatureSet
356
CepstrumPitchTracker::getRemainingFeatures()
357
{
358
    FeatureSet fs;
359
    return fs;
360
}
361

    
362
void
363
CepstrumPitchTracker::fft(unsigned int n, bool inverse,
364
                    double *ri, double *ii, double *ro, double *io)
365
{
366
    if (!ri || !ro || !io) return;
367

    
368
    unsigned int bits;
369
    unsigned int i, j, k, m;
370
    unsigned int blockSize, blockEnd;
371

    
372
    double tr, ti;
373

    
374
    if (n < 2) return;
375
    if (n & (n-1)) return;
376

    
377
    double angle = 2.0 * M_PI;
378
    if (inverse) angle = -angle;
379

    
380
    for (i = 0; ; ++i) {
381
        if (n & (1 << i)) {
382
            bits = i;
383
            break;
384
        }
385
    }
386

    
387
    static unsigned int tableSize = 0;
388
    static int *table = 0;
389

    
390
    if (tableSize != n) {
391

    
392
        delete[] table;
393

    
394
        table = new int[n];
395

    
396
        for (i = 0; i < n; ++i) {
397
        
398
            m = i;
399

    
400
            for (j = k = 0; j < bits; ++j) {
401
                k = (k << 1) | (m & 1);
402
                m >>= 1;
403
            }
404

    
405
            table[i] = k;
406
        }
407

    
408
        tableSize = n;
409
    }
410

    
411
    if (ii) {
412
        for (i = 0; i < n; ++i) {
413
            ro[table[i]] = ri[i];
414
            io[table[i]] = ii[i];
415
        }
416
    } else {
417
        for (i = 0; i < n; ++i) {
418
            ro[table[i]] = ri[i];
419
            io[table[i]] = 0.0;
420
        }
421
    }
422

    
423
    blockEnd = 1;
424

    
425
    for (blockSize = 2; blockSize <= n; blockSize <<= 1) {
426

    
427
        double delta = angle / (double)blockSize;
428
        double sm2 = -sin(-2 * delta);
429
        double sm1 = -sin(-delta);
430
        double cm2 = cos(-2 * delta);
431
        double cm1 = cos(-delta);
432
        double w = 2 * cm1;
433
        double ar[3], ai[3];
434

    
435
        for (i = 0; i < n; i += blockSize) {
436

    
437
            ar[2] = cm2;
438
            ar[1] = cm1;
439

    
440
            ai[2] = sm2;
441
            ai[1] = sm1;
442

    
443
            for (j = i, m = 0; m < blockEnd; j++, m++) {
444

    
445
                ar[0] = w * ar[1] - ar[2];
446
                ar[2] = ar[1];
447
                ar[1] = ar[0];
448

    
449
                ai[0] = w * ai[1] - ai[2];
450
                ai[2] = ai[1];
451
                ai[1] = ai[0];
452

    
453
                k = j + blockEnd;
454
                tr = ar[0] * ro[k] - ai[0] * io[k];
455
                ti = ar[0] * io[k] + ai[0] * ro[k];
456

    
457
                ro[k] = ro[j] - tr;
458
                io[k] = io[j] - ti;
459

    
460
                ro[j] += tr;
461
                io[j] += ti;
462
            }
463
        }
464

    
465
        blockEnd = blockSize;
466
    }
467
}
468

    
469