Chris@23
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
matthiasm@0
|
2
|
Chris@35
|
3 /*
|
Chris@35
|
4 NNLS-Chroma / Chordino
|
Chris@35
|
5
|
Chris@35
|
6 Audio feature extraction plugins for chromagram and chord
|
Chris@35
|
7 estimation.
|
Chris@35
|
8
|
Chris@35
|
9 Centre for Digital Music, Queen Mary University of London.
|
Chris@35
|
10 This file copyright 2008-2010 Matthias Mauch and QMUL.
|
Chris@35
|
11
|
Chris@35
|
12 This program is free software; you can redistribute it and/or
|
Chris@35
|
13 modify it under the terms of the GNU General Public License as
|
Chris@35
|
14 published by the Free Software Foundation; either version 2 of the
|
Chris@35
|
15 License, or (at your option) any later version. See the file
|
Chris@35
|
16 COPYING included with this distribution for more information.
|
Chris@35
|
17 */
|
Chris@35
|
18
|
Chris@35
|
19 #include "NNLSBase.h"
|
Chris@27
|
20
|
Chris@27
|
21 #include "chromamethods.h"
|
Chris@27
|
22
|
Chris@27
|
23 #include <cstdlib>
|
Chris@27
|
24 #include <fstream>
|
matthiasm@0
|
25 #include <cmath>
|
matthiasm@9
|
26
|
Chris@27
|
27 #include <algorithm>
|
matthiasm@0
|
28
|
matthiasm@122
|
29 static bool debug_on = false;
|
matthiasm@0
|
30
|
Chris@35
|
31 NNLSBase::NNLSBase(float inputSampleRate) :
|
Chris@23
|
32 Plugin(inputSampleRate),
|
mail@89
|
33 m_frameCount(0),
|
Chris@35
|
34 m_logSpectrum(0),
|
Chris@23
|
35 m_blockSize(0),
|
Chris@23
|
36 m_stepSize(0),
|
Chris@23
|
37 m_lengthOfNoteIndex(0),
|
mail@80
|
38 m_meanTunings(0),
|
mail@80
|
39 m_localTunings(0),
|
mail@41
|
40 m_whitening(1.0),
|
Chris@23
|
41 m_preset(0.0),
|
matthiasm@92
|
42 m_useNNLS(1.0),
|
matthiasm@92
|
43 m_localTuning(0.0),
|
Chris@23
|
44 m_kernelValue(0),
|
Chris@23
|
45 m_kernelFftIndex(0),
|
Chris@23
|
46 m_kernelNoteIndex(0),
|
Chris@23
|
47 m_dict(0),
|
matthiasm@92
|
48 m_tuneLocal(0.0),
|
Chris@23
|
49 m_doNormalizeChroma(0),
|
matthiasm@92
|
50 m_rollon(0.0),
|
matthiasm@95
|
51 m_boostN(0.1),
|
matthiasm@42
|
52 m_s(0.7),
|
mail@115
|
53 m_harte_syntax(0),
|
mail@80
|
54 sinvalues(0),
|
mail@80
|
55 cosvalues(0)
|
matthiasm@0
|
56 {
|
Chris@35
|
57 if (debug_on) cerr << "--> NNLSBase" << endl;
|
Chris@23
|
58 // make the *note* dictionary matrix
|
Chris@23
|
59 m_dict = new float[nNote * 84];
|
matthiasm@122
|
60 for (int i = 0; i < nNote * 84; ++i) m_dict[i] = 0.0;
|
matthiasm@0
|
61 }
|
matthiasm@0
|
62
|
matthiasm@0
|
63
|
Chris@35
|
64 NNLSBase::~NNLSBase()
|
matthiasm@0
|
65 {
|
Chris@35
|
66 if (debug_on) cerr << "--> ~NNLSBase" << endl;
|
Chris@23
|
67 delete [] m_dict;
|
matthiasm@0
|
68 }
|
matthiasm@0
|
69
|
matthiasm@0
|
70 string
|
Chris@35
|
71 NNLSBase::getMaker() const
|
matthiasm@0
|
72 {
|
Chris@23
|
73 if (debug_on) cerr << "--> getMaker" << endl;
|
matthiasm@0
|
74 // Your name here
|
matthiasm@0
|
75 return "Matthias Mauch";
|
matthiasm@0
|
76 }
|
matthiasm@0
|
77
|
matthiasm@0
|
78 int
|
Chris@35
|
79 NNLSBase::getPluginVersion() const
|
matthiasm@0
|
80 {
|
Chris@23
|
81 if (debug_on) cerr << "--> getPluginVersion" << endl;
|
matthiasm@0
|
82 // Increment this each time you release a version that behaves
|
matthiasm@0
|
83 // differently from the previous one
|
mail@103
|
84 return 3;
|
matthiasm@0
|
85 }
|
matthiasm@0
|
86
|
matthiasm@0
|
87 string
|
Chris@35
|
88 NNLSBase::getCopyright() const
|
matthiasm@0
|
89 {
|
Chris@23
|
90 if (debug_on) cerr << "--> getCopyright" << endl;
|
matthiasm@0
|
91 // This function is not ideally named. It does not necessarily
|
matthiasm@0
|
92 // need to say who made the plugin -- getMaker does that -- but it
|
matthiasm@0
|
93 // should indicate the terms under which it is distributed. For
|
matthiasm@0
|
94 // example, "Copyright (year). All Rights Reserved", or "GPL"
|
Chris@35
|
95 return "GPL";
|
matthiasm@0
|
96 }
|
matthiasm@0
|
97
|
Chris@35
|
98 NNLSBase::InputDomain
|
Chris@35
|
99 NNLSBase::getInputDomain() const
|
matthiasm@0
|
100 {
|
Chris@23
|
101 if (debug_on) cerr << "--> getInputDomain" << endl;
|
matthiasm@0
|
102 return FrequencyDomain;
|
matthiasm@0
|
103 }
|
matthiasm@0
|
104
|
matthiasm@0
|
105 size_t
|
Chris@35
|
106 NNLSBase::getPreferredBlockSize() const
|
matthiasm@0
|
107 {
|
Chris@23
|
108 if (debug_on) cerr << "--> getPreferredBlockSize" << endl;
|
matthiasm@0
|
109 return 16384; // 0 means "I can handle any block size"
|
matthiasm@0
|
110 }
|
matthiasm@0
|
111
|
matthiasm@0
|
112 size_t
|
Chris@35
|
113 NNLSBase::getPreferredStepSize() const
|
matthiasm@0
|
114 {
|
Chris@23
|
115 if (debug_on) cerr << "--> getPreferredStepSize" << endl;
|
matthiasm@0
|
116 return 2048; // 0 means "anything sensible"; in practice this
|
Chris@23
|
117 // means the same as the block size for TimeDomain
|
Chris@23
|
118 // plugins, or half of it for FrequencyDomain plugins
|
matthiasm@0
|
119 }
|
matthiasm@0
|
120
|
matthiasm@0
|
121 size_t
|
Chris@35
|
122 NNLSBase::getMinChannelCount() const
|
matthiasm@0
|
123 {
|
Chris@23
|
124 if (debug_on) cerr << "--> getMinChannelCount" << endl;
|
matthiasm@0
|
125 return 1;
|
matthiasm@0
|
126 }
|
matthiasm@0
|
127
|
matthiasm@0
|
128 size_t
|
Chris@35
|
129 NNLSBase::getMaxChannelCount() const
|
matthiasm@0
|
130 {
|
Chris@23
|
131 if (debug_on) cerr << "--> getMaxChannelCount" << endl;
|
matthiasm@0
|
132 return 1;
|
matthiasm@0
|
133 }
|
matthiasm@0
|
134
|
Chris@35
|
135 NNLSBase::ParameterList
|
Chris@35
|
136 NNLSBase::getParameterDescriptors() const
|
matthiasm@0
|
137 {
|
Chris@23
|
138 if (debug_on) cerr << "--> getParameterDescriptors" << endl;
|
matthiasm@0
|
139 ParameterList list;
|
matthiasm@0
|
140
|
matthiasm@42
|
141 ParameterDescriptor d;
|
matthiasm@42
|
142 d.identifier = "useNNLS";
|
matthiasm@42
|
143 d.name = "use approximate transcription (NNLS)";
|
matthiasm@42
|
144 d.description = "Toggles approximate transcription (NNLS).";
|
matthiasm@42
|
145 d.unit = "";
|
matthiasm@42
|
146 d.minValue = 0.0;
|
matthiasm@42
|
147 d.maxValue = 1.0;
|
matthiasm@42
|
148 d.defaultValue = 1.0;
|
matthiasm@42
|
149 d.isQuantized = true;
|
matthiasm@42
|
150 d.quantizeStep = 1.0;
|
matthiasm@42
|
151 list.push_back(d);
|
matthiasm@42
|
152
|
mail@41
|
153 ParameterDescriptor d0;
|
mail@41
|
154 d0.identifier = "rollon";
|
mail@115
|
155 d0.name = "bass noise threshold";
|
mail@115
|
156 d0.description = "Consider the cumulative energy spectrum (from low to high frequencies). All bins below the first bin whose cumulative energy exceeds the quantile [bass noise threshold] x [total energy] will be set to 0. A threshold value of 0 means that no bins will be changed.";
|
matthiasm@59
|
157 d0.unit = "%";
|
mail@41
|
158 d0.minValue = 0;
|
matthiasm@59
|
159 d0.maxValue = 5;
|
mail@41
|
160 d0.defaultValue = 0;
|
matthiasm@48
|
161 d0.isQuantized = true;
|
matthiasm@59
|
162 d0.quantizeStep = 0.5;
|
mail@41
|
163 list.push_back(d0);
|
matthiasm@4
|
164
|
matthiasm@4
|
165 ParameterDescriptor d1;
|
matthiasm@4
|
166 d1.identifier = "tuningmode";
|
matthiasm@4
|
167 d1.name = "tuning mode";
|
matthiasm@4
|
168 d1.description = "Tuning can be performed locally or on the whole extraction segment. Local tuning is only advisable when the tuning is likely to change over the audio, for example in podcasts, or in a cappella singing.";
|
matthiasm@4
|
169 d1.unit = "";
|
matthiasm@4
|
170 d1.minValue = 0;
|
matthiasm@4
|
171 d1.maxValue = 1;
|
matthiasm@4
|
172 d1.defaultValue = 0;
|
matthiasm@4
|
173 d1.isQuantized = true;
|
matthiasm@4
|
174 d1.valueNames.push_back("global tuning");
|
matthiasm@4
|
175 d1.valueNames.push_back("local tuning");
|
matthiasm@4
|
176 d1.quantizeStep = 1.0;
|
matthiasm@4
|
177 list.push_back(d1);
|
matthiasm@4
|
178
|
mail@41
|
179 ParameterDescriptor d2;
|
mail@41
|
180 d2.identifier = "whitening";
|
mail@41
|
181 d2.name = "spectral whitening";
|
mail@41
|
182 d2.description = "Spectral whitening: no whitening - 0; whitening - 1.";
|
mail@41
|
183 d2.unit = "";
|
mail@41
|
184 d2.isQuantized = true;
|
mail@41
|
185 d2.minValue = 0.0;
|
mail@41
|
186 d2.maxValue = 1.0;
|
mail@41
|
187 d2.defaultValue = 1.0;
|
mail@41
|
188 d2.isQuantized = false;
|
mail@41
|
189 list.push_back(d2);
|
mail@41
|
190
|
mail@41
|
191 ParameterDescriptor d3;
|
mail@41
|
192 d3.identifier = "s";
|
mail@41
|
193 d3.name = "spectral shape";
|
mail@41
|
194 d3.description = "Determines how individual notes in the note dictionary look: higher values mean more dominant higher harmonics.";
|
mail@41
|
195 d3.unit = "";
|
mail@41
|
196 d3.minValue = 0.5;
|
mail@41
|
197 d3.maxValue = 0.9;
|
mail@41
|
198 d3.defaultValue = 0.7;
|
mail@41
|
199 d3.isQuantized = false;
|
mail@41
|
200 list.push_back(d3);
|
mail@41
|
201
|
Chris@23
|
202 ParameterDescriptor d4;
|
matthiasm@12
|
203 d4.identifier = "chromanormalize";
|
matthiasm@12
|
204 d4.name = "chroma normalization";
|
matthiasm@12
|
205 d4.description = "How shall the chroma vector be normalized?";
|
matthiasm@12
|
206 d4.unit = "";
|
matthiasm@12
|
207 d4.minValue = 0;
|
matthiasm@13
|
208 d4.maxValue = 3;
|
matthiasm@12
|
209 d4.defaultValue = 0;
|
matthiasm@12
|
210 d4.isQuantized = true;
|
matthiasm@13
|
211 d4.valueNames.push_back("none");
|
matthiasm@13
|
212 d4.valueNames.push_back("maximum norm");
|
Chris@23
|
213 d4.valueNames.push_back("L1 norm");
|
Chris@23
|
214 d4.valueNames.push_back("L2 norm");
|
matthiasm@12
|
215 d4.quantizeStep = 1.0;
|
matthiasm@12
|
216 list.push_back(d4);
|
matthiasm@4
|
217
|
matthiasm@0
|
218 return list;
|
matthiasm@0
|
219 }
|
matthiasm@0
|
220
|
matthiasm@0
|
221 float
|
Chris@35
|
222 NNLSBase::getParameter(string identifier) const
|
matthiasm@0
|
223 {
|
Chris@23
|
224 if (debug_on) cerr << "--> getParameter" << endl;
|
matthiasm@42
|
225 if (identifier == "useNNLS") {
|
matthiasm@42
|
226 return m_useNNLS;
|
matthiasm@0
|
227 }
|
matthiasm@0
|
228
|
mail@41
|
229 if (identifier == "whitening") {
|
mail@41
|
230 return m_whitening;
|
mail@41
|
231 }
|
mail@41
|
232
|
mail@41
|
233 if (identifier == "s") {
|
mail@41
|
234 return m_s;
|
matthiasm@0
|
235 }
|
matthiasm@17
|
236
|
Chris@23
|
237 if (identifier == "rollon") {
|
matthiasm@17
|
238 return m_rollon;
|
matthiasm@17
|
239 }
|
matthiasm@0
|
240
|
mail@89
|
241 if (identifier == "boostn") {
|
mail@89
|
242 return m_boostN;
|
mail@89
|
243 }
|
mail@89
|
244
|
matthiasm@0
|
245 if (identifier == "tuningmode") {
|
matthiasm@0
|
246 if (m_tuneLocal) {
|
matthiasm@0
|
247 return 1.0;
|
matthiasm@0
|
248 } else {
|
matthiasm@0
|
249 return 0.0;
|
matthiasm@0
|
250 }
|
matthiasm@0
|
251 }
|
Chris@23
|
252 if (identifier == "preset") {
|
Chris@23
|
253 return m_preset;
|
matthiasm@3
|
254 }
|
Chris@23
|
255 if (identifier == "chromanormalize") {
|
Chris@23
|
256 return m_doNormalizeChroma;
|
matthiasm@12
|
257 }
|
matthiasm@50
|
258
|
mail@112
|
259 if (identifier == "usehartesyntax") {
|
mail@115
|
260 return m_harte_syntax;
|
mail@112
|
261 }
|
mail@112
|
262
|
matthiasm@0
|
263 return 0;
|
matthiasm@0
|
264
|
matthiasm@0
|
265 }
|
matthiasm@0
|
266
|
matthiasm@0
|
267 void
|
Chris@35
|
268 NNLSBase::setParameter(string identifier, float value)
|
matthiasm@0
|
269 {
|
Chris@23
|
270 if (debug_on) cerr << "--> setParameter" << endl;
|
matthiasm@42
|
271 if (identifier == "useNNLS") {
|
matthiasm@42
|
272 m_useNNLS = (int) value;
|
matthiasm@0
|
273 }
|
matthiasm@0
|
274
|
mail@41
|
275 if (identifier == "whitening") {
|
mail@41
|
276 m_whitening = value;
|
matthiasm@0
|
277 }
|
matthiasm@0
|
278
|
mail@41
|
279 if (identifier == "s") {
|
mail@41
|
280 m_s = value;
|
mail@41
|
281 }
|
mail@41
|
282
|
mail@89
|
283 if (identifier == "boostn") {
|
mail@89
|
284 m_boostN = value;
|
mail@89
|
285 }
|
mail@89
|
286
|
matthiasm@0
|
287 if (identifier == "tuningmode") {
|
mail@60
|
288 // m_tuneLocal = (value > 0) ? true : false;
|
mail@60
|
289 m_tuneLocal = value;
|
matthiasm@0
|
290 // cerr << "m_tuneLocal :" << m_tuneLocal << endl;
|
matthiasm@0
|
291 }
|
matthiasm@42
|
292 // if (identifier == "preset") {
|
matthiasm@42
|
293 // m_preset = value;
|
matthiasm@42
|
294 // if (m_preset == 0.0) {
|
matthiasm@42
|
295 // m_tuneLocal = false;
|
matthiasm@42
|
296 // m_whitening = 1.0;
|
matthiasm@42
|
297 // m_dictID = 0.0;
|
matthiasm@42
|
298 // }
|
matthiasm@42
|
299 // if (m_preset == 1.0) {
|
matthiasm@42
|
300 // m_tuneLocal = false;
|
matthiasm@42
|
301 // m_whitening = 1.0;
|
matthiasm@42
|
302 // m_dictID = 1.0;
|
matthiasm@42
|
303 // }
|
matthiasm@42
|
304 // if (m_preset == 2.0) {
|
matthiasm@42
|
305 // m_tuneLocal = false;
|
matthiasm@42
|
306 // m_whitening = 0.7;
|
matthiasm@42
|
307 // m_dictID = 0.0;
|
matthiasm@42
|
308 // }
|
matthiasm@42
|
309 // }
|
Chris@23
|
310 if (identifier == "chromanormalize") {
|
Chris@23
|
311 m_doNormalizeChroma = value;
|
Chris@23
|
312 }
|
matthiasm@17
|
313
|
Chris@23
|
314 if (identifier == "rollon") {
|
Chris@23
|
315 m_rollon = value;
|
Chris@23
|
316 }
|
mail@112
|
317
|
mail@112
|
318 if (identifier == "usehartesyntax") {
|
mail@115
|
319 m_harte_syntax = value;
|
mail@112
|
320 }
|
matthiasm@0
|
321 }
|
matthiasm@0
|
322
|
Chris@35
|
323 NNLSBase::ProgramList
|
Chris@35
|
324 NNLSBase::getPrograms() const
|
matthiasm@0
|
325 {
|
Chris@23
|
326 if (debug_on) cerr << "--> getPrograms" << endl;
|
matthiasm@0
|
327 ProgramList list;
|
matthiasm@0
|
328
|
matthiasm@0
|
329 // If you have no programs, return an empty list (or simply don't
|
matthiasm@0
|
330 // implement this function or getCurrentProgram/selectProgram)
|
matthiasm@0
|
331
|
matthiasm@0
|
332 return list;
|
matthiasm@0
|
333 }
|
matthiasm@0
|
334
|
matthiasm@0
|
335 string
|
Chris@35
|
336 NNLSBase::getCurrentProgram() const
|
matthiasm@0
|
337 {
|
Chris@23
|
338 if (debug_on) cerr << "--> getCurrentProgram" << endl;
|
matthiasm@0
|
339 return ""; // no programs
|
matthiasm@0
|
340 }
|
matthiasm@0
|
341
|
matthiasm@0
|
342 void
|
Chris@35
|
343 NNLSBase::selectProgram(string name)
|
matthiasm@0
|
344 {
|
Chris@23
|
345 if (debug_on) cerr << "--> selectProgram" << endl;
|
matthiasm@0
|
346 }
|
matthiasm@0
|
347
|
matthiasm@0
|
348
|
matthiasm@0
|
349 bool
|
Chris@35
|
350 NNLSBase::initialise(size_t channels, size_t stepSize, size_t blockSize)
|
matthiasm@0
|
351 {
|
Chris@23
|
352 if (debug_on) {
|
Chris@23
|
353 cerr << "--> initialise";
|
Chris@23
|
354 }
|
matthiasm@1
|
355
|
mail@100
|
356 dictionaryMatrix(m_dict, m_s);
|
mail@100
|
357
|
mail@80
|
358 // make things for tuning estimation
|
mail@80
|
359 for (int iBPS = 0; iBPS < nBPS; ++iBPS) {
|
mail@80
|
360 sinvalues.push_back(sin(2*M_PI*(iBPS*1.0/nBPS)));
|
mail@80
|
361 cosvalues.push_back(cos(2*M_PI*(iBPS*1.0/nBPS)));
|
mail@80
|
362 }
|
mail@80
|
363
|
mail@80
|
364
|
mail@80
|
365 // make hamming window of length 1/2 octave
|
mail@76
|
366 int hamwinlength = nBPS * 6 + 1;
|
mail@76
|
367 float hamwinsum = 0;
|
mail@76
|
368 for (int i = 0; i < hamwinlength; ++i) {
|
mail@76
|
369 hw.push_back(0.54 - 0.46 * cos((2*M_PI*i)/(hamwinlength-1)));
|
mail@76
|
370 hamwinsum += 0.54 - 0.46 * cos((2*M_PI*i)/(hamwinlength-1));
|
mail@76
|
371 }
|
mail@77
|
372 for (int i = 0; i < hamwinlength; ++i) hw[i] = hw[i] / hamwinsum;
|
mail@80
|
373
|
mail@80
|
374
|
mail@80
|
375 // initialise the tuning
|
mail@80
|
376 for (int iBPS = 0; iBPS < nBPS; ++iBPS) {
|
mail@80
|
377 m_meanTunings.push_back(0);
|
mail@80
|
378 m_localTunings.push_back(0);
|
mail@80
|
379 }
|
mail@76
|
380
|
matthiasm@0
|
381 if (channels < getMinChannelCount() ||
|
matthiasm@0
|
382 channels > getMaxChannelCount()) return false;
|
matthiasm@0
|
383 m_blockSize = blockSize;
|
matthiasm@0
|
384 m_stepSize = stepSize;
|
Chris@35
|
385 m_frameCount = 0;
|
mail@77
|
386 int tempn = nNote * m_blockSize/2;
|
Chris@23
|
387 // cerr << "length of tempkernel : " << tempn << endl;
|
Chris@23
|
388 float *tempkernel;
|
matthiasm@1
|
389
|
Chris@23
|
390 tempkernel = new float[tempn];
|
matthiasm@1
|
391
|
Chris@23
|
392 logFreqMatrix(m_inputSampleRate, m_blockSize, tempkernel);
|
Chris@23
|
393 m_kernelValue.clear();
|
Chris@23
|
394 m_kernelFftIndex.clear();
|
Chris@23
|
395 m_kernelNoteIndex.clear();
|
Chris@23
|
396 int countNonzero = 0;
|
Chris@91
|
397 for (int iNote = 0; iNote < nNote; ++iNote) { // I don't know if this is wise: manually making a sparse matrix
|
matthiasm@122
|
398 for (int iFFT = 0; iFFT < static_cast<int>(blockSize/2); ++iFFT) {
|
Chris@23
|
399 if (tempkernel[iFFT + blockSize/2 * iNote] > 0) {
|
Chris@23
|
400 m_kernelValue.push_back(tempkernel[iFFT + blockSize/2 * iNote]);
|
Chris@23
|
401 if (tempkernel[iFFT + blockSize/2 * iNote] > 0) {
|
Chris@23
|
402 countNonzero++;
|
Chris@23
|
403 }
|
Chris@23
|
404 m_kernelFftIndex.push_back(iFFT);
|
Chris@23
|
405 m_kernelNoteIndex.push_back(iNote);
|
Chris@23
|
406 }
|
Chris@23
|
407 }
|
Chris@23
|
408 }
|
Chris@23
|
409 // cerr << "nonzero count : " << countNonzero << endl;
|
Chris@23
|
410 delete [] tempkernel;
|
Chris@35
|
411 /*
|
Chris@23
|
412 ofstream myfile;
|
Chris@23
|
413 myfile.open ("matrix.txt");
|
matthiasm@3
|
414 // myfile << "Writing this to a file.\n";
|
Chris@23
|
415 for (int i = 0; i < nNote * 84; ++i) {
|
Chris@23
|
416 myfile << m_dict[i] << endl;
|
Chris@23
|
417 }
|
matthiasm@3
|
418 myfile.close();
|
Chris@35
|
419 */
|
matthiasm@0
|
420 return true;
|
matthiasm@0
|
421 }
|
matthiasm@0
|
422
|
matthiasm@0
|
423 void
|
Chris@35
|
424 NNLSBase::reset()
|
matthiasm@0
|
425 {
|
Chris@23
|
426 if (debug_on) cerr << "--> reset";
|
matthiasm@4
|
427
|
matthiasm@0
|
428 // Clear buffers, reset stored values, etc
|
Chris@35
|
429 m_frameCount = 0;
|
matthiasm@42
|
430 // m_dictID = 0;
|
Chris@35
|
431 m_logSpectrum.clear();
|
mail@80
|
432 for (int iBPS = 0; iBPS < nBPS; ++iBPS) {
|
mail@80
|
433 m_meanTunings[iBPS] = 0;
|
mail@80
|
434 m_localTunings[iBPS] = 0;
|
mail@80
|
435 }
|
Chris@23
|
436 m_localTuning.clear();
|
matthiasm@0
|
437 }
|
matthiasm@0
|
438
|
Chris@35
|
439 void
|
Chris@35
|
440 NNLSBase::baseProcess(const float *const *inputBuffers, Vamp::RealTime timestamp)
|
matthiasm@0
|
441 {
|
Chris@35
|
442 m_frameCount++;
|
Chris@23
|
443 float *magnitude = new float[m_blockSize/2];
|
matthiasm@0
|
444
|
Chris@23
|
445 const float *fbuf = inputBuffers[0];
|
Chris@23
|
446 float energysum = 0;
|
Chris@23
|
447 // make magnitude
|
Chris@23
|
448 float maxmag = -10000;
|
matthiasm@122
|
449 for (int iBin = 0; iBin < static_cast<int>(m_blockSize/2); iBin++) {
|
Chris@23
|
450 magnitude[iBin] = sqrt(fbuf[2 * iBin] * fbuf[2 * iBin] +
|
matthiasm@93
|
451 fbuf[2 * iBin + 1] * fbuf[2 * iBin + 1]);
|
matthiasm@95
|
452 if (magnitude[iBin]>m_blockSize*1.0) magnitude[iBin] = m_blockSize; // a valid audio signal (between -1 and 1) should not be limited here.
|
Chris@23
|
453 if (maxmag < magnitude[iBin]) maxmag = magnitude[iBin];
|
Chris@23
|
454 if (m_rollon > 0) {
|
Chris@23
|
455 energysum += pow(magnitude[iBin],2);
|
Chris@23
|
456 }
|
Chris@23
|
457 }
|
matthiasm@14
|
458
|
Chris@23
|
459 float cumenergy = 0;
|
Chris@23
|
460 if (m_rollon > 0) {
|
matthiasm@122
|
461 for (int iBin = 2; iBin < static_cast<int>(m_blockSize/2); iBin++) {
|
Chris@23
|
462 cumenergy += pow(magnitude[iBin],2);
|
matthiasm@59
|
463 if (cumenergy < energysum * m_rollon / 100) magnitude[iBin-2] = 0;
|
Chris@23
|
464 else break;
|
Chris@23
|
465 }
|
Chris@23
|
466 }
|
matthiasm@17
|
467
|
Chris@23
|
468 if (maxmag < 2) {
|
Chris@23
|
469 // cerr << "timestamp " << timestamp << ": very low magnitude, setting magnitude to all zeros" << endl;
|
matthiasm@122
|
470 for (int iBin = 0; iBin < static_cast<int>(m_blockSize/2); iBin++) {
|
Chris@23
|
471 magnitude[iBin] = 0;
|
Chris@23
|
472 }
|
Chris@23
|
473 }
|
matthiasm@4
|
474
|
Chris@23
|
475 // note magnitude mapping using pre-calculated matrix
|
Chris@23
|
476 float *nm = new float[nNote]; // note magnitude
|
Chris@91
|
477 for (int iNote = 0; iNote < nNote; iNote++) {
|
Chris@23
|
478 nm[iNote] = 0; // initialise as 0
|
Chris@23
|
479 }
|
Chris@23
|
480 int binCount = 0;
|
Chris@23
|
481 for (vector<float>::iterator it = m_kernelValue.begin(); it != m_kernelValue.end(); ++it) {
|
Chris@23
|
482 // cerr << ".";
|
Chris@23
|
483 nm[m_kernelNoteIndex[binCount]] += magnitude[m_kernelFftIndex[binCount]] * m_kernelValue[binCount];
|
Chris@23
|
484 // cerr << m_kernelFftIndex[binCount] << " -- " << magnitude[m_kernelFftIndex[binCount]] << " -- "<< m_kernelValue[binCount] << endl;
|
Chris@23
|
485 binCount++;
|
Chris@23
|
486 }
|
Chris@23
|
487 // cerr << nm[20];
|
Chris@23
|
488 // cerr << endl;
|
matthiasm@0
|
489
|
matthiasm@0
|
490
|
Chris@35
|
491 float one_over_N = 1.0/m_frameCount;
|
matthiasm@0
|
492 // update means of complex tuning variables
|
mail@80
|
493 for (int iBPS = 0; iBPS < nBPS; ++iBPS) m_meanTunings[iBPS] *= float(m_frameCount-1)*one_over_N;
|
mail@80
|
494
|
mail@80
|
495 for (int iTone = 0; iTone < round(nNote*0.62/nBPS)*nBPS+1; iTone = iTone + nBPS) {
|
mail@80
|
496 for (int iBPS = 0; iBPS < nBPS; ++iBPS) m_meanTunings[iBPS] += nm[iTone + iBPS]*one_over_N;
|
Chris@23
|
497 float ratioOld = 0.997;
|
mail@80
|
498 for (int iBPS = 0; iBPS < nBPS; ++iBPS) {
|
mail@80
|
499 m_localTunings[iBPS] *= ratioOld;
|
mail@80
|
500 m_localTunings[iBPS] += nm[iTone + iBPS] * (1 - ratioOld);
|
mail@80
|
501 }
|
matthiasm@0
|
502 }
|
matthiasm@0
|
503 // if (m_tuneLocal) {
|
Chris@23
|
504 // local tuning
|
mail@80
|
505 // float localTuningImag = sinvalue * m_localTunings[1] - sinvalue * m_localTunings[2];
|
mail@80
|
506 // float localTuningReal = m_localTunings[0] + cosvalue * m_localTunings[1] + cosvalue * m_localTunings[2];
|
mail@80
|
507
|
mail@80
|
508 float localTuningImag = 0;
|
mail@80
|
509 float localTuningReal = 0;
|
mail@80
|
510 for (int iBPS = 0; iBPS < nBPS; ++iBPS) {
|
mail@80
|
511 localTuningReal += m_localTunings[iBPS] * cosvalues[iBPS];
|
mail@80
|
512 localTuningImag += m_localTunings[iBPS] * sinvalues[iBPS];
|
mail@80
|
513 }
|
mail@80
|
514
|
Chris@23
|
515 float normalisedtuning = atan2(localTuningImag, localTuningReal)/(2*M_PI);
|
Chris@23
|
516 m_localTuning.push_back(normalisedtuning);
|
matthiasm@0
|
517
|
Chris@23
|
518 Feature f1; // logfreqspec
|
Chris@23
|
519 f1.hasTimestamp = true;
|
matthiasm@0
|
520 f1.timestamp = timestamp;
|
Chris@91
|
521 for (int iNote = 0; iNote < nNote; iNote++) {
|
Chris@23
|
522 f1.values.push_back(nm[iNote]);
|
Chris@23
|
523 }
|
matthiasm@0
|
524
|
matthiasm@0
|
525 // deletes
|
matthiasm@0
|
526 delete[] magnitude;
|
matthiasm@0
|
527 delete[] nm;
|
matthiasm@0
|
528
|
Chris@35
|
529 m_logSpectrum.push_back(f1); // remember note magnitude
|
matthiasm@0
|
530 }
|
matthiasm@0
|
531
|