Revision 32:c88a9972975b

View differences:

NoteHypothesis.cpp
1
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
2
/* Copyright Chris Cannam - All Rights Reserved */
3

  
4
#include "NoteHypothesis.h"
5

  
6
#include <cmath>
7

  
8
#include "system/sysutils.h"
9

  
10
namespace Turbot {
11

  
12
NoteHypothesis::NoteHypothesis()
13
{
14
    m_state = New;
15
}
16

  
17
NoteHypothesis::~NoteHypothesis()
18
{
19
}
20

  
21
bool
22
NoteHypothesis::isWithinTolerance(Estimate s) const
23
{
24
    if (m_pending.empty()) {
25
        return true;
26
    }
27

  
28
    // check we are within a relatively close tolerance of the last
29
    // candidate
30
    Estimate last = m_pending[m_pending.size()-1];
31
    double r = s.freq / last.freq;
32
    int cents = lrint(1200.0 * (log(r) / log(2.0)));
33
    if (cents < -60 || cents > 60) return false;
34

  
35
    // and within a slightly bigger tolerance of the current mean
36
    double meanFreq = getMeanFrequency();
37
    r = s.freq / meanFreq;
38
    cents = lrint(1200.0 * (log(r) / log(2.0)));
39
    if (cents < -80 || cents > 80) return false;
40
    
41
    return true;
42
}
43

  
44
bool
45
NoteHypothesis::isOutOfDateFor(Estimate s) const
46
{
47
    if (m_pending.empty()) return false;
48

  
49
    return ((s.time - m_pending[m_pending.size()-1].time) > 
50
            RealTime::fromMilliseconds(40));
51
}
52

  
53
bool 
54
NoteHypothesis::isSatisfied() const
55
{
56
    if (m_pending.empty()) return false;
57
    
58
    double meanConfidence = 0.0;
59
    for (int i = 0; i < (int)m_pending.size(); ++i) {
60
        meanConfidence += m_pending[i].confidence;
61
    }
62
    meanConfidence /= m_pending.size();
63

  
64
    int lengthRequired = 10000;
65
    if (meanConfidence > 0.0) {
66
        lengthRequired = int(2.0 / meanConfidence + 0.5);
67
    }
68

  
69
    return ((int)m_pending.size() > lengthRequired);
70
}
71

  
72
bool
73
NoteHypothesis::accept(Estimate s)
74
{
75
    bool accept = false;
76

  
77
    switch (m_state) {
78

  
79
    case New:
80
        m_state = Provisional;
81
        accept = true;
82
        break;
83

  
84
    case Provisional:
85
        if (isOutOfDateFor(s)) {
86
            m_state = Rejected;
87
        } else if (isWithinTolerance(s)) {
88
            accept = true;
89
        }
90
        break;
91
        
92
    case Satisfied:
93
        if (isOutOfDateFor(s)) {
94
            m_state = Expired;
95
        } else if (isWithinTolerance(s)) {
96
            accept = true;
97
        }
98
        break;
99

  
100
    case Rejected:
101
        break;
102

  
103
    case Expired:
104
        break;
105
    }
106

  
107
    if (accept) {
108
        m_pending.push_back(s);
109
        if (m_state == Provisional && isSatisfied()) {
110
            m_state = Satisfied;
111
        }
112
    }
113

  
114
    return accept;
115
}        
116

  
117
NoteHypothesis::State
118
NoteHypothesis::getState() const
119
{
120
    return m_state;
121
}
122

  
123
NoteHypothesis::Estimates
124
NoteHypothesis::getAcceptedEstimates() const
125
{
126
    if (m_state == Satisfied || m_state == Expired) {
127
        return m_pending;
128
    } else {
129
        return Estimates();
130
    }
131
}
132

  
133
double
134
NoteHypothesis::getMeanFrequency() const
135
{
136
    double acc = 0.0;
137
    for (int i = 0; i < (int)m_pending.size(); ++i) {
138
        acc += m_pending[i].freq;
139
    }
140
    acc /= m_pending.size();
141
    return acc;
142
}
143

  
144
NoteHypothesis::Note
145
NoteHypothesis::getAveragedNote() const
146
{
147
    Note n;
148

  
149
    if (!(m_state == Satisfied || m_state == Expired)) {
150
        n.freq = 0.0;
151
        n.time = RealTime::zeroTime;
152
        n.duration = RealTime::zeroTime;
153
        return n;
154
    }
155

  
156
    n.time = m_pending.begin()->time;
157

  
158
    Estimates::const_iterator i = m_pending.end();
159
    --i;
160
    n.duration = i->time - n.time;
161

  
162
    // just mean frequency for now, but this isn't at all right perceptually
163
    n.freq = getMeanFrequency();
164
    
165
    return n;
166
}
167

  
168
}
169

  
NoteHypothesis.h
1
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
2
/* Copyright Chris Cannam - All Rights Reserved */
3

  
4
#ifndef _NOTE_HYPOTHESIS_H_
5
#define _NOTE_HYPOTHESIS_H_
6

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

  
10
namespace Turbot {
11

  
12
/**
13
 * An agent used to test an incoming series of instantaneous pitch
14
 * estimates to see whether they fit a consistent single-note
15
 * relationship. Contains rules specific to testing note pitch and
16
 * timing.
17
 */
18

  
19
class NoteHypothesis
20
{
21
public:
22
    enum State {
23

  
24
	/// Just constructed, will provisionally accept any estimate
25
	New,
26

  
27
	/// Accepted at least one estimate, but not enough evidence to satisfy
28
	Provisional,
29

  
30
	/// Could not find enough consistency in offered estimates
31
	Rejected,
32

  
33
	/// Have accepted enough consistent estimates to satisfy hypothesis
34
	Satisfied,
35

  
36
	/// Have been satisfied, but evidence has now changed: we're done
37
	Expired
38
    };
39
    
40
    /**
41
     * Construct an empty hypothesis. This will be in New state and
42
     * will provisionally accept any estimate.
43
     */
44
    NoteHypothesis();
45

  
46
    /**
47
     * Destroy the hypothesis
48
     */
49
    ~NoteHypothesis();
50

  
51
    struct Estimate {
52
	double freq;
53
	RealTime time;
54
	double confidence;
55
    };
56
    typedef std::vector<Estimate> Estimates;
57

  
58
    /**
59
     * Test the given estimate to see whether it is consistent with
60
     * this hypothesis, and adjust the hypothesis' internal state
61
     * accordingly. If the estimate is not inconsistent with the
62
     * hypothesis, return true.
63
     */
64
    bool accept(Estimate);
65

  
66
    /**
67
     * Return the current state of this hypothesis.
68
     */
69
    State getState() const;
70

  
71
    /**
72
     * If the hypothesis has been satisfied (i.e. is in Satisfied or
73
     * Expired state), return the series of estimates that it
74
     * accepted. Otherwise return an empty list
75
     */
76
    Estimates getAcceptedEstimates() const;
77

  
78
    struct Note {
79
	double freq;
80
	RealTime time;
81
	RealTime duration;
82
    };
83
    
84
    /**
85
     * Return a single note roughly matching this hypothesis
86
     */
87
    Note getAveragedNote() const;
88
    
89
private:
90
    bool isWithinTolerance(Estimate) const;
91
    bool isOutOfDateFor(Estimate) const;
92
    bool isSatisfied() const;
93
    double getMeanFrequency() const;
94
    
95
    State m_state;
96
    Estimates m_pending;
97
};
98

  
99
}
100

  
101
#endif

Also available in: Unified diff