Chris@0
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@0
|
2
|
Chris@0
|
3 #include "SimpleCepstrum.h"
|
Chris@0
|
4
|
Chris@0
|
5 #include <vector>
|
Chris@0
|
6 #include <algorithm>
|
Chris@0
|
7
|
Chris@0
|
8 #include <cstdio>
|
Chris@0
|
9 #include <cmath>
|
Chris@4
|
10 #include <complex>
|
Chris@0
|
11
|
Chris@0
|
12 using std::string;
|
Chris@0
|
13
|
Chris@0
|
14 SimpleCepstrum::SimpleCepstrum(float inputSampleRate) :
|
Chris@0
|
15 Plugin(inputSampleRate),
|
Chris@0
|
16 m_channels(0),
|
Chris@0
|
17 m_stepSize(256),
|
Chris@0
|
18 m_blockSize(1024),
|
Chris@0
|
19 m_fmin(50),
|
Chris@0
|
20 m_fmax(1000),
|
Chris@5
|
21 m_histlen(3),
|
Chris@3
|
22 m_clamp(false),
|
Chris@5
|
23 m_method(InverseSymmetric),
|
Chris@5
|
24 m_binFrom(0),
|
Chris@5
|
25 m_binTo(0),
|
Chris@5
|
26 m_bins(0),
|
Chris@5
|
27 m_history(0)
|
Chris@0
|
28 {
|
Chris@0
|
29 }
|
Chris@0
|
30
|
Chris@0
|
31 SimpleCepstrum::~SimpleCepstrum()
|
Chris@0
|
32 {
|
Chris@5
|
33 if (m_history) {
|
Chris@5
|
34 for (int i = 0; i < m_histlen; ++i) {
|
Chris@5
|
35 delete[] m_history[i];
|
Chris@5
|
36 }
|
Chris@5
|
37 delete[] m_history;
|
Chris@5
|
38 }
|
Chris@0
|
39 }
|
Chris@0
|
40
|
Chris@0
|
41 string
|
Chris@0
|
42 SimpleCepstrum::getIdentifier() const
|
Chris@0
|
43 {
|
Chris@0
|
44 return "simple-cepstrum";
|
Chris@0
|
45 }
|
Chris@0
|
46
|
Chris@0
|
47 string
|
Chris@0
|
48 SimpleCepstrum::getName() const
|
Chris@0
|
49 {
|
Chris@0
|
50 return "Simple Cepstrum";
|
Chris@0
|
51 }
|
Chris@0
|
52
|
Chris@0
|
53 string
|
Chris@0
|
54 SimpleCepstrum::getDescription() const
|
Chris@0
|
55 {
|
Chris@2
|
56 return "Return simple cepstral data from DFT bins. This plugin is intended for casual inspection of cepstral data. It returns a lot of different sorts of data and is quite slow; it's not a good way to extract a single feature rapidly.";
|
Chris@0
|
57 }
|
Chris@0
|
58
|
Chris@0
|
59 string
|
Chris@0
|
60 SimpleCepstrum::getMaker() const
|
Chris@0
|
61 {
|
Chris@0
|
62 // Your name here
|
Chris@0
|
63 return "";
|
Chris@0
|
64 }
|
Chris@0
|
65
|
Chris@0
|
66 int
|
Chris@0
|
67 SimpleCepstrum::getPluginVersion() const
|
Chris@0
|
68 {
|
Chris@0
|
69 // Increment this each time you release a version that behaves
|
Chris@0
|
70 // differently from the previous one
|
Chris@0
|
71 return 1;
|
Chris@0
|
72 }
|
Chris@0
|
73
|
Chris@0
|
74 string
|
Chris@0
|
75 SimpleCepstrum::getCopyright() const
|
Chris@0
|
76 {
|
Chris@0
|
77 // This function is not ideally named. It does not necessarily
|
Chris@0
|
78 // need to say who made the plugin -- getMaker does that -- but it
|
Chris@0
|
79 // should indicate the terms under which it is distributed. For
|
Chris@0
|
80 // example, "Copyright (year). All Rights Reserved", or "GPL"
|
Chris@0
|
81 return "";
|
Chris@0
|
82 }
|
Chris@0
|
83
|
Chris@0
|
84 SimpleCepstrum::InputDomain
|
Chris@0
|
85 SimpleCepstrum::getInputDomain() const
|
Chris@0
|
86 {
|
Chris@0
|
87 return FrequencyDomain;
|
Chris@0
|
88 }
|
Chris@0
|
89
|
Chris@0
|
90 size_t
|
Chris@0
|
91 SimpleCepstrum::getPreferredBlockSize() const
|
Chris@0
|
92 {
|
Chris@0
|
93 return 1024;
|
Chris@0
|
94 }
|
Chris@0
|
95
|
Chris@0
|
96 size_t
|
Chris@0
|
97 SimpleCepstrum::getPreferredStepSize() const
|
Chris@0
|
98 {
|
Chris@0
|
99 return 256;
|
Chris@0
|
100 }
|
Chris@0
|
101
|
Chris@0
|
102 size_t
|
Chris@0
|
103 SimpleCepstrum::getMinChannelCount() const
|
Chris@0
|
104 {
|
Chris@0
|
105 return 1;
|
Chris@0
|
106 }
|
Chris@0
|
107
|
Chris@0
|
108 size_t
|
Chris@0
|
109 SimpleCepstrum::getMaxChannelCount() const
|
Chris@0
|
110 {
|
Chris@0
|
111 return 1;
|
Chris@0
|
112 }
|
Chris@0
|
113
|
Chris@0
|
114 SimpleCepstrum::ParameterList
|
Chris@0
|
115 SimpleCepstrum::getParameterDescriptors() const
|
Chris@0
|
116 {
|
Chris@0
|
117 ParameterList list;
|
Chris@0
|
118
|
Chris@0
|
119 ParameterDescriptor d;
|
Chris@0
|
120
|
Chris@0
|
121 d.identifier = "fmin";
|
Chris@0
|
122 d.name = "Minimum frequency";
|
Chris@0
|
123 d.description = "";
|
Chris@0
|
124 d.unit = "Hz";
|
Chris@0
|
125 d.minValue = m_inputSampleRate / m_blockSize;
|
Chris@0
|
126 d.maxValue = m_inputSampleRate / 2;
|
Chris@0
|
127 d.defaultValue = 50;
|
Chris@0
|
128 d.isQuantized = false;
|
Chris@0
|
129 list.push_back(d);
|
Chris@0
|
130
|
Chris@0
|
131 d.identifier = "fmax";
|
Chris@0
|
132 d.name = "Maximum frequency";
|
Chris@0
|
133 d.description = "";
|
Chris@0
|
134 d.unit = "Hz";
|
Chris@0
|
135 d.minValue = m_inputSampleRate / m_blockSize;
|
Chris@0
|
136 d.maxValue = m_inputSampleRate / 2;
|
Chris@0
|
137 d.defaultValue = 1000;
|
Chris@0
|
138 d.isQuantized = false;
|
Chris@0
|
139 list.push_back(d);
|
Chris@0
|
140
|
Chris@5
|
141 d.identifier = "histlen";
|
Chris@5
|
142 d.name = "Mean filter history length";
|
Chris@5
|
143 d.description = "";
|
Chris@5
|
144 d.unit = "";
|
Chris@5
|
145 d.minValue = 1;
|
Chris@5
|
146 d.maxValue = 10;
|
Chris@5
|
147 d.defaultValue = 3;
|
Chris@5
|
148 d.isQuantized = true;
|
Chris@5
|
149 d.quantizeStep = 1;
|
Chris@5
|
150 list.push_back(d);
|
Chris@5
|
151
|
Chris@3
|
152 d.identifier = "method";
|
Chris@3
|
153 d.name = "Cepstrum transform method";
|
Chris@3
|
154 d.unit = "";
|
Chris@3
|
155 d.minValue = 0;
|
Chris@5
|
156 d.maxValue = 4;
|
Chris@3
|
157 d.defaultValue = 0;
|
Chris@3
|
158 d.isQuantized = true;
|
Chris@3
|
159 d.quantizeStep = 1;
|
Chris@3
|
160 d.valueNames.push_back("Inverse symmetric");
|
Chris@3
|
161 d.valueNames.push_back("Inverse asymmetric");
|
Chris@4
|
162 d.valueNames.push_back("Inverse complex");
|
Chris@3
|
163 d.valueNames.push_back("Forward magnitude");
|
Chris@3
|
164 d.valueNames.push_back("Forward difference");
|
Chris@3
|
165 list.push_back(d);
|
Chris@3
|
166
|
Chris@0
|
167 d.identifier = "clamp";
|
Chris@0
|
168 d.name = "Clamp negative values in cepstrum at zero";
|
Chris@0
|
169 d.unit = "";
|
Chris@0
|
170 d.minValue = 0;
|
Chris@0
|
171 d.maxValue = 1;
|
Chris@0
|
172 d.defaultValue = 0;
|
Chris@0
|
173 d.isQuantized = true;
|
Chris@0
|
174 d.quantizeStep = 1;
|
Chris@3
|
175 d.valueNames.clear();
|
Chris@0
|
176 list.push_back(d);
|
Chris@0
|
177
|
Chris@0
|
178 return list;
|
Chris@0
|
179 }
|
Chris@0
|
180
|
Chris@0
|
181 float
|
Chris@0
|
182 SimpleCepstrum::getParameter(string identifier) const
|
Chris@0
|
183 {
|
Chris@0
|
184 if (identifier == "fmin") return m_fmin;
|
Chris@0
|
185 else if (identifier == "fmax") return m_fmax;
|
Chris@5
|
186 else if (identifier == "histlen") return m_histlen;
|
Chris@0
|
187 else if (identifier == "clamp") return (m_clamp ? 1 : 0);
|
Chris@3
|
188 else if (identifier == "method") return (int)m_method;
|
Chris@0
|
189 else return 0.f;
|
Chris@0
|
190 }
|
Chris@0
|
191
|
Chris@0
|
192 void
|
Chris@0
|
193 SimpleCepstrum::setParameter(string identifier, float value)
|
Chris@0
|
194 {
|
Chris@0
|
195 if (identifier == "fmin") m_fmin = value;
|
Chris@0
|
196 else if (identifier == "fmax") m_fmax = value;
|
Chris@5
|
197 else if (identifier == "histlen") m_histlen = value;
|
Chris@0
|
198 else if (identifier == "clamp") m_clamp = (value > 0.5);
|
Chris@3
|
199 else if (identifier == "method") m_method = Method(int(value + 0.5));
|
Chris@0
|
200 }
|
Chris@0
|
201
|
Chris@0
|
202 SimpleCepstrum::ProgramList
|
Chris@0
|
203 SimpleCepstrum::getPrograms() const
|
Chris@0
|
204 {
|
Chris@0
|
205 ProgramList list;
|
Chris@0
|
206 return list;
|
Chris@0
|
207 }
|
Chris@0
|
208
|
Chris@0
|
209 string
|
Chris@0
|
210 SimpleCepstrum::getCurrentProgram() const
|
Chris@0
|
211 {
|
Chris@0
|
212 return ""; // no programs
|
Chris@0
|
213 }
|
Chris@0
|
214
|
Chris@0
|
215 void
|
Chris@0
|
216 SimpleCepstrum::selectProgram(string name)
|
Chris@0
|
217 {
|
Chris@0
|
218 }
|
Chris@0
|
219
|
Chris@0
|
220 SimpleCepstrum::OutputList
|
Chris@0
|
221 SimpleCepstrum::getOutputDescriptors() const
|
Chris@0
|
222 {
|
Chris@0
|
223 OutputList outputs;
|
Chris@0
|
224
|
Chris@0
|
225 int n = 0;
|
Chris@0
|
226
|
Chris@0
|
227 OutputDescriptor d;
|
Chris@2
|
228
|
Chris@0
|
229 d.identifier = "f0";
|
Chris@0
|
230 d.name = "Estimated fundamental frequency";
|
Chris@0
|
231 d.description = "";
|
Chris@0
|
232 d.unit = "";
|
Chris@0
|
233 d.hasFixedBinCount = true;
|
Chris@0
|
234 d.binCount = 1;
|
Chris@0
|
235 d.hasKnownExtents = true;
|
Chris@0
|
236 d.minValue = m_fmin;
|
Chris@0
|
237 d.maxValue = m_fmax;
|
Chris@0
|
238 d.isQuantized = false;
|
Chris@0
|
239 d.sampleType = OutputDescriptor::OneSamplePerStep;
|
Chris@0
|
240 d.hasDuration = false;
|
Chris@2
|
241 /*
|
Chris@0
|
242 m_f0Output = n++;
|
Chris@0
|
243 outputs.push_back(d);
|
Chris@2
|
244 */
|
Chris@0
|
245
|
Chris@0
|
246 d.identifier = "raw_cepstral_peak";
|
Chris@0
|
247 d.name = "Frequency corresponding to raw cepstral peak";
|
Chris@2
|
248 d.description = "Return the frequency whose period corresponds to the quefrency with the maximum value within the specified range of the cepstrum";
|
Chris@0
|
249 d.unit = "Hz";
|
Chris@5
|
250 m_pkOutput = n++;
|
Chris@0
|
251 outputs.push_back(d);
|
Chris@0
|
252
|
Chris@0
|
253 d.identifier = "variance";
|
Chris@0
|
254 d.name = "Variance of cepstral bins in range";
|
Chris@0
|
255 d.unit = "";
|
Chris@2
|
256 d.description = "Return the variance of bin values within the specified range of the cepstrum";
|
Chris@0
|
257 m_varOutput = n++;
|
Chris@0
|
258 outputs.push_back(d);
|
Chris@0
|
259
|
Chris@0
|
260 d.identifier = "peak";
|
Chris@0
|
261 d.name = "Peak value";
|
Chris@0
|
262 d.unit = "";
|
Chris@2
|
263 d.description = "Return the value found in the maximum-valued bin within the specified range of the cepstrum";
|
Chris@0
|
264 m_pvOutput = n++;
|
Chris@0
|
265 outputs.push_back(d);
|
Chris@0
|
266
|
Chris@0
|
267 d.identifier = "peak_to_mean";
|
Chris@0
|
268 d.name = "Peak-to-mean distance";
|
Chris@0
|
269 d.unit = "";
|
Chris@2
|
270 d.description = "Return the difference between maximum and mean bin values within the specified range of the cepstrum";
|
Chris@0
|
271 m_p2mOutput = n++;
|
Chris@0
|
272 outputs.push_back(d);
|
Chris@0
|
273
|
Chris@5
|
274 d.identifier = "peak_to_rms";
|
Chris@5
|
275 d.name = "Peak-to-RMS distance";
|
Chris@5
|
276 d.unit = "";
|
Chris@5
|
277 d.description = "Return the difference between maximum and root mean square bin values within the specified range of the cepstrum";
|
Chris@5
|
278 m_p2rOutput = n++;
|
Chris@5
|
279 outputs.push_back(d);
|
Chris@5
|
280
|
Chris@0
|
281 d.identifier = "cepstrum";
|
Chris@0
|
282 d.name = "Cepstrum";
|
Chris@0
|
283 d.unit = "";
|
Chris@2
|
284 d.description = "The unprocessed cepstrum bins within the specified range";
|
Chris@0
|
285
|
Chris@0
|
286 int from = int(m_inputSampleRate / m_fmax);
|
Chris@0
|
287 int to = int(m_inputSampleRate / m_fmin);
|
Chris@0
|
288 if (to >= (int)m_blockSize / 2) {
|
Chris@0
|
289 to = m_blockSize / 2 - 1;
|
Chris@0
|
290 }
|
Chris@0
|
291 d.binCount = to - from + 1;
|
Chris@0
|
292 for (int i = from; i <= to; ++i) {
|
Chris@0
|
293 float freq = m_inputSampleRate / i;
|
Chris@5
|
294 char buffer[20];
|
Chris@2
|
295 sprintf(buffer, "%.2f Hz", freq);
|
Chris@0
|
296 d.binNames.push_back(buffer);
|
Chris@0
|
297 }
|
Chris@0
|
298
|
Chris@0
|
299 d.hasKnownExtents = false;
|
Chris@0
|
300 m_cepOutput = n++;
|
Chris@0
|
301 outputs.push_back(d);
|
Chris@0
|
302
|
Chris@0
|
303 d.identifier = "am";
|
Chris@5
|
304 d.name = "Cepstrum bins relative to RMS";
|
Chris@5
|
305 d.description = "The cepstrum bins within the specified range, expressed as a value relative to the root mean square bin value in the range, with values below the RMS clamped to zero";
|
Chris@0
|
306 m_amOutput = n++;
|
Chris@0
|
307 outputs.push_back(d);
|
Chris@0
|
308
|
Chris@2
|
309 d.identifier = "env";
|
Chris@2
|
310 d.name = "Spectral envelope";
|
Chris@2
|
311 d.description = "Envelope calculated from the cepstral values below the specified minimum";
|
Chris@2
|
312 d.binCount = m_blockSize/2 + 1;
|
Chris@2
|
313 d.binNames.clear();
|
Chris@2
|
314 for (int i = 0; i < d.binCount; ++i) {
|
Chris@2
|
315 float freq = (m_inputSampleRate / m_blockSize) * i;
|
Chris@5
|
316 char buffer[20];
|
Chris@2
|
317 sprintf(buffer, "%.2f Hz", freq);
|
Chris@2
|
318 d.binNames.push_back(buffer);
|
Chris@2
|
319 }
|
Chris@2
|
320 m_envOutput = n++;
|
Chris@2
|
321 outputs.push_back(d);
|
Chris@2
|
322
|
Chris@2
|
323 d.identifier = "es";
|
Chris@2
|
324 d.name = "Spectrum without envelope";
|
Chris@2
|
325 d.description = "Magnitude of spectrum values divided by calculated envelope values, to deconvolve the envelope";
|
Chris@2
|
326 m_esOutput = n++;
|
Chris@2
|
327 outputs.push_back(d);
|
Chris@2
|
328
|
Chris@0
|
329 return outputs;
|
Chris@0
|
330 }
|
Chris@0
|
331
|
Chris@0
|
332 bool
|
Chris@0
|
333 SimpleCepstrum::initialise(size_t channels, size_t stepSize, size_t blockSize)
|
Chris@0
|
334 {
|
Chris@0
|
335 if (channels < getMinChannelCount() ||
|
Chris@0
|
336 channels > getMaxChannelCount()) return false;
|
Chris@0
|
337
|
Chris@0
|
338 // std::cerr << "SimpleCepstrum::initialise: channels = " << channels
|
Chris@0
|
339 // << ", stepSize = " << stepSize << ", blockSize = " << blockSize
|
Chris@0
|
340 // << std::endl;
|
Chris@0
|
341
|
Chris@0
|
342 m_channels = channels;
|
Chris@0
|
343 m_stepSize = stepSize;
|
Chris@0
|
344 m_blockSize = blockSize;
|
Chris@0
|
345
|
Chris@5
|
346 m_binFrom = int(m_inputSampleRate / m_fmax);
|
Chris@5
|
347 m_binTo = int(m_inputSampleRate / m_fmin);
|
Chris@5
|
348
|
Chris@5
|
349 if (m_binTo >= m_blockSize / 2) {
|
Chris@5
|
350 m_binTo = m_blockSize / 2 - 1;
|
Chris@5
|
351 }
|
Chris@5
|
352
|
Chris@5
|
353 m_bins = (m_binTo - m_binFrom) + 1;
|
Chris@5
|
354
|
Chris@5
|
355 m_history = new double *[m_histlen];
|
Chris@5
|
356 for (int i = 0; i < m_histlen; ++i) {
|
Chris@5
|
357 m_history[i] = new double[m_bins];
|
Chris@5
|
358 }
|
Chris@5
|
359
|
Chris@5
|
360 reset();
|
Chris@5
|
361
|
Chris@0
|
362 return true;
|
Chris@0
|
363 }
|
Chris@0
|
364
|
Chris@0
|
365 void
|
Chris@0
|
366 SimpleCepstrum::reset()
|
Chris@0
|
367 {
|
Chris@5
|
368 for (int i = 0; i < m_histlen; ++i) {
|
Chris@5
|
369 for (int j = 0; j < m_bins; ++j) {
|
Chris@5
|
370 m_history[i][j] = 0.0;
|
Chris@5
|
371 }
|
Chris@5
|
372 }
|
Chris@5
|
373 }
|
Chris@5
|
374
|
Chris@5
|
375 void
|
Chris@5
|
376 SimpleCepstrum::filter(const double *cep, double *result)
|
Chris@5
|
377 {
|
Chris@5
|
378 int hix = m_histlen - 1; // current history index
|
Chris@5
|
379
|
Chris@5
|
380 // roll back the history
|
Chris@5
|
381 if (m_histlen > 1) {
|
Chris@5
|
382 double *oldest = m_history[0];
|
Chris@5
|
383 for (int i = 1; i < m_histlen; ++i) {
|
Chris@5
|
384 m_history[i-1] = m_history[i];
|
Chris@5
|
385 }
|
Chris@5
|
386 // and stick this back in the newest spot, to recycle
|
Chris@5
|
387 m_history[hix] = oldest;
|
Chris@5
|
388 }
|
Chris@5
|
389
|
Chris@5
|
390 for (int i = 0; i < m_bins; ++i) {
|
Chris@5
|
391 m_history[hix][i] = cep[i + m_binFrom];
|
Chris@5
|
392 }
|
Chris@5
|
393
|
Chris@5
|
394 for (int i = 0; i < m_bins; ++i) {
|
Chris@5
|
395 double mean = 0.0;
|
Chris@5
|
396 for (int j = 0; j < m_histlen; ++j) {
|
Chris@5
|
397 mean += m_history[j][i];
|
Chris@5
|
398 }
|
Chris@5
|
399 mean /= m_histlen;
|
Chris@5
|
400 result[i] = mean;
|
Chris@5
|
401 }
|
Chris@5
|
402 }
|
Chris@5
|
403
|
Chris@5
|
404 void
|
Chris@5
|
405 SimpleCepstrum::addStatisticalOutputs(FeatureSet &fs, const double *data)
|
Chris@5
|
406 {
|
Chris@5
|
407 int n = m_bins;
|
Chris@5
|
408
|
Chris@5
|
409 double maxval = 0.f;
|
Chris@5
|
410 int maxbin = 0;
|
Chris@5
|
411
|
Chris@5
|
412 for (int i = 0; i < n; ++i) {
|
Chris@5
|
413 if (data[i] > maxval) {
|
Chris@5
|
414 maxval = data[i];
|
Chris@5
|
415 maxbin = i + m_binFrom;
|
Chris@5
|
416 }
|
Chris@5
|
417 }
|
Chris@5
|
418
|
Chris@5
|
419 Feature rf;
|
Chris@5
|
420 if (maxbin > 0) {
|
Chris@5
|
421 rf.values.push_back(m_inputSampleRate / maxbin);
|
Chris@5
|
422 } else {
|
Chris@5
|
423 rf.values.push_back(0);
|
Chris@5
|
424 }
|
Chris@5
|
425 fs[m_pkOutput].push_back(rf);
|
Chris@5
|
426
|
Chris@5
|
427 double mean = 0;
|
Chris@5
|
428 for (int i = 0; i < n; ++i) {
|
Chris@5
|
429 mean += data[i];
|
Chris@5
|
430 }
|
Chris@5
|
431 mean /= n;
|
Chris@5
|
432
|
Chris@5
|
433 double rms = 0;
|
Chris@5
|
434 for (int i = 0; i < n; ++i) {
|
Chris@5
|
435 rms += data[i] * data[i];
|
Chris@5
|
436 }
|
Chris@5
|
437 rms = sqrt(rms / n);
|
Chris@5
|
438
|
Chris@5
|
439 double variance = 0;
|
Chris@5
|
440 for (int i = 0; i < n; ++i) {
|
Chris@5
|
441 double dev = fabs(data[i] - mean);
|
Chris@5
|
442 variance += dev * dev;
|
Chris@5
|
443 }
|
Chris@5
|
444 variance /= n;
|
Chris@5
|
445
|
Chris@5
|
446 Feature vf;
|
Chris@5
|
447 vf.values.push_back(variance);
|
Chris@5
|
448 fs[m_varOutput].push_back(vf);
|
Chris@5
|
449
|
Chris@5
|
450 Feature pf;
|
Chris@5
|
451 pf.values.push_back(maxval - mean);
|
Chris@5
|
452 fs[m_p2mOutput].push_back(pf);
|
Chris@5
|
453
|
Chris@5
|
454 Feature pr;
|
Chris@5
|
455 pr.values.push_back(maxval - rms);
|
Chris@5
|
456 fs[m_p2rOutput].push_back(pr);
|
Chris@5
|
457
|
Chris@5
|
458 Feature pv;
|
Chris@5
|
459 pv.values.push_back(maxval);
|
Chris@5
|
460 fs[m_pvOutput].push_back(pv);
|
Chris@5
|
461
|
Chris@5
|
462 Feature am;
|
Chris@5
|
463 for (int i = 0; i < n; ++i) {
|
Chris@5
|
464 if (data[i] < rms) am.values.push_back(0);
|
Chris@5
|
465 else am.values.push_back(data[i] - rms);
|
Chris@5
|
466 }
|
Chris@5
|
467 fs[m_amOutput].push_back(am);
|
Chris@5
|
468 }
|
Chris@5
|
469
|
Chris@5
|
470 void
|
Chris@5
|
471 SimpleCepstrum::addEnvelopeOutputs(FeatureSet &fs, const float *const *inputBuffers, const double *cep)
|
Chris@5
|
472 {
|
Chris@5
|
473 // Wipe the higher cepstral bins in order to calculate the
|
Chris@5
|
474 // envelope. This calculation uses the raw cepstrum, not the
|
Chris@5
|
475 // filtered values (because only values "in frequency range" are
|
Chris@5
|
476 // filtered).
|
Chris@5
|
477 int bs = m_blockSize;
|
Chris@5
|
478 int hs = m_blockSize/2 + 1;
|
Chris@5
|
479
|
Chris@5
|
480 double *ecep = new double[bs];
|
Chris@5
|
481 for (int i = 0; i < m_binFrom; ++i) {
|
Chris@5
|
482 ecep[i] = cep[i] / bs;
|
Chris@5
|
483 }
|
Chris@5
|
484 for (int i = m_binFrom; i < bs; ++i) {
|
Chris@5
|
485 ecep[i] = 0;
|
Chris@5
|
486 }
|
Chris@5
|
487 ecep[0] /= 2;
|
Chris@5
|
488 ecep[m_binFrom-1] /= 2;
|
Chris@5
|
489
|
Chris@5
|
490 double *env = new double[bs];
|
Chris@5
|
491 double *io = new double[bs];
|
Chris@5
|
492 fft(bs, false, ecep, 0, env, io);
|
Chris@5
|
493
|
Chris@5
|
494 for (int i = 0; i < hs; ++i) {
|
Chris@5
|
495 env[i] = exp(env[i]);
|
Chris@5
|
496 }
|
Chris@5
|
497 Feature envf;
|
Chris@5
|
498 for (int i = 0; i < hs; ++i) {
|
Chris@5
|
499 envf.values.push_back(env[i]);
|
Chris@5
|
500 }
|
Chris@5
|
501 fs[m_envOutput].push_back(envf);
|
Chris@5
|
502
|
Chris@5
|
503 Feature es;
|
Chris@5
|
504 for (int i = 0; i < hs; ++i) {
|
Chris@5
|
505 double re = inputBuffers[0][i*2 ] / env[i];
|
Chris@5
|
506 double im = inputBuffers[0][i*2+1] / env[i];
|
Chris@5
|
507 double mag = sqrt(re*re + im*im);
|
Chris@5
|
508 es.values.push_back(mag);
|
Chris@5
|
509 }
|
Chris@5
|
510 fs[m_esOutput].push_back(es);
|
Chris@5
|
511
|
Chris@5
|
512 delete[] env;
|
Chris@5
|
513 delete[] ecep;
|
Chris@5
|
514 delete[] io;
|
Chris@0
|
515 }
|
Chris@0
|
516
|
Chris@0
|
517 SimpleCepstrum::FeatureSet
|
Chris@0
|
518 SimpleCepstrum::process(const float *const *inputBuffers, Vamp::RealTime timestamp)
|
Chris@0
|
519 {
|
Chris@1
|
520 FeatureSet fs;
|
Chris@1
|
521
|
Chris@0
|
522 int bs = m_blockSize;
|
Chris@0
|
523 int hs = m_blockSize/2 + 1;
|
Chris@0
|
524
|
Chris@5
|
525 double *rawcep = new double[bs];
|
Chris@3
|
526 double *io = new double[bs];
|
Chris@3
|
527
|
Chris@4
|
528 if (m_method != InverseComplex) {
|
Chris@3
|
529
|
Chris@4
|
530 double *logmag = new double[bs];
|
Chris@4
|
531
|
Chris@4
|
532 for (int i = 0; i < hs; ++i) {
|
Chris@3
|
533
|
Chris@4
|
534 double power =
|
Chris@4
|
535 inputBuffers[0][i*2 ] * inputBuffers[0][i*2 ] +
|
Chris@4
|
536 inputBuffers[0][i*2+1] * inputBuffers[0][i*2+1];
|
Chris@5
|
537 double mag = sqrt(power);
|
Chris@3
|
538
|
Chris@5
|
539 double lm = log(mag + 0.00000001);
|
Chris@4
|
540
|
Chris@4
|
541 switch (m_method) {
|
Chris@4
|
542 case InverseSymmetric:
|
Chris@4
|
543 logmag[i] = lm;
|
Chris@4
|
544 if (i > 0) logmag[bs - i] = lm;
|
Chris@4
|
545 break;
|
Chris@4
|
546 case InverseAsymmetric:
|
Chris@4
|
547 logmag[i] = lm;
|
Chris@4
|
548 if (i > 0) logmag[bs - i] = 0;
|
Chris@4
|
549 break;
|
Chris@4
|
550 default:
|
Chris@4
|
551 logmag[bs/2 + i - 1] = lm;
|
Chris@4
|
552 if (i < hs-1) {
|
Chris@4
|
553 logmag[bs/2 - i - 1] = lm;
|
Chris@4
|
554 }
|
Chris@4
|
555 break;
|
Chris@3
|
556 }
|
Chris@3
|
557 }
|
Chris@4
|
558
|
Chris@4
|
559 if (m_method == InverseSymmetric ||
|
Chris@4
|
560 m_method == InverseAsymmetric) {
|
Chris@4
|
561
|
Chris@5
|
562 fft(bs, true, logmag, 0, rawcep, io);
|
Chris@4
|
563
|
Chris@4
|
564 } else {
|
Chris@4
|
565
|
Chris@5
|
566 fft(bs, false, logmag, 0, rawcep, io);
|
Chris@4
|
567
|
Chris@4
|
568 if (m_method == ForwardDifference) {
|
Chris@4
|
569 for (int i = 0; i < hs; ++i) {
|
Chris@5
|
570 rawcep[i] = fabs(io[i]) - fabs(rawcep[i]);
|
Chris@4
|
571 }
|
Chris@4
|
572 } else {
|
Chris@4
|
573 for (int i = 0; i < hs; ++i) {
|
Chris@5
|
574 rawcep[i] = sqrt(rawcep[i]*rawcep[i] + io[i]*io[i]);
|
Chris@4
|
575 }
|
Chris@4
|
576 }
|
Chris@4
|
577 }
|
Chris@4
|
578
|
Chris@4
|
579 delete[] logmag;
|
Chris@4
|
580
|
Chris@4
|
581 } else { // InverseComplex
|
Chris@4
|
582
|
Chris@4
|
583 double *ri = new double[bs];
|
Chris@4
|
584 double *ii = new double[bs];
|
Chris@4
|
585
|
Chris@4
|
586 for (int i = 0; i < hs; ++i) {
|
Chris@4
|
587 double re = inputBuffers[0][i*2];
|
Chris@4
|
588 double im = inputBuffers[0][i*2+1];
|
Chris@4
|
589 std::complex<double> c(re, im);
|
Chris@4
|
590 std::complex<double> clog = std::log(c);
|
Chris@4
|
591 ri[i] = clog.real();
|
Chris@4
|
592 ii[i] = clog.imag();
|
Chris@4
|
593 if (i > 0) {
|
Chris@4
|
594 ri[bs - i] = ri[i];
|
Chris@4
|
595 ii[bs - i] = -ii[i];
|
Chris@4
|
596 }
|
Chris@4
|
597 }
|
Chris@4
|
598
|
Chris@5
|
599 fft(bs, true, ri, ii, rawcep, io);
|
Chris@4
|
600
|
Chris@4
|
601 delete[] ri;
|
Chris@4
|
602 delete[] ii;
|
Chris@3
|
603 }
|
Chris@0
|
604
|
Chris@0
|
605 if (m_clamp) {
|
Chris@0
|
606 for (int i = 0; i < bs; ++i) {
|
Chris@5
|
607 if (rawcep[i] < 0) rawcep[i] = 0;
|
Chris@0
|
608 }
|
Chris@0
|
609 }
|
Chris@0
|
610
|
Chris@5
|
611 delete[] io;
|
Chris@0
|
612
|
Chris@5
|
613 double *latest = new double[m_bins];
|
Chris@5
|
614 filter(rawcep, latest);
|
Chris@5
|
615
|
Chris@5
|
616 int n = m_bins;
|
Chris@0
|
617
|
Chris@0
|
618 Feature cf;
|
Chris@5
|
619 for (int i = 0; i < n; ++i) {
|
Chris@5
|
620 cf.values.push_back(latest[i]);
|
Chris@0
|
621 }
|
Chris@0
|
622 fs[m_cepOutput].push_back(cf);
|
Chris@0
|
623
|
Chris@5
|
624 addStatisticalOutputs(fs, latest);
|
Chris@0
|
625
|
Chris@5
|
626 addEnvelopeOutputs(fs, inputBuffers, rawcep);
|
Chris@0
|
627
|
Chris@5
|
628 delete[] latest;
|
Chris@0
|
629
|
Chris@0
|
630 return fs;
|
Chris@0
|
631 }
|
Chris@0
|
632
|
Chris@0
|
633 SimpleCepstrum::FeatureSet
|
Chris@0
|
634 SimpleCepstrum::getRemainingFeatures()
|
Chris@0
|
635 {
|
Chris@0
|
636 FeatureSet fs;
|
Chris@0
|
637 return fs;
|
Chris@0
|
638 }
|
Chris@0
|
639
|
Chris@0
|
640 void
|
Chris@0
|
641 SimpleCepstrum::fft(unsigned int n, bool inverse,
|
Chris@0
|
642 double *ri, double *ii, double *ro, double *io)
|
Chris@0
|
643 {
|
Chris@0
|
644 if (!ri || !ro || !io) return;
|
Chris@0
|
645
|
Chris@0
|
646 unsigned int bits;
|
Chris@0
|
647 unsigned int i, j, k, m;
|
Chris@0
|
648 unsigned int blockSize, blockEnd;
|
Chris@0
|
649
|
Chris@0
|
650 double tr, ti;
|
Chris@0
|
651
|
Chris@0
|
652 if (n < 2) return;
|
Chris@0
|
653 if (n & (n-1)) return;
|
Chris@0
|
654
|
Chris@0
|
655 double angle = 2.0 * M_PI;
|
Chris@0
|
656 if (inverse) angle = -angle;
|
Chris@0
|
657
|
Chris@0
|
658 for (i = 0; ; ++i) {
|
Chris@0
|
659 if (n & (1 << i)) {
|
Chris@0
|
660 bits = i;
|
Chris@0
|
661 break;
|
Chris@0
|
662 }
|
Chris@0
|
663 }
|
Chris@0
|
664
|
Chris@0
|
665 static unsigned int tableSize = 0;
|
Chris@0
|
666 static int *table = 0;
|
Chris@0
|
667
|
Chris@0
|
668 if (tableSize != n) {
|
Chris@0
|
669
|
Chris@0
|
670 delete[] table;
|
Chris@0
|
671
|
Chris@0
|
672 table = new int[n];
|
Chris@0
|
673
|
Chris@0
|
674 for (i = 0; i < n; ++i) {
|
Chris@0
|
675
|
Chris@0
|
676 m = i;
|
Chris@0
|
677
|
Chris@0
|
678 for (j = k = 0; j < bits; ++j) {
|
Chris@0
|
679 k = (k << 1) | (m & 1);
|
Chris@0
|
680 m >>= 1;
|
Chris@0
|
681 }
|
Chris@0
|
682
|
Chris@0
|
683 table[i] = k;
|
Chris@0
|
684 }
|
Chris@0
|
685
|
Chris@0
|
686 tableSize = n;
|
Chris@0
|
687 }
|
Chris@0
|
688
|
Chris@0
|
689 if (ii) {
|
Chris@0
|
690 for (i = 0; i < n; ++i) {
|
Chris@0
|
691 ro[table[i]] = ri[i];
|
Chris@0
|
692 io[table[i]] = ii[i];
|
Chris@0
|
693 }
|
Chris@0
|
694 } else {
|
Chris@0
|
695 for (i = 0; i < n; ++i) {
|
Chris@0
|
696 ro[table[i]] = ri[i];
|
Chris@0
|
697 io[table[i]] = 0.0;
|
Chris@0
|
698 }
|
Chris@0
|
699 }
|
Chris@0
|
700
|
Chris@0
|
701 blockEnd = 1;
|
Chris@0
|
702
|
Chris@0
|
703 for (blockSize = 2; blockSize <= n; blockSize <<= 1) {
|
Chris@0
|
704
|
Chris@0
|
705 double delta = angle / (double)blockSize;
|
Chris@0
|
706 double sm2 = -sin(-2 * delta);
|
Chris@0
|
707 double sm1 = -sin(-delta);
|
Chris@0
|
708 double cm2 = cos(-2 * delta);
|
Chris@0
|
709 double cm1 = cos(-delta);
|
Chris@0
|
710 double w = 2 * cm1;
|
Chris@0
|
711 double ar[3], ai[3];
|
Chris@0
|
712
|
Chris@0
|
713 for (i = 0; i < n; i += blockSize) {
|
Chris@0
|
714
|
Chris@0
|
715 ar[2] = cm2;
|
Chris@0
|
716 ar[1] = cm1;
|
Chris@0
|
717
|
Chris@0
|
718 ai[2] = sm2;
|
Chris@0
|
719 ai[1] = sm1;
|
Chris@0
|
720
|
Chris@0
|
721 for (j = i, m = 0; m < blockEnd; j++, m++) {
|
Chris@0
|
722
|
Chris@0
|
723 ar[0] = w * ar[1] - ar[2];
|
Chris@0
|
724 ar[2] = ar[1];
|
Chris@0
|
725 ar[1] = ar[0];
|
Chris@0
|
726
|
Chris@0
|
727 ai[0] = w * ai[1] - ai[2];
|
Chris@0
|
728 ai[2] = ai[1];
|
Chris@0
|
729 ai[1] = ai[0];
|
Chris@0
|
730
|
Chris@0
|
731 k = j + blockEnd;
|
Chris@0
|
732 tr = ar[0] * ro[k] - ai[0] * io[k];
|
Chris@0
|
733 ti = ar[0] * io[k] + ai[0] * ro[k];
|
Chris@0
|
734
|
Chris@0
|
735 ro[k] = ro[j] - tr;
|
Chris@0
|
736 io[k] = io[j] - ti;
|
Chris@0
|
737
|
Chris@0
|
738 ro[j] += tr;
|
Chris@0
|
739 io[j] += ti;
|
Chris@0
|
740 }
|
Chris@0
|
741 }
|
Chris@0
|
742
|
Chris@0
|
743 blockEnd = blockSize;
|
Chris@0
|
744 }
|
Chris@0
|
745 }
|
Chris@0
|
746
|
Chris@0
|
747
|