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