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 @ 62:7c463642a0a7

History | View | Annotate | Download (7.96 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_nAccepted(0),
57
    m_feeder(0)
58
{
59
}
60

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
175
    OutputDescriptor d;
176

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

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

    
207
    return outputs;
208
}
209

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

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

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

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

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

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

    
237
    reset();
238

    
239
    return true;
240
}
241

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

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

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

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

    
273
void
274
CepstralPitchTracker::addNewFeatures(FeatureSet &fs)
275
{
276
    int n = m_feeder->getAcceptedHypotheses().size();
277
    if (n == m_nAccepted) return;
278

    
279
    AgentFeeder::Hypotheses accepted = m_feeder->getAcceptedHypotheses();
280

    
281
    for (int i = m_nAccepted; i < n; ++i) {
282
        addFeaturesFrom(accepted[i], fs);
283
    }
284

    
285
    m_nAccepted = n;
286
}
287

    
288
CepstralPitchTracker::FeatureSet
289
CepstralPitchTracker::process(const float *const *inputBuffers, RealTime timestamp)
290
{
291
    double *rawcep = new double[m_blockSize];
292
    double magmean = Cepstrum(m_blockSize).process(inputBuffers[0], rawcep);
293

    
294
    int n = m_bins;
295
    double *data = new double[n];
296
    MeanFilter(m_vflen).filterSubsequence
297
        (rawcep, data, m_blockSize, n, m_binFrom);
298

    
299
    delete[] rawcep;
300

    
301
    double maxval = 0.0;
302
    int maxbin = -1;
303

    
304
    for (int i = 0; i < n; ++i) {
305
        if (data[i] > maxval) {
306
            maxval = data[i];
307
            maxbin = i;
308
        }
309
    }
310

    
311
    if (maxbin < 0) {
312
        delete[] data;
313
        return FeatureSet();
314
    }
315

    
316
    double nextPeakVal = 0.0;
317
    for (int i = 1; i+1 < n; ++i) {
318
        if (data[i] > data[i-1] &&
319
            data[i] > data[i+1] &&
320
            i != maxbin &&
321
            data[i] > nextPeakVal) {
322
            nextPeakVal = data[i];
323
        }
324
    }
325

    
326
    PeakInterpolator pi;
327
    double cimax = pi.findPeakLocation(data, m_bins, maxbin);
328
    double peakfreq = m_inputSampleRate / (cimax + m_binFrom);
329

    
330
    double confidence = 0.0;
331
    double threshold = 0.1; // for magmean
332

    
333
    if (nextPeakVal != 0.0) {
334
        confidence = (maxval - nextPeakVal) * 10.0;
335
        if (magmean < threshold) confidence = 0.0;
336
    }
337

    
338
    delete[] data;
339

    
340
    NoteHypothesis::Estimate e;
341
    e.freq = peakfreq;
342
    e.time = timestamp;
343
    e.confidence = confidence;
344

    
345
    m_feeder->feed(e);
346

    
347
    FeatureSet fs;
348
    addNewFeatures(fs);
349
    return fs;
350
}
351

    
352
CepstralPitchTracker::FeatureSet
353
CepstralPitchTracker::getRemainingFeatures()
354
{
355
    m_feeder->finish();
356

    
357
    FeatureSet fs;
358
    addNewFeatures(fs);
359
    return fs;
360
}