Chris@0
|
1 /* -*- c-basic-offset: 4 -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@0
|
2
|
Chris@0
|
3 /*
|
Chris@0
|
4 A waveform viewer and audio annotation editor.
|
Chris@5
|
5 Chris Cannam, Queen Mary University of London, 2005-2006
|
Chris@0
|
6
|
Chris@0
|
7 This is experimental software. Not for distribution.
|
Chris@0
|
8 */
|
Chris@0
|
9
|
Chris@0
|
10 #include "SpectrogramLayer.h"
|
Chris@0
|
11
|
Chris@0
|
12 #include "base/View.h"
|
Chris@0
|
13 #include "base/Profiler.h"
|
Chris@0
|
14 #include "base/AudioLevel.h"
|
Chris@0
|
15 #include "base/Window.h"
|
Chris@24
|
16 #include "base/Pitch.h"
|
Chris@0
|
17
|
Chris@35
|
18 #include "dsp/maths/MathUtilities.h"
|
Chris@35
|
19
|
Chris@0
|
20 #include <QPainter>
|
Chris@0
|
21 #include <QImage>
|
Chris@0
|
22 #include <QPixmap>
|
Chris@0
|
23 #include <QRect>
|
Chris@0
|
24 #include <QTimer>
|
Chris@0
|
25
|
Chris@0
|
26 #include <iostream>
|
Chris@0
|
27
|
Chris@0
|
28 #include <cassert>
|
Chris@0
|
29 #include <cmath>
|
Chris@0
|
30
|
Chris@0
|
31 //#define DEBUG_SPECTROGRAM_REPAINT 1
|
Chris@0
|
32
|
Chris@0
|
33
|
Chris@0
|
34 SpectrogramLayer::SpectrogramLayer(View *w, Configuration config) :
|
Chris@0
|
35 Layer(w),
|
Chris@0
|
36 m_model(0),
|
Chris@0
|
37 m_channel(0),
|
Chris@0
|
38 m_windowSize(1024),
|
Chris@0
|
39 m_windowType(HanningWindow),
|
Chris@0
|
40 m_windowOverlap(50),
|
Chris@0
|
41 m_gain(1.0),
|
Chris@9
|
42 m_colourRotation(0),
|
Chris@0
|
43 m_maxFrequency(8000),
|
Chris@0
|
44 m_colourScale(dBColourScale),
|
Chris@0
|
45 m_colourScheme(DefaultColours),
|
Chris@0
|
46 m_frequencyScale(LinearFrequencyScale),
|
Chris@35
|
47 m_frequencyAdjustment(RawFrequency),
|
Chris@0
|
48 m_cache(0),
|
Chris@35
|
49 m_phaseAdjustCache(0),
|
Chris@0
|
50 m_cacheInvalid(true),
|
Chris@0
|
51 m_pixmapCache(0),
|
Chris@0
|
52 m_pixmapCacheInvalid(true),
|
Chris@0
|
53 m_fillThread(0),
|
Chris@0
|
54 m_updateTimer(0),
|
Chris@0
|
55 m_lastFillExtent(0),
|
Chris@0
|
56 m_exiting(false)
|
Chris@0
|
57 {
|
Chris@0
|
58 if (config == MelodicRange) {
|
Chris@0
|
59 setWindowSize(8192);
|
Chris@0
|
60 setWindowOverlap(90);
|
Chris@0
|
61 setWindowType(ParzenWindow);
|
Chris@0
|
62 setMaxFrequency(1000);
|
Chris@0
|
63 setColourScale(LinearColourScale);
|
Chris@0
|
64 }
|
Chris@0
|
65
|
Chris@0
|
66 if (m_view) m_view->setLightBackground(false);
|
Chris@0
|
67 m_view->addLayer(this);
|
Chris@0
|
68 }
|
Chris@0
|
69
|
Chris@0
|
70 SpectrogramLayer::~SpectrogramLayer()
|
Chris@0
|
71 {
|
Chris@0
|
72 delete m_updateTimer;
|
Chris@0
|
73 m_updateTimer = 0;
|
Chris@0
|
74
|
Chris@0
|
75 m_exiting = true;
|
Chris@0
|
76 m_condition.wakeAll();
|
Chris@0
|
77 if (m_fillThread) m_fillThread->wait();
|
Chris@0
|
78 delete m_fillThread;
|
Chris@0
|
79
|
Chris@0
|
80 delete m_cache;
|
Chris@35
|
81 delete m_phaseAdjustCache;
|
Chris@0
|
82 }
|
Chris@0
|
83
|
Chris@0
|
84 void
|
Chris@0
|
85 SpectrogramLayer::setModel(const DenseTimeValueModel *model)
|
Chris@0
|
86 {
|
Chris@34
|
87 std::cerr << "SpectrogramLayer(" << this << "): setModel(" << model << ")" << std::endl;
|
Chris@34
|
88
|
Chris@0
|
89 m_mutex.lock();
|
Chris@35
|
90 m_cacheInvalid = true;
|
Chris@0
|
91 m_model = model;
|
Chris@34
|
92 delete m_cache; //!!! hang on, this isn't safe to do here is it?
|
Chris@34
|
93 // we need some sort of guard against the fill
|
Chris@34
|
94 // thread trying to read the defunct model too.
|
Chris@34
|
95 // should we use a scavenger?
|
Chris@31
|
96 m_cache = 0;
|
Chris@35
|
97 delete m_phaseAdjustCache; //!!! likewise
|
Chris@35
|
98 m_phaseAdjustCache = 0;
|
Chris@0
|
99 m_mutex.unlock();
|
Chris@0
|
100
|
Chris@0
|
101 if (!m_model || !m_model->isOK()) return;
|
Chris@0
|
102
|
Chris@0
|
103 connect(m_model, SIGNAL(modelChanged()), this, SIGNAL(modelChanged()));
|
Chris@0
|
104 connect(m_model, SIGNAL(modelChanged(size_t, size_t)),
|
Chris@0
|
105 this, SIGNAL(modelChanged(size_t, size_t)));
|
Chris@0
|
106
|
Chris@0
|
107 connect(m_model, SIGNAL(completionChanged()),
|
Chris@0
|
108 this, SIGNAL(modelCompletionChanged()));
|
Chris@0
|
109
|
Chris@0
|
110 connect(m_model, SIGNAL(modelChanged()), this, SLOT(cacheInvalid()));
|
Chris@0
|
111 connect(m_model, SIGNAL(modelChanged(size_t, size_t)),
|
Chris@0
|
112 this, SLOT(cacheInvalid(size_t, size_t)));
|
Chris@0
|
113
|
Chris@0
|
114 emit modelReplaced();
|
Chris@0
|
115 fillCache();
|
Chris@0
|
116 }
|
Chris@0
|
117
|
Chris@0
|
118 Layer::PropertyList
|
Chris@0
|
119 SpectrogramLayer::getProperties() const
|
Chris@0
|
120 {
|
Chris@0
|
121 PropertyList list;
|
Chris@0
|
122 list.push_back(tr("Colour"));
|
Chris@0
|
123 list.push_back(tr("Colour Scale"));
|
Chris@0
|
124 list.push_back(tr("Window Type"));
|
Chris@0
|
125 list.push_back(tr("Window Size"));
|
Chris@0
|
126 list.push_back(tr("Window Overlap"));
|
Chris@0
|
127 list.push_back(tr("Gain"));
|
Chris@9
|
128 list.push_back(tr("Colour Rotation"));
|
Chris@0
|
129 list.push_back(tr("Max Frequency"));
|
Chris@0
|
130 list.push_back(tr("Frequency Scale"));
|
Chris@35
|
131 list.push_back(tr("Frequency Adjustment"));
|
Chris@0
|
132 return list;
|
Chris@0
|
133 }
|
Chris@0
|
134
|
Chris@0
|
135 Layer::PropertyType
|
Chris@0
|
136 SpectrogramLayer::getPropertyType(const PropertyName &name) const
|
Chris@0
|
137 {
|
Chris@0
|
138 if (name == tr("Gain")) return RangeProperty;
|
Chris@9
|
139 if (name == tr("Colour Rotation")) return RangeProperty;
|
Chris@0
|
140 return ValueProperty;
|
Chris@0
|
141 }
|
Chris@0
|
142
|
Chris@0
|
143 QString
|
Chris@0
|
144 SpectrogramLayer::getPropertyGroupName(const PropertyName &name) const
|
Chris@0
|
145 {
|
Chris@0
|
146 if (name == tr("Window Size") ||
|
Chris@35
|
147 name == tr("Window Type") ||
|
Chris@0
|
148 name == tr("Window Overlap")) return tr("Window");
|
Chris@35
|
149 if (name == tr("Colour") ||
|
Chris@35
|
150 name == tr("Colour Rotation")) return tr("Colour");
|
Chris@0
|
151 if (name == tr("Gain") ||
|
Chris@0
|
152 name == tr("Colour Scale")) return tr("Scale");
|
Chris@0
|
153 if (name == tr("Max Frequency") ||
|
Chris@35
|
154 name == tr("Frequency Scale") ||
|
Chris@35
|
155 name == tr("Frequency Adjustment")) return tr("Frequency");
|
Chris@0
|
156 return QString();
|
Chris@0
|
157 }
|
Chris@0
|
158
|
Chris@0
|
159 int
|
Chris@0
|
160 SpectrogramLayer::getPropertyRangeAndValue(const PropertyName &name,
|
Chris@0
|
161 int *min, int *max) const
|
Chris@0
|
162 {
|
Chris@0
|
163 int deft = 0;
|
Chris@0
|
164
|
Chris@10
|
165 int throwaway;
|
Chris@10
|
166 if (!min) min = &throwaway;
|
Chris@10
|
167 if (!max) max = &throwaway;
|
Chris@10
|
168
|
Chris@0
|
169 if (name == tr("Gain")) {
|
Chris@0
|
170
|
Chris@0
|
171 *min = -50;
|
Chris@0
|
172 *max = 50;
|
Chris@0
|
173
|
Chris@0
|
174 deft = lrint(log10(m_gain) * 20.0);
|
Chris@0
|
175 if (deft < *min) deft = *min;
|
Chris@0
|
176 if (deft > *max) deft = *max;
|
Chris@0
|
177
|
Chris@9
|
178 } else if (name == tr("Colour Rotation")) {
|
Chris@9
|
179
|
Chris@9
|
180 *min = 0;
|
Chris@9
|
181 *max = 256;
|
Chris@9
|
182
|
Chris@9
|
183 deft = m_colourRotation;
|
Chris@9
|
184
|
Chris@0
|
185 } else if (name == tr("Colour Scale")) {
|
Chris@0
|
186
|
Chris@0
|
187 *min = 0;
|
Chris@0
|
188 *max = 3;
|
Chris@0
|
189
|
Chris@0
|
190 deft = (int)m_colourScale;
|
Chris@0
|
191
|
Chris@0
|
192 } else if (name == tr("Colour")) {
|
Chris@0
|
193
|
Chris@0
|
194 *min = 0;
|
Chris@0
|
195 *max = 5;
|
Chris@0
|
196
|
Chris@0
|
197 deft = (int)m_colourScheme;
|
Chris@0
|
198
|
Chris@0
|
199 } else if (name == tr("Window Type")) {
|
Chris@0
|
200
|
Chris@0
|
201 *min = 0;
|
Chris@0
|
202 *max = 6;
|
Chris@0
|
203
|
Chris@0
|
204 deft = (int)m_windowType;
|
Chris@0
|
205
|
Chris@0
|
206 } else if (name == tr("Window Size")) {
|
Chris@0
|
207
|
Chris@0
|
208 *min = 0;
|
Chris@0
|
209 *max = 10;
|
Chris@0
|
210
|
Chris@0
|
211 deft = 0;
|
Chris@0
|
212 int ws = m_windowSize;
|
Chris@0
|
213 while (ws > 32) { ws >>= 1; deft ++; }
|
Chris@0
|
214
|
Chris@0
|
215 } else if (name == tr("Window Overlap")) {
|
Chris@0
|
216
|
Chris@0
|
217 *min = 0;
|
Chris@0
|
218 *max = 4;
|
Chris@0
|
219
|
Chris@0
|
220 deft = m_windowOverlap / 25;
|
Chris@0
|
221 if (m_windowOverlap == 90) deft = 4;
|
Chris@0
|
222
|
Chris@0
|
223 } else if (name == tr("Max Frequency")) {
|
Chris@0
|
224
|
Chris@0
|
225 *min = 0;
|
Chris@0
|
226 *max = 9;
|
Chris@0
|
227
|
Chris@0
|
228 switch (m_maxFrequency) {
|
Chris@0
|
229 case 500: deft = 0; break;
|
Chris@0
|
230 case 1000: deft = 1; break;
|
Chris@0
|
231 case 1500: deft = 2; break;
|
Chris@0
|
232 case 2000: deft = 3; break;
|
Chris@0
|
233 case 4000: deft = 4; break;
|
Chris@0
|
234 case 6000: deft = 5; break;
|
Chris@0
|
235 case 8000: deft = 6; break;
|
Chris@0
|
236 case 12000: deft = 7; break;
|
Chris@0
|
237 case 16000: deft = 8; break;
|
Chris@0
|
238 default: deft = 9; break;
|
Chris@0
|
239 }
|
Chris@0
|
240
|
Chris@0
|
241 } else if (name == tr("Frequency Scale")) {
|
Chris@0
|
242
|
Chris@0
|
243 *min = 0;
|
Chris@0
|
244 *max = 1;
|
Chris@0
|
245 deft = (int)m_frequencyScale;
|
Chris@0
|
246
|
Chris@35
|
247 } else if (name == tr("Frequency Adjustment")) {
|
Chris@35
|
248
|
Chris@35
|
249 *min = 0;
|
Chris@35
|
250 *max = 2;
|
Chris@35
|
251 deft = (int)m_frequencyAdjustment;
|
Chris@35
|
252
|
Chris@0
|
253 } else {
|
Chris@0
|
254 deft = Layer::getPropertyRangeAndValue(name, min, max);
|
Chris@0
|
255 }
|
Chris@0
|
256
|
Chris@0
|
257 return deft;
|
Chris@0
|
258 }
|
Chris@0
|
259
|
Chris@0
|
260 QString
|
Chris@0
|
261 SpectrogramLayer::getPropertyValueLabel(const PropertyName &name,
|
Chris@9
|
262 int value) const
|
Chris@0
|
263 {
|
Chris@0
|
264 if (name == tr("Colour")) {
|
Chris@0
|
265 switch (value) {
|
Chris@0
|
266 default:
|
Chris@0
|
267 case 0: return tr("Default");
|
Chris@0
|
268 case 1: return tr("White on Black");
|
Chris@0
|
269 case 2: return tr("Black on White");
|
Chris@0
|
270 case 3: return tr("Red on Blue");
|
Chris@0
|
271 case 4: return tr("Yellow on Black");
|
Chris@0
|
272 case 5: return tr("Red on Black");
|
Chris@0
|
273 }
|
Chris@0
|
274 }
|
Chris@0
|
275 if (name == tr("Colour Scale")) {
|
Chris@0
|
276 switch (value) {
|
Chris@0
|
277 default:
|
Chris@0
|
278 case 0: return tr("Level Linear");
|
Chris@0
|
279 case 1: return tr("Level Meter");
|
Chris@0
|
280 case 2: return tr("Level dB");
|
Chris@0
|
281 case 3: return tr("Phase");
|
Chris@0
|
282 }
|
Chris@0
|
283 }
|
Chris@0
|
284 if (name == tr("Window Type")) {
|
Chris@0
|
285 switch ((WindowType)value) {
|
Chris@0
|
286 default:
|
Chris@35
|
287 case RectangularWindow: return tr("Rectangle");
|
Chris@0
|
288 case BartlettWindow: return tr("Bartlett");
|
Chris@0
|
289 case HammingWindow: return tr("Hamming");
|
Chris@0
|
290 case HanningWindow: return tr("Hanning");
|
Chris@0
|
291 case BlackmanWindow: return tr("Blackman");
|
Chris@0
|
292 case GaussianWindow: return tr("Gaussian");
|
Chris@0
|
293 case ParzenWindow: return tr("Parzen");
|
Chris@0
|
294 }
|
Chris@0
|
295 }
|
Chris@0
|
296 if (name == tr("Window Size")) {
|
Chris@0
|
297 return QString("%1").arg(32 << value);
|
Chris@0
|
298 }
|
Chris@0
|
299 if (name == tr("Window Overlap")) {
|
Chris@0
|
300 switch (value) {
|
Chris@0
|
301 default:
|
Chris@35
|
302 case 0: return tr("0%");
|
Chris@35
|
303 case 1: return tr("25%");
|
Chris@35
|
304 case 2: return tr("50%");
|
Chris@35
|
305 case 3: return tr("75%");
|
Chris@35
|
306 case 4: return tr("90%");
|
Chris@0
|
307 }
|
Chris@0
|
308 }
|
Chris@0
|
309 if (name == tr("Max Frequency")) {
|
Chris@0
|
310 switch (value) {
|
Chris@0
|
311 default:
|
Chris@0
|
312 case 0: return tr("500 Hz");
|
Chris@0
|
313 case 1: return tr("1 KHz");
|
Chris@0
|
314 case 2: return tr("1.5 KHz");
|
Chris@0
|
315 case 3: return tr("2 KHz");
|
Chris@0
|
316 case 4: return tr("4 KHz");
|
Chris@0
|
317 case 5: return tr("6 KHz");
|
Chris@0
|
318 case 6: return tr("8 KHz");
|
Chris@0
|
319 case 7: return tr("12 KHz");
|
Chris@0
|
320 case 8: return tr("16 KHz");
|
Chris@0
|
321 case 9: return tr("All");
|
Chris@0
|
322 }
|
Chris@0
|
323 }
|
Chris@0
|
324 if (name == tr("Frequency Scale")) {
|
Chris@0
|
325 switch (value) {
|
Chris@0
|
326 default:
|
Chris@0
|
327 case 0: return tr("Linear");
|
Chris@0
|
328 case 1: return tr("Log");
|
Chris@0
|
329 }
|
Chris@0
|
330 }
|
Chris@35
|
331 if (name == tr("Frequency Adjustment")) {
|
Chris@35
|
332 switch (value) {
|
Chris@35
|
333 default:
|
Chris@35
|
334 case 0: return tr("Bins");
|
Chris@35
|
335 case 1: return tr("Pitches");
|
Chris@35
|
336 case 2: return tr("Peaks");
|
Chris@35
|
337 }
|
Chris@35
|
338 }
|
Chris@0
|
339 return tr("<unknown>");
|
Chris@0
|
340 }
|
Chris@0
|
341
|
Chris@0
|
342 void
|
Chris@0
|
343 SpectrogramLayer::setProperty(const PropertyName &name, int value)
|
Chris@0
|
344 {
|
Chris@0
|
345 if (name == tr("Gain")) {
|
Chris@0
|
346 setGain(pow(10, float(value)/20.0));
|
Chris@9
|
347 } else if (name == tr("Colour Rotation")) {
|
Chris@9
|
348 setColourRotation(value);
|
Chris@0
|
349 } else if (name == tr("Colour")) {
|
Chris@0
|
350 if (m_view) m_view->setLightBackground(value == 2);
|
Chris@0
|
351 switch (value) {
|
Chris@0
|
352 default:
|
Chris@0
|
353 case 0: setColourScheme(DefaultColours); break;
|
Chris@0
|
354 case 1: setColourScheme(WhiteOnBlack); break;
|
Chris@0
|
355 case 2: setColourScheme(BlackOnWhite); break;
|
Chris@0
|
356 case 3: setColourScheme(RedOnBlue); break;
|
Chris@0
|
357 case 4: setColourScheme(YellowOnBlack); break;
|
Chris@0
|
358 case 5: setColourScheme(RedOnBlack); break;
|
Chris@0
|
359 }
|
Chris@0
|
360 } else if (name == tr("Window Type")) {
|
Chris@0
|
361 setWindowType(WindowType(value));
|
Chris@0
|
362 } else if (name == tr("Window Size")) {
|
Chris@0
|
363 setWindowSize(32 << value);
|
Chris@0
|
364 } else if (name == tr("Window Overlap")) {
|
Chris@0
|
365 if (value == 4) setWindowOverlap(90);
|
Chris@0
|
366 else setWindowOverlap(25 * value);
|
Chris@0
|
367 } else if (name == tr("Max Frequency")) {
|
Chris@0
|
368 switch (value) {
|
Chris@0
|
369 case 0: setMaxFrequency(500); break;
|
Chris@0
|
370 case 1: setMaxFrequency(1000); break;
|
Chris@0
|
371 case 2: setMaxFrequency(1500); break;
|
Chris@0
|
372 case 3: setMaxFrequency(2000); break;
|
Chris@0
|
373 case 4: setMaxFrequency(4000); break;
|
Chris@0
|
374 case 5: setMaxFrequency(6000); break;
|
Chris@0
|
375 case 6: setMaxFrequency(8000); break;
|
Chris@0
|
376 case 7: setMaxFrequency(12000); break;
|
Chris@0
|
377 case 8: setMaxFrequency(16000); break;
|
Chris@0
|
378 default:
|
Chris@0
|
379 case 9: setMaxFrequency(0); break;
|
Chris@0
|
380 }
|
Chris@0
|
381 } else if (name == tr("Colour Scale")) {
|
Chris@0
|
382 switch (value) {
|
Chris@0
|
383 default:
|
Chris@0
|
384 case 0: setColourScale(LinearColourScale); break;
|
Chris@0
|
385 case 1: setColourScale(MeterColourScale); break;
|
Chris@0
|
386 case 2: setColourScale(dBColourScale); break;
|
Chris@0
|
387 case 3: setColourScale(PhaseColourScale); break;
|
Chris@0
|
388 }
|
Chris@0
|
389 } else if (name == tr("Frequency Scale")) {
|
Chris@0
|
390 switch (value) {
|
Chris@0
|
391 default:
|
Chris@0
|
392 case 0: setFrequencyScale(LinearFrequencyScale); break;
|
Chris@0
|
393 case 1: setFrequencyScale(LogFrequencyScale); break;
|
Chris@0
|
394 }
|
Chris@35
|
395 } else if (name == tr("Frequency Adjustment")) {
|
Chris@35
|
396 switch (value) {
|
Chris@35
|
397 default:
|
Chris@35
|
398 case 0: setFrequencyAdjustment(RawFrequency); break;
|
Chris@35
|
399 case 1: setFrequencyAdjustment(PhaseAdjustedFrequency); break;
|
Chris@35
|
400 case 2: setFrequencyAdjustment(PhaseAdjustedPeaks); break;
|
Chris@35
|
401 }
|
Chris@0
|
402 }
|
Chris@0
|
403 }
|
Chris@0
|
404
|
Chris@0
|
405 void
|
Chris@0
|
406 SpectrogramLayer::setChannel(int ch)
|
Chris@0
|
407 {
|
Chris@0
|
408 if (m_channel == ch) return;
|
Chris@0
|
409
|
Chris@0
|
410 m_mutex.lock();
|
Chris@0
|
411 m_cacheInvalid = true;
|
Chris@0
|
412 m_pixmapCacheInvalid = true;
|
Chris@0
|
413
|
Chris@0
|
414 m_channel = ch;
|
Chris@9
|
415
|
Chris@9
|
416 m_mutex.unlock();
|
Chris@9
|
417
|
Chris@0
|
418 emit layerParametersChanged();
|
Chris@9
|
419
|
Chris@0
|
420 fillCache();
|
Chris@0
|
421 }
|
Chris@0
|
422
|
Chris@0
|
423 int
|
Chris@0
|
424 SpectrogramLayer::getChannel() const
|
Chris@0
|
425 {
|
Chris@0
|
426 return m_channel;
|
Chris@0
|
427 }
|
Chris@0
|
428
|
Chris@0
|
429 void
|
Chris@0
|
430 SpectrogramLayer::setWindowSize(size_t ws)
|
Chris@0
|
431 {
|
Chris@0
|
432 if (m_windowSize == ws) return;
|
Chris@0
|
433
|
Chris@0
|
434 m_mutex.lock();
|
Chris@0
|
435 m_cacheInvalid = true;
|
Chris@0
|
436 m_pixmapCacheInvalid = true;
|
Chris@0
|
437
|
Chris@0
|
438 m_windowSize = ws;
|
Chris@0
|
439
|
Chris@0
|
440 m_mutex.unlock();
|
Chris@9
|
441
|
Chris@9
|
442 emit layerParametersChanged();
|
Chris@9
|
443
|
Chris@0
|
444 fillCache();
|
Chris@0
|
445 }
|
Chris@0
|
446
|
Chris@0
|
447 size_t
|
Chris@0
|
448 SpectrogramLayer::getWindowSize() const
|
Chris@0
|
449 {
|
Chris@0
|
450 return m_windowSize;
|
Chris@0
|
451 }
|
Chris@0
|
452
|
Chris@0
|
453 void
|
Chris@0
|
454 SpectrogramLayer::setWindowOverlap(size_t wi)
|
Chris@0
|
455 {
|
Chris@0
|
456 if (m_windowOverlap == wi) return;
|
Chris@0
|
457
|
Chris@0
|
458 m_mutex.lock();
|
Chris@0
|
459 m_cacheInvalid = true;
|
Chris@0
|
460 m_pixmapCacheInvalid = true;
|
Chris@0
|
461
|
Chris@0
|
462 m_windowOverlap = wi;
|
Chris@0
|
463
|
Chris@0
|
464 m_mutex.unlock();
|
Chris@9
|
465
|
Chris@9
|
466 emit layerParametersChanged();
|
Chris@9
|
467
|
Chris@0
|
468 fillCache();
|
Chris@0
|
469 }
|
Chris@0
|
470
|
Chris@0
|
471 size_t
|
Chris@0
|
472 SpectrogramLayer::getWindowOverlap() const
|
Chris@0
|
473 {
|
Chris@0
|
474 return m_windowOverlap;
|
Chris@0
|
475 }
|
Chris@0
|
476
|
Chris@0
|
477 void
|
Chris@0
|
478 SpectrogramLayer::setWindowType(WindowType w)
|
Chris@0
|
479 {
|
Chris@0
|
480 if (m_windowType == w) return;
|
Chris@0
|
481
|
Chris@0
|
482 m_mutex.lock();
|
Chris@0
|
483 m_cacheInvalid = true;
|
Chris@0
|
484 m_pixmapCacheInvalid = true;
|
Chris@0
|
485
|
Chris@0
|
486 m_windowType = w;
|
Chris@0
|
487
|
Chris@0
|
488 m_mutex.unlock();
|
Chris@9
|
489
|
Chris@9
|
490 emit layerParametersChanged();
|
Chris@9
|
491
|
Chris@0
|
492 fillCache();
|
Chris@0
|
493 }
|
Chris@0
|
494
|
Chris@0
|
495 WindowType
|
Chris@0
|
496 SpectrogramLayer::getWindowType() const
|
Chris@0
|
497 {
|
Chris@0
|
498 return m_windowType;
|
Chris@0
|
499 }
|
Chris@0
|
500
|
Chris@0
|
501 void
|
Chris@0
|
502 SpectrogramLayer::setGain(float gain)
|
Chris@0
|
503 {
|
Chris@0
|
504 if (m_gain == gain) return; //!!! inadequate for floats!
|
Chris@0
|
505
|
Chris@0
|
506 m_mutex.lock();
|
Chris@0
|
507 m_cacheInvalid = true;
|
Chris@0
|
508 m_pixmapCacheInvalid = true;
|
Chris@0
|
509
|
Chris@0
|
510 m_gain = gain;
|
Chris@0
|
511
|
Chris@0
|
512 m_mutex.unlock();
|
Chris@9
|
513
|
Chris@9
|
514 emit layerParametersChanged();
|
Chris@9
|
515
|
Chris@0
|
516 fillCache();
|
Chris@0
|
517 }
|
Chris@0
|
518
|
Chris@0
|
519 float
|
Chris@0
|
520 SpectrogramLayer::getGain() const
|
Chris@0
|
521 {
|
Chris@0
|
522 return m_gain;
|
Chris@0
|
523 }
|
Chris@0
|
524
|
Chris@0
|
525 void
|
Chris@0
|
526 SpectrogramLayer::setMaxFrequency(size_t mf)
|
Chris@0
|
527 {
|
Chris@0
|
528 if (m_maxFrequency == mf) return;
|
Chris@0
|
529
|
Chris@0
|
530 m_mutex.lock();
|
Chris@1
|
531 // don't need to invalidate main cache here
|
Chris@0
|
532 m_pixmapCacheInvalid = true;
|
Chris@0
|
533
|
Chris@0
|
534 m_maxFrequency = mf;
|
Chris@0
|
535
|
Chris@0
|
536 m_mutex.unlock();
|
Chris@9
|
537
|
Chris@9
|
538 emit layerParametersChanged();
|
Chris@0
|
539 }
|
Chris@0
|
540
|
Chris@0
|
541 size_t
|
Chris@0
|
542 SpectrogramLayer::getMaxFrequency() const
|
Chris@0
|
543 {
|
Chris@0
|
544 return m_maxFrequency;
|
Chris@0
|
545 }
|
Chris@0
|
546
|
Chris@0
|
547 void
|
Chris@9
|
548 SpectrogramLayer::setColourRotation(int r)
|
Chris@9
|
549 {
|
Chris@9
|
550 m_mutex.lock();
|
Chris@9
|
551 // don't need to invalidate main cache here
|
Chris@9
|
552 m_pixmapCacheInvalid = true;
|
Chris@9
|
553
|
Chris@9
|
554 if (r < 0) r = 0;
|
Chris@9
|
555 if (r > 256) r = 256;
|
Chris@9
|
556 int distance = r - m_colourRotation;
|
Chris@9
|
557
|
Chris@9
|
558 if (distance != 0) {
|
Chris@9
|
559 rotateCacheColourmap(-distance);
|
Chris@9
|
560 m_colourRotation = r;
|
Chris@9
|
561 }
|
Chris@9
|
562
|
Chris@9
|
563 m_mutex.unlock();
|
Chris@9
|
564
|
Chris@9
|
565 emit layerParametersChanged();
|
Chris@9
|
566 }
|
Chris@9
|
567
|
Chris@9
|
568 void
|
Chris@0
|
569 SpectrogramLayer::setColourScale(ColourScale colourScale)
|
Chris@0
|
570 {
|
Chris@0
|
571 if (m_colourScale == colourScale) return;
|
Chris@0
|
572
|
Chris@0
|
573 m_mutex.lock();
|
Chris@0
|
574 m_cacheInvalid = true;
|
Chris@0
|
575 m_pixmapCacheInvalid = true;
|
Chris@0
|
576
|
Chris@0
|
577 m_colourScale = colourScale;
|
Chris@0
|
578
|
Chris@0
|
579 m_mutex.unlock();
|
Chris@0
|
580 fillCache();
|
Chris@9
|
581
|
Chris@9
|
582 emit layerParametersChanged();
|
Chris@0
|
583 }
|
Chris@0
|
584
|
Chris@0
|
585 SpectrogramLayer::ColourScale
|
Chris@0
|
586 SpectrogramLayer::getColourScale() const
|
Chris@0
|
587 {
|
Chris@0
|
588 return m_colourScale;
|
Chris@0
|
589 }
|
Chris@0
|
590
|
Chris@0
|
591 void
|
Chris@0
|
592 SpectrogramLayer::setColourScheme(ColourScheme scheme)
|
Chris@0
|
593 {
|
Chris@0
|
594 if (m_colourScheme == scheme) return;
|
Chris@0
|
595
|
Chris@0
|
596 m_mutex.lock();
|
Chris@0
|
597 // don't need to invalidate main cache here
|
Chris@0
|
598 m_pixmapCacheInvalid = true;
|
Chris@0
|
599
|
Chris@0
|
600 m_colourScheme = scheme;
|
Chris@0
|
601 setCacheColourmap();
|
Chris@9
|
602
|
Chris@9
|
603 m_mutex.unlock();
|
Chris@9
|
604
|
Chris@0
|
605 emit layerParametersChanged();
|
Chris@0
|
606 }
|
Chris@0
|
607
|
Chris@0
|
608 SpectrogramLayer::ColourScheme
|
Chris@0
|
609 SpectrogramLayer::getColourScheme() const
|
Chris@0
|
610 {
|
Chris@0
|
611 return m_colourScheme;
|
Chris@0
|
612 }
|
Chris@0
|
613
|
Chris@0
|
614 void
|
Chris@0
|
615 SpectrogramLayer::setFrequencyScale(FrequencyScale frequencyScale)
|
Chris@0
|
616 {
|
Chris@0
|
617 if (m_frequencyScale == frequencyScale) return;
|
Chris@0
|
618
|
Chris@0
|
619 m_mutex.lock();
|
Chris@35
|
620
|
Chris@0
|
621 // don't need to invalidate main cache here
|
Chris@0
|
622 m_pixmapCacheInvalid = true;
|
Chris@0
|
623
|
Chris@0
|
624 m_frequencyScale = frequencyScale;
|
Chris@0
|
625
|
Chris@0
|
626 m_mutex.unlock();
|
Chris@9
|
627
|
Chris@9
|
628 emit layerParametersChanged();
|
Chris@0
|
629 }
|
Chris@0
|
630
|
Chris@0
|
631 SpectrogramLayer::FrequencyScale
|
Chris@0
|
632 SpectrogramLayer::getFrequencyScale() const
|
Chris@0
|
633 {
|
Chris@0
|
634 return m_frequencyScale;
|
Chris@0
|
635 }
|
Chris@0
|
636
|
Chris@0
|
637 void
|
Chris@35
|
638 SpectrogramLayer::setFrequencyAdjustment(FrequencyAdjustment frequencyAdjustment)
|
Chris@35
|
639 {
|
Chris@35
|
640 if (m_frequencyAdjustment == frequencyAdjustment) return;
|
Chris@35
|
641
|
Chris@35
|
642 m_mutex.lock();
|
Chris@35
|
643
|
Chris@35
|
644 m_cacheInvalid = true;
|
Chris@35
|
645 m_pixmapCacheInvalid = true;
|
Chris@35
|
646
|
Chris@35
|
647 m_frequencyAdjustment = frequencyAdjustment;
|
Chris@35
|
648
|
Chris@35
|
649 m_mutex.unlock();
|
Chris@35
|
650
|
Chris@35
|
651 fillCache();
|
Chris@35
|
652
|
Chris@35
|
653 emit layerParametersChanged();
|
Chris@35
|
654 }
|
Chris@35
|
655
|
Chris@35
|
656 SpectrogramLayer::FrequencyAdjustment
|
Chris@35
|
657 SpectrogramLayer::getFrequencyAdjustment() const
|
Chris@35
|
658 {
|
Chris@35
|
659 return m_frequencyAdjustment;
|
Chris@35
|
660 }
|
Chris@35
|
661
|
Chris@35
|
662 void
|
Chris@33
|
663 SpectrogramLayer::setLayerDormant(bool dormant)
|
Chris@29
|
664 {
|
Chris@33
|
665 if (dormant == m_dormant) return;
|
Chris@33
|
666
|
Chris@33
|
667 if (dormant) {
|
Chris@33
|
668
|
Chris@33
|
669 m_mutex.lock();
|
Chris@33
|
670 m_dormant = true;
|
Chris@33
|
671
|
Chris@34
|
672 // delete m_cache;
|
Chris@34
|
673 // m_cache = 0;
|
Chris@33
|
674
|
Chris@34
|
675 m_cacheInvalid = true;
|
Chris@33
|
676 m_pixmapCacheInvalid = true;
|
Chris@34
|
677 m_cachedInitialVisibleArea = false;
|
Chris@33
|
678 delete m_pixmapCache;
|
Chris@33
|
679 m_pixmapCache = 0;
|
Chris@33
|
680
|
Chris@33
|
681 m_mutex.unlock();
|
Chris@33
|
682
|
Chris@33
|
683 } else {
|
Chris@33
|
684
|
Chris@33
|
685 m_dormant = false;
|
Chris@33
|
686 fillCache();
|
Chris@33
|
687 }
|
Chris@29
|
688 }
|
Chris@29
|
689
|
Chris@29
|
690 void
|
Chris@0
|
691 SpectrogramLayer::cacheInvalid()
|
Chris@0
|
692 {
|
Chris@0
|
693 m_cacheInvalid = true;
|
Chris@0
|
694 m_pixmapCacheInvalid = true;
|
Chris@0
|
695 m_cachedInitialVisibleArea = false;
|
Chris@0
|
696 fillCache();
|
Chris@0
|
697 }
|
Chris@0
|
698
|
Chris@0
|
699 void
|
Chris@0
|
700 SpectrogramLayer::cacheInvalid(size_t, size_t)
|
Chris@0
|
701 {
|
Chris@0
|
702 // for now (or forever?)
|
Chris@0
|
703 cacheInvalid();
|
Chris@0
|
704 }
|
Chris@0
|
705
|
Chris@0
|
706 void
|
Chris@0
|
707 SpectrogramLayer::fillCache()
|
Chris@0
|
708 {
|
Chris@0
|
709 #ifdef DEBUG_SPECTROGRAM_REPAINT
|
Chris@0
|
710 std::cerr << "SpectrogramLayer::fillCache" << std::endl;
|
Chris@0
|
711 #endif
|
Chris@0
|
712 QMutexLocker locker(&m_mutex);
|
Chris@0
|
713
|
Chris@0
|
714 m_lastFillExtent = 0;
|
Chris@0
|
715
|
Chris@0
|
716 delete m_updateTimer;
|
Chris@0
|
717 m_updateTimer = new QTimer(this);
|
Chris@0
|
718 connect(m_updateTimer, SIGNAL(timeout()), this, SLOT(fillTimerTimedOut()));
|
Chris@0
|
719 m_updateTimer->start(200);
|
Chris@0
|
720
|
Chris@0
|
721 if (!m_fillThread) {
|
Chris@0
|
722 std::cerr << "SpectrogramLayer::fillCache creating thread" << std::endl;
|
Chris@0
|
723 m_fillThread = new CacheFillThread(*this);
|
Chris@0
|
724 m_fillThread->start();
|
Chris@0
|
725 }
|
Chris@0
|
726
|
Chris@0
|
727 m_condition.wakeAll();
|
Chris@0
|
728 }
|
Chris@0
|
729
|
Chris@0
|
730 void
|
Chris@0
|
731 SpectrogramLayer::fillTimerTimedOut()
|
Chris@0
|
732 {
|
Chris@0
|
733 if (m_fillThread && m_model) {
|
Chris@0
|
734 size_t fillExtent = m_fillThread->getFillExtent();
|
Chris@0
|
735 #ifdef DEBUG_SPECTROGRAM_REPAINT
|
Chris@0
|
736 std::cerr << "SpectrogramLayer::fillTimerTimedOut: extent " << fillExtent << ", last " << m_lastFillExtent << ", total " << m_model->getEndFrame() << std::endl;
|
Chris@0
|
737 #endif
|
Chris@0
|
738 if (fillExtent >= m_lastFillExtent) {
|
Chris@0
|
739 if (fillExtent >= m_model->getEndFrame() && m_lastFillExtent > 0) {
|
Chris@0
|
740 #ifdef DEBUG_SPECTROGRAM_REPAINT
|
Chris@0
|
741 std::cerr << "complete!" << std::endl;
|
Chris@0
|
742 #endif
|
Chris@0
|
743 emit modelChanged();
|
Chris@0
|
744 m_pixmapCacheInvalid = true;
|
Chris@0
|
745 delete m_updateTimer;
|
Chris@0
|
746 m_updateTimer = 0;
|
Chris@0
|
747 m_lastFillExtent = 0;
|
Chris@0
|
748 } else if (fillExtent > m_lastFillExtent) {
|
Chris@0
|
749 #ifdef DEBUG_SPECTROGRAM_REPAINT
|
Chris@0
|
750 std::cerr << "SpectrogramLayer: emitting modelChanged("
|
Chris@0
|
751 << m_lastFillExtent << "," << fillExtent << ")" << std::endl;
|
Chris@0
|
752 #endif
|
Chris@0
|
753 emit modelChanged(m_lastFillExtent, fillExtent);
|
Chris@0
|
754 m_pixmapCacheInvalid = true;
|
Chris@0
|
755 m_lastFillExtent = fillExtent;
|
Chris@0
|
756 }
|
Chris@0
|
757 } else {
|
Chris@0
|
758 if (m_view) {
|
Chris@0
|
759 size_t sf = 0;
|
Chris@0
|
760 if (m_view->getStartFrame() > 0) sf = m_view->getStartFrame();
|
Chris@0
|
761 #ifdef DEBUG_SPECTROGRAM_REPAINT
|
Chris@0
|
762 std::cerr << "SpectrogramLayer: going backwards, emitting modelChanged("
|
Chris@0
|
763 << sf << "," << m_view->getEndFrame() << ")" << std::endl;
|
Chris@0
|
764 #endif
|
Chris@0
|
765 emit modelChanged(sf, m_view->getEndFrame());
|
Chris@0
|
766 m_pixmapCacheInvalid = true;
|
Chris@0
|
767 }
|
Chris@0
|
768 m_lastFillExtent = fillExtent;
|
Chris@0
|
769 }
|
Chris@0
|
770 }
|
Chris@0
|
771 }
|
Chris@0
|
772
|
Chris@0
|
773 void
|
Chris@0
|
774 SpectrogramLayer::setCacheColourmap()
|
Chris@0
|
775 {
|
Chris@0
|
776 if (m_cacheInvalid || !m_cache) return;
|
Chris@0
|
777
|
Chris@10
|
778 int formerRotation = m_colourRotation;
|
Chris@10
|
779
|
Chris@31
|
780 m_cache->setColour(0, Qt::white);
|
Chris@0
|
781
|
Chris@0
|
782 for (int pixel = 1; pixel < 256; ++pixel) {
|
Chris@0
|
783
|
Chris@0
|
784 QColor colour;
|
Chris@0
|
785 int hue, px;
|
Chris@0
|
786
|
Chris@0
|
787 switch (m_colourScheme) {
|
Chris@0
|
788
|
Chris@0
|
789 default:
|
Chris@0
|
790 case DefaultColours:
|
Chris@0
|
791 hue = 256 - pixel;
|
Chris@0
|
792 colour = QColor::fromHsv(hue, pixel/2 + 128, pixel);
|
Chris@0
|
793 break;
|
Chris@0
|
794
|
Chris@0
|
795 case WhiteOnBlack:
|
Chris@0
|
796 colour = QColor(pixel, pixel, pixel);
|
Chris@0
|
797 break;
|
Chris@0
|
798
|
Chris@0
|
799 case BlackOnWhite:
|
Chris@0
|
800 colour = QColor(256-pixel, 256-pixel, 256-pixel);
|
Chris@0
|
801 break;
|
Chris@0
|
802
|
Chris@0
|
803 case RedOnBlue:
|
Chris@0
|
804 colour = QColor(pixel > 128 ? (pixel - 128) * 2 : 0, 0,
|
Chris@0
|
805 pixel < 128 ? pixel : (256 - pixel));
|
Chris@0
|
806 break;
|
Chris@0
|
807
|
Chris@0
|
808 case YellowOnBlack:
|
Chris@0
|
809 px = 256 - pixel;
|
Chris@0
|
810 colour = QColor(px < 64 ? 255 - px/2 :
|
Chris@0
|
811 px < 128 ? 224 - (px - 64) :
|
Chris@0
|
812 px < 192 ? 160 - (px - 128) * 3 / 2 :
|
Chris@0
|
813 256 - px,
|
Chris@0
|
814 pixel,
|
Chris@0
|
815 pixel / 4);
|
Chris@0
|
816 break;
|
Chris@0
|
817
|
Chris@0
|
818 case RedOnBlack:
|
Chris@0
|
819 colour = QColor::fromHsv(10, pixel, pixel);
|
Chris@0
|
820 break;
|
Chris@0
|
821 }
|
Chris@0
|
822
|
Chris@31
|
823 m_cache->setColour(pixel, colour);
|
Chris@0
|
824 }
|
Chris@9
|
825
|
Chris@9
|
826 m_colourRotation = 0;
|
Chris@10
|
827 rotateCacheColourmap(m_colourRotation - formerRotation);
|
Chris@10
|
828 m_colourRotation = formerRotation;
|
Chris@9
|
829 }
|
Chris@9
|
830
|
Chris@9
|
831 void
|
Chris@9
|
832 SpectrogramLayer::rotateCacheColourmap(int distance)
|
Chris@9
|
833 {
|
Chris@10
|
834 if (!m_cache) return;
|
Chris@10
|
835
|
Chris@31
|
836 QColor newPixels[256];
|
Chris@9
|
837
|
Chris@31
|
838 newPixels[0] = m_cache->getColour(0);
|
Chris@9
|
839
|
Chris@9
|
840 for (int pixel = 1; pixel < 256; ++pixel) {
|
Chris@9
|
841 int target = pixel + distance;
|
Chris@9
|
842 while (target < 1) target += 255;
|
Chris@9
|
843 while (target > 255) target -= 255;
|
Chris@31
|
844 newPixels[target] = m_cache->getColour(pixel);
|
Chris@9
|
845 }
|
Chris@9
|
846
|
Chris@9
|
847 for (int pixel = 0; pixel < 256; ++pixel) {
|
Chris@31
|
848 m_cache->setColour(pixel, newPixels[pixel]);
|
Chris@9
|
849 }
|
Chris@0
|
850 }
|
Chris@0
|
851
|
Chris@0
|
852 bool
|
Chris@0
|
853 SpectrogramLayer::fillCacheColumn(int column, double *input,
|
Chris@0
|
854 fftw_complex *output,
|
Chris@0
|
855 fftw_plan plan,
|
Chris@9
|
856 size_t windowSize,
|
Chris@9
|
857 size_t increment,
|
Chris@0
|
858 const Window<double> &windower,
|
Chris@35
|
859 bool resetStoredPhase) const
|
Chris@0
|
860 {
|
Chris@35
|
861 static std::vector<double> storedPhase;
|
Chris@35
|
862
|
Chris@35
|
863 bool phaseAdjust =
|
Chris@35
|
864 (m_frequencyAdjustment == PhaseAdjustedFrequency ||
|
Chris@35
|
865 m_frequencyAdjustment == PhaseAdjustedPeaks);
|
Chris@35
|
866 bool haveStoredPhase = true;
|
Chris@35
|
867 size_t sampleRate = 0;
|
Chris@35
|
868
|
Chris@35
|
869 static int counter = 0;
|
Chris@35
|
870
|
Chris@35
|
871 if (phaseAdjust) {
|
Chris@35
|
872 if (resetStoredPhase || (storedPhase.size() != windowSize / 2)) {
|
Chris@35
|
873 haveStoredPhase = false;
|
Chris@35
|
874 storedPhase.clear();
|
Chris@35
|
875 for (size_t i = 0; i < windowSize / 2; ++i) {
|
Chris@35
|
876 storedPhase.push_back(0.0);
|
Chris@35
|
877 }
|
Chris@35
|
878 counter = 0;
|
Chris@35
|
879 }
|
Chris@35
|
880 ++counter;
|
Chris@35
|
881 sampleRate = m_model->getSampleRate();
|
Chris@35
|
882 }
|
Chris@35
|
883
|
Chris@0
|
884 int startFrame = increment * column;
|
Chris@9
|
885 int endFrame = startFrame + windowSize;
|
Chris@0
|
886
|
Chris@9
|
887 startFrame -= int(windowSize - increment) / 2;
|
Chris@9
|
888 endFrame -= int(windowSize - increment) / 2;
|
Chris@0
|
889 size_t pfx = 0;
|
Chris@0
|
890
|
Chris@0
|
891 if (startFrame < 0) {
|
Chris@0
|
892 pfx = size_t(-startFrame);
|
Chris@0
|
893 for (size_t i = 0; i < pfx; ++i) {
|
Chris@0
|
894 input[i] = 0.0;
|
Chris@0
|
895 }
|
Chris@0
|
896 }
|
Chris@0
|
897
|
Chris@0
|
898 size_t got = m_model->getValues(m_channel, startFrame + pfx,
|
Chris@0
|
899 endFrame, input + pfx);
|
Chris@9
|
900 while (got + pfx < windowSize) {
|
Chris@0
|
901 input[got + pfx] = 0.0;
|
Chris@0
|
902 ++got;
|
Chris@0
|
903 }
|
Chris@0
|
904
|
Chris@0
|
905 if (m_gain != 1.0) {
|
Chris@9
|
906 for (size_t i = 0; i < windowSize; ++i) {
|
Chris@0
|
907 input[i] *= m_gain;
|
Chris@0
|
908 }
|
Chris@0
|
909 }
|
Chris@0
|
910
|
Chris@0
|
911 windower.cut(input);
|
Chris@0
|
912
|
Chris@35
|
913 for (size_t i = 0; i < windowSize/2; ++i) {
|
Chris@35
|
914 double temp = input[i];
|
Chris@35
|
915 input[i] = input[i + windowSize/2];
|
Chris@35
|
916 input[i + windowSize/2] = temp;
|
Chris@35
|
917 }
|
Chris@35
|
918
|
Chris@0
|
919 fftw_execute(plan);
|
Chris@0
|
920
|
Chris@0
|
921 bool interrupted = false;
|
Chris@0
|
922
|
Chris@35
|
923 double prevMag = 0.0;
|
Chris@35
|
924
|
Chris@9
|
925 for (size_t i = 0; i < windowSize / 2; ++i) {
|
Chris@0
|
926
|
Chris@0
|
927 int value = 0;
|
Chris@35
|
928 double phase = 0.0;
|
Chris@35
|
929
|
Chris@35
|
930 double mag = sqrt(output[i][0] * output[i][0] +
|
Chris@35
|
931 output[i][1] * output[i][1]);
|
Chris@35
|
932 mag /= windowSize / 2;
|
Chris@35
|
933
|
Chris@35
|
934 if (phaseAdjust || (m_colourScale == PhaseColourScale)) {
|
Chris@35
|
935
|
Chris@35
|
936 // phase = atan2(-output[i][1], output[i][0]);
|
Chris@35
|
937 // phase = atan2(output[i][1], output[i][0]);
|
Chris@35
|
938 phase = atan2(output[i][0], output[i][1]);
|
Chris@35
|
939 // phase = MathUtilities::princarg(phase);
|
Chris@35
|
940 }
|
Chris@35
|
941
|
Chris@35
|
942 if (phaseAdjust && m_phaseAdjustCache && haveStoredPhase) {
|
Chris@35
|
943
|
Chris@35
|
944 bool peak = true;
|
Chris@35
|
945 if (m_frequencyAdjustment == PhaseAdjustedPeaks) {
|
Chris@35
|
946 if (mag < prevMag) peak = false;
|
Chris@35
|
947 else {
|
Chris@35
|
948 double nextMag = 0.0;
|
Chris@35
|
949 if (i < windowSize / 2 - 1) {
|
Chris@35
|
950 nextMag = sqrt(output[i+1][0] * output[i+1][0] +
|
Chris@35
|
951 output[i+1][1] * output[i+1][1]);
|
Chris@35
|
952 nextMag /= windowSize / 2;
|
Chris@35
|
953 }
|
Chris@35
|
954 if (mag < nextMag) peak = false;
|
Chris@35
|
955 }
|
Chris@35
|
956 prevMag = mag;
|
Chris@35
|
957 }
|
Chris@35
|
958
|
Chris@35
|
959 if (!peak) {
|
Chris@35
|
960 if (m_cacheInvalid || m_exiting) {
|
Chris@35
|
961 interrupted = true;
|
Chris@35
|
962 break;
|
Chris@35
|
963 }
|
Chris@35
|
964 m_phaseAdjustCache->setValueAt(column, i, SCHAR_MIN);
|
Chris@35
|
965 } else {
|
Chris@35
|
966
|
Chris@35
|
967 // if (i > 45 && i < 55 && counter == 10) {
|
Chris@35
|
968
|
Chris@35
|
969 double freq = (double(i) * sampleRate) / m_windowSize;
|
Chris@35
|
970 // std::cout << "\nbin = " << i << " initial estimate freq = " << freq
|
Chris@35
|
971 // << " mag = " << mag << std::endl;
|
Chris@35
|
972
|
Chris@35
|
973 double prevPhase = storedPhase[i];
|
Chris@35
|
974
|
Chris@35
|
975 double expectedPhase =
|
Chris@35
|
976 prevPhase + (2 * M_PI * i * increment) / m_windowSize;
|
Chris@35
|
977
|
Chris@35
|
978 double phaseError = MathUtilities::princarg(phase - expectedPhase);
|
Chris@35
|
979
|
Chris@35
|
980 // if (fabs(phaseError) > (1.2 * (increment * M_PI) / m_windowSize)) {
|
Chris@35
|
981 // std::cout << "error > " << (1.2 * (increment * M_PI) / m_windowSize) << std::endl;
|
Chris@35
|
982 // }// else {
|
Chris@35
|
983
|
Chris@35
|
984 // std::cout << "prevPhase = " << prevPhase << ", phase = " << phase
|
Chris@35
|
985 // << ", expected = " << MathUtilities::princarg(expectedPhase) << ", error = "
|
Chris@35
|
986 // << phaseError << std::endl;
|
Chris@35
|
987
|
Chris@35
|
988 double newFreq =
|
Chris@35
|
989 (sampleRate *
|
Chris@35
|
990 (expectedPhase + phaseError - prevPhase)) /
|
Chris@35
|
991 //(prevPhase - (expectedPhase + phaseError))) /
|
Chris@35
|
992 (2 * M_PI * increment);
|
Chris@35
|
993
|
Chris@35
|
994 // std::cout << freq << " (" << Pitch::getPitchLabelForFrequency(freq).toStdString() << ") -> " << newFreq << " (" << Pitch::getPitchLabelForFrequency(newFreq).toStdString() << ")" << std::endl;
|
Chris@35
|
995 // }
|
Chris@35
|
996 //}
|
Chris@35
|
997
|
Chris@35
|
998 double binRange = (double(i + 1) * sampleRate) / windowSize - freq;
|
Chris@35
|
999
|
Chris@35
|
1000 int offset = lrint(((newFreq - freq) / binRange) * 10);//!!!
|
Chris@35
|
1001
|
Chris@35
|
1002 if (m_cacheInvalid || m_exiting) {
|
Chris@35
|
1003 interrupted = true;
|
Chris@35
|
1004 break;
|
Chris@35
|
1005 }
|
Chris@35
|
1006 if (offset > SCHAR_MIN && offset <= SCHAR_MAX) {
|
Chris@35
|
1007 signed char coff = offset;
|
Chris@35
|
1008 m_phaseAdjustCache->setValueAt(column, i, (unsigned char)coff);
|
Chris@35
|
1009 } else {
|
Chris@35
|
1010 m_phaseAdjustCache->setValueAt(column, i, 0);
|
Chris@35
|
1011 }
|
Chris@35
|
1012 }
|
Chris@35
|
1013 storedPhase[i] = phase;
|
Chris@35
|
1014 }
|
Chris@0
|
1015
|
Chris@0
|
1016 if (m_colourScale == PhaseColourScale) {
|
Chris@0
|
1017
|
Chris@35
|
1018 phase = MathUtilities::princarg(phase);
|
Chris@0
|
1019 value = int((phase * 128 / M_PI) + 128);
|
Chris@0
|
1020
|
Chris@0
|
1021 } else {
|
Chris@1
|
1022
|
Chris@0
|
1023 double mag = sqrt(output[i][0] * output[i][0] +
|
Chris@0
|
1024 output[i][1] * output[i][1]);
|
Chris@9
|
1025 mag /= windowSize / 2;
|
Chris@0
|
1026
|
Chris@0
|
1027 switch (m_colourScale) {
|
Chris@0
|
1028
|
Chris@0
|
1029 default:
|
Chris@0
|
1030 case LinearColourScale:
|
Chris@0
|
1031 value = int(mag * 50 * 256);
|
Chris@0
|
1032 break;
|
Chris@0
|
1033
|
Chris@0
|
1034 case MeterColourScale:
|
Chris@0
|
1035 value = AudioLevel::multiplier_to_preview(mag * 50, 256);
|
Chris@0
|
1036 break;
|
Chris@0
|
1037
|
Chris@0
|
1038 case dBColourScale:
|
Chris@0
|
1039 mag = 20.0 * log10(mag);
|
Chris@0
|
1040 mag = (mag + 80.0) / 80.0;
|
Chris@0
|
1041 if (mag < 0.0) mag = 0.0;
|
Chris@0
|
1042 if (mag > 1.0) mag = 1.0;
|
Chris@0
|
1043 value = int(mag * 256);
|
Chris@0
|
1044 }
|
Chris@0
|
1045 }
|
Chris@0
|
1046
|
Chris@0
|
1047 if (value > 254) value = 254;
|
Chris@0
|
1048 if (value < 0) value = 0;
|
Chris@0
|
1049
|
Chris@0
|
1050 if (m_cacheInvalid || m_exiting) {
|
Chris@0
|
1051 interrupted = true;
|
Chris@0
|
1052 break;
|
Chris@0
|
1053 }
|
Chris@0
|
1054
|
Chris@31
|
1055 m_cache->setValueAt(column, i, value + 1);
|
Chris@0
|
1056 }
|
Chris@0
|
1057
|
Chris@0
|
1058 return !interrupted;
|
Chris@0
|
1059 }
|
Chris@0
|
1060
|
Chris@31
|
1061 SpectrogramLayer::Cache::Cache(size_t width, size_t height) :
|
Chris@31
|
1062 m_width(width),
|
Chris@31
|
1063 m_height(height)
|
Chris@31
|
1064 {
|
Chris@35
|
1065 // use malloc rather than new[], because we want to be able to use realloc
|
Chris@35
|
1066 m_values = (unsigned char *)
|
Chris@35
|
1067 malloc(m_width * m_height * sizeof(unsigned char));
|
Chris@35
|
1068 if (!m_values) throw std::bad_alloc();
|
Chris@31
|
1069 MUNLOCK(m_values, m_width * m_height * sizeof(unsigned char));
|
Chris@31
|
1070 }
|
Chris@31
|
1071
|
Chris@31
|
1072 SpectrogramLayer::Cache::~Cache()
|
Chris@31
|
1073 {
|
Chris@35
|
1074 if (m_values) free(m_values);
|
Chris@31
|
1075 }
|
Chris@31
|
1076
|
Chris@35
|
1077 void
|
Chris@35
|
1078 SpectrogramLayer::Cache::resize(size_t width, size_t height)
|
Chris@35
|
1079 {
|
Chris@35
|
1080 m_values = (unsigned char *)
|
Chris@35
|
1081 realloc(m_values, m_width * m_height * sizeof(unsigned char));
|
Chris@35
|
1082 if (!m_values) throw std::bad_alloc();
|
Chris@35
|
1083 MUNLOCK(m_values, m_width * m_height * sizeof(unsigned char));
|
Chris@35
|
1084 }
|
Chris@35
|
1085
|
Chris@31
|
1086 size_t
|
Chris@31
|
1087 SpectrogramLayer::Cache::getWidth() const
|
Chris@31
|
1088 {
|
Chris@31
|
1089 return m_width;
|
Chris@31
|
1090 }
|
Chris@31
|
1091
|
Chris@31
|
1092 size_t
|
Chris@31
|
1093 SpectrogramLayer::Cache::getHeight() const
|
Chris@31
|
1094 {
|
Chris@31
|
1095 return m_height;
|
Chris@31
|
1096 }
|
Chris@31
|
1097
|
Chris@31
|
1098 unsigned char
|
Chris@31
|
1099 SpectrogramLayer::Cache::getValueAt(size_t x, size_t y) const
|
Chris@31
|
1100 {
|
Chris@31
|
1101 if (x >= m_width || y >= m_height) return 0;
|
Chris@31
|
1102 return m_values[y * m_width + x];
|
Chris@31
|
1103 }
|
Chris@31
|
1104
|
Chris@31
|
1105 void
|
Chris@31
|
1106 SpectrogramLayer::Cache::setValueAt(size_t x, size_t y, unsigned char value)
|
Chris@31
|
1107 {
|
Chris@31
|
1108 if (x >= m_width || y >= m_height) return;
|
Chris@31
|
1109 m_values[y * m_width + x] = value;
|
Chris@31
|
1110 }
|
Chris@31
|
1111
|
Chris@31
|
1112 QColor
|
Chris@31
|
1113 SpectrogramLayer::Cache::getColour(unsigned char index) const
|
Chris@31
|
1114 {
|
Chris@31
|
1115 return m_colours[index];
|
Chris@31
|
1116 }
|
Chris@31
|
1117
|
Chris@31
|
1118 void
|
Chris@31
|
1119 SpectrogramLayer::Cache::setColour(unsigned char index, QColor colour)
|
Chris@31
|
1120 {
|
Chris@31
|
1121 m_colours[index] = colour;
|
Chris@31
|
1122 }
|
Chris@31
|
1123
|
Chris@31
|
1124 void
|
Chris@31
|
1125 SpectrogramLayer::Cache::fill(unsigned char value)
|
Chris@31
|
1126 {
|
Chris@31
|
1127 for (size_t i = 0; i < m_width * m_height; ++i) {
|
Chris@31
|
1128 m_values[i] = value;
|
Chris@31
|
1129 }
|
Chris@31
|
1130 }
|
Chris@31
|
1131
|
Chris@0
|
1132 void
|
Chris@0
|
1133 SpectrogramLayer::CacheFillThread::run()
|
Chris@0
|
1134 {
|
Chris@0
|
1135 // std::cerr << "SpectrogramLayer::CacheFillThread::run" << std::endl;
|
Chris@0
|
1136
|
Chris@0
|
1137 m_layer.m_mutex.lock();
|
Chris@0
|
1138
|
Chris@0
|
1139 while (!m_layer.m_exiting) {
|
Chris@0
|
1140
|
Chris@0
|
1141 bool interrupted = false;
|
Chris@0
|
1142
|
Chris@0
|
1143 // std::cerr << "SpectrogramLayer::CacheFillThread::run in loop" << std::endl;
|
Chris@0
|
1144
|
Chris@34
|
1145 if (m_layer.m_dormant) {
|
Chris@34
|
1146
|
Chris@34
|
1147 if (m_layer.m_cacheInvalid) {
|
Chris@34
|
1148 delete m_layer.m_cache;
|
Chris@35
|
1149 delete m_layer.m_phaseAdjustCache;
|
Chris@34
|
1150 m_layer.m_cache = 0;
|
Chris@35
|
1151 m_layer.m_phaseAdjustCache = 0;
|
Chris@34
|
1152 }
|
Chris@34
|
1153
|
Chris@34
|
1154 } else if (m_layer.m_model && m_layer.m_cacheInvalid) {
|
Chris@0
|
1155
|
Chris@0
|
1156 // std::cerr << "SpectrogramLayer::CacheFillThread::run: something to do" << std::endl;
|
Chris@0
|
1157
|
Chris@0
|
1158 while (!m_layer.m_model->isReady()) {
|
Chris@0
|
1159 m_layer.m_condition.wait(&m_layer.m_mutex, 100);
|
Chris@0
|
1160 }
|
Chris@0
|
1161
|
Chris@0
|
1162 m_layer.m_cachedInitialVisibleArea = false;
|
Chris@0
|
1163 m_layer.m_cacheInvalid = false;
|
Chris@0
|
1164 m_fillExtent = 0;
|
Chris@0
|
1165 m_fillCompletion = 0;
|
Chris@0
|
1166
|
Chris@0
|
1167 std::cerr << "SpectrogramLayer::CacheFillThread::run: model is ready" << std::endl;
|
Chris@0
|
1168
|
Chris@0
|
1169 size_t start = m_layer.m_model->getStartFrame();
|
Chris@0
|
1170 size_t end = m_layer.m_model->getEndFrame();
|
Chris@9
|
1171
|
Chris@9
|
1172 WindowType windowType = m_layer.m_windowType;
|
Chris@0
|
1173 size_t windowSize = m_layer.m_windowSize;
|
Chris@0
|
1174 size_t windowIncrement = m_layer.getWindowIncrement();
|
Chris@0
|
1175
|
Chris@0
|
1176 size_t visibleStart = start;
|
Chris@0
|
1177 size_t visibleEnd = end;
|
Chris@0
|
1178
|
Chris@0
|
1179 if (m_layer.m_view) {
|
Chris@0
|
1180 if (m_layer.m_view->getStartFrame() < 0) {
|
Chris@0
|
1181 visibleStart = 0;
|
Chris@0
|
1182 } else {
|
Chris@0
|
1183 visibleStart = m_layer.m_view->getStartFrame();
|
Chris@0
|
1184 visibleStart = (visibleStart / windowIncrement) *
|
Chris@0
|
1185 windowIncrement;
|
Chris@0
|
1186 }
|
Chris@0
|
1187 visibleEnd = m_layer.m_view->getEndFrame();
|
Chris@0
|
1188 }
|
Chris@0
|
1189
|
Chris@9
|
1190 size_t width = (end - start) / windowIncrement + 1;
|
Chris@9
|
1191 size_t height = windowSize / 2;
|
Chris@35
|
1192
|
Chris@35
|
1193 if (!m_layer.m_cache) {
|
Chris@35
|
1194 m_layer.m_cache = new Cache(width, height);
|
Chris@35
|
1195 } else if (width != m_layer.m_cache->getWidth() ||
|
Chris@35
|
1196 height != m_layer.m_cache->getHeight()) {
|
Chris@35
|
1197 m_layer.m_cache->resize(width, height);
|
Chris@35
|
1198 }
|
Chris@9
|
1199
|
Chris@0
|
1200 m_layer.setCacheColourmap();
|
Chris@33
|
1201 m_layer.m_cache->fill(0);
|
Chris@10
|
1202
|
Chris@35
|
1203 if (m_layer.m_frequencyAdjustment == PhaseAdjustedFrequency ||
|
Chris@35
|
1204 m_layer.m_frequencyAdjustment == PhaseAdjustedPeaks) {
|
Chris@35
|
1205
|
Chris@35
|
1206 if (!m_layer.m_phaseAdjustCache) {
|
Chris@35
|
1207 m_layer.m_phaseAdjustCache = new Cache(width, height);
|
Chris@35
|
1208 } else if (width != m_layer.m_phaseAdjustCache->getWidth() ||
|
Chris@35
|
1209 height != m_layer.m_phaseAdjustCache->getHeight()) {
|
Chris@35
|
1210 m_layer.m_phaseAdjustCache->resize(width, height);
|
Chris@35
|
1211 }
|
Chris@35
|
1212
|
Chris@35
|
1213 m_layer.m_phaseAdjustCache->fill(0);
|
Chris@35
|
1214
|
Chris@35
|
1215 } else {
|
Chris@35
|
1216 delete m_layer.m_phaseAdjustCache;
|
Chris@35
|
1217 m_layer.m_phaseAdjustCache = 0;
|
Chris@35
|
1218 }
|
Chris@35
|
1219
|
Chris@33
|
1220 // We don't need a lock when writing to or reading from
|
Chris@33
|
1221 // the pixels in the cache, because it's a fixed size
|
Chris@33
|
1222 // array. We do need to ensure we have the width and
|
Chris@35
|
1223 // height of the cache and the FFT parameters known before
|
Chris@33
|
1224 // we unlock, in case they change in the model while we
|
Chris@33
|
1225 // aren't holding a lock. It's safe for us to continue to
|
Chris@33
|
1226 // use the "old" values if that happens, because they will
|
Chris@33
|
1227 // continue to match the dimensions of the actual cache
|
Chris@33
|
1228 // (which we manage, not the model).
|
Chris@0
|
1229 m_layer.m_mutex.unlock();
|
Chris@0
|
1230
|
Chris@0
|
1231 double *input = (double *)
|
Chris@0
|
1232 fftw_malloc(windowSize * sizeof(double));
|
Chris@0
|
1233
|
Chris@0
|
1234 fftw_complex *output = (fftw_complex *)
|
Chris@0
|
1235 fftw_malloc(windowSize * sizeof(fftw_complex));
|
Chris@0
|
1236
|
Chris@0
|
1237 fftw_plan plan = fftw_plan_dft_r2c_1d(windowSize, input,
|
Chris@1
|
1238 output, FFTW_ESTIMATE);
|
Chris@0
|
1239
|
Chris@9
|
1240 Window<double> windower(windowType, windowSize);
|
Chris@0
|
1241
|
Chris@0
|
1242 if (!plan) {
|
Chris@1
|
1243 std::cerr << "WARNING: fftw_plan_dft_r2c_1d(" << windowSize << ") failed!" << std::endl;
|
Chris@0
|
1244 fftw_free(input);
|
Chris@0
|
1245 fftw_free(output);
|
Chris@0
|
1246 continue;
|
Chris@0
|
1247 }
|
Chris@0
|
1248
|
Chris@0
|
1249 int counter = 0;
|
Chris@0
|
1250 int updateAt = (end / windowIncrement) / 20;
|
Chris@0
|
1251 if (updateAt < 100) updateAt = 100;
|
Chris@0
|
1252
|
Chris@0
|
1253 bool doVisibleFirst = (visibleStart != start && visibleEnd != end);
|
Chris@0
|
1254
|
Chris@0
|
1255 if (doVisibleFirst) {
|
Chris@0
|
1256
|
Chris@0
|
1257 for (size_t f = visibleStart; f < visibleEnd; f += windowIncrement) {
|
Chris@0
|
1258
|
Chris@0
|
1259 m_layer.fillCacheColumn(int((f - start) / windowIncrement),
|
Chris@9
|
1260 input, output, plan,
|
Chris@9
|
1261 windowSize, windowIncrement,
|
Chris@35
|
1262 //!!! actually if we're doing phase adjustment we also want to fill the column preceding the visible area so that we have the right values for the first visible one (also applies below)
|
Chris@35
|
1263 windower, f == visibleStart);
|
Chris@0
|
1264
|
Chris@0
|
1265 if (m_layer.m_cacheInvalid || m_layer.m_exiting) {
|
Chris@0
|
1266 interrupted = true;
|
Chris@0
|
1267 m_fillExtent = 0;
|
Chris@0
|
1268 break;
|
Chris@0
|
1269 }
|
Chris@0
|
1270
|
Chris@0
|
1271 if (++counter == updateAt) {
|
Chris@0
|
1272 if (f < end) m_fillExtent = f;
|
Chris@0
|
1273 m_fillCompletion = size_t(100 * fabsf(float(f - visibleStart) /
|
Chris@0
|
1274 float(end - start)));
|
Chris@0
|
1275 counter = 0;
|
Chris@0
|
1276 }
|
Chris@0
|
1277 }
|
Chris@0
|
1278 }
|
Chris@0
|
1279
|
Chris@0
|
1280 m_layer.m_cachedInitialVisibleArea = true;
|
Chris@0
|
1281
|
Chris@0
|
1282 if (!interrupted && doVisibleFirst) {
|
Chris@0
|
1283
|
Chris@0
|
1284 for (size_t f = visibleEnd; f < end; f += windowIncrement) {
|
Chris@0
|
1285
|
Chris@0
|
1286 if (!m_layer.fillCacheColumn(int((f - start) / windowIncrement),
|
Chris@9
|
1287 input, output, plan,
|
Chris@9
|
1288 windowSize, windowIncrement,
|
Chris@35
|
1289 windower, f == visibleEnd)) {
|
Chris@0
|
1290 interrupted = true;
|
Chris@0
|
1291 m_fillExtent = 0;
|
Chris@0
|
1292 break;
|
Chris@0
|
1293 }
|
Chris@0
|
1294
|
Chris@0
|
1295
|
Chris@0
|
1296 if (++counter == updateAt) {
|
Chris@0
|
1297 if (f < end) m_fillExtent = f;
|
Chris@0
|
1298 m_fillCompletion = size_t(100 * fabsf(float(f - visibleStart) /
|
Chris@0
|
1299 float(end - start)));
|
Chris@0
|
1300 counter = 0;
|
Chris@0
|
1301 }
|
Chris@0
|
1302 }
|
Chris@0
|
1303 }
|
Chris@0
|
1304
|
Chris@0
|
1305 if (!interrupted) {
|
Chris@0
|
1306
|
Chris@0
|
1307 size_t remainingEnd = end;
|
Chris@0
|
1308 if (doVisibleFirst) {
|
Chris@0
|
1309 remainingEnd = visibleStart;
|
Chris@0
|
1310 if (remainingEnd > start) --remainingEnd;
|
Chris@0
|
1311 else remainingEnd = start;
|
Chris@0
|
1312 }
|
Chris@0
|
1313 size_t baseCompletion = m_fillCompletion;
|
Chris@0
|
1314
|
Chris@0
|
1315 for (size_t f = start; f < remainingEnd; f += windowIncrement) {
|
Chris@0
|
1316
|
Chris@0
|
1317 if (!m_layer.fillCacheColumn(int((f - start) / windowIncrement),
|
Chris@9
|
1318 input, output, plan,
|
Chris@9
|
1319 windowSize, windowIncrement,
|
Chris@35
|
1320 windower, f == start)) {
|
Chris@0
|
1321 interrupted = true;
|
Chris@0
|
1322 m_fillExtent = 0;
|
Chris@0
|
1323 break;
|
Chris@0
|
1324 }
|
Chris@0
|
1325
|
Chris@0
|
1326 if (++counter == updateAt) {
|
Chris@0
|
1327 m_fillExtent = f;
|
Chris@0
|
1328 m_fillCompletion = baseCompletion +
|
Chris@0
|
1329 size_t(100 * fabsf(float(f - start) /
|
Chris@0
|
1330 float(end - start)));
|
Chris@0
|
1331 counter = 0;
|
Chris@0
|
1332 }
|
Chris@0
|
1333 }
|
Chris@0
|
1334 }
|
Chris@0
|
1335
|
Chris@0
|
1336 fftw_destroy_plan(plan);
|
Chris@0
|
1337 fftw_free(output);
|
Chris@0
|
1338 fftw_free(input);
|
Chris@0
|
1339
|
Chris@0
|
1340 if (!interrupted) {
|
Chris@0
|
1341 m_fillExtent = end;
|
Chris@0
|
1342 m_fillCompletion = 100;
|
Chris@0
|
1343 }
|
Chris@0
|
1344
|
Chris@0
|
1345 m_layer.m_mutex.lock();
|
Chris@0
|
1346 }
|
Chris@0
|
1347
|
Chris@0
|
1348 if (!interrupted) m_layer.m_condition.wait(&m_layer.m_mutex, 2000);
|
Chris@0
|
1349 }
|
Chris@0
|
1350 }
|
Chris@0
|
1351
|
Chris@0
|
1352 bool
|
Chris@0
|
1353 SpectrogramLayer::getYBinRange(int y, float &q0, float &q1) const
|
Chris@0
|
1354 {
|
Chris@0
|
1355 int h = m_view->height();
|
Chris@0
|
1356 if (y < 0 || y >= h) return false;
|
Chris@0
|
1357
|
Chris@0
|
1358 // Each pixel in a column is drawn from a possibly non-
|
Chris@0
|
1359 // integral set of frequency bins.
|
Chris@0
|
1360
|
Chris@0
|
1361 if (m_frequencyScale == LinearFrequencyScale) {
|
Chris@0
|
1362
|
Chris@0
|
1363 size_t bins = m_windowSize / 2;
|
Chris@0
|
1364
|
Chris@0
|
1365 if (m_maxFrequency > 0) {
|
Chris@0
|
1366 int sr = m_model->getSampleRate();
|
Chris@0
|
1367 bins = int((double(m_maxFrequency) * m_windowSize) / sr + 0.1);
|
Chris@0
|
1368 if (bins > m_windowSize / 2) bins = m_windowSize / 2;
|
Chris@0
|
1369 }
|
Chris@0
|
1370
|
Chris@0
|
1371 q0 = float(h - y - 1) * bins / h;
|
Chris@0
|
1372 q1 = float(h - y) * bins / h;
|
Chris@0
|
1373
|
Chris@0
|
1374 } else {
|
Chris@0
|
1375
|
Chris@0
|
1376 // This is all most ad-hoc. I'm not at my brightest.
|
Chris@0
|
1377
|
Chris@0
|
1378 int sr = m_model->getSampleRate();
|
Chris@0
|
1379
|
Chris@0
|
1380 float maxf = m_maxFrequency;
|
Chris@0
|
1381 if (maxf == 0.0) maxf = float(sr) / 2;
|
Chris@0
|
1382
|
Chris@0
|
1383 float minf = float(sr) / m_windowSize;
|
Chris@0
|
1384
|
Chris@0
|
1385 float maxlogf = log10f(maxf);
|
Chris@0
|
1386 float minlogf = log10f(minf);
|
Chris@35
|
1387
|
Chris@0
|
1388 float logf0 = minlogf + ((maxlogf - minlogf) * (h - y - 1)) / h;
|
Chris@0
|
1389 float logf1 = minlogf + ((maxlogf - minlogf) * (h - y)) / h;
|
Chris@0
|
1390
|
Chris@0
|
1391 float f0 = pow(10.f, logf0);
|
Chris@0
|
1392 float f1 = pow(10.f, logf1);
|
Chris@0
|
1393
|
Chris@0
|
1394 q0 = ((f0 * m_windowSize) / sr) - 1;
|
Chris@0
|
1395 q1 = ((f1 * m_windowSize) / sr) - 1;
|
Chris@0
|
1396
|
Chris@0
|
1397 // std::cout << "y=" << y << " h=" << h << " maxf=" << maxf << " maxlogf="
|
Chris@0
|
1398 // << maxlogf << " logf0=" << logf0 << " f0=" << f0 << " q0="
|
Chris@0
|
1399 // << q0 << std::endl;
|
Chris@0
|
1400 }
|
Chris@0
|
1401
|
Chris@0
|
1402 return true;
|
Chris@0
|
1403 }
|
Chris@0
|
1404
|
Chris@0
|
1405 bool
|
Chris@20
|
1406 SpectrogramLayer::getXBinRange(int x, float &s0, float &s1) const
|
Chris@0
|
1407 {
|
Chris@21
|
1408 size_t modelStart = m_model->getStartFrame();
|
Chris@21
|
1409 size_t modelEnd = m_model->getEndFrame();
|
Chris@0
|
1410
|
Chris@0
|
1411 // Each pixel column covers an exact range of sample frames:
|
Chris@20
|
1412 int f0 = getFrameForX(x) - modelStart;
|
Chris@20
|
1413 int f1 = getFrameForX(x + 1) - modelStart - 1;
|
Chris@20
|
1414
|
Chris@0
|
1415 if (f1 < int(modelStart) || f0 > int(modelEnd)) return false;
|
Chris@20
|
1416
|
Chris@0
|
1417 // And that range may be drawn from a possibly non-integral
|
Chris@0
|
1418 // range of spectrogram windows:
|
Chris@0
|
1419
|
Chris@0
|
1420 size_t windowIncrement = getWindowIncrement();
|
Chris@0
|
1421 s0 = float(f0) / windowIncrement;
|
Chris@0
|
1422 s1 = float(f1) / windowIncrement;
|
Chris@0
|
1423
|
Chris@0
|
1424 return true;
|
Chris@0
|
1425 }
|
Chris@0
|
1426
|
Chris@0
|
1427 bool
|
Chris@0
|
1428 SpectrogramLayer::getXBinSourceRange(int x, RealTime &min, RealTime &max) const
|
Chris@0
|
1429 {
|
Chris@0
|
1430 float s0 = 0, s1 = 0;
|
Chris@0
|
1431 if (!getXBinRange(x, s0, s1)) return false;
|
Chris@0
|
1432
|
Chris@0
|
1433 int s0i = int(s0 + 0.001);
|
Chris@0
|
1434 int s1i = int(s1);
|
Chris@0
|
1435
|
Chris@0
|
1436 int windowIncrement = getWindowIncrement();
|
Chris@0
|
1437 int w0 = s0i * windowIncrement - (m_windowSize - windowIncrement)/2;
|
Chris@0
|
1438 int w1 = s1i * windowIncrement + windowIncrement +
|
Chris@0
|
1439 (m_windowSize - windowIncrement)/2 - 1;
|
Chris@0
|
1440
|
Chris@0
|
1441 min = RealTime::frame2RealTime(w0, m_model->getSampleRate());
|
Chris@0
|
1442 max = RealTime::frame2RealTime(w1, m_model->getSampleRate());
|
Chris@0
|
1443 return true;
|
Chris@0
|
1444 }
|
Chris@0
|
1445
|
Chris@0
|
1446 bool
|
Chris@0
|
1447 SpectrogramLayer::getYBinSourceRange(int y, float &freqMin, float &freqMax)
|
Chris@0
|
1448 const
|
Chris@0
|
1449 {
|
Chris@0
|
1450 float q0 = 0, q1 = 0;
|
Chris@0
|
1451 if (!getYBinRange(y, q0, q1)) return false;
|
Chris@0
|
1452
|
Chris@0
|
1453 int q0i = int(q0 + 0.001);
|
Chris@0
|
1454 int q1i = int(q1);
|
Chris@0
|
1455
|
Chris@0
|
1456 int sr = m_model->getSampleRate();
|
Chris@0
|
1457
|
Chris@0
|
1458 for (int q = q0i; q <= q1i; ++q) {
|
Chris@35
|
1459 int binfreq = (sr * q) / m_windowSize;
|
Chris@0
|
1460 if (q == q0i) freqMin = binfreq;
|
Chris@0
|
1461 if (q == q1i) freqMax = binfreq;
|
Chris@0
|
1462 }
|
Chris@0
|
1463 return true;
|
Chris@0
|
1464 }
|
Chris@35
|
1465
|
Chris@35
|
1466 bool
|
Chris@35
|
1467 SpectrogramLayer::getAdjustedYBinSourceRange(int x, int y,
|
Chris@35
|
1468 float &freqMin, float &freqMax,
|
Chris@35
|
1469 float &adjFreqMin, float &adjFreqMax)
|
Chris@35
|
1470 const
|
Chris@35
|
1471 {
|
Chris@35
|
1472 float s0 = 0, s1 = 0;
|
Chris@35
|
1473 if (!getXBinRange(x, s0, s1)) return false;
|
Chris@35
|
1474
|
Chris@35
|
1475 float q0 = 0, q1 = 0;
|
Chris@35
|
1476 if (!getYBinRange(y, q0, q1)) return false;
|
Chris@35
|
1477
|
Chris@35
|
1478 int s0i = int(s0 + 0.001);
|
Chris@35
|
1479 int s1i = int(s1);
|
Chris@35
|
1480
|
Chris@35
|
1481 int q0i = int(q0 + 0.001);
|
Chris@35
|
1482 int q1i = int(q1);
|
Chris@35
|
1483
|
Chris@35
|
1484 int sr = m_model->getSampleRate();
|
Chris@35
|
1485
|
Chris@35
|
1486 bool haveAdj = false;
|
Chris@35
|
1487
|
Chris@35
|
1488 for (int q = q0i; q <= q1i; ++q) {
|
Chris@35
|
1489
|
Chris@35
|
1490 for (int s = s0i; s <= s1i; ++s) {
|
Chris@35
|
1491
|
Chris@35
|
1492 float binfreq = (sr * q) / m_windowSize;
|
Chris@35
|
1493 if (q == q0i) freqMin = binfreq;
|
Chris@35
|
1494 if (q == q1i) freqMax = binfreq;
|
Chris@35
|
1495
|
Chris@35
|
1496 if ((m_frequencyAdjustment == PhaseAdjustedFrequency ||
|
Chris@35
|
1497 m_frequencyAdjustment == PhaseAdjustedPeaks) &&
|
Chris@35
|
1498 m_phaseAdjustCache) {
|
Chris@35
|
1499
|
Chris@35
|
1500 unsigned char cadj = m_phaseAdjustCache->getValueAt(s, q);
|
Chris@35
|
1501 int adjust = int((signed char)cadj);
|
Chris@35
|
1502 if (adjust == SCHAR_MIN &&
|
Chris@35
|
1503 m_frequencyAdjustment == PhaseAdjustedPeaks) {
|
Chris@35
|
1504 continue;
|
Chris@35
|
1505 }
|
Chris@35
|
1506
|
Chris@35
|
1507 float nextBinFreq = (sr * (q + 1)) / m_windowSize;
|
Chris@35
|
1508 float fadjust = (adjust * (nextBinFreq - binfreq)) / 10.0;//!!!
|
Chris@35
|
1509 float f = binfreq + fadjust;
|
Chris@35
|
1510 if (!haveAdj || f < adjFreqMin) adjFreqMin = f;
|
Chris@35
|
1511 if (!haveAdj || f > adjFreqMax) adjFreqMax = f;
|
Chris@35
|
1512 haveAdj = true;
|
Chris@35
|
1513 }
|
Chris@35
|
1514 }
|
Chris@35
|
1515 }
|
Chris@35
|
1516
|
Chris@35
|
1517 if (!haveAdj) {
|
Chris@35
|
1518 adjFreqMin = adjFreqMax = 0.0f;
|
Chris@35
|
1519 }
|
Chris@35
|
1520
|
Chris@35
|
1521 return haveAdj;
|
Chris@35
|
1522 }
|
Chris@0
|
1523
|
Chris@0
|
1524 bool
|
Chris@0
|
1525 SpectrogramLayer::getXYBinSourceRange(int x, int y, float &dbMin, float &dbMax) const
|
Chris@0
|
1526 {
|
Chris@0
|
1527 float q0 = 0, q1 = 0;
|
Chris@0
|
1528 if (!getYBinRange(y, q0, q1)) return false;
|
Chris@0
|
1529
|
Chris@0
|
1530 float s0 = 0, s1 = 0;
|
Chris@0
|
1531 if (!getXBinRange(x, s0, s1)) return false;
|
Chris@0
|
1532
|
Chris@0
|
1533 int q0i = int(q0 + 0.001);
|
Chris@0
|
1534 int q1i = int(q1);
|
Chris@0
|
1535
|
Chris@0
|
1536 int s0i = int(s0 + 0.001);
|
Chris@0
|
1537 int s1i = int(s1);
|
Chris@0
|
1538
|
Chris@0
|
1539 if (m_mutex.tryLock()) {
|
Chris@0
|
1540 if (m_cache && !m_cacheInvalid) {
|
Chris@0
|
1541
|
Chris@31
|
1542 int cw = m_cache->getWidth();
|
Chris@31
|
1543 int ch = m_cache->getHeight();
|
Chris@0
|
1544
|
Chris@0
|
1545 int min = -1, max = -1;
|
Chris@0
|
1546
|
Chris@0
|
1547 for (int q = q0i; q <= q1i; ++q) {
|
Chris@0
|
1548 for (int s = s0i; s <= s1i; ++s) {
|
Chris@0
|
1549 if (s >= 0 && q >= 0 && s < cw && q < ch) {
|
Chris@31
|
1550 int value = int(m_cache->getValueAt(s, q));
|
Chris@0
|
1551 if (min == -1 || value < min) min = value;
|
Chris@0
|
1552 if (max == -1 || value > max) max = value;
|
Chris@0
|
1553 }
|
Chris@0
|
1554 }
|
Chris@0
|
1555 }
|
Chris@0
|
1556
|
Chris@0
|
1557 if (min < 0) return false;
|
Chris@0
|
1558
|
Chris@0
|
1559 dbMin = (float(min) / 256.0) * 80.0 - 80.0;
|
Chris@0
|
1560 dbMax = (float(max + 1) / 256.0) * 80.0 - 80.1;
|
Chris@0
|
1561
|
Chris@0
|
1562 m_mutex.unlock();
|
Chris@0
|
1563 return true;
|
Chris@0
|
1564 }
|
Chris@0
|
1565
|
Chris@0
|
1566 m_mutex.unlock();
|
Chris@0
|
1567 }
|
Chris@0
|
1568
|
Chris@0
|
1569 return false;
|
Chris@0
|
1570 }
|
Chris@0
|
1571
|
Chris@0
|
1572 void
|
Chris@0
|
1573 SpectrogramLayer::paint(QPainter &paint, QRect rect) const
|
Chris@0
|
1574 {
|
Chris@0
|
1575 // Profiler profiler("SpectrogramLayer::paint", true);
|
Chris@0
|
1576 #ifdef DEBUG_SPECTROGRAM_REPAINT
|
Chris@0
|
1577 std::cerr << "SpectrogramLayer::paint(): m_model is " << m_model << ", zoom level is " << m_view->getZoomLevel() << ", m_updateTimer " << m_updateTimer << ", pixmap cache invalid " << m_pixmapCacheInvalid << std::endl;
|
Chris@0
|
1578 #endif
|
Chris@0
|
1579
|
Chris@0
|
1580 if (!m_model || !m_model->isOK() || !m_model->isReady()) {
|
Chris@0
|
1581 return;
|
Chris@0
|
1582 }
|
Chris@0
|
1583
|
Chris@29
|
1584 if (m_dormant) {
|
Chris@33
|
1585 std::cerr << "SpectrogramLayer::paint(): Layer is dormant" << std::endl;
|
Chris@29
|
1586 return;
|
Chris@29
|
1587 }
|
Chris@29
|
1588
|
Chris@0
|
1589 #ifdef DEBUG_SPECTROGRAM_REPAINT
|
Chris@0
|
1590 std::cerr << "SpectrogramLayer::paint(): About to lock" << std::endl;
|
Chris@0
|
1591 #endif
|
Chris@0
|
1592
|
Chris@0
|
1593 /*
|
Chris@0
|
1594 if (m_cachedInitialVisibleArea) {
|
Chris@0
|
1595 if (!m_mutex.tryLock()) {
|
Chris@0
|
1596 m_view->update();
|
Chris@0
|
1597 return;
|
Chris@0
|
1598 }
|
Chris@0
|
1599 } else {
|
Chris@0
|
1600 */
|
Chris@0
|
1601 m_mutex.lock();
|
Chris@0
|
1602 // }
|
Chris@0
|
1603
|
Chris@0
|
1604 #ifdef DEBUG_SPECTROGRAM_REPAINT
|
Chris@0
|
1605 std::cerr << "SpectrogramLayer::paint(): locked" << std::endl;
|
Chris@0
|
1606 #endif
|
Chris@0
|
1607
|
Chris@0
|
1608 if (m_cacheInvalid) { // lock the mutex before checking this
|
Chris@0
|
1609 m_mutex.unlock();
|
Chris@0
|
1610 #ifdef DEBUG_SPECTROGRAM_REPAINT
|
Chris@0
|
1611 std::cerr << "SpectrogramLayer::paint(): Cache invalid, returning" << std::endl;
|
Chris@0
|
1612 #endif
|
Chris@0
|
1613 return;
|
Chris@0
|
1614 }
|
Chris@0
|
1615
|
Chris@0
|
1616 bool stillCacheing = (m_updateTimer != 0);
|
Chris@0
|
1617
|
Chris@0
|
1618 #ifdef DEBUG_SPECTROGRAM_REPAINT
|
Chris@0
|
1619 std::cerr << "SpectrogramLayer::paint(): Still cacheing = " << stillCacheing << std::endl;
|
Chris@0
|
1620 #endif
|
Chris@0
|
1621
|
Chris@0
|
1622 long startFrame = m_view->getStartFrame();
|
Chris@0
|
1623 int zoomLevel = m_view->getZoomLevel();
|
Chris@0
|
1624
|
Chris@0
|
1625 int x0 = 0;
|
Chris@0
|
1626 int x1 = m_view->width();
|
Chris@0
|
1627 int y0 = 0;
|
Chris@0
|
1628 int y1 = m_view->height();
|
Chris@0
|
1629
|
Chris@0
|
1630 bool recreateWholePixmapCache = true;
|
Chris@0
|
1631
|
Chris@0
|
1632 if (!m_pixmapCacheInvalid) {
|
Chris@0
|
1633
|
Chris@0
|
1634 //!!! This cache may have been obsoleted entirely by the
|
Chris@0
|
1635 //scrolling cache in View. Perhaps experiment with
|
Chris@0
|
1636 //removing it and see if it makes things even quicker (or else
|
Chris@0
|
1637 //make it optional)
|
Chris@0
|
1638
|
Chris@0
|
1639 if (int(m_pixmapCacheZoomLevel) == zoomLevel &&
|
Chris@0
|
1640 m_pixmapCache->width() == m_view->width() &&
|
Chris@0
|
1641 m_pixmapCache->height() == m_view->height()) {
|
Chris@0
|
1642
|
Chris@20
|
1643 if (getXForFrame(m_pixmapCacheStartFrame) ==
|
Chris@20
|
1644 getXForFrame(startFrame)) {
|
Chris@0
|
1645
|
Chris@0
|
1646 #ifdef DEBUG_SPECTROGRAM_REPAINT
|
Chris@0
|
1647 std::cerr << "SpectrogramLayer: pixmap cache good" << std::endl;
|
Chris@0
|
1648 #endif
|
Chris@0
|
1649
|
Chris@0
|
1650 m_mutex.unlock();
|
Chris@0
|
1651 paint.drawPixmap(rect, *m_pixmapCache, rect);
|
Chris@0
|
1652 return;
|
Chris@0
|
1653
|
Chris@0
|
1654 } else {
|
Chris@0
|
1655
|
Chris@0
|
1656 #ifdef DEBUG_SPECTROGRAM_REPAINT
|
Chris@0
|
1657 std::cerr << "SpectrogramLayer: pixmap cache partially OK" << std::endl;
|
Chris@0
|
1658 #endif
|
Chris@0
|
1659
|
Chris@0
|
1660 recreateWholePixmapCache = false;
|
Chris@0
|
1661
|
Chris@20
|
1662 int dx = getXForFrame(m_pixmapCacheStartFrame) -
|
Chris@20
|
1663 getXForFrame(startFrame);
|
Chris@0
|
1664
|
Chris@0
|
1665 #ifdef DEBUG_SPECTROGRAM_REPAINT
|
Chris@0
|
1666 std::cerr << "SpectrogramLayer: dx = " << dx << " (pixmap cache " << m_pixmapCache->width() << "x" << m_pixmapCache->height() << ")" << std::endl;
|
Chris@0
|
1667 #endif
|
Chris@0
|
1668
|
Chris@0
|
1669 if (dx > -m_pixmapCache->width() && dx < m_pixmapCache->width()) {
|
Chris@0
|
1670
|
Chris@0
|
1671 #if defined(Q_WS_WIN32) || defined(Q_WS_MAC)
|
Chris@0
|
1672 // Copying a pixmap to itself doesn't work
|
Chris@0
|
1673 // properly on Windows or Mac (it only works when
|
Chris@0
|
1674 // moving in one direction).
|
Chris@0
|
1675
|
Chris@0
|
1676 //!!! Need a utility function for this
|
Chris@0
|
1677
|
Chris@0
|
1678 static QPixmap *tmpPixmap = 0;
|
Chris@0
|
1679 if (!tmpPixmap ||
|
Chris@0
|
1680 tmpPixmap->width() != m_pixmapCache->width() ||
|
Chris@0
|
1681 tmpPixmap->height() != m_pixmapCache->height()) {
|
Chris@0
|
1682 delete tmpPixmap;
|
Chris@0
|
1683 tmpPixmap = new QPixmap(m_pixmapCache->width(),
|
Chris@0
|
1684 m_pixmapCache->height());
|
Chris@0
|
1685 }
|
Chris@0
|
1686 QPainter cachePainter;
|
Chris@0
|
1687 cachePainter.begin(tmpPixmap);
|
Chris@0
|
1688 cachePainter.drawPixmap(0, 0, *m_pixmapCache);
|
Chris@0
|
1689 cachePainter.end();
|
Chris@0
|
1690 cachePainter.begin(m_pixmapCache);
|
Chris@0
|
1691 cachePainter.drawPixmap(dx, 0, *tmpPixmap);
|
Chris@0
|
1692 cachePainter.end();
|
Chris@0
|
1693 #else
|
Chris@0
|
1694 QPainter cachePainter(m_pixmapCache);
|
Chris@0
|
1695 cachePainter.drawPixmap(dx, 0, *m_pixmapCache);
|
Chris@0
|
1696 cachePainter.end();
|
Chris@0
|
1697 #endif
|
Chris@0
|
1698
|
Chris@0
|
1699 paint.drawPixmap(rect, *m_pixmapCache, rect);
|
Chris@0
|
1700
|
Chris@0
|
1701 if (dx < 0) {
|
Chris@0
|
1702 x0 = m_pixmapCache->width() + dx;
|
Chris@0
|
1703 x1 = m_pixmapCache->width();
|
Chris@0
|
1704 } else {
|
Chris@0
|
1705 x0 = 0;
|
Chris@0
|
1706 x1 = dx;
|
Chris@0
|
1707 }
|
Chris@0
|
1708 }
|
Chris@0
|
1709 }
|
Chris@0
|
1710 } else {
|
Chris@0
|
1711 #ifdef DEBUG_SPECTROGRAM_REPAINT
|
Chris@0
|
1712 std::cerr << "SpectrogramLayer: pixmap cache useless" << std::endl;
|
Chris@0
|
1713 #endif
|
Chris@0
|
1714 }
|
Chris@0
|
1715 }
|
Chris@0
|
1716
|
Chris@0
|
1717 if (stillCacheing) {
|
Chris@0
|
1718 x0 = rect.left();
|
Chris@0
|
1719 x1 = rect.right() + 1;
|
Chris@0
|
1720 y0 = rect.top();
|
Chris@0
|
1721 y1 = rect.bottom() + 1;
|
Chris@0
|
1722 }
|
Chris@0
|
1723
|
Chris@0
|
1724 int w = x1 - x0;
|
Chris@0
|
1725 int h = y1 - y0;
|
Chris@0
|
1726
|
Chris@0
|
1727 // std::cerr << "x0 " << x0 << ", x1 " << x1 << ", w " << w << ", h " << h << std::endl;
|
Chris@0
|
1728
|
Chris@0
|
1729 QImage scaled(w, h, QImage::Format_RGB32);
|
Chris@35
|
1730 scaled.fill(0);
|
Chris@35
|
1731
|
Chris@35
|
1732 float ymag[h];
|
Chris@35
|
1733 float ydiv[h];
|
Chris@35
|
1734
|
Chris@35
|
1735 size_t bins = m_windowSize / 2;
|
Chris@35
|
1736 int sr = m_model->getSampleRate();
|
Chris@35
|
1737
|
Chris@35
|
1738 if (m_maxFrequency > 0) {
|
Chris@35
|
1739 bins = int((double(m_maxFrequency) * m_windowSize) / sr + 0.1);
|
Chris@35
|
1740 if (bins > m_windowSize / 2) bins = m_windowSize / 2;
|
Chris@35
|
1741 }
|
Chris@35
|
1742
|
Chris@35
|
1743 float maxFreq = (float(bins) * sr) / m_windowSize;
|
Chris@0
|
1744
|
Chris@0
|
1745 m_mutex.unlock();
|
Chris@0
|
1746
|
Chris@35
|
1747 for (int x = 0; x < w; ++x) {
|
Chris@35
|
1748
|
Chris@35
|
1749 m_mutex.lock();
|
Chris@35
|
1750 if (m_cacheInvalid) {
|
Chris@35
|
1751 m_mutex.unlock();
|
Chris@35
|
1752 break;
|
Chris@35
|
1753 }
|
Chris@35
|
1754
|
Chris@35
|
1755 for (int y = 0; y < h; ++y) {
|
Chris@35
|
1756 ymag[y] = 0.0f;
|
Chris@35
|
1757 ydiv[y] = 0.0f;
|
Chris@35
|
1758 }
|
Chris@35
|
1759
|
Chris@35
|
1760 float s0 = 0, s1 = 0;
|
Chris@35
|
1761
|
Chris@35
|
1762 if (!getXBinRange(x0 + x, s0, s1)) {
|
Chris@35
|
1763 assert(x <= scaled.width());
|
Chris@35
|
1764 for (int y = 0; y < h; ++y) {
|
Chris@35
|
1765 scaled.setPixel(x, y, qRgb(0, 0, 0));
|
Chris@35
|
1766 }
|
Chris@35
|
1767 m_mutex.unlock();
|
Chris@35
|
1768 continue;
|
Chris@35
|
1769 }
|
Chris@35
|
1770
|
Chris@35
|
1771 int s0i = int(s0 + 0.001);
|
Chris@35
|
1772 int s1i = int(s1);
|
Chris@35
|
1773
|
Chris@35
|
1774 for (int q = 0; q < bins; ++q) {
|
Chris@35
|
1775
|
Chris@35
|
1776 for (int s = s0i; s <= s1i; ++s) {
|
Chris@35
|
1777
|
Chris@35
|
1778 float sprop = 1.0;
|
Chris@35
|
1779 if (s == s0i) sprop *= (s + 1) - s0;
|
Chris@35
|
1780 if (s == s1i) sprop *= s1 - s;
|
Chris@35
|
1781
|
Chris@35
|
1782 float f0 = (float(q) * sr) / m_windowSize;
|
Chris@35
|
1783 float f1 = (float(q + 1) * sr) / m_windowSize;
|
Chris@35
|
1784
|
Chris@35
|
1785 if ((m_frequencyAdjustment == PhaseAdjustedFrequency ||
|
Chris@35
|
1786 m_frequencyAdjustment == PhaseAdjustedPeaks) &&
|
Chris@35
|
1787 m_phaseAdjustCache) {
|
Chris@35
|
1788
|
Chris@35
|
1789 unsigned char cadj = m_phaseAdjustCache->getValueAt(s, q);
|
Chris@35
|
1790 int adjust = int((signed char)cadj);
|
Chris@35
|
1791
|
Chris@35
|
1792 if (adjust == SCHAR_MIN &&
|
Chris@35
|
1793 m_frequencyAdjustment == PhaseAdjustedPeaks) {
|
Chris@35
|
1794 continue;
|
Chris@35
|
1795 }
|
Chris@35
|
1796
|
Chris@35
|
1797 float fadjust = (adjust * (f1 - f0)) / 10.0;//!!! was 100
|
Chris@35
|
1798 f0 = f1 = f0 + fadjust;
|
Chris@35
|
1799 }
|
Chris@35
|
1800
|
Chris@35
|
1801 float y0 = h - (h * f1) / maxFreq;
|
Chris@35
|
1802 float y1 = h - (h * f0) / maxFreq;
|
Chris@35
|
1803
|
Chris@35
|
1804 if (m_frequencyScale == LogFrequencyScale) {
|
Chris@35
|
1805
|
Chris@35
|
1806 float maxf = m_maxFrequency;
|
Chris@35
|
1807 if (maxf == 0.0) maxf = float(sr) / 2;
|
Chris@35
|
1808
|
Chris@35
|
1809 float minf = float(sr) / m_windowSize;
|
Chris@35
|
1810
|
Chris@35
|
1811 float maxlogf = log10f(maxf);
|
Chris@35
|
1812 float minlogf = log10f(minf);
|
Chris@35
|
1813
|
Chris@35
|
1814 y0 = h - (h * (log10f(f1) - minlogf)) / (maxlogf - minlogf);
|
Chris@35
|
1815 y1 = h - (h * (log10f(f0) - minlogf)) / (maxlogf - minlogf);
|
Chris@35
|
1816 }
|
Chris@35
|
1817
|
Chris@35
|
1818 int y0i = int(y0 + 0.001);
|
Chris@35
|
1819 int y1i = int(y1);
|
Chris@35
|
1820
|
Chris@35
|
1821 for (int y = y0i; y <= y1i; ++y) {
|
Chris@35
|
1822
|
Chris@35
|
1823 if (y < 0 || y >= h) continue;
|
Chris@35
|
1824
|
Chris@35
|
1825 float yprop = sprop;
|
Chris@35
|
1826 if (y == y0i) yprop *= (y + 1) - y0;
|
Chris@35
|
1827 if (y == y1i) yprop *= y1 - y;
|
Chris@35
|
1828
|
Chris@35
|
1829 ymag[y] += yprop * m_cache->getValueAt(s, q);
|
Chris@35
|
1830 ydiv[y] += yprop;
|
Chris@35
|
1831 }
|
Chris@35
|
1832 }
|
Chris@35
|
1833 }
|
Chris@35
|
1834
|
Chris@35
|
1835 for (int y = 0; y < h; ++y) {
|
Chris@35
|
1836
|
Chris@35
|
1837 int pixel = 1;
|
Chris@35
|
1838
|
Chris@35
|
1839 if (ydiv[y] > 0.0) {
|
Chris@35
|
1840 pixel = int(ymag[y] / ydiv[y]);
|
Chris@35
|
1841 if (pixel > 255) pixel = 255;
|
Chris@35
|
1842 if (pixel < 1) pixel = 1;
|
Chris@35
|
1843 }
|
Chris@35
|
1844
|
Chris@35
|
1845 assert(x <= scaled.width());
|
Chris@35
|
1846 QColor c = m_cache->getColour(pixel);
|
Chris@35
|
1847 scaled.setPixel(x, y,
|
Chris@35
|
1848 qRgb(c.red(), c.green(), c.blue()));
|
Chris@35
|
1849 }
|
Chris@35
|
1850
|
Chris@35
|
1851
|
Chris@35
|
1852 m_mutex.unlock();
|
Chris@35
|
1853 }
|
Chris@35
|
1854
|
Chris@35
|
1855 #ifdef NOT_DEFINED
|
Chris@0
|
1856 for (int y = 0; y < h; ++y) {
|
Chris@0
|
1857
|
Chris@0
|
1858 m_mutex.lock();
|
Chris@0
|
1859 if (m_cacheInvalid) {
|
Chris@0
|
1860 m_mutex.unlock();
|
Chris@0
|
1861 break;
|
Chris@0
|
1862 }
|
Chris@0
|
1863
|
Chris@31
|
1864 int cw = m_cache->getWidth();
|
Chris@31
|
1865 int ch = m_cache->getHeight();
|
Chris@0
|
1866
|
Chris@0
|
1867 float q0 = 0, q1 = 0;
|
Chris@0
|
1868
|
Chris@0
|
1869 if (!getYBinRange(y0 + y, q0, q1)) {
|
Chris@0
|
1870 for (int x = 0; x < w; ++x) {
|
Chris@0
|
1871 assert(x <= scaled.width());
|
Chris@0
|
1872 scaled.setPixel(x, y, qRgb(0, 0, 0));
|
Chris@0
|
1873 }
|
Chris@0
|
1874 m_mutex.unlock();
|
Chris@0
|
1875 continue;
|
Chris@0
|
1876 }
|
Chris@0
|
1877
|
Chris@0
|
1878 int q0i = int(q0 + 0.001);
|
Chris@0
|
1879 int q1i = int(q1);
|
Chris@0
|
1880
|
Chris@0
|
1881 for (int x = 0; x < w; ++x) {
|
Chris@0
|
1882
|
Chris@0
|
1883 float s0 = 0, s1 = 0;
|
Chris@0
|
1884
|
Chris@20
|
1885 if (!getXBinRange(x0 + x, s0, s1)) {
|
Chris@0
|
1886 assert(x <= scaled.width());
|
Chris@0
|
1887 scaled.setPixel(x, y, qRgb(0, 0, 0));
|
Chris@0
|
1888 continue;
|
Chris@0
|
1889 }
|
Chris@0
|
1890
|
Chris@0
|
1891 int s0i = int(s0 + 0.001);
|
Chris@0
|
1892 int s1i = int(s1);
|
Chris@0
|
1893
|
Chris@0
|
1894 float total = 0, divisor = 0;
|
Chris@0
|
1895
|
Chris@0
|
1896 for (int s = s0i; s <= s1i; ++s) {
|
Chris@0
|
1897
|
Chris@0
|
1898 float sprop = 1.0;
|
Chris@0
|
1899 if (s == s0i) sprop *= (s + 1) - s0;
|
Chris@0
|
1900 if (s == s1i) sprop *= s1 - s;
|
Chris@0
|
1901
|
Chris@0
|
1902 for (int q = q0i; q <= q1i; ++q) {
|
Chris@0
|
1903
|
Chris@0
|
1904 float qprop = sprop;
|
Chris@0
|
1905 if (q == q0i) qprop *= (q + 1) - q0;
|
Chris@0
|
1906 if (q == q1i) qprop *= q1 - q;
|
Chris@0
|
1907
|
Chris@0
|
1908 if (s >= 0 && q >= 0 && s < cw && q < ch) {
|
Chris@31
|
1909 total += qprop * m_cache->getValueAt(s, q);
|
Chris@0
|
1910 divisor += qprop;
|
Chris@0
|
1911 }
|
Chris@0
|
1912 }
|
Chris@0
|
1913 }
|
Chris@0
|
1914
|
Chris@0
|
1915 if (divisor > 0.0) {
|
Chris@0
|
1916 int pixel = int(total / divisor);
|
Chris@0
|
1917 if (pixel > 255) pixel = 255;
|
Chris@0
|
1918 if (pixel < 1) pixel = 1;
|
Chris@0
|
1919 assert(x <= scaled.width());
|
Chris@31
|
1920 QColor c = m_cache->getColour(pixel);
|
Chris@31
|
1921 scaled.setPixel(x, y,
|
Chris@31
|
1922 qRgb(c.red(), c.green(), c.blue()));
|
Chris@31
|
1923 /*
|
Chris@9
|
1924 float pixel = total / divisor;
|
Chris@9
|
1925 float lq = pixel - int(pixel);
|
Chris@9
|
1926 float hq = int(pixel) + 1 - pixel;
|
Chris@9
|
1927 int pixNum = int(pixel);
|
Chris@9
|
1928 QRgb low = m_cache->color(pixNum > 255 ? 255 : pixNum);
|
Chris@9
|
1929 QRgb high = m_cache->color(pixNum > 254 ? 255 : pixNum + 1);
|
Chris@9
|
1930 QRgb mixed = qRgb
|
Chris@9
|
1931 (qRed(low) * lq + qRed(high) * hq + 0.01,
|
Chris@9
|
1932 qGreen(low) * lq + qGreen(high) * hq + 0.01,
|
Chris@9
|
1933 qBlue(low) * lq + qBlue(high) * hq + 0.01);
|
Chris@9
|
1934 scaled.setPixel(x, y, mixed);
|
Chris@31
|
1935 */
|
Chris@0
|
1936 } else {
|
Chris@0
|
1937 assert(x <= scaled.width());
|
Chris@0
|
1938 scaled.setPixel(x, y, qRgb(0, 0, 0));
|
Chris@0
|
1939 }
|
Chris@0
|
1940 }
|
Chris@0
|
1941
|
Chris@0
|
1942 m_mutex.unlock();
|
Chris@0
|
1943 }
|
Chris@35
|
1944 #endif
|
Chris@0
|
1945
|
Chris@0
|
1946 paint.drawImage(x0, y0, scaled);
|
Chris@0
|
1947
|
Chris@0
|
1948 if (recreateWholePixmapCache) {
|
Chris@0
|
1949 delete m_pixmapCache;
|
Chris@0
|
1950 m_pixmapCache = new QPixmap(w, h);
|
Chris@0
|
1951 }
|
Chris@0
|
1952
|
Chris@0
|
1953 QPainter cachePainter(m_pixmapCache);
|
Chris@0
|
1954 cachePainter.drawImage(x0, y0, scaled);
|
Chris@0
|
1955 cachePainter.end();
|
Chris@0
|
1956
|
Chris@0
|
1957 m_pixmapCacheInvalid = false;
|
Chris@0
|
1958 m_pixmapCacheStartFrame = startFrame;
|
Chris@0
|
1959 m_pixmapCacheZoomLevel = zoomLevel;
|
Chris@0
|
1960
|
Chris@0
|
1961 #ifdef DEBUG_SPECTROGRAM_REPAINT
|
Chris@0
|
1962 std::cerr << "SpectrogramLayer::paint() returning" << std::endl;
|
Chris@0
|
1963 #endif
|
Chris@0
|
1964 }
|
Chris@0
|
1965
|
Chris@0
|
1966 int
|
Chris@0
|
1967 SpectrogramLayer::getCompletion() const
|
Chris@0
|
1968 {
|
Chris@0
|
1969 if (m_updateTimer == 0) return 100;
|
Chris@0
|
1970 size_t completion = m_fillThread->getFillCompletion();
|
Chris@0
|
1971 // std::cerr << "SpectrogramLayer::getCompletion: completion = " << completion << std::endl;
|
Chris@0
|
1972 return completion;
|
Chris@0
|
1973 }
|
Chris@0
|
1974
|
Chris@28
|
1975 bool
|
Chris@28
|
1976 SpectrogramLayer::snapToFeatureFrame(int &frame,
|
Chris@28
|
1977 size_t &resolution,
|
Chris@28
|
1978 SnapType snap) const
|
Chris@13
|
1979 {
|
Chris@13
|
1980 resolution = getWindowIncrement();
|
Chris@28
|
1981 int left = (frame / resolution) * resolution;
|
Chris@28
|
1982 int right = left + resolution;
|
Chris@28
|
1983
|
Chris@28
|
1984 switch (snap) {
|
Chris@28
|
1985 case SnapLeft: frame = left; break;
|
Chris@28
|
1986 case SnapRight: frame = right; break;
|
Chris@28
|
1987 case SnapNearest:
|
Chris@28
|
1988 case SnapNeighbouring:
|
Chris@28
|
1989 if (frame - left > right - frame) frame = right;
|
Chris@28
|
1990 else frame = left;
|
Chris@28
|
1991 break;
|
Chris@28
|
1992 }
|
Chris@28
|
1993
|
Chris@28
|
1994 return true;
|
Chris@28
|
1995 }
|
Chris@13
|
1996
|
Chris@25
|
1997 QString
|
Chris@25
|
1998 SpectrogramLayer::getFeatureDescription(QPoint &pos) const
|
Chris@25
|
1999 {
|
Chris@25
|
2000 int x = pos.x();
|
Chris@25
|
2001 int y = pos.y();
|
Chris@0
|
2002
|
Chris@25
|
2003 if (!m_model || !m_model->isOK()) return "";
|
Chris@0
|
2004
|
Chris@0
|
2005 float dbMin = 0, dbMax = 0;
|
Chris@0
|
2006 float freqMin = 0, freqMax = 0;
|
Chris@35
|
2007 float adjFreqMin = 0, adjFreqMax = 0;
|
Chris@25
|
2008 QString pitchMin, pitchMax;
|
Chris@0
|
2009 RealTime rtMin, rtMax;
|
Chris@0
|
2010
|
Chris@25
|
2011 bool haveDb = false;
|
Chris@0
|
2012
|
Chris@25
|
2013 if (!getXBinSourceRange(x, rtMin, rtMax)) return "";
|
Chris@25
|
2014 if (getXYBinSourceRange(x, y, dbMin, dbMax)) haveDb = true;
|
Chris@0
|
2015
|
Chris@35
|
2016 QString adjFreqText = "", adjPitchText = "";
|
Chris@35
|
2017
|
Chris@35
|
2018 if ((m_frequencyAdjustment == PhaseAdjustedFrequency ||
|
Chris@35
|
2019 m_frequencyAdjustment == PhaseAdjustedPeaks) &&
|
Chris@35
|
2020 m_phaseAdjustCache) {
|
Chris@35
|
2021
|
Chris@35
|
2022 if (!getAdjustedYBinSourceRange(x, y, freqMin, freqMax,
|
Chris@35
|
2023 adjFreqMin, adjFreqMax)) return "";
|
Chris@35
|
2024
|
Chris@35
|
2025 if (adjFreqMin != adjFreqMax) {
|
Chris@35
|
2026 adjFreqText = tr("Adjusted Frequency:\t%1 - %2 Hz\n")
|
Chris@35
|
2027 .arg(adjFreqMin).arg(adjFreqMax);
|
Chris@35
|
2028 adjPitchText = tr("Adjusted Pitch:\t%3 - %4\n")
|
Chris@35
|
2029 .arg(Pitch::getPitchLabelForFrequency(adjFreqMin))
|
Chris@35
|
2030 .arg(Pitch::getPitchLabelForFrequency(adjFreqMax));
|
Chris@35
|
2031 } else {
|
Chris@35
|
2032 adjFreqText = tr("Adjusted Frequency:\t%1 Hz\n")
|
Chris@35
|
2033 .arg(adjFreqMin);
|
Chris@35
|
2034 adjPitchText = tr("Adjusted Pitch:\t%2\n")
|
Chris@35
|
2035 .arg(Pitch::getPitchLabelForFrequency(adjFreqMin));
|
Chris@35
|
2036 }
|
Chris@35
|
2037
|
Chris@35
|
2038 } else {
|
Chris@35
|
2039
|
Chris@35
|
2040 if (!getYBinSourceRange(y, freqMin, freqMax)) return "";
|
Chris@35
|
2041 }
|
Chris@35
|
2042
|
Chris@25
|
2043 //!!! want to actually do a one-off FFT to recalculate the dB value!
|
Chris@25
|
2044
|
Chris@25
|
2045 QString text;
|
Chris@25
|
2046
|
Chris@25
|
2047 if (rtMin != rtMax) {
|
Chris@25
|
2048 text += tr("Time:\t%1 - %2\n")
|
Chris@25
|
2049 .arg(rtMin.toText(true).c_str())
|
Chris@25
|
2050 .arg(rtMax.toText(true).c_str());
|
Chris@25
|
2051 } else {
|
Chris@25
|
2052 text += tr("Time:\t%1\n")
|
Chris@25
|
2053 .arg(rtMin.toText(true).c_str());
|
Chris@0
|
2054 }
|
Chris@0
|
2055
|
Chris@25
|
2056 if (freqMin != freqMax) {
|
Chris@35
|
2057 text += tr("Frequency:\t%1 - %2 Hz\n%3Pitch:\t%4 - %5\n%6")
|
Chris@25
|
2058 .arg(freqMin)
|
Chris@25
|
2059 .arg(freqMax)
|
Chris@35
|
2060 .arg(adjFreqText)
|
Chris@25
|
2061 .arg(Pitch::getPitchLabelForFrequency(freqMin))
|
Chris@35
|
2062 .arg(Pitch::getPitchLabelForFrequency(freqMax))
|
Chris@35
|
2063 .arg(adjPitchText);
|
Chris@25
|
2064 } else {
|
Chris@35
|
2065 text += tr("Frequency:\t%1 Hz\n%2Pitch:\t%3\n%4")
|
Chris@25
|
2066 .arg(freqMin)
|
Chris@35
|
2067 .arg(adjFreqText)
|
Chris@35
|
2068 .arg(Pitch::getPitchLabelForFrequency(freqMin))
|
Chris@35
|
2069 .arg(adjPitchText);
|
Chris@25
|
2070 }
|
Chris@25
|
2071
|
Chris@25
|
2072 if (haveDb) {
|
Chris@25
|
2073 if (lrintf(dbMin) != lrintf(dbMax)) {
|
Chris@25
|
2074 text += tr("dB:\t%1 - %2").arg(lrintf(dbMin)).arg(lrintf(dbMax));
|
Chris@25
|
2075 } else {
|
Chris@25
|
2076 text += tr("dB:\t%1").arg(lrintf(dbMin));
|
Chris@25
|
2077 }
|
Chris@25
|
2078 }
|
Chris@25
|
2079
|
Chris@25
|
2080 return text;
|
Chris@0
|
2081 }
|
Chris@25
|
2082
|
Chris@0
|
2083 int
|
Chris@0
|
2084 SpectrogramLayer::getVerticalScaleWidth(QPainter &paint) const
|
Chris@0
|
2085 {
|
Chris@0
|
2086 if (!m_model || !m_model->isOK()) return 0;
|
Chris@0
|
2087
|
Chris@0
|
2088 int tw = paint.fontMetrics().width(QString("%1")
|
Chris@0
|
2089 .arg(m_maxFrequency > 0 ?
|
Chris@0
|
2090 m_maxFrequency - 1 :
|
Chris@0
|
2091 m_model->getSampleRate() / 2));
|
Chris@0
|
2092
|
Chris@0
|
2093 int fw = paint.fontMetrics().width(QString("43Hz"));
|
Chris@0
|
2094 if (tw < fw) tw = fw;
|
Chris@0
|
2095
|
Chris@0
|
2096 return tw + 13;
|
Chris@0
|
2097 }
|
Chris@0
|
2098
|
Chris@0
|
2099 void
|
Chris@0
|
2100 SpectrogramLayer::paintVerticalScale(QPainter &paint, QRect rect) const
|
Chris@0
|
2101 {
|
Chris@0
|
2102 if (!m_model || !m_model->isOK()) {
|
Chris@0
|
2103 return;
|
Chris@0
|
2104 }
|
Chris@0
|
2105
|
Chris@0
|
2106 int h = rect.height(), w = rect.width();
|
Chris@0
|
2107
|
Chris@0
|
2108 size_t bins = m_windowSize / 2;
|
Chris@0
|
2109 int sr = m_model->getSampleRate();
|
Chris@0
|
2110
|
Chris@0
|
2111 if (m_maxFrequency > 0) {
|
Chris@0
|
2112 bins = int((double(m_maxFrequency) * m_windowSize) / sr + 0.1);
|
Chris@0
|
2113 if (bins > m_windowSize / 2) bins = m_windowSize / 2;
|
Chris@0
|
2114 }
|
Chris@0
|
2115
|
Chris@0
|
2116 int py = -1;
|
Chris@0
|
2117 int textHeight = paint.fontMetrics().height();
|
Chris@0
|
2118 int toff = -textHeight + paint.fontMetrics().ascent() + 2;
|
Chris@0
|
2119
|
Chris@0
|
2120 int bin = -1;
|
Chris@0
|
2121
|
Chris@0
|
2122 for (int y = 0; y < m_view->height(); ++y) {
|
Chris@0
|
2123
|
Chris@0
|
2124 float q0, q1;
|
Chris@0
|
2125 if (!getYBinRange(m_view->height() - y, q0, q1)) continue;
|
Chris@0
|
2126
|
Chris@0
|
2127 int vy;
|
Chris@0
|
2128
|
Chris@0
|
2129 if (int(q0) > bin) {
|
Chris@0
|
2130 vy = y;
|
Chris@0
|
2131 bin = int(q0);
|
Chris@0
|
2132 } else {
|
Chris@0
|
2133 continue;
|
Chris@0
|
2134 }
|
Chris@0
|
2135
|
Chris@0
|
2136 int freq = (sr * (bin + 1)) / m_windowSize;
|
Chris@0
|
2137
|
Chris@0
|
2138 if (py >= 0 && (vy - py) < textHeight - 1) {
|
Chris@0
|
2139 paint.drawLine(w - 4, h - vy, w, h - vy);
|
Chris@0
|
2140 continue;
|
Chris@0
|
2141 }
|
Chris@0
|
2142
|
Chris@0
|
2143 QString text = QString("%1").arg(freq);
|
Chris@0
|
2144 if (bin == 0) text = QString("%1Hz").arg(freq);
|
Chris@0
|
2145 paint.drawLine(0, h - vy, w, h - vy);
|
Chris@0
|
2146
|
Chris@0
|
2147 if (h - vy - textHeight >= -2) {
|
Chris@0
|
2148 int tx = w - 10 - paint.fontMetrics().width(text);
|
Chris@0
|
2149 paint.drawText(tx, h - vy + toff, text);
|
Chris@0
|
2150 }
|
Chris@0
|
2151
|
Chris@0
|
2152 py = vy;
|
Chris@0
|
2153 }
|
Chris@0
|
2154 }
|
Chris@0
|
2155
|
Chris@6
|
2156 QString
|
Chris@6
|
2157 SpectrogramLayer::toXmlString(QString indent, QString extraAttributes) const
|
Chris@6
|
2158 {
|
Chris@6
|
2159 QString s;
|
Chris@6
|
2160
|
Chris@6
|
2161 s += QString("channel=\"%1\" "
|
Chris@6
|
2162 "windowSize=\"%2\" "
|
Chris@6
|
2163 "windowType=\"%3\" "
|
Chris@6
|
2164 "windowOverlap=\"%4\" "
|
Chris@35
|
2165 "gain=\"%5\" ")
|
Chris@6
|
2166 .arg(m_channel)
|
Chris@6
|
2167 .arg(m_windowSize)
|
Chris@6
|
2168 .arg(m_windowType)
|
Chris@6
|
2169 .arg(m_windowOverlap)
|
Chris@35
|
2170 .arg(m_gain);
|
Chris@35
|
2171
|
Chris@35
|
2172 s += QString("maxFrequency=\"%1\" "
|
Chris@35
|
2173 "colourScale=\"%2\" "
|
Chris@35
|
2174 "colourScheme=\"%3\" "
|
Chris@35
|
2175 "frequencyScale=\"%4\" "
|
Chris@35
|
2176 "frequencyAdjustment=\"%5\"")
|
Chris@6
|
2177 .arg(m_maxFrequency)
|
Chris@6
|
2178 .arg(m_colourScale)
|
Chris@6
|
2179 .arg(m_colourScheme)
|
Chris@35
|
2180 .arg(m_frequencyScale)
|
Chris@35
|
2181 .arg(m_frequencyAdjustment);
|
Chris@6
|
2182
|
Chris@6
|
2183 return Layer::toXmlString(indent, extraAttributes + " " + s);
|
Chris@6
|
2184 }
|
Chris@6
|
2185
|
Chris@11
|
2186 void
|
Chris@11
|
2187 SpectrogramLayer::setProperties(const QXmlAttributes &attributes)
|
Chris@11
|
2188 {
|
Chris@11
|
2189 bool ok = false;
|
Chris@11
|
2190
|
Chris@11
|
2191 int channel = attributes.value("channel").toInt(&ok);
|
Chris@11
|
2192 if (ok) setChannel(channel);
|
Chris@11
|
2193
|
Chris@11
|
2194 size_t windowSize = attributes.value("windowSize").toUInt(&ok);
|
Chris@11
|
2195 if (ok) setWindowSize(windowSize);
|
Chris@11
|
2196
|
Chris@11
|
2197 WindowType windowType = (WindowType)
|
Chris@11
|
2198 attributes.value("windowType").toInt(&ok);
|
Chris@11
|
2199 if (ok) setWindowType(windowType);
|
Chris@11
|
2200
|
Chris@11
|
2201 size_t windowOverlap = attributes.value("windowOverlap").toUInt(&ok);
|
Chris@11
|
2202 if (ok) setWindowOverlap(windowOverlap);
|
Chris@11
|
2203
|
Chris@11
|
2204 float gain = attributes.value("gain").toFloat(&ok);
|
Chris@11
|
2205 if (ok) setGain(gain);
|
Chris@11
|
2206
|
Chris@11
|
2207 size_t maxFrequency = attributes.value("maxFrequency").toUInt(&ok);
|
Chris@11
|
2208 if (ok) setMaxFrequency(maxFrequency);
|
Chris@11
|
2209
|
Chris@11
|
2210 ColourScale colourScale = (ColourScale)
|
Chris@11
|
2211 attributes.value("colourScale").toInt(&ok);
|
Chris@11
|
2212 if (ok) setColourScale(colourScale);
|
Chris@11
|
2213
|
Chris@11
|
2214 ColourScheme colourScheme = (ColourScheme)
|
Chris@11
|
2215 attributes.value("colourScheme").toInt(&ok);
|
Chris@11
|
2216 if (ok) setColourScheme(colourScheme);
|
Chris@11
|
2217
|
Chris@11
|
2218 FrequencyScale frequencyScale = (FrequencyScale)
|
Chris@11
|
2219 attributes.value("frequencyScale").toInt(&ok);
|
Chris@11
|
2220 if (ok) setFrequencyScale(frequencyScale);
|
Chris@35
|
2221
|
Chris@35
|
2222 FrequencyAdjustment frequencyAdjustment = (FrequencyAdjustment)
|
Chris@35
|
2223 attributes.value("frequencyAdjustment").toInt(&ok);
|
Chris@35
|
2224 if (ok) setFrequencyAdjustment(frequencyAdjustment);
|
Chris@11
|
2225 }
|
Chris@11
|
2226
|
Chris@11
|
2227
|
Chris@0
|
2228 #ifdef INCLUDE_MOCFILES
|
Chris@0
|
2229 #include "SpectrogramLayer.moc.cpp"
|
Chris@0
|
2230 #endif
|
Chris@0
|
2231
|