Revision 35:2f5b169e4a3b

View differences:

CepstralPitchTracker.cpp
37 37
using std::vector;
38 38
using Vamp::RealTime;
39 39

  
40
CepstralPitchTracker::Hypothesis::Hypothesis()
41
{
42
    m_state = New;
43
}
44

  
45
CepstralPitchTracker::Hypothesis::~Hypothesis()
46
{
47
}
48

  
49
bool
50
CepstralPitchTracker::Hypothesis::isWithinTolerance(Estimate s) const
51
{
52
    if (m_pending.empty()) {
53
        return true;
54
    }
55

  
56
    // check we are within a relatively close tolerance of the last
57
    // candidate
58
    Estimate last = m_pending[m_pending.size()-1];
59
    double r = s.freq / last.freq;
60
    int cents = lrint(1200.0 * (log(r) / log(2.0)));
61
    if (cents < -60 || cents > 60) return false;
62

  
63
    // and within a slightly bigger tolerance of the current mean
64
    double meanFreq = getMeanFrequency();
65
    r = s.freq / meanFreq;
66
    cents = lrint(1200.0 * (log(r) / log(2.0)));
67
    if (cents < -80 || cents > 80) return false;
68
    
69
    return true;
70
}
71

  
72
bool
73
CepstralPitchTracker::Hypothesis::isOutOfDateFor(Estimate s) const
74
{
75
    if (m_pending.empty()) return false;
76

  
77
    return ((s.time - m_pending[m_pending.size()-1].time) > 
78
            RealTime::fromMilliseconds(40));
79
}
80

  
81
bool 
82
CepstralPitchTracker::Hypothesis::isSatisfied() const
83
{
84
    if (m_pending.empty()) return false;
85
    
86
    double meanConfidence = 0.0;
87
    for (int i = 0; i < m_pending.size(); ++i) {
88
        meanConfidence += m_pending[i].confidence;
89
    }
90
    meanConfidence /= m_pending.size();
91

  
92
    int lengthRequired = 10000;
93
    if (meanConfidence > 0.0) {
94
        lengthRequired = int(2.0 / meanConfidence + 0.5);
95
    }
96

  
97
    return (m_pending.size() > lengthRequired);
98
}
99

  
100
bool
101
CepstralPitchTracker::Hypothesis::accept(Estimate s)
102
{
103
    bool accept = false;
104

  
105
    switch (m_state) {
106

  
107
    case New:
108
        m_state = Provisional;
109
        accept = true;
110
        break;
111

  
112
    case Provisional:
113
        if (isOutOfDateFor(s)) {
114
            m_state = Rejected;
115
        } else if (isWithinTolerance(s)) {
116
            accept = true;
117
        }
118
        break;
119
        
120
    case Satisfied:
121
        if (isOutOfDateFor(s)) {
122
            m_state = Expired;
123
        } else if (isWithinTolerance(s)) {
124
            accept = true;
125
        }
126
        break;
127

  
128
    case Rejected:
129
        break;
130

  
131
    case Expired:
132
        break;
133
    }
134

  
135
    if (accept) {
136
        m_pending.push_back(s);
137
        if (m_state == Provisional && isSatisfied()) {
138
            m_state = Satisfied;
139
        }
140
    }
141

  
142
    return accept;
143
}        
144

  
145
CepstralPitchTracker::Hypothesis::State
146
CepstralPitchTracker::Hypothesis::getState() const
147
{
148
    return m_state;
149
}
150

  
151
CepstralPitchTracker::Hypothesis::Estimates
152
CepstralPitchTracker::Hypothesis::getAcceptedEstimates() const
153
{
154
    if (m_state == Satisfied || m_state == Expired) {
155
        return m_pending;
156
    } else {
157
        return Estimates();
158
    }
159
}
160

  
161
double
162
CepstralPitchTracker::Hypothesis::getMeanFrequency() const
163
{
164
    double acc = 0.0;
165
    for (int i = 0; i < m_pending.size(); ++i) {
166
        acc += m_pending[i].freq;
167
    }
168
    acc /= m_pending.size();
169
    return acc;
170
}
171

  
172
CepstralPitchTracker::Hypothesis::Note
173
CepstralPitchTracker::Hypothesis::getAveragedNote() const
174
{
175
    Note n;
176

  
177
    if (!(m_state == Satisfied || m_state == Expired)) {
178
        n.freq = 0.0;
179
        n.time = RealTime::zeroTime;
180
        n.duration = RealTime::zeroTime;
181
        return n;
182
    }
183

  
184
    n.time = m_pending.begin()->time;
185

  
186
    Estimates::const_iterator i = m_pending.end();
187
    --i;
188
    n.duration = i->time - n.time;
189

  
190
    // just mean frequency for now, but this isn't at all right perceptually
191
    n.freq = getMeanFrequency();
192
    
193
    return n;
194
}
195 40

  
196 41
CepstralPitchTracker::CepstralPitchTracker(float inputSampleRate) :
197 42
    Plugin(inputSampleRate),
