Chris@0
|
1
|
Chris@0
|
2 #include "ConstrainedHarmonicPeak.h"
|
Chris@0
|
3
|
Chris@0
|
4 #include <cmath>
|
Chris@0
|
5 #include <cstdio>
|
Chris@0
|
6
|
Chris@0
|
7 using std::cerr;
|
Chris@0
|
8 using std::endl;
|
Chris@0
|
9 using std::vector;
|
Chris@0
|
10
|
Chris@0
|
11 ConstrainedHarmonicPeak::ConstrainedHarmonicPeak(float inputSampleRate) :
|
Chris@0
|
12 Plugin(inputSampleRate),
|
Chris@4
|
13 m_fftSize(0),
|
Chris@0
|
14 m_minFreq(0),
|
Chris@1
|
15 m_maxFreq(inputSampleRate/2),
|
Chris@1
|
16 m_harmonics(5)
|
Chris@0
|
17 {
|
Chris@0
|
18 }
|
Chris@0
|
19
|
Chris@0
|
20 ConstrainedHarmonicPeak::~ConstrainedHarmonicPeak()
|
Chris@0
|
21 {
|
Chris@0
|
22 }
|
Chris@0
|
23
|
Chris@0
|
24 string
|
Chris@0
|
25 ConstrainedHarmonicPeak::getIdentifier() const
|
Chris@0
|
26 {
|
Chris@0
|
27 return "constrainedharmonicpeak";
|
Chris@0
|
28 }
|
Chris@0
|
29
|
Chris@0
|
30 string
|
Chris@0
|
31 ConstrainedHarmonicPeak::getName() const
|
Chris@0
|
32 {
|
Chris@0
|
33 return "Frequency-Constrained Harmonic Peak";
|
Chris@0
|
34 }
|
Chris@0
|
35
|
Chris@0
|
36 string
|
Chris@0
|
37 ConstrainedHarmonicPeak::getDescription() const
|
Chris@0
|
38 {
|
Chris@0
|
39 //!!! Return something helpful here!
|
Chris@0
|
40 return "";
|
Chris@0
|
41 }
|
Chris@0
|
42
|
Chris@0
|
43 string
|
Chris@0
|
44 ConstrainedHarmonicPeak::getMaker() const
|
Chris@0
|
45 {
|
Chris@0
|
46 return "Queen Mary, University of London";
|
Chris@0
|
47 }
|
Chris@0
|
48
|
Chris@0
|
49 int
|
Chris@0
|
50 ConstrainedHarmonicPeak::getPluginVersion() const
|
Chris@0
|
51 {
|
Chris@0
|
52 return 1;
|
Chris@0
|
53 }
|
Chris@0
|
54
|
Chris@0
|
55 string
|
Chris@0
|
56 ConstrainedHarmonicPeak::getCopyright() const
|
Chris@0
|
57 {
|
Chris@0
|
58 return "GPL";
|
Chris@0
|
59 }
|
Chris@0
|
60
|
Chris@0
|
61 ConstrainedHarmonicPeak::InputDomain
|
Chris@0
|
62 ConstrainedHarmonicPeak::getInputDomain() const
|
Chris@0
|
63 {
|
Chris@0
|
64 return FrequencyDomain;
|
Chris@0
|
65 }
|
Chris@0
|
66
|
Chris@0
|
67 size_t
|
Chris@0
|
68 ConstrainedHarmonicPeak::getPreferredBlockSize() const
|
Chris@0
|
69 {
|
Chris@0
|
70 return 2048;
|
Chris@0
|
71 }
|
Chris@0
|
72
|
Chris@0
|
73 size_t
|
Chris@0
|
74 ConstrainedHarmonicPeak::getPreferredStepSize() const
|
Chris@0
|
75 {
|
Chris@0
|
76 return 512;
|
Chris@0
|
77 }
|
Chris@0
|
78
|
Chris@0
|
79 size_t
|
Chris@0
|
80 ConstrainedHarmonicPeak::getMinChannelCount() const
|
Chris@0
|
81 {
|
Chris@0
|
82 return 1;
|
Chris@0
|
83 }
|
Chris@0
|
84
|
Chris@0
|
85 size_t
|
Chris@0
|
86 ConstrainedHarmonicPeak::getMaxChannelCount() const
|
Chris@0
|
87 {
|
Chris@0
|
88 return 1;
|
Chris@0
|
89 }
|
Chris@0
|
90
|
Chris@0
|
91 ConstrainedHarmonicPeak::ParameterList
|
Chris@0
|
92 ConstrainedHarmonicPeak::getParameterDescriptors() const
|
Chris@0
|
93 {
|
Chris@0
|
94 ParameterList list;
|
Chris@0
|
95
|
Chris@0
|
96 ParameterDescriptor d;
|
Chris@0
|
97 d.identifier = "minfreq";
|
Chris@0
|
98 d.name = "Minimum frequency";
|
Chris@0
|
99 d.description = "";
|
Chris@0
|
100 d.unit = "Hz";
|
Chris@0
|
101 d.minValue = 0;
|
Chris@0
|
102 d.maxValue = m_inputSampleRate/2;
|
Chris@0
|
103 d.defaultValue = 0;
|
Chris@0
|
104 d.isQuantized = false;
|
Chris@0
|
105 list.push_back(d);
|
Chris@0
|
106
|
Chris@0
|
107 d.identifier = "maxfreq";
|
Chris@0
|
108 d.name = "Maximum frequency";
|
Chris@0
|
109 d.description = "";
|
Chris@0
|
110 d.unit = "Hz";
|
Chris@0
|
111 d.minValue = 0;
|
Chris@0
|
112 d.maxValue = m_inputSampleRate/2;
|
Chris@0
|
113 d.defaultValue = 0;
|
Chris@0
|
114 d.isQuantized = false;
|
Chris@0
|
115 list.push_back(d);
|
Chris@0
|
116
|
Chris@1
|
117 d.identifier = "harmonics";
|
Chris@1
|
118 d.name = "Harmonics";
|
Chris@1
|
119 d.description = "Maximum number of harmonics to consider";
|
Chris@1
|
120 d.unit = "";
|
Chris@1
|
121 d.minValue = 1;
|
Chris@1
|
122 d.maxValue = 20;
|
Chris@1
|
123 d.defaultValue = 5;
|
Chris@1
|
124 d.isQuantized = true;
|
Chris@1
|
125 d.quantizeStep = 1;
|
Chris@1
|
126 list.push_back(d);
|
Chris@1
|
127
|
Chris@0
|
128 return list;
|
Chris@0
|
129 }
|
Chris@0
|
130
|
Chris@0
|
131 float
|
Chris@0
|
132 ConstrainedHarmonicPeak::getParameter(string identifier) const
|
Chris@0
|
133 {
|
Chris@0
|
134 if (identifier == "minfreq") {
|
Chris@0
|
135 return m_minFreq;
|
Chris@0
|
136 } else if (identifier == "maxfreq") {
|
Chris@0
|
137 return m_maxFreq;
|
Chris@1
|
138 } else if (identifier == "harmonics") {
|
Chris@1
|
139 return m_harmonics;
|
Chris@0
|
140 }
|
Chris@0
|
141 return 0;
|
Chris@0
|
142 }
|
Chris@0
|
143
|
Chris@0
|
144 void
|
Chris@0
|
145 ConstrainedHarmonicPeak::setParameter(string identifier, float value)
|
Chris@0
|
146 {
|
Chris@0
|
147 if (identifier == "minfreq") {
|
Chris@0
|
148 m_minFreq = value;
|
Chris@0
|
149 } else if (identifier == "maxfreq") {
|
Chris@0
|
150 m_maxFreq = value;
|
Chris@1
|
151 } else if (identifier == "harmonics") {
|
Chris@1
|
152 m_harmonics = int(round(value));
|
Chris@0
|
153 }
|
Chris@0
|
154 }
|
Chris@0
|
155
|
Chris@0
|
156 ConstrainedHarmonicPeak::ProgramList
|
Chris@0
|
157 ConstrainedHarmonicPeak::getPrograms() const
|
Chris@0
|
158 {
|
Chris@0
|
159 ProgramList list;
|
Chris@0
|
160 return list;
|
Chris@0
|
161 }
|
Chris@0
|
162
|
Chris@0
|
163 string
|
Chris@0
|
164 ConstrainedHarmonicPeak::getCurrentProgram() const
|
Chris@0
|
165 {
|
Chris@0
|
166 return ""; // no programs
|
Chris@0
|
167 }
|
Chris@0
|
168
|
Chris@0
|
169 void
|
Chris@0
|
170 ConstrainedHarmonicPeak::selectProgram(string name)
|
Chris@0
|
171 {
|
Chris@0
|
172 }
|
Chris@0
|
173
|
Chris@0
|
174 ConstrainedHarmonicPeak::OutputList
|
Chris@0
|
175 ConstrainedHarmonicPeak::getOutputDescriptors() const
|
Chris@0
|
176 {
|
Chris@0
|
177 OutputList list;
|
Chris@0
|
178
|
Chris@0
|
179 OutputDescriptor d;
|
Chris@0
|
180 d.identifier = "peak";
|
Chris@0
|
181 d.name = "Peak frequency";
|
Chris@0
|
182 d.description = "";
|
Chris@0
|
183 d.unit = "Hz";
|
Chris@0
|
184 d.sampleType = OutputDescriptor::OneSamplePerStep;
|
Chris@0
|
185 d.hasDuration = false;
|
Chris@0
|
186 list.push_back(d);
|
Chris@0
|
187
|
Chris@0
|
188 return list;
|
Chris@0
|
189 }
|
Chris@0
|
190
|
Chris@0
|
191 bool
|
Chris@0
|
192 ConstrainedHarmonicPeak::initialise(size_t channels, size_t stepSize, size_t blockSize)
|
Chris@0
|
193 {
|
Chris@0
|
194 if (channels < getMinChannelCount() ||
|
Chris@0
|
195 channels > getMaxChannelCount()) {
|
Chris@0
|
196 cerr << "ConstrainedHarmonicPeak::initialise: ERROR: channels " << channels
|
Chris@0
|
197 << " out of acceptable range " << getMinChannelCount()
|
Chris@0
|
198 << " -> " << getMaxChannelCount() << endl;
|
Chris@0
|
199 return false;
|
Chris@0
|
200 }
|
Chris@0
|
201
|
Chris@4
|
202 m_fftSize = blockSize;
|
Chris@0
|
203
|
Chris@0
|
204 return true;
|
Chris@0
|
205 }
|
Chris@0
|
206
|
Chris@0
|
207 void
|
Chris@0
|
208 ConstrainedHarmonicPeak::reset()
|
Chris@0
|
209 {
|
Chris@0
|
210 }
|
Chris@0
|
211
|
Chris@1
|
212 double
|
Chris@1
|
213 ConstrainedHarmonicPeak::findInterpolatedPeak(const double *in,
|
Chris@1
|
214 int peakbin,
|
Chris@1
|
215 int bins)
|
Chris@1
|
216 {
|
Chris@1
|
217 // duplicate with SimpleCepstrum plugin
|
Chris@1
|
218 // after jos,
|
Chris@1
|
219 // https://ccrma.stanford.edu/~jos/sasp/Quadratic_Interpolation_Spectral_Peaks.html
|
Chris@1
|
220
|
Chris@1
|
221 if (peakbin < 1 || peakbin > bins - 2) {
|
Chris@1
|
222 return peakbin;
|
Chris@1
|
223 }
|
Chris@1
|
224
|
Chris@1
|
225 double alpha = in[peakbin-1];
|
Chris@1
|
226 double beta = in[peakbin];
|
Chris@1
|
227 double gamma = in[peakbin+1];
|
Chris@1
|
228
|
Chris@1
|
229 double denom = (alpha - 2*beta + gamma);
|
Chris@1
|
230
|
Chris@1
|
231 if (denom == 0) {
|
Chris@1
|
232 // flat
|
Chris@1
|
233 return peakbin;
|
Chris@1
|
234 }
|
Chris@1
|
235
|
Chris@1
|
236 double p = ((alpha - gamma) / denom) / 2.0;
|
Chris@1
|
237
|
Chris@1
|
238 return double(peakbin) + p;
|
Chris@1
|
239 }
|
Chris@1
|
240
|
Chris@0
|
241 ConstrainedHarmonicPeak::FeatureSet
|
Chris@0
|
242 ConstrainedHarmonicPeak::process(const float *const *inputBuffers, Vamp::RealTime timestamp)
|
Chris@0
|
243 {
|
Chris@0
|
244 FeatureSet fs;
|
Chris@0
|
245
|
Chris@4
|
246 int hs = m_fftSize/2;
|
Chris@1
|
247
|
Chris@1
|
248 double *mags = new double[hs+1];
|
Chris@1
|
249 for (int i = 0; i <= hs; ++i) {
|
Chris@4
|
250 mags[i] = sqrt(inputBuffers[0][i*2] * inputBuffers[0][i*2] +
|
Chris@4
|
251 inputBuffers[0][i*2+1] * inputBuffers[0][i*2+1]);
|
Chris@1
|
252 }
|
Chris@1
|
253
|
Chris@1
|
254 // bin freq is bin * samplerate / fftsize
|
Chris@1
|
255
|
Chris@4
|
256 int minbin = int(floor((m_minFreq * m_fftSize) / m_inputSampleRate));
|
Chris@4
|
257 int maxbin = int(ceil((m_maxFreq * m_fftSize) / m_inputSampleRate));
|
Chris@1
|
258 if (minbin > hs) minbin = hs;
|
Chris@1
|
259 if (maxbin > hs) maxbin = hs;
|
Chris@1
|
260 if (maxbin <= minbin) return fs;
|
Chris@1
|
261
|
Chris@1
|
262 double *hps = new double[maxbin - minbin + 1];
|
Chris@1
|
263
|
Chris@1
|
264 // HPS in dB after MzHarmonicSpectrum
|
Chris@1
|
265
|
Chris@1
|
266 for (int bin = minbin; bin <= maxbin; ++bin) {
|
Chris@1
|
267
|
Chris@1
|
268 int i = bin - minbin;
|
Chris@1
|
269 hps[i] = 1.0;
|
Chris@1
|
270
|
Chris@1
|
271 int contributing = 0;
|
Chris@1
|
272
|
Chris@1
|
273 for (int j = 1; j <= m_harmonics; ++j) {
|
Chris@1
|
274 if (j * bin > hs) break;
|
Chris@1
|
275 hps[i] *= mags[j * bin];
|
Chris@1
|
276 ++contributing;
|
Chris@1
|
277 }
|
Chris@1
|
278
|
Chris@1
|
279 if (hps[i] <= 0.0) {
|
Chris@1
|
280 hps[i] = -120.0;
|
Chris@1
|
281 } else {
|
Chris@1
|
282 hps[i] = 20.0 / contributing * log10(hps[i]);
|
Chris@1
|
283 }
|
Chris@1
|
284 }
|
Chris@1
|
285
|
Chris@1
|
286 double maxdb = -120.0;
|
Chris@1
|
287 int maxidx = 0;
|
Chris@1
|
288 for (int i = 0; i <= maxbin - minbin; ++i) {
|
Chris@1
|
289 if (hps[i] > maxdb) {
|
Chris@1
|
290 maxdb = hps[i];
|
Chris@1
|
291 maxidx = i;
|
Chris@1
|
292 }
|
Chris@1
|
293 }
|
Chris@1
|
294
|
Chris@1
|
295 double interpolated = findInterpolatedPeak(hps, maxidx, maxbin - minbin + 1);
|
Chris@1
|
296 interpolated = interpolated + minbin;
|
Chris@1
|
297
|
Chris@4
|
298 double freq = interpolated * m_inputSampleRate / m_fftSize;
|
Chris@1
|
299
|
Chris@1
|
300 Feature f;
|
Chris@1
|
301 f.values.push_back(freq);
|
Chris@1
|
302 fs[0].push_back(f);
|
Chris@1
|
303
|
Chris@0
|
304 return fs;
|
Chris@0
|
305 }
|
Chris@0
|
306
|
Chris@0
|
307 ConstrainedHarmonicPeak::FeatureSet
|
Chris@0
|
308 ConstrainedHarmonicPeak::getRemainingFeatures()
|
Chris@0
|
309 {
|
Chris@0
|
310 FeatureSet fs;
|
Chris@0
|
311 return fs;
|
Chris@0
|
312 }
|
Chris@0
|
313
|