Chris@9
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@9
|
2
|
Chris@9
|
3 /*
|
Chris@9
|
4 pYIN - A fundamental frequency estimator for monophonic audio
|
Chris@9
|
5 Centre for Digital Music, Queen Mary, University of London.
|
Chris@9
|
6
|
Chris@9
|
7 This program is free software; you can redistribute it and/or
|
Chris@9
|
8 modify it under the terms of the GNU General Public License as
|
Chris@9
|
9 published by the Free Software Foundation; either version 2 of the
|
Chris@9
|
10 License, or (at your option) any later version. See the file
|
Chris@9
|
11 COPYING included with this distribution for more information.
|
Chris@9
|
12 */
|
Chris@9
|
13
|
matthiasm@0
|
14 #include "SparseHMM.h"
|
matthiasm@0
|
15 #include <vector>
|
matthiasm@0
|
16 #include <cstdio>
|
matthiasm@0
|
17 #include <iostream>
|
matthiasm@0
|
18
|
matthiasm@0
|
19 using std::vector;
|
matthiasm@0
|
20 using std::pair;
|
matthiasm@0
|
21
|
mail@132
|
22 SparseHMM::SparseHMM(int fixedLag) :
|
mail@132
|
23 m_fixedLag(fixedLag),
|
mail@130
|
24 m_nState(0),
|
mail@130
|
25 m_nTrans(0),
|
mail@130
|
26 m_init(0),
|
mail@130
|
27 m_from(0),
|
mail@130
|
28 m_to(0),
|
mail@130
|
29 m_transProb(0),
|
mail@130
|
30 m_scale(0),
|
mail@130
|
31 m_psi(0),
|
mail@130
|
32 m_delta(0),
|
mail@130
|
33 m_oldDelta(0)
|
mail@130
|
34 {
|
mail@130
|
35
|
mail@130
|
36 }
|
mail@130
|
37
|
matthiasm@0
|
38 const vector<double>
|
matthiasm@0
|
39 SparseHMM::calculateObsProb(const vector<pair<double, double> > data)
|
matthiasm@0
|
40 {
|
matthiasm@0
|
41 // dummy (virtual?) implementation to be overloaded
|
matthiasm@0
|
42 return(vector<double>());
|
matthiasm@0
|
43 }
|
matthiasm@0
|
44
|
mail@130
|
45 void
|
mail@130
|
46 SparseHMM::build()
|
mail@130
|
47 { }
|
mail@130
|
48
|
matthiasm@0
|
49 const std::vector<int>
|
mail@130
|
50 SparseHMM::decodeViterbi(std::vector<vector<double> > obsProb)
|
matthiasm@0
|
51 {
|
mail@130
|
52 size_t nFrame = obsProb.size();
|
mail@130
|
53 if (nFrame < 1) {
|
Chris@38
|
54 return vector<int>();
|
Chris@38
|
55 }
|
mail@131
|
56
|
mail@130
|
57 initialise(obsProb[0]);
|
matthiasm@0
|
58
|
matthiasm@0
|
59 // rest of forward step
|
matthiasm@0
|
60 for (size_t iFrame = 1; iFrame < nFrame; ++iFrame)
|
matthiasm@0
|
61 {
|
mail@130
|
62 process(obsProb[iFrame]);
|
mail@130
|
63 }
|
matthiasm@0
|
64
|
mail@132
|
65 vector<int> path = track();
|
mail@130
|
66 return(path);
|
mail@130
|
67 }
|
mail@130
|
68
|
mail@131
|
69 void
|
mail@131
|
70 SparseHMM::reset()
|
mail@131
|
71 {
|
mail@131
|
72 m_scale.clear();
|
mail@131
|
73 m_psi.clear();
|
mail@131
|
74 for (size_t i = 0; i < m_delta.size(); ++i) m_delta[i] = 0;
|
mail@131
|
75 for (size_t i = 0; i < m_oldDelta.size(); ++i) m_oldDelta[i] = 0;
|
mail@131
|
76 }
|
mail@130
|
77
|
mail@130
|
78 void
|
mail@130
|
79 SparseHMM::initialise(vector<double> firstObs)
|
mail@130
|
80 {
|
mail@131
|
81 reset();
|
mail@131
|
82
|
mail@130
|
83 double deltasum = 0;
|
mail@130
|
84
|
mail@130
|
85 // initialise first frame
|
mail@130
|
86 for (size_t iState = 0; iState < m_nState; ++iState)
|
mail@130
|
87 {
|
mail@130
|
88 m_oldDelta[iState] = m_init[iState] * firstObs[iState];
|
mail@130
|
89 deltasum += m_oldDelta[iState];
|
mail@130
|
90 }
|
mail@130
|
91
|
mail@130
|
92 for (size_t iState = 0; iState < m_nState; ++iState)
|
mail@130
|
93 {
|
mail@130
|
94 m_oldDelta[iState] /= deltasum; // normalise (scale)
|
mail@130
|
95 }
|
mail@130
|
96
|
mail@130
|
97 m_scale.push_back(1.0/deltasum);
|
mail@130
|
98 m_psi.push_back(vector<int>(m_nState,0));
|
mail@130
|
99 }
|
mail@130
|
100
|
mail@130
|
101 int
|
mail@130
|
102 SparseHMM::process(vector<double> newObs)
|
mail@130
|
103 {
|
mail@130
|
104 vector<int> tempPsi = vector<int>(m_nState,0);
|
mail@130
|
105
|
mail@130
|
106 // calculate best previous state for every current state
|
mail@130
|
107 size_t fromState;
|
mail@130
|
108 size_t toState;
|
mail@130
|
109 double currentTransProb;
|
mail@130
|
110 double currentValue;
|
mail@130
|
111
|
mail@130
|
112 // this is the "sparse" loop
|
mail@130
|
113 for (size_t iTrans = 0; iTrans < m_nTrans; ++iTrans)
|
mail@130
|
114 {
|
mail@130
|
115 fromState = m_from[iTrans];
|
mail@130
|
116 toState = m_to[iTrans];
|
mail@130
|
117 currentTransProb = m_transProb[iTrans];
|
matthiasm@0
|
118
|
mail@130
|
119 currentValue = m_oldDelta[fromState] * currentTransProb;
|
mail@130
|
120 if (currentValue > m_delta[toState])
|
matthiasm@0
|
121 {
|
mail@130
|
122 // will be multiplied by the right obs later!
|
mail@130
|
123 m_delta[toState] = currentValue;
|
mail@130
|
124 tempPsi[toState] = fromState;
|
matthiasm@0
|
125 }
|
matthiasm@0
|
126 }
|
mail@130
|
127 m_psi.push_back(tempPsi);
|
matthiasm@0
|
128
|
mail@130
|
129
|
mail@130
|
130 double deltasum = 0;
|
mail@130
|
131 for (size_t jState = 0; jState < m_nState; ++jState)
|
mail@130
|
132 {
|
mail@130
|
133 m_delta[jState] *= newObs[jState];
|
mail@130
|
134 deltasum += m_delta[jState];
|
mail@130
|
135 }
|
mail@130
|
136
|
mail@130
|
137 if (deltasum > 0)
|
mail@130
|
138 {
|
mail@130
|
139 for (size_t iState = 0; iState < m_nState; ++iState)
|
mail@130
|
140 {
|
mail@130
|
141 m_oldDelta[iState] = m_delta[iState] / deltasum;// normalise (scale)
|
mail@130
|
142 m_delta[iState] = 0;
|
mail@130
|
143 }
|
mail@130
|
144 m_scale.push_back(1.0/deltasum);
|
mail@130
|
145 } else
|
mail@130
|
146 {
|
mail@130
|
147 std::cerr << "WARNING: Viterbi has been fed some zero "
|
mail@130
|
148 "probabilities, at least they become zero "
|
mail@130
|
149 "in combination with the model." << std::endl;
|
mail@130
|
150 for (size_t iState = 0; iState < m_nState; ++iState)
|
mail@130
|
151 {
|
mail@130
|
152 m_oldDelta[iState] = 1.0/m_nState;
|
mail@130
|
153 m_delta[iState] = 0;
|
mail@130
|
154 }
|
mail@130
|
155 m_scale.push_back(1.0);
|
mail@130
|
156 }
|
mail@132
|
157
|
mail@132
|
158 if (m_fixedLag > 0 && m_psi.size() > m_fixedLag)
|
mail@132
|
159 {
|
mail@132
|
160 m_psi.pop_front();
|
mail@132
|
161 m_scale.pop_front();
|
mail@132
|
162 }
|
mail@132
|
163
|
mail@132
|
164 // std::cerr << m_fixedLag << " " << m_psi.size() << std::endl;
|
mail@132
|
165
|
mail@130
|
166 return 0;
|
mail@130
|
167 }
|
mail@130
|
168
|
mail@132
|
169 const vector<int>
|
mail@132
|
170 SparseHMM::track()
|
mail@130
|
171 {
|
matthiasm@0
|
172 // initialise backward step
|
mail@130
|
173 size_t nFrame = m_psi.size();
|
mail@130
|
174
|
mail@130
|
175 // The final output path (current assignment arbitrary, makes sense only for
|
mail@130
|
176 // Chordino, where nChord-1 is the "no chord" label)
|
mail@130
|
177 vector<int> path = vector<int>(nFrame, m_nState-1);
|
mail@130
|
178
|
matthiasm@0
|
179 double bestValue = 0;
|
mail@130
|
180 for (size_t iState = 0; iState < m_nState; ++iState)
|
matthiasm@0
|
181 {
|
mail@130
|
182 double currentValue = m_oldDelta[iState];
|
matthiasm@0
|
183 if (currentValue > bestValue)
|
matthiasm@0
|
184 {
|
matthiasm@0
|
185 bestValue = currentValue;
|
matthiasm@0
|
186 path[nFrame-1] = iState;
|
matthiasm@0
|
187 }
|
matthiasm@0
|
188 }
|
matthiasm@0
|
189
|
mail@130
|
190 // Rest of backward step
|
matthiasm@0
|
191 for (int iFrame = nFrame-2; iFrame != -1; --iFrame)
|
matthiasm@0
|
192 {
|
mail@130
|
193 path[iFrame] = m_psi[iFrame+1][path[iFrame+1]];
|
matthiasm@0
|
194 }
|
mail@130
|
195
|
matthiasm@0
|
196 return path;
|
mail@130
|
197 } |