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