Chris@4
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@4
|
2
|
Chris@4
|
3 /*
|
Chris@4
|
4 Vamp feature extraction plugin for the BeatRoot beat tracker.
|
Chris@4
|
5
|
Chris@4
|
6 Centre for Digital Music, Queen Mary, University of London.
|
Chris@4
|
7 This file copyright 2011 Simon Dixon, Chris Cannam and QMUL.
|
Chris@4
|
8
|
Chris@4
|
9 This program is free software; you can redistribute it and/or
|
Chris@4
|
10 modify it under the terms of the GNU General Public License as
|
Chris@4
|
11 published by the Free Software Foundation; either version 2 of the
|
Chris@4
|
12 License, or (at your option) any later version. See the file
|
Chris@4
|
13 COPYING included with this distribution for more information.
|
Chris@4
|
14 */
|
Chris@4
|
15
|
Chris@4
|
16 #ifndef _BEATROOT_PEAKS_H_
|
Chris@4
|
17 #define _BEATROOT_PEAKS_H_
|
Chris@4
|
18
|
Chris@4
|
19 #include <vector>
|
Chris@4
|
20 #include <cmath>
|
Chris@4
|
21
|
Chris@4
|
22 using std::vector;
|
Chris@4
|
23
|
Chris@4
|
24 class Peaks
|
Chris@4
|
25 {
|
Chris@4
|
26 protected:
|
Chris@4
|
27 static int pre;
|
Chris@4
|
28 static int post;
|
Chris@4
|
29
|
Chris@4
|
30 public:
|
Chris@4
|
31 /** General peak picking method for finding n local maxima in an array
|
Chris@4
|
32 * @param data input data
|
Chris@4
|
33 * @param peaks list of peak indexes
|
Chris@4
|
34 * @param width minimum distance between peaks
|
Chris@4
|
35 */
|
Chris@4
|
36 static int findPeaks(const vector<double> &data, vector<int> peaks, int width) {
|
Chris@4
|
37 int peakCount = 0;
|
Chris@4
|
38 int maxp = 0;
|
Chris@4
|
39 int mid = 0;
|
Chris@4
|
40 int end = data.size();
|
Chris@4
|
41 while (mid < end) {
|
Chris@4
|
42 int i = mid - width;
|
Chris@4
|
43 if (i < 0)
|
Chris@4
|
44 i = 0;
|
Chris@4
|
45 int stop = mid + width + 1;
|
Chris@4
|
46 if (stop > data.size())
|
Chris@4
|
47 stop = data.size();
|
Chris@4
|
48 maxp = i;
|
Chris@4
|
49 for (i++; i < stop; i++)
|
Chris@4
|
50 if (data[i] > data[maxp])
|
Chris@4
|
51 maxp = i;
|
Chris@4
|
52 if (maxp == mid) {
|
Chris@4
|
53 int j;
|
Chris@4
|
54 for (j = peakCount; j > 0; j--) {
|
Chris@4
|
55 if (data[maxp] <= data[peaks[j-1]])
|
Chris@4
|
56 break;
|
Chris@4
|
57 else if (j < peaks.size())
|
Chris@4
|
58 peaks[j] = peaks[j-1];
|
Chris@4
|
59 }
|
Chris@4
|
60 if (j != peaks.size())
|
Chris@4
|
61 peaks[j] = maxp;
|
Chris@4
|
62 if (peakCount != peaks.size())
|
Chris@4
|
63 peakCount++;
|
Chris@4
|
64 }
|
Chris@4
|
65 mid++;
|
Chris@4
|
66 }
|
Chris@4
|
67 return peakCount;
|
Chris@4
|
68 } // findPeaks()
|
Chris@4
|
69
|
Chris@4
|
70 /** General peak picking method for finding local maxima in an array
|
Chris@4
|
71 * @param data input data
|
Chris@4
|
72 * @param width minimum distance between peaks
|
Chris@4
|
73 * @param threshold minimum value of peaks
|
Chris@4
|
74 * @return list of peak indexes
|
Chris@4
|
75 */
|
Chris@4
|
76 static vector<int> findPeaks(const vector<double> &data, int width,
|
Chris@4
|
77 double threshold) {
|
Chris@4
|
78 return findPeaks(data, width, threshold, 0, false);
|
Chris@4
|
79 } // findPeaks()
|
Chris@4
|
80
|
Chris@4
|
81 /** General peak picking method for finding local maxima in an array
|
Chris@4
|
82 * @param data input data
|
Chris@4
|
83 * @param width minimum distance between peaks
|
Chris@4
|
84 * @param threshold minimum value of peaks
|
Chris@4
|
85 * @param decayRate how quickly previous peaks are forgotten
|
Chris@4
|
86 * @param isRelative minimum value of peaks is relative to local average
|
Chris@4
|
87 * @return list of peak indexes
|
Chris@4
|
88 */
|
Chris@4
|
89 static vector<int> findPeaks(const vector<double> &data, int width,
|
Chris@4
|
90 double threshold, double decayRate, bool isRelative) {
|
Chris@4
|
91 vector<int> peaks;
|
Chris@4
|
92 int maxp = 0;
|
Chris@4
|
93 int mid = 0;
|
Chris@4
|
94 int end = data.size();
|
Chris@4
|
95 double av = data[0];
|
Chris@4
|
96 while (mid < end) {
|
Chris@4
|
97 av = decayRate * av + (1 - decayRate) * data[mid];
|
Chris@4
|
98 if (av < data[mid])
|
Chris@4
|
99 av = data[mid];
|
Chris@4
|
100 int i = mid - width;
|
Chris@4
|
101 if (i < 0)
|
Chris@4
|
102 i = 0;
|
Chris@4
|
103 int stop = mid + width + 1;
|
Chris@4
|
104 if (stop > data.size())
|
Chris@4
|
105 stop = data.size();
|
Chris@4
|
106 maxp = i;
|
Chris@4
|
107 for (i++; i < stop; i++)
|
Chris@4
|
108 if (data[i] > data[maxp])
|
Chris@4
|
109 maxp = i;
|
Chris@4
|
110 if (maxp == mid) {
|
Chris@4
|
111 if (overThreshold(data, maxp, width, threshold, isRelative,av)){
|
Chris@4
|
112 peaks.push_back(maxp);
|
Chris@4
|
113 }
|
Chris@4
|
114 }
|
Chris@4
|
115 mid++;
|
Chris@4
|
116 }
|
Chris@4
|
117 return peaks;
|
Chris@4
|
118 } // findPeaks()
|
Chris@4
|
119
|
Chris@4
|
120 static double expDecayWithHold(double av, double decayRate,
|
Chris@4
|
121 const vector<double> &data, int start, int stop) {
|
Chris@4
|
122 while (start < stop) {
|
Chris@4
|
123 av = decayRate * av + (1 - decayRate) * data[start];
|
Chris@4
|
124 if (av < data[start])
|
Chris@4
|
125 av = data[start];
|
Chris@4
|
126 start++;
|
Chris@4
|
127 }
|
Chris@4
|
128 return av;
|
Chris@4
|
129 } // expDecayWithHold()
|
Chris@4
|
130
|
Chris@4
|
131 static bool overThreshold(const vector<double> &data, int index, int width,
|
Chris@4
|
132 double threshold, bool isRelative,
|
Chris@4
|
133 double av) {
|
Chris@4
|
134 if (data[index] < av)
|
Chris@4
|
135 return false;
|
Chris@4
|
136 if (isRelative) {
|
Chris@4
|
137 int iStart = index - pre * width;
|
Chris@4
|
138 if (iStart < 0)
|
Chris@4
|
139 iStart = 0;
|
Chris@4
|
140 int iStop = index + post * width;
|
Chris@4
|
141 if (iStop > data.size())
|
Chris@4
|
142 iStop = data.size();
|
Chris@4
|
143 double sum = 0;
|
Chris@4
|
144 int count = iStop - iStart;
|
Chris@4
|
145 while (iStart < iStop)
|
Chris@4
|
146 sum += data[iStart++];
|
Chris@4
|
147 return (data[index] > sum / count + threshold);
|
Chris@4
|
148 } else
|
Chris@4
|
149 return (data[index] > threshold);
|
Chris@4
|
150 } // overThreshold()
|
Chris@4
|
151
|
Chris@4
|
152 static void normalise(vector<double> &data) {
|
Chris@4
|
153 double sx = 0;
|
Chris@4
|
154 double sxx = 0;
|
Chris@4
|
155 for (int i = 0; i < data.size(); i++) {
|
Chris@4
|
156 sx += data[i];
|
Chris@4
|
157 sxx += data[i] * data[i];
|
Chris@4
|
158 }
|
Chris@4
|
159 double mean = sx / data.size();
|
Chris@4
|
160 double sd = sqrt((sxx - sx * mean) / data.size());
|
Chris@4
|
161 if (sd == 0)
|
Chris@4
|
162 sd = 1; // all data[i] == mean -> 0; avoids div by 0
|
Chris@4
|
163 for (int i = 0; i < data.size(); i++) {
|
Chris@4
|
164 data[i] = (data[i] - mean) / sd;
|
Chris@4
|
165 }
|
Chris@4
|
166 } // normalise()
|
Chris@4
|
167
|
Chris@4
|
168 /** Uses an n-point linear regression to estimate the slope of data.
|
Chris@4
|
169 * @param data input data
|
Chris@4
|
170 * @param hop spacing of data points
|
Chris@4
|
171 * @param n length of linear regression
|
Chris@4
|
172 * @param slope output data
|
Chris@4
|
173 */
|
Chris@4
|
174 static void getSlope(const vector<double> &data, double hop, int n,
|
Chris@4
|
175 vector<double> &slope) {
|
Chris@4
|
176 int i = 0, j = 0;
|
Chris@4
|
177 double t;
|
Chris@4
|
178 double sx = 0, sxx = 0, sy = 0, sxy = 0;
|
Chris@4
|
179 for ( ; i < n; i++) {
|
Chris@4
|
180 t = i * hop;
|
Chris@4
|
181 sx += t;
|
Chris@4
|
182 sxx += t * t;
|
Chris@4
|
183 sy += data[i];
|
Chris@4
|
184 sxy += t * data[i];
|
Chris@4
|
185 }
|
Chris@4
|
186 double delta = n * sxx - sx * sx;
|
Chris@4
|
187 for ( ; j < n / 2; j++)
|
Chris@4
|
188 slope[j] = (n * sxy - sx * sy) / delta;
|
Chris@4
|
189 for ( ; j < data.size() - (n + 1) / 2; j++, i++) {
|
Chris@4
|
190 slope[j] = (n * sxy - sx * sy) / delta;
|
Chris@4
|
191 sy += data[i] - data[i - n];
|
Chris@4
|
192 sxy += hop * (n * data[i] - sy);
|
Chris@4
|
193 }
|
Chris@4
|
194 for ( ; j < data.size(); j++)
|
Chris@4
|
195 slope[j] = (n * sxy - sx * sy) / delta;
|
Chris@4
|
196 } // getSlope()
|
Chris@4
|
197
|
Chris@4
|
198 static double min(const vector<double> &arr) { return arr[imin(arr)]; }
|
Chris@4
|
199
|
Chris@4
|
200 static double max(const vector<double> &arr) { return arr[imax(arr)]; }
|
Chris@4
|
201
|
Chris@4
|
202 static int imin(const vector<double> &arr) {
|
Chris@4
|
203 int i = 0;
|
Chris@4
|
204 for (int j = 1; j < arr.size(); j++)
|
Chris@4
|
205 if (arr[j] < arr[i])
|
Chris@4
|
206 i = j;
|
Chris@4
|
207 return i;
|
Chris@4
|
208 } // imin()
|
Chris@4
|
209
|
Chris@4
|
210 static int imax(const vector<double> &arr) {
|
Chris@4
|
211 int i = 0;
|
Chris@4
|
212 for (int j = 1; j < arr.size(); j++)
|
Chris@4
|
213 if (arr[j] > arr[i])
|
Chris@4
|
214 i = j;
|
Chris@4
|
215 return i;
|
Chris@4
|
216 } // imax()
|
Chris@4
|
217
|
Chris@4
|
218 }; // class Peaks
|
Chris@4
|
219
|
Chris@4
|
220 #endif
|