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
|
mail@130
|
38 void
|
mail@130
|
39 SparseHMM::build()
|
mail@130
|
40 { }
|
mail@130
|
41
|
Chris@145
|
42 std::vector<int>
|
mail@130
|
43 SparseHMM::decodeViterbi(std::vector<vector<double> > obsProb)
|
matthiasm@0
|
44 {
|
Chris@142
|
45 int nFrame = obsProb.size();
|
mail@130
|
46 if (nFrame < 1) {
|
Chris@38
|
47 return vector<int>();
|
Chris@38
|
48 }
|
Chris@38
|
49
|
mail@130
|
50 initialise(obsProb[0]);
|
Chris@38
|
51
|
Chris@38
|
52 // rest of forward step
|
Chris@142
|
53 for (int iFrame = 1; iFrame < nFrame; ++iFrame)
|
Chris@38
|
54 {
|
mail@130
|
55 process(obsProb[iFrame]);
|
mail@130
|
56 }
|
Chris@38
|
57
|
mail@132
|
58 vector<int> path = track();
|
mail@130
|
59 return(path);
|
mail@130
|
60 }
|
mail@130
|
61
|
mail@131
|
62 void
|
mail@131
|
63 SparseHMM::reset()
|
mail@131
|
64 {
|
mail@131
|
65 m_scale.clear();
|
mail@131
|
66 m_psi.clear();
|
Chris@142
|
67 for (int i = 0; i < int(m_delta.size()); ++i) m_delta[i] = 0;
|
Chris@142
|
68 for (int i = 0; i < int(m_oldDelta.size()); ++i) m_oldDelta[i] = 0;
|
mail@131
|
69 }
|
mail@130
|
70
|
mail@130
|
71 void
|
mail@130
|
72 SparseHMM::initialise(vector<double> firstObs)
|
mail@130
|
73 {
|
mail@131
|
74 reset();
|
matthiasm@0
|
75
|
matthiasm@0
|
76 double deltasum = 0;
|
matthiasm@0
|
77
|
matthiasm@0
|
78 // initialise first frame
|
Chris@142
|
79 for (int iState = 0; iState < m_nState; ++iState)
|
matthiasm@0
|
80 {
|
mail@130
|
81 m_oldDelta[iState] = m_init[iState] * firstObs[iState];
|
mail@130
|
82 deltasum += m_oldDelta[iState];
|
matthiasm@0
|
83 }
|
matthiasm@0
|
84
|
Chris@142
|
85 for (int iState = 0; iState < m_nState; ++iState)
|
matthiasm@0
|
86 {
|
mail@130
|
87 m_oldDelta[iState] /= deltasum; // normalise (scale)
|
matthiasm@0
|
88 }
|
matthiasm@0
|
89
|
mail@130
|
90 m_scale.push_back(1.0/deltasum);
|
mail@130
|
91 m_psi.push_back(vector<int>(m_nState,0));
|
mail@130
|
92 }
|
matthiasm@0
|
93
|
mail@130
|
94 int
|
mail@130
|
95 SparseHMM::process(vector<double> newObs)
|
mail@130
|
96 {
|
mail@130
|
97 vector<int> tempPsi = vector<int>(m_nState,0);
|
mail@130
|
98
|
mail@130
|
99 // calculate best previous state for every current state
|
Chris@142
|
100 int fromState;
|
Chris@142
|
101 int toState;
|
mail@130
|
102 double currentTransProb;
|
mail@130
|
103 double currentValue;
|
mail@130
|
104
|
mail@130
|
105 // this is the "sparse" loop
|
Chris@142
|
106 for (int iTrans = 0; iTrans < m_nTrans; ++iTrans)
|
matthiasm@0
|
107 {
|
mail@130
|
108 fromState = m_from[iTrans];
|
mail@130
|
109 toState = m_to[iTrans];
|
mail@130
|
110 currentTransProb = m_transProb[iTrans];
|
matthiasm@0
|
111
|
mail@130
|
112 currentValue = m_oldDelta[fromState] * currentTransProb;
|
mail@130
|
113 if (currentValue > m_delta[toState])
|
matthiasm@0
|
114 {
|
mail@130
|
115 // will be multiplied by the right obs later!
|
mail@130
|
116 m_delta[toState] = currentValue;
|
mail@130
|
117 tempPsi[toState] = fromState;
|
matthiasm@0
|
118 }
|
matthiasm@0
|
119 }
|
mail@130
|
120 m_psi.push_back(tempPsi);
|
matthiasm@0
|
121
|
mail@130
|
122 double deltasum = 0;
|
Chris@142
|
123 for (int jState = 0; jState < m_nState; ++jState)
|
mail@130
|
124 {
|
mail@130
|
125 m_delta[jState] *= newObs[jState];
|
mail@130
|
126 deltasum += m_delta[jState];
|
mail@130
|
127 }
|
mail@130
|
128
|
mail@130
|
129 if (deltasum > 0)
|
mail@130
|
130 {
|
Chris@142
|
131 for (int iState = 0; iState < m_nState; ++iState)
|
mail@130
|
132 {
|
mail@130
|
133 m_oldDelta[iState] = m_delta[iState] / deltasum;// normalise (scale)
|
mail@130
|
134 m_delta[iState] = 0;
|
mail@130
|
135 }
|
mail@130
|
136 m_scale.push_back(1.0/deltasum);
|
mail@130
|
137 } else
|
mail@130
|
138 {
|
Chris@149
|
139 // std::cerr << "WARNING: Viterbi has been fed some zero "
|
Chris@149
|
140 // "probabilities, at least they become zero "
|
Chris@149
|
141 // "in combination with the model." << std::endl;
|
Chris@142
|
142 for (int iState = 0; iState < m_nState; ++iState)
|
mail@130
|
143 {
|
mail@130
|
144 m_oldDelta[iState] = 1.0/m_nState;
|
mail@130
|
145 m_delta[iState] = 0;
|
mail@130
|
146 }
|
mail@130
|
147 m_scale.push_back(1.0);
|
mail@130
|
148 }
|
mail@132
|
149
|
Chris@142
|
150 if (m_fixedLag > 0 && int(m_psi.size()) > m_fixedLag)
|
mail@132
|
151 {
|
mail@132
|
152 m_psi.pop_front();
|
mail@132
|
153 m_scale.pop_front();
|
mail@132
|
154 }
|
mail@132
|
155
|
mail@132
|
156 // std::cerr << m_fixedLag << " " << m_psi.size() << std::endl;
|
mail@132
|
157
|
mail@130
|
158 return 0;
|
mail@130
|
159 }
|
mail@130
|
160
|
Chris@145
|
161 vector<int>
|
mail@132
|
162 SparseHMM::track()
|
mail@130
|
163 {
|
matthiasm@0
|
164 // initialise backward step
|
Chris@142
|
165 int nFrame = m_psi.size();
|
mail@130
|
166
|
mail@130
|
167 // The final output path (current assignment arbitrary, makes sense only for
|
mail@130
|
168 // Chordino, where nChord-1 is the "no chord" label)
|
mail@130
|
169 vector<int> path = vector<int>(nFrame, m_nState-1);
|
mail@130
|
170
|
matthiasm@0
|
171 double bestValue = 0;
|
Chris@142
|
172 for (int iState = 0; iState < m_nState; ++iState)
|
matthiasm@0
|
173 {
|
mail@130
|
174 double currentValue = m_oldDelta[iState];
|
matthiasm@0
|
175 if (currentValue > bestValue)
|
matthiasm@0
|
176 {
|
matthiasm@0
|
177 bestValue = currentValue;
|
matthiasm@0
|
178 path[nFrame-1] = iState;
|
matthiasm@0
|
179 }
|
matthiasm@0
|
180 }
|
matthiasm@0
|
181
|
mail@130
|
182 // Rest of backward step
|
matthiasm@0
|
183 for (int iFrame = nFrame-2; iFrame != -1; --iFrame)
|
matthiasm@0
|
184 {
|
mail@130
|
185 path[iFrame] = m_psi[iFrame+1][path[iFrame+1]];
|
matthiasm@0
|
186 }
|
mail@130
|
187
|
matthiasm@0
|
188 return path;
|
Chris@142
|
189 }
|