annotate MonoPitchHMM.cpp @ 164:a7d9c6142f8f tip

Added tag v1.2 for changeset 4a97f7638ffd
author Chris Cannam
date Thu, 06 Feb 2020 15:02:47 +0000
parents 3faac48d491d
children
rev   line source
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 "MonoPitchHMM.h"
matthiasm@0 15
matthiasm@0 16 #include <boost/math/distributions.hpp>
matthiasm@0 17
matthiasm@0 18 #include <cstdio>
matthiasm@0 19 #include <cmath>
mail@130 20 #include <iostream>
matthiasm@0 21
matthiasm@0 22 using std::vector;
matthiasm@0 23 using std::pair;
matthiasm@0 24
mail@132 25 MonoPitchHMM::MonoPitchHMM(int fixedLag) :
Chris@141 26 SparseHMM(fixedLag),
Chris@138 27 m_minFreq(61.735),
Chris@138 28 m_nBPS(5),
Chris@138 29 m_nPitch(0),
Chris@138 30 m_transitionWidth(0),
Chris@138 31 m_selfTrans(0.99),
Chris@138 32 m_yinTrust(.5),
Chris@138 33 m_freqs(0)
matthiasm@0 34 {
matthiasm@0 35 m_transitionWidth = 5*(m_nBPS/2) + 1;
matthiasm@102 36 m_nPitch = 69 * m_nBPS;
mail@130 37 m_nState = 2 * m_nPitch; // voiced and unvoiced
matthiasm@25 38 m_freqs = vector<double>(2*m_nPitch);
Chris@138 39 for (int iPitch = 0; iPitch < m_nPitch; ++iPitch)
matthiasm@0 40 {
matthiasm@0 41 m_freqs[iPitch] = m_minFreq * std::pow(2, iPitch * 1.0 / (12 * m_nBPS));
matthiasm@0 42 m_freqs[iPitch+m_nPitch] = -m_freqs[iPitch];
matthiasm@0 43 }
matthiasm@0 44 build();
matthiasm@0 45 }
matthiasm@0 46
Chris@145 47 vector<double>
Chris@156 48 MonoPitchHMM::calculateObsProb(const vector<pair<double, double> > &pitchProb)
matthiasm@0 49 {
matthiasm@0 50 vector<double> out = vector<double>(2*m_nPitch+1);
matthiasm@0 51 double probYinPitched = 0;
Chris@156 52 int nPair = int(pitchProb.size());
Chris@156 53
matthiasm@0 54 // BIN THE PITCHES
Chris@156 55 for (int iPair = 0; iPair < nPair; ++iPair)
matthiasm@0 56 {
matthiasm@0 57 double freq = 440. * std::pow(2, (pitchProb[iPair].first - 69)/12);
matthiasm@0 58 if (freq <= m_minFreq) continue;
matthiasm@0 59 double d = 0;
matthiasm@0 60 double oldd = 1000;
Chris@138 61 for (int iPitch = 0; iPitch < m_nPitch; ++iPitch)
matthiasm@0 62 {
matthiasm@0 63 d = std::abs(freq-m_freqs[iPitch]);
matthiasm@0 64 if (oldd < d && iPitch > 0)
matthiasm@0 65 {
matthiasm@0 66 // previous bin must have been the closest
matthiasm@0 67 out[iPitch-1] = pitchProb[iPair].second;
matthiasm@0 68 probYinPitched += out[iPitch-1];
matthiasm@0 69 break;
matthiasm@0 70 }
matthiasm@0 71 oldd = d;
matthiasm@0 72 }
matthiasm@0 73 }
matthiasm@0 74
matthiasm@0 75 double probReallyPitched = m_yinTrust * probYinPitched;
matthiasm@58 76 // std::cerr << probReallyPitched << " " << probYinPitched << std::endl;
matthiasm@58 77 // damn, I forget what this is all about...
Chris@138 78 for (int iPitch = 0; iPitch < m_nPitch; ++iPitch)
matthiasm@0 79 {
matthiasm@0 80 if (probYinPitched > 0) out[iPitch] *= (probReallyPitched/probYinPitched) ;
matthiasm@0 81 out[iPitch+m_nPitch] = (1 - probReallyPitched) / m_nPitch;
matthiasm@0 82 }
matthiasm@0 83 // out[2*m_nPitch] = m_yinTrust * (1 - probYinPitched);
matthiasm@0 84 return(out);
matthiasm@0 85 }
matthiasm@0 86
matthiasm@0 87 void
matthiasm@0 88 MonoPitchHMM::build()
matthiasm@0 89 {
matthiasm@0 90 // INITIAL VECTOR
mail@130 91 m_init = vector<double>(2*m_nPitch, 1.0 / 2*m_nPitch);
matthiasm@0 92
matthiasm@0 93 // TRANSITIONS
Chris@138 94 for (int iPitch = 0; iPitch < int(m_nPitch); ++iPitch)
matthiasm@0 95 {
Chris@138 96 int theoreticalMinNextPitch = iPitch-m_transitionWidth/2;
matthiasm@0 97 int minNextPitch = iPitch>m_transitionWidth/2 ? iPitch-m_transitionWidth/2 : 0;
matthiasm@0 98 int maxNextPitch = iPitch<m_nPitch-m_transitionWidth/2 ? iPitch+m_transitionWidth/2 : m_nPitch-1;
matthiasm@0 99
matthiasm@0 100 // WEIGHT VECTOR
matthiasm@0 101 double weightSum = 0;
matthiasm@0 102 vector<double> weights;
Chris@138 103 for (int i = minNextPitch; i <= maxNextPitch; ++i)
matthiasm@0 104 {
matthiasm@0 105 if (i <= iPitch)
matthiasm@0 106 {
matthiasm@0 107 weights.push_back(i-theoreticalMinNextPitch+1);
matthiasm@0 108 // weights.push_back(i-theoreticalMinNextPitch+1+m_transitionWidth/2);
matthiasm@0 109 } else {
matthiasm@0 110 weights.push_back(iPitch-theoreticalMinNextPitch+1-(i-iPitch));
matthiasm@0 111 // weights.push_back(iPitch-theoreticalMinNextPitch+1-(i-iPitch)+m_transitionWidth/2);
matthiasm@0 112 }
matthiasm@0 113 weightSum += weights[weights.size()-1];
matthiasm@0 114 }
matthiasm@0 115
matthiasm@0 116 // std::cerr << minNextPitch << " " << maxNextPitch << std::endl;
matthiasm@0 117 // TRANSITIONS TO CLOSE PITCH
Chris@138 118 for (int i = minNextPitch; i <= maxNextPitch; ++i)
matthiasm@0 119 {
mail@130 120 m_from.push_back(iPitch);
mail@130 121 m_to.push_back(i);
mail@130 122 m_transProb.push_back(weights[i-minNextPitch] / weightSum * m_selfTrans);
matthiasm@0 123
mail@130 124 m_from.push_back(iPitch);
mail@130 125 m_to.push_back(i+m_nPitch);
mail@130 126 m_transProb.push_back(weights[i-minNextPitch] / weightSum * (1-m_selfTrans));
matthiasm@0 127
mail@130 128 m_from.push_back(iPitch+m_nPitch);
mail@130 129 m_to.push_back(i+m_nPitch);
mail@130 130 m_transProb.push_back(weights[i-minNextPitch] / weightSum * m_selfTrans);
matthiasm@0 131 // transProb.push_back(weights[i-minNextPitch] / weightSum * 0.5);
matthiasm@0 132
mail@130 133 m_from.push_back(iPitch+m_nPitch);
mail@130 134 m_to.push_back(i);
mail@130 135 m_transProb.push_back(weights[i-minNextPitch] / weightSum * (1-m_selfTrans));
matthiasm@0 136 // transProb.push_back(weights[i-minNextPitch] / weightSum * 0.5);
matthiasm@0 137 }
matthiasm@0 138
matthiasm@0 139 // TRANSITION TO UNVOICED
matthiasm@0 140 // from.push_back(iPitch+m_nPitch);
matthiasm@0 141 // to.push_back(2*m_nPitch);
matthiasm@0 142 // transProb.push_back(1-m_selfTrans);
matthiasm@0 143
matthiasm@0 144 // TRANSITION FROM UNVOICED TO PITCH
matthiasm@25 145 // from.push_back(2*m_nPitch);
matthiasm@25 146 // to.push_back(iPitch+m_nPitch);
matthiasm@25 147 // transProb.push_back(1.0/m_nPitch);
matthiasm@0 148 }
matthiasm@0 149 // UNVOICED SELFTRANSITION
matthiasm@0 150 // from.push_back(2*m_nPitch);
matthiasm@0 151 // to.push_back(2*m_nPitch);
matthiasm@0 152 // transProb.push_back(m_selfTrans);
matthiasm@0 153
Chris@138 154 // for (int i = 0; i < from.size(); ++i) {
matthiasm@0 155 // std::cerr << "P(["<< from[i] << " --> " << to[i] << "]) = " << transProb[i] << std::endl;
matthiasm@0 156 // }
mail@130 157 m_nTrans = m_transProb.size();
mail@130 158 m_delta = vector<double>(m_nState);
mail@130 159 m_oldDelta = vector<double>(m_nState);
Chris@9 160 }
mail@132 161
mail@132 162 /*
mail@132 163 Takes a state number and a pitch-prob vector, then finds the pitch that would
mail@132 164 have been closest to the pitch of the state. Easy to understand? ;)
mail@132 165 */
Chris@145 166 float
Chris@156 167 MonoPitchHMM::nearestFreq(int state, const vector<pair<double, double> > &pitchProb)
mail@132 168 {
mail@132 169 float hmmFreq = m_freqs[state];
mail@132 170 // std::cerr << "hmmFreq " << hmmFreq << std::endl;
mail@132 171 float bestFreq = 0;
mail@132 172 float leastDist = 10000;
mail@132 173 if (hmmFreq > 0)
mail@132 174 {
mail@132 175 // This was a Yin estimate, so try to get original pitch estimate back
mail@132 176 // ... a bit hacky, since we could have direclty saved the frequency
mail@132 177 // that was assigned to the HMM bin in hmm.calculateObsProb -- but would
mail@132 178 // have had to rethink the interface of that method.
mail@132 179
mail@132 180 // std::cerr << "pitch prob size " << pitchProb.size() << std::endl;
mail@132 181
mail@132 182 for (size_t iPt = 0; iPt < pitchProb.size(); ++iPt)
mail@132 183 {
mail@132 184 float freq = 440. *
mail@132 185 std::pow(2,
mail@132 186 (pitchProb[iPt].first - 69)/12);
mail@132 187 float dist = std::abs(hmmFreq-freq);
mail@132 188 if (dist < leastDist)
mail@132 189 {
mail@132 190 leastDist = dist;
mail@132 191 bestFreq = freq;
mail@132 192 }
mail@132 193 }
mail@132 194 } else {
mail@132 195 bestFreq = hmmFreq;
mail@132 196 }
mail@132 197 return bestFreq;
Chris@141 198 }