......
320 165
{
321 166
    OutputList outputs;
322 167

  
323
    int n = 0;
324

  
325 168
    OutputDescriptor d;
326 169

  
327 170
    d.identifier = "f0";
......
391 234
}
392 235

  
393 236
void
394
CepstralPitchTracker::addFeaturesFrom(Hypothesis h, FeatureSet &fs)
237
CepstralPitchTracker::addFeaturesFrom(NoteHypothesis h, FeatureSet &fs)
395 238
{
396
    Hypothesis::Estimates es = h.getAcceptedEstimates();
239
    NoteHypothesis::Estimates es = h.getAcceptedEstimates();
397 240

  
398
    for (int i = 0; i < es.size(); ++i) {
241
    for (int i = 0; i < (int)es.size(); ++i) {
399 242
	Feature f;
400 243
	f.hasTimestamp = true;
401 244
	f.timestamp = es[i].time;
......
406 249
    Feature nf;
407 250
    nf.hasTimestamp = true;
408 251
    nf.hasDuration = true;
409
    Hypothesis::Note n = h.getAveragedNote();
252
    NoteHypothesis::Note n = h.getAveragedNote();
410 253
    nf.timestamp = n.time;
411 254
    nf.duration = n.duration;
412 255
    nf.values.push_back(n.freq);
......
422 265
        // average according to the vertical filter length
423 266
        for (int j = -m_vflen/2; j <= m_vflen/2; ++j) {
424 267
            int ix = i + m_binFrom + j;
425
            if (ix >= 0 && ix < m_blockSize) {
268
            if (ix >= 0 && ix < (int)m_blockSize) {
426 269
                v += cep[ix];
427 270
                ++n;
428 271
            }
......
575 418
        std::cerr << "magmean = " << magmean << ", confidence = " << confidence << std::endl;
576 419
    }
577 420

  
578
    Hypothesis::Estimate e;
421
    NoteHypothesis::Estimate e;
579 422
    e.freq = peakfreq;
580 423
    e.time = timestamp;
581 424
    e.confidence = confidence;
582 425

  
583
//    m_good.advanceTime();
584
    for (int i = 0; i < m_possible.size(); ++i) {
585
//        m_possible[i].advanceTime();
586
    }
587

  
588 426
    if (!m_good.accept(e)) {
589 427

  
590 428
        int candidate = -1;
591 429
        bool accepted = false;
592 430

  
593
        for (int i = 0; i < m_possible.size(); ++i) {
431
        for (int i = 0; i < (int)m_possible.size(); ++i) {
594 432
            if (m_possible[i].accept(e)) {
595
                if (m_possible[i].getState() == Hypothesis::Satisfied) {
433
                if (m_possible[i].getState() == NoteHypothesis::Satisfied) {
596 434
                    accepted = true;
597 435
                    candidate = i;
598 436
                }
......
601 439
        }
602 440

  
603 441
        if (!accepted) {
604
            Hypothesis h;
442
            NoteHypothesis h;
605 443
            h.accept(e); //!!! must succeed as h is new, so perhaps there should be a ctor for this
606 444
            m_possible.push_back(h);
607 445
        }
608 446

  
609
        if (m_good.getState() == Hypothesis::Expired) {
447
        if (m_good.getState() == NoteHypothesis::Expired) {
610 448
            addFeaturesFrom(m_good, fs);
611 449
        }
612 450
        
613
        if (m_good.getState() == Hypothesis::Expired ||
614
            m_good.getState() == Hypothesis::Rejected) {
451
        if (m_good.getState() == NoteHypothesis::Expired ||
452
            m_good.getState() == NoteHypothesis::Rejected) {
615 453
            if (candidate >= 0) {
616 454
                m_good = m_possible[candidate];
617 455
            } else {
618
                m_good = Hypothesis();
456
                m_good = NoteHypothesis();
619 457
            }
620 458
        }
621 459

  
622 460
        // reap rejected/expired hypotheses from possible list
623 461
        Hypotheses toReap = m_possible;
624 462
        m_possible.clear();
625
        for (int i = 0; i < toReap.size(); ++i) {
626
            Hypothesis h = toReap[i];
627
            if (h.getState() != Hypothesis::Rejected && 
628
                h.getState() != Hypothesis::Expired) {
463
        for (int i = 0; i < (int)toReap.size(); ++i) {
464
            NoteHypothesis h = toReap[i];
465
            if (h.getState() != NoteHypothesis::Rejected && 
466
                h.getState() != NoteHypothesis::Expired) {
629 467
                m_possible.push_back(h);
630 468
            }
631 469
        }
......
639 477
CepstralPitchTracker::getRemainingFeatures()
640 478
{
641 479
    FeatureSet fs;
642
    if (m_good.getState() == Hypothesis::Satisfied) {
480
    if (m_good.getState() == NoteHypothesis::Satisfied) {
643 481
        addFeaturesFrom(m_good, fs);
644 482
    }
645 483
    return fs;
CepstralPitchTracker.h
22 22
    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 23
*/
24 24

  
25
#ifndef _CEPSTRUM_PITCH_H_
26
#define _CEPSTRUM_PITCH_H_
25
#ifndef _CEPSTRAL_PITCH_H_
26
#define _CEPSTRAL_PITCH_H_
27 27

  
28 28
#include <vamp-sdk/Plugin.h>
29 29

  
30
#include "NoteHypothesis.h"
31

  
30 32
class CepstralPitchTracker : public Vamp::Plugin
31 33
{
32 34
public:
......
76 78
    int m_binTo;
77 79
    int m_bins; // count of "interesting" bins, those returned in m_cepOutput
78 80

  
79
    class Hypothesis {
81
    typedef std::vector<NoteHypothesis> Hypotheses;
82
    Hypotheses m_possible;
83
    NoteHypothesis m_good;
80 84

  
81
    public:
82
        struct Estimate {
83
            double freq;
84
            Vamp::RealTime time;
85
            double confidence;
86
        };
87
        typedef std::vector<Estimate> Estimates;
88

  
89
        struct Note {
90
            double freq;
91
            Vamp::RealTime time;
92
            Vamp::RealTime duration;
93
        };
94
        
95
        Hypothesis();
96
        ~Hypothesis();
97

  
98
        enum State {
99
            New,
100
            Provisional,
101
            Satisfied,
102
            Rejected,
103
            Expired
104
        };
105

  
106
        bool accept(Estimate);
107

  
108
        State getState() const;
109
        Estimates getAcceptedEstimates() const;
110
        Note getAveragedNote() const;
111

  
112
    private:
113
        bool isWithinTolerance(Estimate) const;
114
        bool isOutOfDateFor(Estimate) const;
115
        bool isSatisfied() const;
116
        double getMeanFrequency() const;
117

  
118
        State m_state;
119
        Estimates m_pending;
120
    };
121

  
122
    typedef std::vector<Hypothesis> Hypotheses;
123
    Hypotheses m_possible;
124
    Hypothesis m_good;
125

  
126
    void addFeaturesFrom(Hypothesis h, FeatureSet &fs);
85
    void addFeaturesFrom(NoteHypothesis h, FeatureSet &fs);
127 86

  
128 87
    void filter(const double *in, double *out);
129 88
    double cubicInterpolate(const double[4], double);
Makefile.inc
8 8
CXXFLAGS	:= $(CXXFLAGS) 
9 9
LDFLAGS		:= $(LDFLAGS)
10 10

  
11
HEADERS := CepstralPitchTracker.h
11
HEADERS := CepstralPitchTracker.h \
12
	   NoteHypothesis.h
12 13

  
13 14
SOURCES := CepstralPitchTracker.cpp \
15
	   NoteHypothesis.cpp \
14 16
           libmain.cpp
15 17

  
16 18
OBJECTS := $(SOURCES:.cpp=.o)
NoteHypothesis.cpp
5 5

  
6 6
#include <cmath>
7 7

  
8
#include "system/sysutils.h"
9

  
10
namespace Turbot {
8
using Vamp::RealTime;
11 9

  
12 10
NoteHypothesis::NoteHypothesis()
13 11
{
......
166 164
    return n;
167 165
}
168 166

  
169
}
170

  
NoteHypothesis.h
4 4
#ifndef _NOTE_HYPOTHESIS_H_
5 5
#define _NOTE_HYPOTHESIS_H_
6 6

  
7
#include "base/RealTime.h"
7
#include "vamp-sdk/RealTime.h"
8 8
#include <vector>
9 9

  
10
namespace Turbot {
11

  
12 10
/**
13 11
 * An agent used to test an incoming series of instantaneous pitch
14 12
 * estimates to see whether they fit a consistent single-note
......
50 48

  
51 49
    struct Estimate {
52 50
        Estimate() : freq(0), time(), confidence(0) { }
53
        Estimate(double _f, RealTime _t, double _c) :
51
        Estimate(double _f, Vamp::RealTime _t, double _c) :
54 52
            freq(_f), time(_t), confidence(_c) { }
55 53
        bool operator==(const Estimate &e) const {
56 54
            return e.freq == freq && e.time == time && e.confidence == confidence;
57 55
        }
58 56
	double freq;
59
	RealTime time;
57
        Vamp::RealTime time;
60 58
	double confidence;
61 59
    };
62 60
    typedef std::vector<Estimate> Estimates;
......
83 81

  
84 82
    struct Note {
85 83
        Note() : freq(0), time(), duration() { }
86
        Note(double _f, RealTime _t, RealTime _d) :
84
        Note(double _f, Vamp::RealTime _t, Vamp::RealTime _d) :
87 85
            freq(_f), time(_t), duration(_d) { }
88 86
        bool operator==(const Note &e) const {
89 87
            return e.freq == freq && e.time == time && e.duration == duration;
90 88
        }
91 89
	double freq;
92
	RealTime time;
93
	RealTime duration;
90
	Vamp::RealTime time;
91
	Vamp::RealTime duration;
94 92
    };
95 93
    
96 94
    /**
......
112 110
    Estimates m_pending;
113 111
};
114 112

  
115
}
116

  
117 113
#endif

Also available in: Unified diff