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 / NoteHypothesis.cpp @ 52:34f42a384a7f

History | View | Annotate | Download (4.64 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 "NoteHypothesis.h"
26

    
27
#include <cmath>
28

    
29
using Vamp::RealTime;
30

    
31
NoteHypothesis::NoteHypothesis()
32
{
33
    m_state = New;
34
}
35

    
36
NoteHypothesis::~NoteHypothesis()
37
{
38
}
39

    
40
bool
41
NoteHypothesis::isWithinTolerance(Estimate s) const
42
{
43
    if (m_pending.empty()) {
44
        return true;
45
    }
46

    
47
    // check we are within a relatively close tolerance of the last
48
    // candidate
49
    Estimate last = m_pending[m_pending.size()-1];
50
    double r = s.freq / last.freq;
51
    int cents = lrint(1200.0 * (log(r) / log(2.0)));
52
    if (cents < -60 || cents > 60) return false;
53

    
54
    // and within a slightly bigger tolerance of the current mean
55
    double meanFreq = getMeanFrequency();
56
    r = s.freq / meanFreq;
57
    cents = lrint(1200.0 * (log(r) / log(2.0)));
58
    if (cents < -80 || cents > 80) return false;
59
    
60
    return true;
61
}
62

    
63
bool
64
NoteHypothesis::isOutOfDateFor(Estimate s) const
65
{
66
    if (m_pending.empty()) return false;
67

    
68
    return ((s.time - m_pending[m_pending.size()-1].time) > 
69
            RealTime::fromMilliseconds(40));
70
}
71

    
72
bool 
73
NoteHypothesis::isSatisfied() const
74
{
75
    if (m_pending.empty()) return false;
76
    
77
    double meanConfidence = 0.0;
78
    for (int i = 0; i < (int)m_pending.size(); ++i) {
79
        meanConfidence += m_pending[i].confidence;
80
    }
81
    meanConfidence /= m_pending.size();
82

    
83
    int lengthRequired = 10000;
84
    if (meanConfidence > 0.0) {
85
        lengthRequired = int(2.0 / meanConfidence + 0.5);
86
    }
87

    
88
    return ((int)m_pending.size() > lengthRequired);
89
}
90

    
91
bool
92
NoteHypothesis::accept(Estimate s)
93
{
94
    bool accept = false;
95

    
96
    switch (m_state) {
97

    
98
    case New:
99
        m_state = Provisional;
100
        accept = true;
101
        break;
102

    
103
    case Provisional:
104
        if (isOutOfDateFor(s)) {
105
            m_state = Rejected;
106
        } else if (isWithinTolerance(s)) {
107
            accept = true;
108
        }
109
        break;
110
        
111
    case Satisfied:
112
        if (isOutOfDateFor(s)) {
113
            m_state = Expired;
114
        } else if (isWithinTolerance(s)) {
115
            accept = true;
116
        }
117
        break;
118

    
119
    case Rejected:
120
        break;
121

    
122
    case Expired:
123
        break;
124
    }
125

    
126
    if (accept) {
127
        m_pending.push_back(s);
128
        if (m_state == Provisional && isSatisfied()) {
129
            m_state = Satisfied;
130
        }
131
    }
132

    
133
    return accept;
134
}        
135

    
136
NoteHypothesis::State
137
NoteHypothesis::getState() const
138
{
139
    return m_state;
140
}
141

    
142
NoteHypothesis::Estimates
143
NoteHypothesis::getAcceptedEstimates() const
144
{
145
    if (m_state == Satisfied || m_state == Expired) {
146
        return m_pending;
147
    } else {
148
        return Estimates();
149
    }
150
}
151

    
152
RealTime
153
NoteHypothesis::getStartTime() const
154
{
155
    if (!(m_state == Satisfied || m_state == Expired)) {
156
        return RealTime::zeroTime;
157
    } else {
158
        return m_pending.begin()->time;
159
    }
160
}
161

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

    
174
NoteHypothesis::Note
175
NoteHypothesis::getAveragedNote() const
176
{
177
    Note n;
178

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

    
186
    n.time = m_pending.begin()->time;
187

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

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