PeakFinder.cpp 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  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: 2012-12-28 21:52:47 +0200 (Fri, 28 Dec 2012) $
  15. // File revision : $Revision: 4 $
  16. //
  17. // $Id: PeakFinder.cpp 164 2012-12-28 19:52:47Z 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. minPos = maxPos = 0;
  49. }
  50. // Finds real 'top' of a peak hump from neighnourhood of the given 'peakpos'.
  51. int PeakFinder::findTop(const float *data, int peakpos) const
  52. {
  53. int i;
  54. int start, end;
  55. float refvalue;
  56. refvalue = data[peakpos];
  57. // seek within ±10 points
  58. start = peakpos - 10;
  59. if (start < minPos) start = minPos;
  60. end = peakpos + 10;
  61. if (end > maxPos) end = maxPos;
  62. for (i = start; i <= end; i ++)
  63. {
  64. if (data[i] > refvalue)
  65. {
  66. peakpos = i;
  67. refvalue = data[i];
  68. }
  69. }
  70. // failure if max value is at edges of seek range => it's not peak, it's at slope.
  71. if ((peakpos == start) || (peakpos == end)) return 0;
  72. return peakpos;
  73. }
  74. // Finds 'ground level' of a peak hump by starting from 'peakpos' and proceeding
  75. // to direction defined by 'direction' until next 'hump' after minimum value will
  76. // begin
  77. int PeakFinder::findGround(const float *data, int peakpos, int direction) const
  78. {
  79. int lowpos;
  80. int pos;
  81. int climb_count;
  82. float refvalue;
  83. float delta;
  84. climb_count = 0;
  85. refvalue = data[peakpos];
  86. lowpos = peakpos;
  87. pos = peakpos;
  88. while ((pos > minPos+1) && (pos < maxPos-1))
  89. {
  90. int prevpos;
  91. prevpos = pos;
  92. pos += direction;
  93. // calculate derivate
  94. delta = data[pos] - data[prevpos];
  95. if (delta <= 0)
  96. {
  97. // going downhill, ok
  98. if (climb_count)
  99. {
  100. climb_count --; // decrease climb count
  101. }
  102. // check if new minimum found
  103. if (data[pos] < refvalue)
  104. {
  105. // new minimum found
  106. lowpos = pos;
  107. refvalue = data[pos];
  108. }
  109. }
  110. else
  111. {
  112. // going uphill, increase climbing counter
  113. climb_count ++;
  114. if (climb_count > 5) break; // we've been climbing too long => it's next uphill => quit
  115. }
  116. }
  117. return lowpos;
  118. }
  119. // Find offset where the value crosses the given level, when starting from 'peakpos' and
  120. // proceeds to direction defined in 'direction'
  121. int PeakFinder::findCrossingLevel(const float *data, float level, int peakpos, int direction) const
  122. {
  123. float peaklevel;
  124. int pos;
  125. peaklevel = data[peakpos];
  126. assert(peaklevel >= level);
  127. pos = peakpos;
  128. while ((pos >= minPos) && (pos < maxPos))
  129. {
  130. if (data[pos + direction] < level) return pos; // crossing found
  131. pos += direction;
  132. }
  133. return -1; // not found
  134. }
  135. // Calculates the center of mass location of 'data' array items between 'firstPos' and 'lastPos'
  136. double PeakFinder::calcMassCenter(const float *data, int firstPos, int lastPos) const
  137. {
  138. int i;
  139. float sum;
  140. float wsum;
  141. sum = 0;
  142. wsum = 0;
  143. for (i = firstPos; i <= lastPos; i ++)
  144. {
  145. sum += (float)i * data[i];
  146. wsum += data[i];
  147. }
  148. if (wsum < 1e-6) return 0;
  149. return sum / wsum;
  150. }
  151. /// get exact center of peak near given position by calculating local mass of center
  152. double PeakFinder::getPeakCenter(const float *data, int peakpos) const
  153. {
  154. float peakLevel; // peak level
  155. int crosspos1, crosspos2; // position where the peak 'hump' crosses cutting level
  156. float cutLevel; // cutting value
  157. float groundLevel; // ground level of the peak
  158. int gp1, gp2; // bottom positions of the peak 'hump'
  159. // find ground positions.
  160. gp1 = findGround(data, peakpos, -1);
  161. gp2 = findGround(data, peakpos, 1);
  162. groundLevel = 0.5f * (data[gp1] + data[gp2]);
  163. peakLevel = data[peakpos];
  164. // calculate 70%-level of the peak
  165. cutLevel = 0.70f * peakLevel + 0.30f * groundLevel;
  166. // find mid-level crossings
  167. crosspos1 = findCrossingLevel(data, cutLevel, peakpos, -1);
  168. crosspos2 = findCrossingLevel(data, cutLevel, peakpos, 1);
  169. if ((crosspos1 < 0) || (crosspos2 < 0)) return 0; // no crossing, no peak..
  170. // calculate mass center of the peak surroundings
  171. return calcMassCenter(data, crosspos1, crosspos2);
  172. }
  173. double PeakFinder::detectPeak(const float *data, int aminPos, int amaxPos)
  174. {
  175. int i;
  176. int peakpos; // position of peak level
  177. double highPeak, peak;
  178. this->minPos = aminPos;
  179. this->maxPos = amaxPos;
  180. // find absolute peak
  181. peakpos = minPos;
  182. peak = data[minPos];
  183. for (i = minPos + 1; i < maxPos; i ++)
  184. {
  185. if (data[i] > peak)
  186. {
  187. peak = data[i];
  188. peakpos = i;
  189. }
  190. }
  191. // Calculate exact location of the highest peak mass center
  192. highPeak = getPeakCenter(data, peakpos);
  193. peak = highPeak;
  194. // Now check if the highest peak were in fact harmonic of the true base beat peak
  195. // - sometimes the highest peak can be Nth harmonic of the true base peak yet
  196. // just a slightly higher than the true base
  197. for (i = 3; i < 10; i ++)
  198. {
  199. double peaktmp, harmonic;
  200. int i1,i2;
  201. harmonic = (double)i * 0.5;
  202. peakpos = (int)(highPeak / harmonic + 0.5f);
  203. if (peakpos < minPos) break;
  204. peakpos = findTop(data, peakpos); // seek true local maximum index
  205. if (peakpos == 0) continue; // no local max here
  206. // calculate mass-center of possible harmonic peak
  207. peaktmp = getPeakCenter(data, peakpos);
  208. // accept harmonic peak if
  209. // (a) it is found
  210. // (b) is within ±4% of the expected harmonic interval
  211. // (c) has at least half x-corr value of the max. peak
  212. double diff = harmonic * peaktmp / highPeak;
  213. if ((diff < 0.96) || (diff > 1.04)) continue; // peak too afar from expected
  214. // now compare to highest detected peak
  215. i1 = (int)(highPeak + 0.5);
  216. i2 = (int)(peaktmp + 0.5);
  217. if (data[i2] >= 0.4*data[i1])
  218. {
  219. // The harmonic is at least half as high primary peak,
  220. // thus use the harmonic peak instead
  221. peak = peaktmp;
  222. }
  223. }
  224. return peak;
  225. }