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 #include "Peaks.h"
|
Chris@4
|
17
|
Chris@9
|
18 int Peaks::pre = 3;
|
Chris@9
|
19 int Peaks::post = 1;
|
Chris@4
|
20
|
Chris@15
|
21
|
Chris@15
|
22 int Peaks::findPeaks(const vector<double> &data, vector<int> peaks, int width) {
|
Chris@15
|
23 int peakCount = 0;
|
Chris@15
|
24 int maxp = 0;
|
Chris@15
|
25 int mid = 0;
|
Chris@15
|
26 int end = data.size();
|
Chris@15
|
27 while (mid < end) {
|
Chris@15
|
28 int i = mid - width;
|
Chris@15
|
29 if (i < 0)
|
Chris@15
|
30 i = 0;
|
Chris@15
|
31 int stop = mid + width + 1;
|
Chris@22
|
32 if (stop > (int)data.size())
|
Chris@15
|
33 stop = data.size();
|
Chris@15
|
34 maxp = i;
|
Chris@15
|
35 for (i++; i < stop; i++)
|
Chris@15
|
36 if (data[i] > data[maxp])
|
Chris@15
|
37 maxp = i;
|
Chris@15
|
38 if (maxp == mid) {
|
Chris@15
|
39 int j;
|
Chris@15
|
40 for (j = peakCount; j > 0; j--) {
|
Chris@15
|
41 if (data[maxp] <= data[peaks[j-1]])
|
Chris@15
|
42 break;
|
Chris@22
|
43 else if (j < (int)peaks.size())
|
Chris@15
|
44 peaks[j] = peaks[j-1];
|
Chris@15
|
45 }
|
Chris@22
|
46 if (j != (int)peaks.size())
|
Chris@15
|
47 peaks[j] = maxp;
|
Chris@22
|
48 if (peakCount != (int)peaks.size())
|
Chris@15
|
49 peakCount++;
|
Chris@15
|
50 }
|
Chris@15
|
51 mid++;
|
Chris@15
|
52 }
|
Chris@15
|
53 return peakCount;
|
Chris@15
|
54 } // findPeaks()
|
Chris@15
|
55
|
Chris@15
|
56 vector<int> Peaks::findPeaks(const vector<double> &data, int width,
|
Chris@15
|
57 double threshold, double decayRate, bool isRelative) {
|
Chris@15
|
58 vector<int> peaks;
|
Chris@15
|
59 int maxp = 0;
|
Chris@15
|
60 int mid = 0;
|
Chris@18
|
61 if (data.empty()) return peaks;
|
Chris@15
|
62 int end = data.size();
|
Chris@15
|
63 double av = data[0];
|
Chris@15
|
64 while (mid < end) {
|
Chris@15
|
65 av = decayRate * av + (1 - decayRate) * data[mid];
|
Chris@15
|
66 if (av < data[mid])
|
Chris@15
|
67 av = data[mid];
|
Chris@15
|
68 int i = mid - width;
|
Chris@15
|
69 if (i < 0)
|
Chris@15
|
70 i = 0;
|
Chris@15
|
71 int stop = mid + width + 1;
|
Chris@22
|
72 if (stop > (int)data.size())
|
Chris@15
|
73 stop = data.size();
|
Chris@15
|
74 maxp = i;
|
Chris@15
|
75 for (i++; i < stop; i++)
|
Chris@15
|
76 if (data[i] > data[maxp])
|
Chris@15
|
77 maxp = i;
|
Chris@15
|
78 if (maxp == mid) {
|
Chris@15
|
79 if (overThreshold(data, maxp, width, threshold, isRelative,av)){
|
Chris@15
|
80 peaks.push_back(maxp);
|
Chris@15
|
81 }
|
Chris@15
|
82 }
|
Chris@15
|
83 mid++;
|
Chris@15
|
84 }
|
Chris@15
|
85 return peaks;
|
Chris@15
|
86 } // findPeaks()
|
Chris@15
|
87
|
Chris@15
|
88 double Peaks::expDecayWithHold(double av, double decayRate,
|
Chris@15
|
89 const vector<double> &data, int start, int stop) {
|
Chris@15
|
90 while (start < stop) {
|
Chris@15
|
91 av = decayRate * av + (1 - decayRate) * data[start];
|
Chris@15
|
92 if (av < data[start])
|
Chris@15
|
93 av = data[start];
|
Chris@15
|
94 start++;
|
Chris@15
|
95 }
|
Chris@15
|
96 return av;
|
Chris@15
|
97 } // expDecayWithHold()
|
Chris@15
|
98
|
Chris@15
|
99 bool Peaks::overThreshold(const vector<double> &data, int index, int width,
|
Chris@15
|
100 double threshold, bool isRelative,
|
Chris@15
|
101 double av) {
|
Chris@15
|
102 if (data[index] < av)
|
Chris@15
|
103 return false;
|
Chris@15
|
104 if (isRelative) {
|
Chris@15
|
105 int iStart = index - pre * width;
|
Chris@15
|
106 if (iStart < 0)
|
Chris@15
|
107 iStart = 0;
|
Chris@15
|
108 int iStop = index + post * width;
|
Chris@22
|
109 if (iStop > (int)data.size())
|
Chris@15
|
110 iStop = data.size();
|
Chris@15
|
111 double sum = 0;
|
Chris@15
|
112 int count = iStop - iStart;
|
Chris@15
|
113 while (iStart < iStop)
|
Chris@15
|
114 sum += data[iStart++];
|
Chris@15
|
115 return (data[index] > sum / count + threshold);
|
Chris@15
|
116 } else
|
Chris@15
|
117 return (data[index] > threshold);
|
Chris@15
|
118 } // overThreshold()
|
Chris@15
|
119
|
Chris@15
|
120 void Peaks::normalise(vector<double> &data) {
|
Chris@15
|
121 double sx = 0;
|
Chris@15
|
122 double sxx = 0;
|
Chris@22
|
123 for (int i = 0; i < (int)data.size(); i++) {
|
Chris@15
|
124 sx += data[i];
|
Chris@15
|
125 sxx += data[i] * data[i];
|
Chris@15
|
126 }
|
Chris@15
|
127 double mean = sx / data.size();
|
Chris@15
|
128 double sd = sqrt((sxx - sx * mean) / data.size());
|
Chris@15
|
129 if (sd == 0)
|
Chris@15
|
130 sd = 1; // all data[i] == mean -> 0; avoids div by 0
|
Chris@22
|
131 for (int i = 0; i < (int)data.size(); i++) {
|
Chris@15
|
132 data[i] = (data[i] - mean) / sd;
|
Chris@15
|
133 }
|
Chris@15
|
134 } // normalise()
|
Chris@15
|
135
|
Chris@15
|
136 /** Uses an n-point linear regression to estimate the slope of data.
|
Chris@15
|
137 * @param data input data
|
Chris@15
|
138 * @param hop spacing of data points
|
Chris@15
|
139 * @param n length of linear regression
|
Chris@15
|
140 * @param slope output data
|
Chris@15
|
141 */
|
Chris@15
|
142 void Peaks::getSlope(const vector<double> &data, double hop, int n,
|
Chris@15
|
143 vector<double> &slope) {
|
Chris@15
|
144 int i = 0, j = 0;
|
Chris@15
|
145 double t;
|
Chris@15
|
146 double sx = 0, sxx = 0, sy = 0, sxy = 0;
|
Chris@15
|
147 for ( ; i < n; i++) {
|
Chris@15
|
148 t = i * hop;
|
Chris@15
|
149 sx += t;
|
Chris@15
|
150 sxx += t * t;
|
Chris@15
|
151 sy += data[i];
|
Chris@15
|
152 sxy += t * data[i];
|
Chris@15
|
153 }
|
Chris@15
|
154 double delta = n * sxx - sx * sx;
|
Chris@15
|
155 for ( ; j < n / 2; j++)
|
Chris@15
|
156 slope[j] = (n * sxy - sx * sy) / delta;
|
Chris@22
|
157 for ( ; j < (int)data.size() - (n + 1) / 2; j++, i++) {
|
Chris@15
|
158 slope[j] = (n * sxy - sx * sy) / delta;
|
Chris@15
|
159 sy += data[i] - data[i - n];
|
Chris@15
|
160 sxy += hop * (n * data[i] - sy);
|
Chris@15
|
161 }
|
Chris@22
|
162 for ( ; j < (int)data.size(); j++)
|
Chris@15
|
163 slope[j] = (n * sxy - sx * sy) / delta;
|
Chris@15
|
164 } // getSlope()
|
Chris@15
|
165
|
Chris@15
|
166 int Peaks::imin(const vector<double> &arr) {
|
Chris@15
|
167 int i = 0;
|
Chris@22
|
168 for (int j = 1; j < (int)arr.size(); j++)
|
Chris@15
|
169 if (arr[j] < arr[i])
|
Chris@15
|
170 i = j;
|
Chris@15
|
171 return i;
|
Chris@15
|
172 } // imin()
|
Chris@15
|
173
|
Chris@15
|
174 int Peaks::imax(const vector<double> &arr) {
|
Chris@15
|
175 int i = 0;
|
Chris@22
|
176 for (int j = 1; j < (int)arr.size(); j++)
|
Chris@15
|
177 if (arr[j] > arr[i])
|
Chris@15
|
178 i = j;
|
Chris@15
|
179 return i;
|
Chris@15
|
180 } // imax()
|
Chris@15
|
181
|