Improved BPM detection routine to better spot harmonics for the strongest beat pattern.

This commit is contained in:
oparviai 2011-12-30 20:33:46 +00:00
parent ad164d96db
commit d64e9ae0d3
3 changed files with 58 additions and 12 deletions

View File

@ -67,7 +67,7 @@ namespace soundtouch
#define MIN_BPM 29 #define MIN_BPM 29
/// Maximum allowed BPM rate. Used to restrict accepted result below a reasonable limit. /// 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. /// Class for calculating BPM rate for audio data.

View File

@ -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 // 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 // to direction defined by 'direction' until next 'hump' after minimum value will
// begin // begin
int PeakFinder::findGround(const float *data, int peakpos, int direction) const int PeakFinder::findGround(const float *data, int peakpos, int direction) const
{ {
float refvalue;
int lowpos; int lowpos;
int pos; int pos;
int climb_count; int climb_count;
float refvalue;
float delta; float delta;
climb_count = 0; 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 // 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 // - sometimes the highest peak can be Nth harmonic of the true base peak yet
// just a slightly higher than the true base // 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; 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; 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); 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 // now compare to highest detected peak
i1 = (int)(highPeak + 0.5); i1 = (int)(highPeak + 0.5);
i2 = (int)(peaktmp + 0.5); i2 = (int)(peaktmp + 0.5);
tmp = 2 * (data[i2] - data[i1]) / (data[i2] + data[i1]); if (data[i2] >= 0.5*data[i1])
if (fabs(tmp) < 0.1)
{ {
// The highest peak is harmonic of almost as high base peak, // The harmonic is at least half as high primary peak,
// thus use the base peak instead // thus use the harmonic peak instead
peak = peaktmp; peak = peaktmp;
} }
} }
return peak; return peak;
} }

View File

@ -63,6 +63,10 @@ protected:
int direction /// Direction where to proceed from the peak: 1 = right, -1 = left. int direction /// Direction where to proceed from the peak: 1 = right, -1 = left.
) const; ) 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- /// Finds the 'ground' level, i.e. smallest level between two neighbouring peaks, to right-
/// or left-hand side of the given peak position. /// or left-hand side of the given peak position.
int findGround(const float *data, /// Data vector. int findGround(const float *data, /// Data vector.