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 / CepstralPitchTracker.cpp @ 56:d8eeba570d70

History | View | Annotate | Download (7.79 KB)

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 "CepstralPitchTracker.h"
26
#include "Cepstrum.h"
27
#include "MeanFilter.h"
28
#include "PeakInterpolator.h"
29
#include "AgentFeeder.h"
30

    
31
#include "vamp-sdk/FFT.h"
32

    
33
#include <vector>
34
#include <algorithm>
35

    
36
#include <cstdio>
37
#include <cmath>
38
#include <complex>
39

    
40
using std::string;
41
using std::vector;
42
using Vamp::RealTime;
43

    
44

    
45
CepstralPitchTracker::CepstralPitchTracker(float inputSampleRate) :
46
    Plugin(inputSampleRate),
47
    m_channels(0),
48
    m_stepSize(256),
49
    m_blockSize(1024),
50
    m_fmin(50),
51
    m_fmax(900),
52
    m_vflen(1),
53
    m_binFrom(0),
54
    m_binTo(0),
55
    m_bins(0),
56
    m_feeder(0)
57
{
58
}
59

    
60
CepstralPitchTracker::~CepstralPitchTracker()
61
{
62
    delete m_feeder;
63
}
64

    
65
string
66
CepstralPitchTracker::getIdentifier() const
67
{
68
    return "cepstral-pitchtracker";
69
}
70

    
71
string
72
CepstralPitchTracker::getName() const
73
{
74
    return "Cepstral Pitch Tracker";
75
}
76

    
77
string
78
CepstralPitchTracker::getDescription() const
79
{
80
    return "Estimate f0 of monophonic material using a cepstrum method.";
81
}
82

    
83
string
84
CepstralPitchTracker::getMaker() const
85
{
86
    return "Chris Cannam";
87
}
88

    
89
int
90
CepstralPitchTracker::getPluginVersion() const
91
{
92
    // Increment this each time you release a version that behaves
93
    // differently from the previous one
94
    return 1;
95
}
96

    
97
string
98
CepstralPitchTracker::getCopyright() const
99
{
100
    return "Freely redistributable (BSD license)";
101
}
102

    
103
CepstralPitchTracker::InputDomain
104
CepstralPitchTracker::getInputDomain() const
105
{
106
    return FrequencyDomain;
107
}
108

    
109
size_t
110
CepstralPitchTracker::getPreferredBlockSize() const
111
{
112
    return 1024;
113
}
114

    
115
size_t 
116
CepstralPitchTracker::getPreferredStepSize() const
117
{
118
    return 256;
119
}
120

    
121
size_t
122
CepstralPitchTracker::getMinChannelCount() const
123
{
124
    return 1;
125
}
126

    
127
size_t
128
CepstralPitchTracker::getMaxChannelCount() const
129
{
130
    return 1;
131
}
132

    
133
CepstralPitchTracker::ParameterList
134
CepstralPitchTracker::getParameterDescriptors() const
135
{
136
    ParameterList list;
137
    return list;
138
}
139

    
140
float
141
CepstralPitchTracker::getParameter(string identifier) const
142
{
143
    return 0.f;
144
}
145

    
146
void
147
CepstralPitchTracker::setParameter(string identifier, float value) 
148
{
149
}
150

    
151
CepstralPitchTracker::ProgramList
152
CepstralPitchTracker::getPrograms() const
153
{
154
    ProgramList list;
155
    return list;
156
}
157

    
158
string
159
CepstralPitchTracker::getCurrentProgram() const
160
{
161
    return ""; // no programs
162
}
163

    
164
void
165
CepstralPitchTracker::selectProgram(string name)
166
{
167
}
168

    
169
CepstralPitchTracker::OutputList
170
CepstralPitchTracker::getOutputDescriptors() const
171
{
172
    OutputList outputs;
173

    
174
    OutputDescriptor d;
175

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

    
191
    d.identifier = "notes";
192
    d.name = "Notes";
193
    d.description = "Derived fixed-pitch note frequencies";
194
    d.unit = "Hz";
195
    d.hasFixedBinCount = true;
196
    d.binCount = 1;
197
    d.hasKnownExtents = true;
198
    d.minValue = m_fmin;
199
    d.maxValue = m_fmax;
200
    d.isQuantized = false;
201
    d.sampleType = OutputDescriptor::FixedSampleRate;
202
    d.sampleRate = (m_inputSampleRate / m_stepSize);
203
    d.hasDuration = true;
204
    outputs.push_back(d);
205

    
206
    return outputs;
207
}
208

    
209
bool
210
CepstralPitchTracker::initialise(size_t channels, size_t stepSize, size_t blockSize)
211
{
212
    if (channels < getMinChannelCount() ||
213
        channels > getMaxChannelCount()) return false;
214

    
215
//    std::cerr << "CepstralPitchTracker::initialise: channels = " << channels
216
//              << ", stepSize = " << stepSize << ", blockSize = " << blockSize
217
//              << std::endl;
218

    
219
    m_channels = channels;
220
    m_stepSize = stepSize;
221
    m_blockSize = blockSize;
222

    
223
    m_binFrom = int(m_inputSampleRate / m_fmax);
224
    m_binTo = int(m_inputSampleRate / m_fmin); 
225

    
226
    if (m_binTo >= (int)m_blockSize / 2) {
227
        m_binTo = m_blockSize / 2 - 1;
228
    }
229
    if (m_binFrom >= m_binTo) {
230
        // shouldn't happen except for degenerate samplerate / blocksize combos
231
        m_binFrom = m_binTo - 1;
232
    }
233

    
234
    m_bins = (m_binTo - m_binFrom) + 1;
235

    
236
    reset();
237

    
238
    return true;
239
}
240

    
241
void
242
CepstralPitchTracker::reset()
243
{
244
    delete m_feeder;
245
    m_feeder = new AgentFeeder();
246
}
247

    
248
void
249
CepstralPitchTracker::addFeaturesFrom(NoteHypothesis h, FeatureSet &fs)
250
{
251
    NoteHypothesis::Estimates es = h.getAcceptedEstimates();
252

    
253
    for (int i = 0; i < (int)es.size(); ++i) {
254
        Feature f;
255
        f.hasTimestamp = true;
256
        f.timestamp = es[i].time;
257
        f.values.push_back(es[i].freq);
258
        fs[0].push_back(f);
259
    }
260

    
261
    Feature nf;
262
    nf.hasTimestamp = true;
263
    nf.hasDuration = true;
264
    NoteHypothesis::Note n = h.getAveragedNote();
265
    nf.timestamp = n.time;
266
    nf.duration = n.duration;
267
    nf.values.push_back(n.freq);
268
    fs[1].push_back(nf);
269
}
270

    
271
CepstralPitchTracker::FeatureSet
272
CepstralPitchTracker::process(const float *const *inputBuffers, RealTime timestamp)
273
{
274
    FeatureSet fs;
275

    
276
    double *rawcep = new double[m_blockSize];
277
    double magmean = Cepstrum(m_blockSize).process(inputBuffers[0], rawcep);
278

    
279
    int n = m_bins;
280
    double *data = new double[n];
281
    MeanFilter(m_vflen).filterSubsequence
282
        (rawcep, data, m_blockSize, n, m_binFrom);
283

    
284
    delete[] rawcep;
285

    
286
    double maxval = 0.0;
287
    int maxbin = -1;
288

    
289
    for (int i = 0; i < n; ++i) {
290
        if (data[i] > maxval) {
291
            maxval = data[i];
292
            maxbin = i;
293
        }
294
    }
295

    
296
    if (maxbin < 0) {
297
        delete[] data;
298
        return fs;
299
    }
300

    
301
    double nextPeakVal = 0.0;
302
    for (int i = 1; i+1 < n; ++i) {
303
        if (data[i] > data[i-1] &&
304
            data[i] > data[i+1] &&
305
            i != maxbin &&
306
            data[i] > nextPeakVal) {
307
            nextPeakVal = data[i];
308
        }
309
    }
310

    
311
    PeakInterpolator pi;
312
    double cimax = pi.findPeakLocation(data, m_bins, maxbin);
313
    double peakfreq = m_inputSampleRate / (cimax + m_binFrom);
314

    
315
    double confidence = 0.0;
316
    double threshold = 0.1; // for magmean
317

    
318
    if (nextPeakVal != 0.0) {
319
        confidence = (maxval - nextPeakVal) * 10.0;
320
        if (magmean < threshold) confidence = 0.0;
321
//        std::cerr << "magmean = " << magmean << ", confidence = " << confidence << std::endl;
322
    }
323

    
324
    NoteHypothesis::Estimate e;
325
    e.freq = peakfreq;
326
    e.time = timestamp;
327
    e.confidence = confidence;
328

    
329
    m_feeder->feed(e);
330

    
331
    delete[] data;
332
    return fs;
333
}
334

    
335
CepstralPitchTracker::FeatureSet
336
CepstralPitchTracker::getRemainingFeatures()
337
{
338
    m_feeder->finish();
339

    
340
    AgentFeeder::Hypotheses accepted = m_feeder->getAcceptedHypotheses();
341

    
342
    FeatureSet fs;
343
    for (int i = 0; i < accepted.size(); ++i) {
344
        addFeaturesFrom(accepted[i], fs);
345
    }
346
    return fs;
347
}