yinacf.h

Go to the documentation of this file.
00001 #ifndef YINACF_H
00002 #define YINACF_H
00003 /* Copyight (c) 1005 Michaël Roy
00004 *
00005 * This program is free software; you can redistribute it and/or modify
00006 * it under the terms of the GNU General Public License as published by
00007 * the Free Software Foundation; either version 2 of the License, or
00008 * (at your option) any later version.
00009 *
00010 * This program is distributed in the hope that it will be useful,
00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013 * GNU General Public License for more details.
00014 *
00015 * You should have received a copy of the GNU General Public License
00016 * along with this program; if not, write to the Free Software
00017 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00018 *
00019 */
00034 // undef to remove Intel EMM support
00035 #define YIN_EMMSUPPORT
00036 #ifdef YIN_EMMSUPPORT
00037 # include <emmintrin.h>
00038 #endif
00039 
00056 template <class Sample>
00057 class YinACF {
00058 private:
00059 
00070     class Solution {
00071     public:
00072         Sample* diff;           
00073         Sample* cmndiff;        
00074         unsigned est;           
00075         Sample freq;            
00077     public:
00078 
00085         Solution() {
00086             diff = 0;
00087             cmndiff = 0;
00088             est = 0;
00089             freq = 0;
00090         }
00091 
00100         Solution(unsigned W) {
00101             diff = new Sample[W];
00102             cmndiff = new Sample[W];
00103             est = 0;
00104             freq = 0;
00105         }
00106 
00113         ~Solution() {
00114             if (diff) delete [] diff;
00115             if (cmndiff) delete [] cmndiff;
00116         }
00117     };
00118 
00119 private:
00120     Sample* in;             
00121     unsigned head;          
00122     unsigned N;             
00123     unsigned W;             
00124     Sample THRESHOLD;       
00125     unsigned TMAX;          
00126     Solution* sol;          
00127     unsigned ho;            
00129 public:
00139     YinACF() {
00140         THRESHOLD = Sample(1.e-1);
00141         in = 0;
00142         head = 0;
00143         N = 0;
00144         W = 0;
00145         TMAX = 0;
00146         ho = 0;
00147         sol = 0;
00148     }
00149 
00156     ~YinACF() {
00157         destroy();
00158     }
00159 
00177     bool build (unsigned windowSize, unsigned tmax) {
00178 
00179         destroy();
00180 
00181         W = windowSize;
00182         N = (2 * W);
00183         TMAX = tmax | 1;
00184 
00185         head = 0;
00186         in = new Sample[N];
00187 
00188         ho = 0;
00189         sol = new Solution[TMAX];
00190         
00191         for (unsigned i = 0; i < TMAX; ++i)
00192             sol[i].Solution::Solution(W);
00193 
00194         reset();
00195         return true;
00196     }
00197     
00206     void destroy() {
00207         N = 0;
00208         W = 0;
00209         TMAX = 0;
00210 
00211         if (in) delete [] in;
00212         if (sol) delete[] sol;
00213         head = 0;
00214         in = 0;
00215 
00216         ho = 0;
00217         sol = 0;
00218     }
00219 
00226     void reset() {
00227         unsigned i, j;
00228         if (N) {
00229             head = 0;
00230             for (i = 0; i < N; ++i)
00231                 in[i] = 0;
00232 
00233             ho = 0;
00234             for (i = 0; i < TMAX; ++i) {
00235                 sol[i].est = 0;
00236                 sol[i].freq = 0;
00237                 sol[i].diff[0] = 0;
00238                 sol[i].cmndiff[0] = 1;
00239                 for (j = 1; j < W; ++j) {
00240                     sol[i].diff[j] = 0;
00241                     sol[i].cmndiff[j] = 0;
00242                 }
00243             }
00244         }
00245     }
00246 
00260     virtual Sample tick(const Sample& s) {
00261         Sample freq = 0;
00262         pushSolution();
00263         Solution* psin = getSolution(TMAX - 1);
00264         Solution* psprev = getSolution(TMAX - 2);
00265 
00266         psin->est = 0;
00267         psin->freq = 0;
00268 
00269         computeDiff(s, psin, psprev);  // step 2,3
00270 
00271         //  make preliminary guesstimate
00272         estimateFreq(psin, 0, W);
00273 
00274         // return best local frequency estimate (is delayed by TMAX /2)
00275         return getBestLocalEstimate();
00276     }
00277 
00286     inline unsigned getLatency() const {
00287         return N + (TMAX / 2);
00288     }
00289 
00298     inline unsigned getWindowSize() const {
00299         return W;
00300     }
00301 
00315     inline const Sample* getDiff(int offset = 0) const {
00316         return getSolution(offset)->diff;
00317     }
00318 
00332     inline const Sample* getCMNDiff(int offset = 0) const {
00333         return getSolution(offset)->cmndiff;
00334     }
00335 
00349     inline const getFrequency(int offset = 0) const {
00350         return getSolution(offset)->freq;
00351     }
00352 
00361     inline Sample getThreshold() const {
00362         return THRESHOLD;
00363     }
00364 
00371     inline void setThreshold(const Sample& threshold) {
00372         THRESHOLD = threshold;
00373     }
00374 
00383     inline unsigned getMaxPeriod() const {
00384         return TMAX;
00385     }
00386 
00387 private:
00388     inline void pushInput(const Sample& s) {
00389         in[head++] = s;
00390         if (head >= N) head = 0;
00391     }
00392 
00393     inline void pushSolution() {
00394         if (++ho >= (int)TMAX) ho = 0;
00395     }
00396 
00397     const Sample& getSample(unsigned delay) const {
00398         unsigned n = head + delay;
00399         if (n >= N) n -= N;
00400         return in[n];
00401     }
00402 
00403     const void getSamples(unsigned delay, unsigned n, Sample* p) const {
00404         unsigned o = head + delay;
00405         while(n--) {
00406             if (o >= N) o -= N;
00407             *(p++) = in[o++];
00408         }
00409     }
00410 
00411     Solution* getSolution(unsigned n) const {
00412         n = n + ho;
00413         if (n >= TMAX) n -= TMAX;
00414         return (Solution*)&sol[n];
00415     }
00416 
00417     virtual void computeDiff(const Sample& s, Solution* pscur, Solution* psprev) {
00418         // call appropriate optimized template
00419         computeDiff(this, s, pscur, psprev);
00420     }
00421 
00422     virtual void estimateFreq(Solution* ps, unsigned mn, unsigned mx) {
00423         //  make preliminary guesstimate
00424         unsigned est = getDip(ps, mn, mx);       // step 4
00425         if (est) ps->est = est;
00426     }
00427 
00428     virtual unsigned getDip(Solution* ps, int mn, int mx) const {
00429         static const int MIN_WL = 2;
00430         int i;
00431         Sample *cmndiff = ps->cmndiff;
00432         Sample mindip = Sample(10000);
00433         unsigned mini = 0;
00434         bool lt1, lt2;
00435 
00436         if (mn < MIN_WL) mn = MIN_WL;
00437         if (mx >= (int)W) mx = (int)(W - 1);
00438         // return global minima or first minima below threshold
00439         lt2 = (cmndiff[mn - 1] < cmndiff[mn]);
00440         for (i = mn; i <= mx; ++i) {
00441             lt1 = lt2;
00442             lt2 = (cmndiff[i] < cmndiff[i + 1]);
00443             if (!lt1 && lt2) {
00444                 if (cmndiff[i] < mindip) {
00445                     mindip = cmndiff[i];
00446                     mini = i;
00447                     if (mindip < THRESHOLD)
00448                         break;
00449                 }
00450             }
00451         }
00452         return mini;
00453     }
00454 
00455     virtual Sample parabolicInterpolation(unsigned est, Solution* ps) {
00456         //  parabolic interpolation with b-a = 1, b-c = -1, fb < fa && fb < fc
00457         //  returns normalized frequency 
00458         //  
00459         Sample d1, d2;
00460         unsigned i = 0;
00461         Sample* p = &ps->diff[est];
00462 
00463         //  dip index could be slightly offset from cmndiff[]'s
00464         while(1) {
00465             if (*(p - 1) < *p) {
00466                 --est;
00467                 --p;
00468             }
00469             else if (*(p + 1) <= *p) {
00470                 ++est;
00471                 ++p;
00472             }
00473             else
00474                 break;
00475             if (++i > 4 || est >= W - 1)
00476                 return 0;
00477         }
00478 
00479         //  xmin = b - (1/2)[(((b-a)^2*(fb-fc))-((b-c)^2*(fb-fa))) / (((b-a)(fb-fc)-(b-c)(fb-fa)))]
00480         //  xmin = b - (1/2)[((fb-fc)-(fb-fa))/((fb-fc)+(fb-fa))]
00481         //  let d1 = fb-fa, d2 = fb-fc
00482         //  xmin = b-((d2-d1)/(2(d2+d1)))
00483 
00484         d1 = *p - *(p - 1);
00485         d2 = *p - *(p + 1);
00486         Sample b = Sample(est);
00487         Sample num = (d2 - d1);
00488         Sample div = (d2 + d1);
00489         if (div == 0)
00490             return Sample(1) / b;
00491         return Sample(1) / (b - (num / (2 * div)));
00492     }
00493 
00494     virtual Sample getBestLocalEstimate() {
00495         Solution* pscur = getSolution(TMAX / 2);
00496         Sample dmin = pscur->cmndiff[pscur->est];
00497         Solution* psmin = pscur;
00498 
00499         for (unsigned i = 0; i < TMAX; ++i) {
00500             Solution* ps = getSolution(i);
00501             if (ps->est && ps->cmndiff[ps->est] < dmin) {
00502                 dmin = ps->cmndiff[ps->est];
00503                 psmin = ps;
00504             }
00505         }
00506 
00507         if (psmin != pscur) {
00508             //  re-estimate around local minima
00509             unsigned est = getDip(pscur, (int)psmin->est - TMAX / 5, (int)psmin->est + TMAX / 5);
00510             if (est && (!pscur->est || pscur->cmndiff[est] < pscur->cmndiff[pscur->est])) {
00511                 pscur->est = est;
00512             }
00513         }
00514 
00515         if (pscur->est)
00516             pscur->freq = parabolicInterpolation(pscur->est, pscur);
00517         else 
00518             pscur->freq = 0;
00519         return pscur->freq;
00520     }
00521 
00522     template<class T>
00523     inline static void computeDiff(YinACF<T>* that, const Sample& s, typename YinACF<T>::Solution* pscur, typename YinACF<T>::Solution* psprev) {
00524         Sample sum = 1;
00525         Sample ii = 1;
00526         Sample sm1 = that->getSample(0);
00527         that->pushInput(s);
00528         Sample sw = that->getSample(that->W - 1);
00529         Sample* d = pscur->diff;
00530         Sample* p = psprev->diff;
00531         Sample* c = pscur->cmndiff;
00532         for (unsigned i = 1; i < that->W; ++i) {
00533             Sample d2 = sw - that->getSample(i + that->W - 1);
00534             Sample d1 = sm1 - that->getSample(i - 1);
00535             d[i] = p[i] + ((d2 + d1) * (d2 - d1));     //  a2 - b2 = (a + b)(a - b)
00536             sum += d[i];
00537             c[i] = ii * (d[i] / sum);
00538             ii += Sample(1);
00539         }
00540     }
00541 
00542 #ifdef YIN_EMMSUPPORT
00543 #pragma optimize("s", on)
00544     const void getSamples(unsigned delay, __m128& f) const {
00545         float p[4];
00546         unsigned n = head + delay;
00547         if (n >= N) n -= N;
00548         int x = (int)N - (int)(n + 4);
00549         if (x >= 0)
00550             f = _mm_loadu_ps(&in[n]);
00551         else {
00552             if (x == -1) {
00553                 *(long*)&p[0] = *(long*)&in[N-3];
00554                 *(long*)&p[1] = *(long*)&in[N-2];
00555                 *(long*)&p[2] = *(long*)&in[N-1];
00556                 *(long*)&p[3] = *(long*)&in[0];
00557             }
00558             else if (x == -2) {
00559                 *(long*)&p[0] = *(long*)&in[N-2];
00560                 *(long*)&p[1] = *(long*)&in[N-1];
00561                 *(long*)&p[2] = *(long*)&in[0];
00562                 *(long*)&p[3] = *(long*)&in[1];
00563             }
00564             else {
00565                 *(long*)&p[0] = *(long*)&in[N-1];
00566                 *(long*)&p[1] = *(long*)&in[0];
00567                 *(long*)&p[2] = *(long*)&in[1];
00568                 *(long*)&p[3] = *(long*)&in[2];
00569             }
00570             f = _mm_loadu_ps(p);
00571         }
00572     }
00573 
00574     inline static void computeDiff(YinACF<Sample>* that, const float& s, typename YinACF<Sample>::Solution* pscur, typename YinACF<Sample>::Solution* psprev) {
00575         static const __m128 ZERO = _mm_set_ps1(0.f);
00576         static const __m128 ONE = _mm_set_ps(1.f, 2.f, 3.f, 4.f);
00577         static const __m128 FOUR = _mm_set_ps1(4.f);
00578         __m128 sums = _mm_set_ps1(1.f), t;
00579         __m128 ii = ONE;
00580         __m128 sm1 = _mm_set_ps1(that->getSample(0));
00581         that->pushInput(s);
00582         __m128 sw = _mm_set_ps1(that->getSample(that->W - 1));
00583 
00584         float* d = pscur->diff;
00585         float* p = psprev->diff;
00586         float* c = pscur->cmndiff;
00587 
00588         unsigned i;
00589         for (i = 1; i < that->W - 4; i += 4) {
00590             __m128 smp1, smp2;
00591             that->getSamples(i + that->W - 1, smp1);
00592             that->getSamples(i - 1, smp2);
00593 
00594             __m128 d2 = _mm_sub_ps(sw, smp1);
00595             __m128 d1 = _mm_sub_ps(sm1, smp2);
00596             __m128 di = _mm_add_ps(_mm_loadu_ps(p + i), _mm_mul_ps(_mm_add_ps(d2, d1), _mm_sub_ps(d2, d1)));
00597             _mm_storeu_ps(d + i, di);
00598 
00599             sums = _mm_shuffle_ps(sums, sums, _MM_SHUFFLE(3,3,3,3));
00600             sums = _mm_add_ps(sums, di);
00601             sums = _mm_add_ps(sums, _mm_movelh_ps(ZERO, di));
00602             t = _mm_shuffle_ps(di, di, _MM_SHUFFLE(2,1,0,3));
00603             t = _mm_move_ss(t, ZERO);
00604             sums = _mm_add_ps(sums, t);
00605             sums = _mm_add_ps(sums, _mm_movelh_ps(ZERO, t));
00606 
00607             _mm_storeu_ps(c + i, _mm_mul_ps(ii, _mm_div_ps(di, sums)));
00608             ii = _mm_add_ps(ii, FOUR);
00609         }
00610     }
00611 #endif
00612 };
00613 
00614 #endif // YINACF_H

Generated on Thu Aug 24 04:21:11 2006 for The Yin Algorithm by  doxygen 1.4.5