c@92
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
c@92
|
2
|
c@92
|
3 /*
|
c@92
|
4 QM Vamp Plugin Set
|
c@92
|
5
|
c@92
|
6 Centre for Digital Music, Queen Mary, University of London.
|
c@135
|
7
|
c@135
|
8 This program is free software; you can redistribute it and/or
|
c@135
|
9 modify it under the terms of the GNU General Public License as
|
c@135
|
10 published by the Free Software Foundation; either version 2 of the
|
c@135
|
11 License, or (at your option) any later version. See the file
|
c@135
|
12 COPYING included with this distribution for more information.
|
c@92
|
13 */
|
c@92
|
14
|
c@92
|
15 #include "AdaptiveSpectrogram.h"
|
c@92
|
16
|
c@92
|
17 #include <cstdlib>
|
c@133
|
18 #include <cstdio>
|
c@92
|
19 #include <cstring>
|
c@114
|
20 #include <cfloat>
|
c@92
|
21
|
c@92
|
22 #include <iostream>
|
c@92
|
23
|
c@92
|
24 #include <dsp/transforms/FFT.h>
|
c@156
|
25 #include <dsp/rateconversion/Decimator.h>
|
c@92
|
26
|
c@92
|
27 using std::string;
|
c@92
|
28 using std::vector;
|
c@92
|
29 using std::cerr;
|
c@92
|
30 using std::endl;
|
c@92
|
31
|
c@92
|
32 using Vamp::RealTime;
|
c@92
|
33
|
c@99
|
34 //#define DEBUG_VERBOSE 1
|
c@99
|
35
|
c@92
|
36 AdaptiveSpectrogram::AdaptiveSpectrogram(float inputSampleRate) :
|
c@92
|
37 Plugin(inputSampleRate),
|
c@104
|
38 m_w(8),
|
c@114
|
39 m_n(2),
|
c@114
|
40 m_coarse(false),
|
c@109
|
41 m_threaded(true),
|
c@156
|
42 m_decFactor(1),
|
c@178
|
43 m_buffer(0),
|
c@156
|
44 m_buflen(0),
|
c@178
|
45 m_decimator(0),
|
c@178
|
46 m_threadsInUse(false)
|
c@92
|
47 {
|
c@92
|
48 }
|
c@92
|
49
|
c@92
|
50 AdaptiveSpectrogram::~AdaptiveSpectrogram()
|
c@92
|
51 {
|
c@178
|
52 for (int i = 0; i < int(m_cutThreads.size()); ++i) {
|
c@104
|
53 delete m_cutThreads[i];
|
c@104
|
54 }
|
c@104
|
55 m_cutThreads.clear();
|
c@105
|
56
|
c@110
|
57 for (FFTMap::iterator i = m_fftThreads.begin();
|
c@110
|
58 i != m_fftThreads.end(); ++i) {
|
c@106
|
59 delete i->second;
|
c@105
|
60 }
|
c@105
|
61 m_fftThreads.clear();
|
c@156
|
62
|
c@156
|
63 delete[] m_buffer;
|
c@156
|
64 delete m_decimator;
|
c@92
|
65 }
|
c@92
|
66
|
c@92
|
67 string
|
c@92
|
68 AdaptiveSpectrogram::getIdentifier() const
|
c@92
|
69 {
|
c@93
|
70 return "qm-adaptivespectrogram";
|
c@92
|
71 }
|
c@92
|
72
|
c@92
|
73 string
|
c@92
|
74 AdaptiveSpectrogram::getName() const
|
c@92
|
75 {
|
c@92
|
76 return "Adaptive Spectrogram";
|
c@92
|
77 }
|
c@92
|
78
|
c@92
|
79 string
|
c@92
|
80 AdaptiveSpectrogram::getDescription() const
|
c@92
|
81 {
|
c@92
|
82 return "Produce an adaptive spectrogram by adaptive selection from spectrograms at multiple resolutions";
|
c@92
|
83 }
|
c@92
|
84
|
c@92
|
85 string
|
c@92
|
86 AdaptiveSpectrogram::getMaker() const
|
c@92
|
87 {
|
c@92
|
88 return "Queen Mary, University of London";
|
c@92
|
89 }
|
c@92
|
90
|
c@92
|
91 int
|
c@92
|
92 AdaptiveSpectrogram::getPluginVersion() const
|
c@92
|
93 {
|
c@156
|
94 return 2;
|
c@92
|
95 }
|
c@92
|
96
|
c@92
|
97 string
|
c@92
|
98 AdaptiveSpectrogram::getCopyright() const
|
c@92
|
99 {
|
c@92
|
100 return "Plugin by Wen Xue and Chris Cannam. Copyright (c) 2009 Wen Xue and QMUL - All Rights Reserved";
|
c@92
|
101 }
|
c@92
|
102
|
c@92
|
103 size_t
|
c@92
|
104 AdaptiveSpectrogram::getPreferredStepSize() const
|
c@92
|
105 {
|
c@156
|
106 return getPreferredBlockSize();
|
c@92
|
107 }
|
c@92
|
108
|
c@92
|
109 size_t
|
c@92
|
110 AdaptiveSpectrogram::getPreferredBlockSize() const
|
c@92
|
111 {
|
c@156
|
112 // the /2 at the end is for 50% overlap (we handle framing ourselves)
|
c@156
|
113 return ((2 << m_w) << m_n) * m_decFactor / 2;
|
c@92
|
114 }
|
c@92
|
115
|
c@92
|
116 bool
|
c@92
|
117 AdaptiveSpectrogram::initialise(size_t channels, size_t stepSize, size_t blockSize)
|
c@92
|
118 {
|
c@92
|
119 if (channels < getMinChannelCount() ||
|
c@92
|
120 channels > getMaxChannelCount()) return false;
|
c@92
|
121
|
c@156
|
122 if (blockSize != getPreferredBlockSize()) {
|
c@156
|
123 std::cerr << "AdaptiveSpectrogram::initialise: Block size " << blockSize << " does not match required block size of " << getPreferredBlockSize() << std::endl;
|
c@156
|
124 return false;
|
c@156
|
125 }
|
c@156
|
126 if (stepSize != getPreferredStepSize()) {
|
c@156
|
127 std::cerr << "AdaptiveSpectrogram::initialise: Step size " << stepSize << " does not match required step size of " << getPreferredStepSize() << std::endl;
|
c@156
|
128 return false;
|
c@156
|
129 }
|
c@156
|
130
|
c@156
|
131 if (m_decFactor > 1) {
|
c@156
|
132 m_decimator = new Decimator(blockSize, m_decFactor);
|
c@156
|
133 }
|
c@156
|
134
|
c@156
|
135 m_buflen = (blockSize * 2) / m_decFactor; // *2 for 50% overlap
|
c@156
|
136 m_buffer = new float[m_buflen];
|
c@156
|
137
|
c@156
|
138 reset();
|
c@156
|
139
|
c@92
|
140 return true;
|
c@92
|
141 }
|
c@92
|
142
|
c@92
|
143 void
|
c@92
|
144 AdaptiveSpectrogram::reset()
|
c@92
|
145 {
|
c@156
|
146 if (m_decimator) m_decimator->resetFilter();
|
c@156
|
147 for (int i = 0; i < m_buflen; ++i) m_buffer[i] = 0.f;
|
c@92
|
148 }
|
c@92
|
149
|
c@92
|
150 AdaptiveSpectrogram::ParameterList
|
c@92
|
151 AdaptiveSpectrogram::getParameterDescriptors() const
|
c@92
|
152 {
|
c@92
|
153 ParameterList list;
|
c@92
|
154
|
c@92
|
155 ParameterDescriptor desc;
|
c@92
|
156 desc.identifier = "n";
|
c@92
|
157 desc.name = "Number of resolutions";
|
c@114
|
158 desc.description = "Number of consecutive powers of two in the range to be used as spectrogram resolutions, starting with the minimum resolution specified";
|
c@92
|
159 desc.unit = "";
|
c@114
|
160 desc.minValue = 2;
|
c@92
|
161 desc.maxValue = 10;
|
c@114
|
162 desc.defaultValue = 3;
|
c@92
|
163 desc.isQuantized = true;
|
c@92
|
164 desc.quantizeStep = 1;
|
c@92
|
165 list.push_back(desc);
|
c@92
|
166
|
c@92
|
167 ParameterDescriptor desc2;
|
c@92
|
168 desc2.identifier = "w";
|
c@92
|
169 desc2.name = "Smallest resolution";
|
c@92
|
170 desc2.description = "Smallest of the consecutive powers of two to use as spectrogram resolutions";
|
c@92
|
171 desc2.unit = "";
|
c@92
|
172 desc2.minValue = 1;
|
c@92
|
173 desc2.maxValue = 14;
|
c@104
|
174 desc2.defaultValue = 9;
|
c@92
|
175 desc2.isQuantized = true;
|
c@92
|
176 desc2.quantizeStep = 1;
|
c@92
|
177 // I am so lazy
|
c@92
|
178 desc2.valueNames.push_back("2");
|
c@92
|
179 desc2.valueNames.push_back("4");
|
c@92
|
180 desc2.valueNames.push_back("8");
|
c@92
|
181 desc2.valueNames.push_back("16");
|
c@92
|
182 desc2.valueNames.push_back("32");
|
c@92
|
183 desc2.valueNames.push_back("64");
|
c@92
|
184 desc2.valueNames.push_back("128");
|
c@92
|
185 desc2.valueNames.push_back("256");
|
c@92
|
186 desc2.valueNames.push_back("512");
|
c@92
|
187 desc2.valueNames.push_back("1024");
|
c@92
|
188 desc2.valueNames.push_back("2048");
|
c@92
|
189 desc2.valueNames.push_back("4096");
|
c@92
|
190 desc2.valueNames.push_back("8192");
|
c@92
|
191 desc2.valueNames.push_back("16384");
|
c@92
|
192 list.push_back(desc2);
|
c@92
|
193
|
c@156
|
194 desc2.identifier = "dec";
|
c@156
|
195 desc2.name = "Decimation factor";
|
c@156
|
196 desc2.description = "Factor to down-sample by, increasing speed but lowering maximum frequency";
|
c@156
|
197 desc2.unit = "";
|
c@156
|
198 desc2.minValue = 0;
|
c@156
|
199 desc2.maxValue = 3;
|
c@156
|
200 desc2.defaultValue = 0;
|
c@156
|
201 desc2.isQuantized = true;
|
c@156
|
202 desc2.quantizeStep = 1;
|
c@156
|
203 desc2.valueNames.clear();
|
c@156
|
204 desc2.valueNames.push_back("No decimation");
|
c@156
|
205 desc2.valueNames.push_back("2");
|
c@156
|
206 desc2.valueNames.push_back("4");
|
c@156
|
207 desc2.valueNames.push_back("8");
|
c@156
|
208 list.push_back(desc2);
|
c@156
|
209
|
c@109
|
210 ParameterDescriptor desc3;
|
c@114
|
211 desc3.identifier = "coarse";
|
c@114
|
212 desc3.name = "Omit alternate resolutions";
|
c@114
|
213 desc3.description = "Generate a coarser spectrogram faster by excluding every alternate resolution (first and last resolution are always retained)";
|
c@114
|
214 desc3.unit = "";
|
c@114
|
215 desc3.minValue = 0;
|
c@114
|
216 desc3.maxValue = 1;
|
c@114
|
217 desc3.defaultValue = 0;
|
c@114
|
218 desc3.isQuantized = true;
|
c@114
|
219 desc3.quantizeStep = 1;
|
c@114
|
220 list.push_back(desc3);
|
c@114
|
221
|
c@109
|
222 desc3.identifier = "threaded";
|
c@109
|
223 desc3.name = "Multi-threaded processing";
|
c@110
|
224 desc3.description = "Perform calculations using several threads in parallel";
|
c@109
|
225 desc3.unit = "";
|
c@109
|
226 desc3.minValue = 0;
|
c@109
|
227 desc3.maxValue = 1;
|
c@109
|
228 desc3.defaultValue = 1;
|
c@109
|
229 desc3.isQuantized = true;
|
c@109
|
230 desc3.quantizeStep = 1;
|
c@109
|
231 list.push_back(desc3);
|
c@109
|
232
|
c@92
|
233 return list;
|
c@92
|
234 }
|
c@92
|
235
|
c@92
|
236 float
|
c@92
|
237 AdaptiveSpectrogram::getParameter(std::string id) const
|
c@92
|
238 {
|
c@92
|
239 if (id == "n") return m_n+1;
|
c@92
|
240 else if (id == "w") return m_w+1;
|
c@109
|
241 else if (id == "threaded") return (m_threaded ? 1 : 0);
|
c@114
|
242 else if (id == "coarse") return (m_coarse ? 1 : 0);
|
c@156
|
243 else if (id == "dec") {
|
c@156
|
244 int f = m_decFactor;
|
c@156
|
245 int p = 0;
|
c@156
|
246 while (f > 1) {
|
c@156
|
247 f >>= 1;
|
c@156
|
248 p += 1;
|
c@156
|
249 }
|
c@156
|
250 return p;
|
c@156
|
251 }
|
c@92
|
252 return 0.f;
|
c@92
|
253 }
|
c@92
|
254
|
c@92
|
255 void
|
c@92
|
256 AdaptiveSpectrogram::setParameter(std::string id, float value)
|
c@92
|
257 {
|
c@92
|
258 if (id == "n") {
|
c@92
|
259 int n = lrintf(value);
|
c@92
|
260 if (n >= 1 && n <= 10) m_n = n-1;
|
c@92
|
261 } else if (id == "w") {
|
c@92
|
262 int w = lrintf(value);
|
c@92
|
263 if (w >= 1 && w <= 14) m_w = w-1;
|
c@109
|
264 } else if (id == "threaded") {
|
c@109
|
265 m_threaded = (value > 0.5);
|
c@114
|
266 } else if (id == "coarse") {
|
c@114
|
267 m_coarse = (value > 0.5);
|
c@156
|
268 } else if (id == "dec") {
|
c@156
|
269 int p = lrintf(value);
|
c@156
|
270 if (p >= 0 && p <= 3) m_decFactor = 1 << p;
|
c@109
|
271 }
|
c@92
|
272 }
|
c@92
|
273
|
c@92
|
274 AdaptiveSpectrogram::OutputList
|
c@92
|
275 AdaptiveSpectrogram::getOutputDescriptors() const
|
c@92
|
276 {
|
c@92
|
277 OutputList list;
|
c@92
|
278
|
c@92
|
279 OutputDescriptor d;
|
c@92
|
280 d.identifier = "output";
|
c@92
|
281 d.name = "Output";
|
c@92
|
282 d.description = "The output of the plugin";
|
c@92
|
283 d.unit = "";
|
c@92
|
284 d.hasFixedBinCount = true;
|
c@156
|
285 d.binCount = getPreferredBlockSize() / (m_decFactor * 2);
|
c@92
|
286 d.hasKnownExtents = false;
|
c@92
|
287 d.isQuantized = false;
|
c@92
|
288 d.sampleType = OutputDescriptor::FixedSampleRate;
|
c@156
|
289 d.sampleRate = m_inputSampleRate / (m_decFactor * ((2 << m_w) / 2));
|
c@92
|
290 d.hasDuration = false;
|
c@112
|
291 char name[20];
|
c@178
|
292 for (int i = 0; i < int(d.binCount); ++i) {
|
c@156
|
293 float freq = (m_inputSampleRate / (m_decFactor * (d.binCount * 2)) * (i + 1)); // no DC bin
|
c@157
|
294 sprintf(name, "%.1f Hz", freq);
|
c@112
|
295 d.binNames.push_back(name);
|
c@112
|
296 }
|
c@92
|
297 list.push_back(d);
|
c@92
|
298
|
c@92
|
299 return list;
|
c@92
|
300 }
|
c@92
|
301
|
c@92
|
302 AdaptiveSpectrogram::FeatureSet
|
c@92
|
303 AdaptiveSpectrogram::getRemainingFeatures()
|
c@92
|
304 {
|
c@92
|
305 FeatureSet fs;
|
c@92
|
306 return fs;
|
c@92
|
307 }
|
c@92
|
308
|
c@100
|
309 AdaptiveSpectrogram::FeatureSet
|
c@178
|
310 AdaptiveSpectrogram::process(const float *const *inputBuffers, RealTime)
|
c@100
|
311 {
|
c@156
|
312 // framing: shift and write the new data to right half
|
c@156
|
313 for (int i = 0; i < m_buflen/2; ++i) {
|
c@156
|
314 m_buffer[i] = m_buffer[m_buflen/2 + i];
|
c@156
|
315 }
|
c@156
|
316
|
c@156
|
317 if (m_decFactor == 1) {
|
c@156
|
318 for (int i = 0; i < m_buflen/2; ++i) {
|
c@156
|
319 m_buffer[m_buflen/2 + i] = inputBuffers[0][i];
|
c@156
|
320 }
|
c@156
|
321 } else {
|
c@156
|
322 m_decimator->process(inputBuffers[0], m_buffer + m_buflen/2);
|
c@156
|
323 }
|
c@156
|
324
|
c@100
|
325 FeatureSet fs;
|
c@100
|
326
|
c@100
|
327 int minwid = (2 << m_w), maxwid = ((2 << m_w) << m_n);
|
c@100
|
328
|
c@101
|
329 #ifdef DEBUG_VERBOSE
|
c@100
|
330 cerr << "widths from " << minwid << " to " << maxwid << " ("
|
c@100
|
331 << minwid/2 << " to " << maxwid/2 << " in real parts)" << endl;
|
c@101
|
332 #endif
|
c@100
|
333
|
c@100
|
334 Spectrograms s(minwid/2, maxwid/2, 1);
|
c@100
|
335
|
c@100
|
336 int w = minwid;
|
c@100
|
337 int index = 0;
|
c@100
|
338
|
c@100
|
339 while (w <= maxwid) {
|
c@114
|
340
|
c@114
|
341 if (!isResolutionWanted(s, w/2)) {
|
c@114
|
342 w *= 2;
|
c@114
|
343 ++index;
|
c@114
|
344 continue;
|
c@114
|
345 }
|
c@114
|
346
|
c@106
|
347 if (m_fftThreads.find(w) == m_fftThreads.end()) {
|
c@106
|
348 m_fftThreads[w] = new FFTThread(w);
|
c@106
|
349 }
|
c@109
|
350 if (m_threaded) {
|
c@156
|
351 m_fftThreads[w]->startCalculation(m_buffer, s, index, maxwid);
|
c@109
|
352 } else {
|
c@156
|
353 m_fftThreads[w]->setParameters(m_buffer, s, index, maxwid);
|
c@109
|
354 m_fftThreads[w]->performTask();
|
c@109
|
355 }
|
c@100
|
356 w *= 2;
|
c@100
|
357 ++index;
|
c@100
|
358 }
|
c@100
|
359
|
c@109
|
360 if (m_threaded) {
|
c@109
|
361 w = minwid;
|
c@114
|
362 index = 0;
|
c@109
|
363 while (w <= maxwid) {
|
c@114
|
364 if (!isResolutionWanted(s, w/2)) {
|
c@114
|
365 w *= 2;
|
c@114
|
366 ++index;
|
c@114
|
367 continue;
|
c@114
|
368 }
|
c@109
|
369 m_fftThreads[w]->await();
|
c@109
|
370 w *= 2;
|
c@114
|
371 ++index;
|
c@109
|
372 }
|
c@105
|
373 }
|
c@102
|
374
|
c@109
|
375 m_threadsInUse = false;
|
c@104
|
376
|
c@114
|
377 // std::cerr << "maxwid/2 = " << maxwid/2 << ", minwid/2 = " << minwid/2 << ", n+1 = " << m_n+1 << ", 2^(n+1) = " << (2<<m_n) << std::endl;
|
c@110
|
378
|
c@114
|
379 int cutwid = maxwid/2;
|
c@114
|
380 Cutting *cutting = cut(s, cutwid, 0, 0, cutwid, 0);
|
c@100
|
381
|
c@101
|
382 #ifdef DEBUG_VERBOSE
|
c@100
|
383 printCutting(cutting, " ");
|
c@101
|
384 #endif
|
c@100
|
385
|
c@100
|
386 vector<vector<float> > rmat(maxwid/minwid);
|
c@100
|
387 for (int i = 0; i < maxwid/minwid; ++i) {
|
c@100
|
388 rmat[i] = vector<float>(maxwid/2);
|
c@100
|
389 }
|
c@100
|
390
|
c@114
|
391 assemble(s, cutting, rmat, 0, 0, maxwid/minwid, cutwid);
|
c@100
|
392
|
c@110
|
393 cutting->erase();
|
c@100
|
394
|
c@178
|
395 for (int i = 0; i < int(rmat.size()); ++i) {
|
c@100
|
396 Feature f;
|
c@100
|
397 f.hasTimestamp = false;
|
c@100
|
398 f.values = rmat[i];
|
c@100
|
399 fs[0].push_back(f);
|
c@100
|
400 }
|
c@100
|
401
|
c@104
|
402 // std::cerr << "process returning!\n" << std::endl;
|
c@104
|
403
|
c@100
|
404 return fs;
|
c@100
|
405 }
|
c@100
|
406
|
c@100
|
407 void
|
c@104
|
408 AdaptiveSpectrogram::printCutting(Cutting *c, string pfx) const
|
c@100
|
409 {
|
c@100
|
410 if (c->first) {
|
c@100
|
411 if (c->cut == Cutting::Horizontal) {
|
c@100
|
412 cerr << pfx << "H" << endl;
|
c@100
|
413 } else if (c->cut == Cutting::Vertical) {
|
c@100
|
414 cerr << pfx << "V" << endl;
|
c@100
|
415 }
|
c@100
|
416 printCutting(c->first, pfx + " ");
|
c@100
|
417 printCutting(c->second, pfx + " ");
|
c@100
|
418 } else {
|
c@100
|
419 cerr << pfx << "* " << c->value << endl;
|
c@100
|
420 }
|
c@100
|
421 }
|
c@100
|
422
|
c@104
|
423 void
|
c@104
|
424 AdaptiveSpectrogram::getSubCuts(const Spectrograms &s,
|
c@104
|
425 int res,
|
c@104
|
426 int x, int y, int h,
|
c@114
|
427 Cutting **top, Cutting **bottom,
|
c@114
|
428 Cutting **left, Cutting **right,
|
c@113
|
429 BlockAllocator *allocator) const
|
c@104
|
430 {
|
c@109
|
431 if (m_threaded && !m_threadsInUse) {
|
c@104
|
432
|
c@109
|
433 m_threadsInUse = true;
|
c@104
|
434
|
c@104
|
435 if (m_cutThreads.empty()) {
|
c@104
|
436 for (int i = 0; i < 4; ++i) {
|
c@104
|
437 CutThread *t = new CutThread(this);
|
c@104
|
438 m_cutThreads.push_back(t);
|
c@104
|
439 }
|
c@104
|
440 }
|
c@104
|
441
|
c@109
|
442 // Cut threads 0 and 1 calculate the top and bottom halves;
|
c@110
|
443 // threads 2 and 3 calculate left and right. See notes in
|
c@110
|
444 // unthreaded code below for more information.
|
c@104
|
445
|
c@114
|
446 if (top) m_cutThreads[0]->cut(s, res, x, y + h/2, h/2);
|
c@114
|
447 if (bottom) m_cutThreads[1]->cut(s, res, x, y, h/2);
|
c@104
|
448
|
c@114
|
449 if (left) m_cutThreads[2]->cut(s, res/2, 2 * x, y/2, h/2);
|
c@114
|
450 if (right) m_cutThreads[3]->cut(s, res/2, 2 * x + 1, y/2, h/2);
|
c@114
|
451
|
c@114
|
452 if (top) *top = m_cutThreads[0]->get();
|
c@114
|
453 if (bottom) *bottom = m_cutThreads[1]->get();
|
c@114
|
454 if (left) *left = m_cutThreads[2]->get();
|
c@114
|
455 if (right) *right = m_cutThreads[3]->get();
|
c@104
|
456
|
c@104
|
457 } else {
|
c@104
|
458
|
c@110
|
459 // Unthreaded version
|
c@104
|
460
|
c@104
|
461 // The "vertical" division is a top/bottom split.
|
c@104
|
462 // Splitting this way keeps us in the same resolution,
|
c@104
|
463 // but with two vertical subregions of height h/2.
|
c@104
|
464
|
c@114
|
465 if (top) *top = cut(s, res, x, y + h/2, h/2, allocator);
|
c@114
|
466 if (bottom) *bottom = cut(s, res, x, y, h/2, allocator);
|
c@104
|
467
|
c@104
|
468 // The "horizontal" division is a left/right split. Splitting
|
c@104
|
469 // this way places us in resolution res/2, which has lower
|
c@104
|
470 // vertical resolution but higher horizontal resolution. We
|
c@104
|
471 // need to double x accordingly.
|
c@104
|
472
|
c@114
|
473 if (left) *left = cut(s, res/2, 2 * x, y/2, h/2, allocator);
|
c@114
|
474 if (right) *right = cut(s, res/2, 2 * x + 1, y/2, h/2, allocator);
|
c@104
|
475 }
|
c@104
|
476 }
|
c@104
|
477
|
c@100
|
478 AdaptiveSpectrogram::Cutting *
|
c@100
|
479 AdaptiveSpectrogram::cut(const Spectrograms &s,
|
c@100
|
480 int res,
|
c@110
|
481 int x, int y, int h,
|
c@110
|
482 BlockAllocator *allocator) const
|
c@100
|
483 {
|
c@100
|
484 // cerr << "res = " << res << ", x = " << x << ", y = " << y << ", h = " << h << endl;
|
c@100
|
485
|
c@110
|
486 Cutting *cutting;
|
c@110
|
487 if (allocator) {
|
c@110
|
488 cutting = (Cutting *)(allocator->allocate());
|
c@110
|
489 cutting->allocator = allocator;
|
c@110
|
490 } else {
|
c@110
|
491 cutting = new Cutting;
|
c@110
|
492 cutting->allocator = 0;
|
c@110
|
493 }
|
c@110
|
494
|
c@100
|
495 if (h > 1 && res > s.minres) {
|
c@100
|
496
|
c@114
|
497 if (!isResolutionWanted(s, res)) {
|
c@100
|
498
|
c@114
|
499 Cutting *left = 0, *right = 0;
|
c@114
|
500 getSubCuts(s, res, x, y, h, 0, 0, &left, &right, allocator);
|
c@114
|
501
|
c@114
|
502 double hcost = left->cost + right->cost;
|
c@101
|
503 double henergy = left->value + right->value;
|
c@114
|
504 hcost = normalize(hcost, henergy);
|
c@114
|
505
|
c@100
|
506 cutting->cut = Cutting::Horizontal;
|
c@100
|
507 cutting->first = left;
|
c@100
|
508 cutting->second = right;
|
c@100
|
509 cutting->cost = hcost;
|
c@111
|
510 cutting->value = left->value + right->value;
|
c@100
|
511
|
c@114
|
512 } else if (h == 2 && !isResolutionWanted(s, res/2)) {
|
c@100
|
513
|
c@114
|
514 Cutting *top = 0, *bottom = 0;
|
c@114
|
515 getSubCuts(s, res, x, y, h, &top, &bottom, 0, 0, allocator);
|
c@114
|
516
|
c@114
|
517 double vcost = top->cost + bottom->cost;
|
c@114
|
518 double venergy = top->value + bottom->value;
|
c@114
|
519 vcost = normalize(vcost, venergy);
|
c@114
|
520
|
c@100
|
521 cutting->cut = Cutting::Vertical;
|
c@100
|
522 cutting->first = top;
|
c@100
|
523 cutting->second = bottom;
|
c@100
|
524 cutting->cost = vcost;
|
c@111
|
525 cutting->value = top->value + bottom->value;
|
c@114
|
526
|
c@114
|
527 } else {
|
c@114
|
528
|
c@114
|
529 Cutting *top = 0, *bottom = 0, *left = 0, *right = 0;
|
c@114
|
530 getSubCuts(s, res, x, y, h, &top, &bottom, &left, &right, allocator);
|
c@114
|
531
|
c@114
|
532 double vcost = top->cost + bottom->cost;
|
c@114
|
533 double venergy = top->value + bottom->value;
|
c@114
|
534 vcost = normalize(vcost, venergy);
|
c@114
|
535
|
c@114
|
536 double hcost = left->cost + right->cost;
|
c@114
|
537 double henergy = left->value + right->value;
|
c@114
|
538 hcost = normalize(hcost, henergy);
|
c@114
|
539
|
c@114
|
540 if (vcost > hcost) {
|
c@114
|
541 cutting->cut = Cutting::Horizontal;
|
c@114
|
542 cutting->first = left;
|
c@114
|
543 cutting->second = right;
|
c@114
|
544 cutting->cost = hcost;
|
c@114
|
545 cutting->value = left->value + right->value;
|
c@114
|
546 top->erase();
|
c@114
|
547 bottom->erase();
|
c@114
|
548 return cutting;
|
c@114
|
549 } else {
|
c@114
|
550 cutting->cut = Cutting::Vertical;
|
c@114
|
551 cutting->first = top;
|
c@114
|
552 cutting->second = bottom;
|
c@114
|
553 cutting->cost = vcost;
|
c@114
|
554 cutting->value = top->value + bottom->value;
|
c@114
|
555 left->erase();
|
c@114
|
556 right->erase();
|
c@114
|
557 return cutting;
|
c@114
|
558 }
|
c@100
|
559 }
|
c@100
|
560
|
c@100
|
561 } else {
|
c@100
|
562
|
c@100
|
563 // no cuts possible from this level
|
c@100
|
564
|
c@100
|
565 cutting->cut = Cutting::Finished;
|
c@100
|
566 cutting->first = 0;
|
c@100
|
567 cutting->second = 0;
|
c@100
|
568
|
c@100
|
569 int n = 0;
|
c@114
|
570 for (int r = res; r > s.minres; r >>= 1) ++n;
|
c@100
|
571 const Spectrogram *spectrogram = s.spectrograms[n];
|
c@100
|
572 cutting->cost = cost(*spectrogram, x, y);
|
c@100
|
573 cutting->value = value(*spectrogram, x, y);
|
c@114
|
574 }
|
c@100
|
575
|
c@114
|
576 return cutting;
|
c@100
|
577 }
|
c@100
|
578
|
c@100
|
579 void
|
c@100
|
580 AdaptiveSpectrogram::assemble(const Spectrograms &s,
|
c@100
|
581 const Cutting *cutting,
|
c@100
|
582 vector<vector<float> > &rmat,
|
c@104
|
583 int x, int y, int w, int h) const
|
c@100
|
584 {
|
c@100
|
585 switch (cutting->cut) {
|
c@100
|
586
|
c@100
|
587 case Cutting::Finished:
|
c@100
|
588 for (int i = 0; i < w; ++i) {
|
c@100
|
589 for (int j = 0; j < h; ++j) {
|
c@114
|
590 rmat[x+i][y+j] = cutting->value;
|
c@100
|
591 }
|
c@100
|
592 }
|
c@100
|
593 return;
|
c@100
|
594
|
c@100
|
595 case Cutting::Horizontal:
|
c@100
|
596 assemble(s, cutting->first, rmat, x, y, w/2, h);
|
c@100
|
597 assemble(s, cutting->second, rmat, x+w/2, y, w/2, h);
|
c@100
|
598 break;
|
c@100
|
599
|
c@100
|
600 case Cutting::Vertical:
|
c@100
|
601 assemble(s, cutting->first, rmat, x, y+h/2, w, h/2);
|
c@100
|
602 assemble(s, cutting->second, rmat, x, y, w, h/2);
|
c@100
|
603 break;
|
c@100
|
604 }
|
c@100
|
605 }
|
c@100
|
606
|