/external/pysoundtouch14/libsoundtouch/BPMDetect.cpp

http://echo-nest-remix.googlecode.com/ · C++ · 311 lines · 151 code · 60 blank · 100 comment · 16 complexity · b48719eba476d06baea1e61dbb100bb4 MD5 · raw file

  1. ////////////////////////////////////////////////////////////////////////////////
  2. ///
  3. /// Beats-per-minute (BPM) detection routine.
  4. ///
  5. /// The beat detection algorithm works as follows:
  6. /// - Use function 'inputSamples' to input a chunks of samples to the class for
  7. /// analysis. It's a good idea to enter a large sound file or stream in smallish
  8. /// chunks of around few kilosamples in order not to extinguish too much RAM memory.
  9. /// - Inputted sound data is decimated to approx 500 Hz to reduce calculation burden,
  10. /// which is basically ok as low (bass) frequencies mostly determine the beat rate.
  11. /// Simple averaging is used for anti-alias filtering because the resulting signal
  12. /// quality isn't of that high importance.
  13. /// - Decimated sound data is enveloped, i.e. the amplitude shape is detected by
  14. /// taking absolute value that's smoothed by sliding average. Signal levels that
  15. /// are below a couple of times the general RMS amplitude level are cut away to
  16. /// leave only notable peaks there.
  17. /// - Repeating sound patterns (e.g. beats) are detected by calculating short-term
  18. /// autocorrelation function of the enveloped signal.
  19. /// - After whole sound data file has been analyzed as above, the bpm level is
  20. /// detected by function 'getBpm' that finds the highest peak of the autocorrelation
  21. /// function, calculates it's precise location and converts this reading to bpm's.
  22. ///
  23. /// Author : Copyright (c) Olli Parviainen
  24. /// Author e-mail : oparviai 'at' iki.fi
  25. /// SoundTouch WWW: http://www.surina.net/soundtouch
  26. ///
  27. ////////////////////////////////////////////////////////////////////////////////
  28. //
  29. // Last changed : $Date: 2008-12-25 19:54:41 +0200 (Thu, 25 Dec 2008) $
  30. // File revision : $Revision: 4 $
  31. //
  32. // $Id: BPMDetect.cpp 43 2008-12-25 17:54:41Z oparviai $
  33. //
  34. ////////////////////////////////////////////////////////////////////////////////
  35. //
  36. // License :
  37. //
  38. // SoundTouch audio processing library
  39. // Copyright (c) Olli Parviainen
  40. //
  41. // This library is free software; you can redistribute it and/or
  42. // modify it under the terms of the GNU Lesser General Public
  43. // License as published by the Free Software Foundation; either
  44. // version 2.1 of the License, or (at your option) any later version.
  45. //
  46. // This library is distributed in the hope that it will be useful,
  47. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  48. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  49. // Lesser General Public License for more details.
  50. //
  51. // You should have received a copy of the GNU Lesser General Public
  52. // License along with this library; if not, write to the Free Software
  53. // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  54. //
  55. ////////////////////////////////////////////////////////////////////////////////
  56. #include <math.h>
  57. #include <assert.h>
  58. #include <string.h>
  59. #include "FIFOSampleBuffer.h"
  60. #include "PeakFinder.h"
  61. #include "BPMDetect.h"
  62. using namespace soundtouch;
  63. #define INPUT_BLOCK_SAMPLES 2048
  64. #define DECIMATED_BLOCK_SAMPLES 256
  65. typedef unsigned short ushort;
  66. /// decay constant for calculating RMS volume sliding average approximation
  67. /// (time constant is about 10 sec)
  68. const float avgdecay = 0.99986f;
  69. /// Normalization coefficient for calculating RMS sliding average approximation.
  70. const float avgnorm = (1 - avgdecay);
  71. BPMDetect::BPMDetect(int numChannels, int sampleRate)
  72. {
  73. xcorr = NULL;
  74. buffer = new FIFOSampleBuffer();
  75. decimateSum = 0;
  76. decimateCount = 0;
  77. decimateBy = 0;
  78. this->sampleRate = sampleRate;
  79. this->channels = numChannels;
  80. envelopeAccu = 0;
  81. // Initialize RMS volume accumulator to RMS level of 3000 (out of 32768) that's
  82. // a typical RMS signal level value for song data. This value is then adapted
  83. // to the actual level during processing.
  84. #ifdef INTEGER_SAMPLES
  85. // integer samples
  86. RMSVolumeAccu = (3000 * 3000) / avgnorm;
  87. #else
  88. // float samples, scaled to range [-1..+1[
  89. RMSVolumeAccu = (0.092f * 0.092f) / avgnorm;
  90. #endif
  91. init(numChannels, sampleRate);
  92. }
  93. BPMDetect::~BPMDetect()
  94. {
  95. delete[] xcorr;
  96. delete buffer;
  97. }
  98. /// low-pass filter & decimate to about 500 Hz. return number of outputted samples.
  99. ///
  100. /// Decimation is used to remove the unnecessary frequencies and thus to reduce
  101. /// the amount of data needed to be processed as calculating autocorrelation
  102. /// function is a very-very heavy operation.
  103. ///
  104. /// Anti-alias filtering is done simply by averaging the samples. This is really a
  105. /// poor-man's anti-alias filtering, but it's not so critical in this kind of application
  106. /// (it'd also be difficult to design a high-quality filter with steep cut-off at very
  107. /// narrow band)
  108. int BPMDetect::decimate(SAMPLETYPE *dest, const SAMPLETYPE *src, int numsamples)
  109. {
  110. int count, outcount;
  111. LONG_SAMPLETYPE out;
  112. assert(decimateBy != 0);
  113. outcount = 0;
  114. for (count = 0; count < numsamples; count ++)
  115. {
  116. decimateSum += src[count];
  117. decimateCount ++;
  118. if (decimateCount >= decimateBy)
  119. {
  120. // Store every Nth sample only
  121. out = (LONG_SAMPLETYPE)(decimateSum / decimateBy);
  122. decimateSum = 0;
  123. decimateCount = 0;
  124. #ifdef INTEGER_SAMPLES
  125. // check ranges for sure (shouldn't actually be necessary)
  126. if (out > 32767)
  127. {
  128. out = 32767;
  129. }
  130. else if (out < -32768)
  131. {
  132. out = -32768;
  133. }
  134. #endif // INTEGER_SAMPLES
  135. dest[outcount] = (SAMPLETYPE)out;
  136. outcount ++;
  137. }
  138. }
  139. return outcount;
  140. }
  141. // Calculates autocorrelation function of the sample history buffer
  142. void BPMDetect::updateXCorr(int process_samples)
  143. {
  144. int offs;
  145. SAMPLETYPE *pBuffer;
  146. assert(buffer->numSamples() >= (uint)(process_samples + windowLen));
  147. pBuffer = buffer->ptrBegin();
  148. for (offs = windowStart; offs < windowLen; offs ++)
  149. {
  150. LONG_SAMPLETYPE sum;
  151. int i;
  152. sum = 0;
  153. for (i = 0; i < process_samples; i ++)
  154. {
  155. sum += pBuffer[i] * pBuffer[i + offs]; // scaling the sub-result shouldn't be necessary
  156. }
  157. // xcorr[offs] *= xcorr_decay; // decay 'xcorr' here with suitable coefficients
  158. // if it's desired that the system adapts automatically to
  159. // various bpms, e.g. in processing continouos music stream.
  160. // The 'xcorr_decay' should be a value that's smaller than but
  161. // close to one, and should also depend on 'process_samples' value.
  162. xcorr[offs] += (float)sum;
  163. }
  164. }
  165. // Calculates envelope of the sample data
  166. void BPMDetect::calcEnvelope(SAMPLETYPE *samples, int numsamples)
  167. {
  168. const float decay = 0.7f; // decay constant for smoothing the envelope
  169. const float norm = (1 - decay);
  170. int i;
  171. LONG_SAMPLETYPE out;
  172. float val;
  173. for (i = 0; i < numsamples; i ++)
  174. {
  175. // calc average RMS volume
  176. RMSVolumeAccu *= avgdecay;
  177. val = (float)fabs((float)samples[i]);
  178. RMSVolumeAccu += val * val;
  179. // cut amplitudes that are below 2 times average RMS volume
  180. // (we're interested in peak values, not the silent moments)
  181. val -= 2 * (float)sqrt(RMSVolumeAccu * avgnorm);
  182. val = (val > 0) ? val : 0;
  183. // smooth amplitude envelope
  184. envelopeAccu *= decay;
  185. envelopeAccu += val;
  186. out = (LONG_SAMPLETYPE)(envelopeAccu * norm);
  187. #ifdef INTEGER_SAMPLES
  188. // cut peaks (shouldn't be necessary though)
  189. if (out > 32767) out = 32767;
  190. #endif // INTEGER_SAMPLES
  191. samples[i] = (SAMPLETYPE)out;
  192. }
  193. }
  194. void BPMDetect::inputSamples(SAMPLETYPE *samples, int numSamples)
  195. {
  196. SAMPLETYPE decimated[DECIMATED_BLOCK_SAMPLES];
  197. // convert from stereo to mono if necessary
  198. if (channels == 2)
  199. {
  200. int i;
  201. for (i = 0; i < numSamples; i ++)
  202. {
  203. samples[i] = (samples[i * 2] + samples[i * 2 + 1]) / 2;
  204. }
  205. }
  206. // decimate
  207. numSamples = decimate(decimated, samples, numSamples);
  208. // envelope new samples and add them to buffer
  209. calcEnvelope(decimated, numSamples);
  210. buffer->putSamples(decimated, numSamples);
  211. // when the buffer has enought samples for processing...
  212. if ((int)buffer->numSamples() > windowLen)
  213. {
  214. int processLength;
  215. // how many samples are processed
  216. processLength = buffer->numSamples() - windowLen;
  217. // ... calculate autocorrelations for oldest samples...
  218. updateXCorr(processLength);
  219. // ... and remove them from the buffer
  220. buffer->receiveSamples(processLength);
  221. }
  222. }
  223. void BPMDetect::init(int numChannels, int sampleRate)
  224. {
  225. this->sampleRate = sampleRate;
  226. // choose decimation factor so that result is approx. 500 Hz
  227. decimateBy = sampleRate / 500;
  228. assert(decimateBy > 0);
  229. assert(INPUT_BLOCK_SAMPLES < decimateBy * DECIMATED_BLOCK_SAMPLES);
  230. // Calculate window length & starting item according to desired min & max bpms
  231. windowLen = (60 * sampleRate) / (decimateBy * MIN_BPM);
  232. windowStart = (60 * sampleRate) / (decimateBy * MAX_BPM);
  233. assert(windowLen > windowStart);
  234. // allocate new working objects
  235. xcorr = new float[windowLen];
  236. memset(xcorr, 0, windowLen * sizeof(float));
  237. // we do processing in mono mode
  238. buffer->setChannels(1);
  239. buffer->clear();
  240. }
  241. float BPMDetect::getBpm()
  242. {
  243. double peakPos;
  244. PeakFinder peakFinder;
  245. // find peak position
  246. peakPos = peakFinder.detectPeak(xcorr, windowStart, windowLen);
  247. assert(decimateBy != 0);
  248. if (peakPos < 1e-6) return 0.0; // detection failed.
  249. // calculate BPM
  250. return (float)(60.0 * (((double)sampleRate / (double)decimateBy) / peakPos));
  251. }