/external/pysoundtouch14/libsoundtouch/PeakFinder.cpp

http://echo-nest-remix.googlecode.com/ · C++ · 236 lines · 128 code · 43 blank · 65 comment · 19 complexity · 8a0046cf4734a08e9238fa30061d1260 MD5 · raw file

  1. ////////////////////////////////////////////////////////////////////////////////
  2. ///
  3. /// Peak detection routine.
  4. ///
  5. /// The routine detects highest value on an array of values and calculates the
  6. /// precise peak location as a mass-center of the 'hump' around the peak value.
  7. ///
  8. /// Author : Copyright (c) Olli Parviainen
  9. /// Author e-mail : oparviai 'at' iki.fi
  10. /// SoundTouch WWW: http://www.surina.net/soundtouch
  11. ///
  12. ////////////////////////////////////////////////////////////////////////////////
  13. //
  14. // Last changed : $Date: 2008-12-25 19:54:41 +0200 (Thu, 25 Dec 2008) $
  15. // File revision : $Revision: 4 $
  16. //
  17. // $Id: PeakFinder.cpp 43 2008-12-25 17:54:41Z oparviai $
  18. //
  19. ////////////////////////////////////////////////////////////////////////////////
  20. //
  21. // License :
  22. //
  23. // SoundTouch audio processing library
  24. // Copyright (c) Olli Parviainen
  25. //
  26. // This library is free software; you can redistribute it and/or
  27. // modify it under the terms of the GNU Lesser General Public
  28. // License as published by the Free Software Foundation; either
  29. // version 2.1 of the License, or (at your option) any later version.
  30. //
  31. // This library is distributed in the hope that it will be useful,
  32. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  33. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  34. // Lesser General Public License for more details.
  35. //
  36. // You should have received a copy of the GNU Lesser General Public
  37. // License along with this library; if not, write to the Free Software
  38. // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  39. //
  40. ////////////////////////////////////////////////////////////////////////////////
  41. #include <math.h>
  42. #include <assert.h>
  43. #include "PeakFinder.h"
  44. using namespace soundtouch;
  45. #define max(x, y) (((x) > (y)) ? (x) : (y))
  46. PeakFinder::PeakFinder()
  47. {
  48. }
  49. // Finds 'ground level' of a peak hump by starting from 'peakpos' and proceeding
  50. // to direction defined by 'direction' until next 'hump' after minimum value will
  51. // begin
  52. int PeakFinder::findGround(const float *data, int peakpos, int direction) const
  53. {
  54. float refvalue;
  55. int lowpos;
  56. int pos;
  57. int climb_count;
  58. float delta;
  59. climb_count = 0;
  60. refvalue = data[peakpos];
  61. lowpos = peakpos;
  62. pos = peakpos;
  63. while ((pos > minPos) && (pos < maxPos))
  64. {
  65. int prevpos;
  66. prevpos = pos;
  67. pos += direction;
  68. // calculate derivate
  69. delta = data[pos] - data[prevpos];
  70. if (delta <= 0)
  71. {
  72. // going downhill, ok
  73. if (climb_count)
  74. {
  75. climb_count --; // decrease climb count
  76. }
  77. // check if new minimum found
  78. if (data[pos] < refvalue)
  79. {
  80. // new minimum found
  81. lowpos = pos;
  82. refvalue = data[pos];
  83. }
  84. }
  85. else
  86. {
  87. // going uphill, increase climbing counter
  88. climb_count ++;
  89. if (climb_count > 5) break; // we've been climbing too long => it's next uphill => quit
  90. }
  91. }
  92. return lowpos;
  93. }
  94. // Find offset where the value crosses the given level, when starting from 'peakpos' and
  95. // proceeds to direction defined in 'direction'
  96. int PeakFinder::findCrossingLevel(const float *data, float level, int peakpos, int direction) const
  97. {
  98. float peaklevel;
  99. int pos;
  100. peaklevel = data[peakpos];
  101. assert(peaklevel >= level);
  102. pos = peakpos;
  103. while ((pos >= minPos) && (pos < maxPos))
  104. {
  105. if (data[pos + direction] < level) return pos; // crossing found
  106. pos += direction;
  107. }
  108. return -1; // not found
  109. }
  110. // Calculates the center of mass location of 'data' array items between 'firstPos' and 'lastPos'
  111. double PeakFinder::calcMassCenter(const float *data, int firstPos, int lastPos) const
  112. {
  113. int i;
  114. float sum;
  115. float wsum;
  116. sum = 0;
  117. wsum = 0;
  118. for (i = firstPos; i <= lastPos; i ++)
  119. {
  120. sum += (float)i * data[i];
  121. wsum += data[i];
  122. }
  123. return sum / wsum;
  124. }
  125. /// get exact center of peak near given position by calculating local mass of center
  126. double PeakFinder::getPeakCenter(const float *data, int peakpos)
  127. {
  128. float peakLevel; // peak level
  129. int crosspos1, crosspos2; // position where the peak 'hump' crosses cutting level
  130. float cutLevel; // cutting value
  131. float groundLevel; // ground level of the peak
  132. int gp1, gp2; // bottom positions of the peak 'hump'
  133. // find ground positions.
  134. gp1 = findGround(data, peakpos, -1);
  135. gp2 = findGround(data, peakpos, 1);
  136. groundLevel = max(data[gp1], data[gp2]);
  137. peakLevel = data[peakpos];
  138. if (groundLevel < 1e-6) return 0; // ground level too small => detection failed
  139. if ((peakLevel / groundLevel) < 1.3) return 0; // peak less than 30% of the ground level => no good peak detected
  140. // calculate 70%-level of the peak
  141. cutLevel = 0.70f * peakLevel + 0.30f * groundLevel;
  142. // find mid-level crossings
  143. crosspos1 = findCrossingLevel(data, cutLevel, peakpos, -1);
  144. crosspos2 = findCrossingLevel(data, cutLevel, peakpos, 1);
  145. if ((crosspos1 < 0) || (crosspos2 < 0)) return 0; // no crossing, no peak..
  146. // calculate mass center of the peak surroundings
  147. return calcMassCenter(data, crosspos1, crosspos2);
  148. }
  149. double PeakFinder::detectPeak(const float *data, int minPos, int maxPos)
  150. {
  151. int i;
  152. int peakpos; // position of peak level
  153. double highPeak, peak;
  154. this->minPos = minPos;
  155. this->maxPos = maxPos;
  156. // find absolute peak
  157. peakpos = minPos;
  158. peak = data[minPos];
  159. for (i = minPos + 1; i < maxPos; i ++)
  160. {
  161. if (data[i] > peak)
  162. {
  163. peak = data[i];
  164. peakpos = i;
  165. }
  166. }
  167. // Calculate exact location of the highest peak mass center
  168. highPeak = getPeakCenter(data, peakpos);
  169. peak = highPeak;
  170. // Now check if the highest peak were in fact harmonic of the true base beat peak
  171. // - sometimes the highest peak can be Nth harmonic of the true base peak yet
  172. // just a slightly higher than the true base
  173. for (i = 2; i < 10; i ++)
  174. {
  175. double peaktmp, tmp;
  176. int i1,i2;
  177. peakpos = (int)(highPeak / (double)i + 0.5f);
  178. if (peakpos < minPos) break;
  179. // calculate mass-center of possible base peak
  180. peaktmp = getPeakCenter(data, peakpos);
  181. // now compare to highest detected peak
  182. i1 = (int)(highPeak + 0.5);
  183. i2 = (int)(peaktmp + 0.5);
  184. tmp = 2 * (data[i2] - data[i1]) / (data[i2] + data[i1]);
  185. if (fabs(tmp) < 0.1)
  186. {
  187. // The highest peak is harmonic of almost as high base peak,
  188. // thus use the base peak instead
  189. peak = peaktmp;
  190. }
  191. }
  192. return peak;
  193. }