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