mirror of
https://github.com/azahar-emu/soundtouch
synced 2025-11-12 18:10:03 +01:00
Improved BPM detection routine to better spot harmonics for the strongest beat pattern.
This commit is contained in:
parent
ad164d96db
commit
d64e9ae0d3
@ -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.
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user