From d64e9ae0d35c79e2525b2652d8610f5946b25634 Mon Sep 17 00:00:00 2001 From: oparviai Date: Fri, 30 Dec 2011 20:33:46 +0000 Subject: [PATCH] Improved BPM detection routine to better spot harmonics for the strongest beat pattern. --- include/BPMDetect.h | 2 +- source/SoundTouch/PeakFinder.cpp | 64 ++++++++++++++++++++++++++------ source/SoundTouch/PeakFinder.h | 4 ++ 3 files changed, 58 insertions(+), 12 deletions(-) diff --git a/include/BPMDetect.h b/include/BPMDetect.h index ff1d3c4..c5989cf 100644 --- a/include/BPMDetect.h +++ b/include/BPMDetect.h @@ -67,7 +67,7 @@ namespace soundtouch #define MIN_BPM 29 /// Maximum allowed BPM rate. Used to restrict accepted result below a reasonable limit. -#define MAX_BPM 230 +#define MAX_BPM 200 /// Class for calculating BPM rate for audio data. diff --git a/source/SoundTouch/PeakFinder.cpp b/source/SoundTouch/PeakFinder.cpp index 9ad601c..cc2113b 100644 --- a/source/SoundTouch/PeakFinder.cpp +++ b/source/SoundTouch/PeakFinder.cpp @@ -55,15 +55,46 @@ PeakFinder::PeakFinder() } +// Finds real 'top' of a peak hump from neighnourhood of the given 'peakpos'. +int PeakFinder::findTop(const float *data, int peakpos) const +{ + int i; + int start, end; + float refvalue; + + refvalue = data[peakpos]; + + // seek within ±10 points + start = peakpos - 10; + if (start < minPos) start = minPos; + end = peakpos + 10; + if (end > maxPos) end = maxPos; + + for (i = start; i <= end; i ++) + { + if (data[i] > refvalue) + { + peakpos = i; + refvalue = data[i]; + } + } + + // failure if max value is at edges of seek range => it's not peak, it's at slope. + if ((peakpos == start) || (peakpos == end)) return 0; + + return peakpos; +} + + // Finds 'ground level' of a peak hump by starting from 'peakpos' and proceeding // to direction defined by 'direction' until next 'hump' after minimum value will // begin int PeakFinder::findGround(const float *data, int peakpos, int direction) const { - float refvalue; int lowpos; int pos; int climb_count; + float refvalue; float delta; climb_count = 0; @@ -210,30 +241,41 @@ double PeakFinder::detectPeak(const float *data, int aminPos, int amaxPos) // Now check if the highest peak were in fact harmonic of the true base beat peak // - sometimes the highest peak can be Nth harmonic of the true base peak yet // just a slightly higher than the true base - for (i = 2; i < 10; i ++) + + int hp = (int)(highPeak + 0.5); + + for (i = 3; i < 10; i ++) { - double peaktmp, tmp; + double peaktmp, harmonic; int i1,i2; - peakpos = (int)(highPeak / (double)i + 0.5f); + harmonic = (double)i * 0.5; + peakpos = (int)(highPeak / harmonic + 0.5f); if (peakpos < minPos) break; + peakpos = findTop(data, peakpos); // seek true local maximum index + if (peakpos == 0) continue; // no local max here - // calculate mass-center of possible base peak + // calculate mass-center of possible harmonic peak peaktmp = getPeakCenter(data, peakpos); + // accept harmonic peak if + // (a) it is found + // (b) is within ±4% of the expected harmonic interval + // (c) has at least half x-corr value of the max. peak + + double diff = harmonic * peaktmp / highPeak; + if ((diff < 0.96) || (diff > 1.04)) continue; // peak too afar from expected + // now compare to highest detected peak i1 = (int)(highPeak + 0.5); i2 = (int)(peaktmp + 0.5); - tmp = 2 * (data[i2] - data[i1]) / (data[i2] + data[i1]); - if (fabs(tmp) < 0.1) + if (data[i2] >= 0.5*data[i1]) { - // The highest peak is harmonic of almost as high base peak, - // thus use the base peak instead + // The harmonic is at least half as high primary peak, + // thus use the harmonic peak instead peak = peaktmp; } } return peak; } - - diff --git a/source/SoundTouch/PeakFinder.h b/source/SoundTouch/PeakFinder.h index a72b24f..fce4dc1 100644 --- a/source/SoundTouch/PeakFinder.h +++ b/source/SoundTouch/PeakFinder.h @@ -63,6 +63,10 @@ protected: int direction /// Direction where to proceed from the peak: 1 = right, -1 = left. ) const; + // Finds real 'top' of a peak hump from neighnourhood of the given 'peakpos'. + int findTop(const float *data, int peakpos) const; + + /// Finds the 'ground' level, i.e. smallest level between two neighbouring peaks, to right- /// or left-hand side of the given peak position. int findGround(const float *data, /// Data vector.