Mercurial > hg > svgui
comparison layer/SpectrogramLayer.cpp @ 1324:13d9b422f7fe zoom
Merge from default branch
author | Chris Cannam |
---|---|
date | Mon, 17 Sep 2018 13:51:31 +0100 |
parents | 6e724c81f18f |
children | d79e21855aef |
comparison
equal
deleted
inserted
replaced
1183:57d192e26331 | 1324:13d9b422f7fe |
---|---|
23 #include "base/Preferences.h" | 23 #include "base/Preferences.h" |
24 #include "base/RangeMapper.h" | 24 #include "base/RangeMapper.h" |
25 #include "base/LogRange.h" | 25 #include "base/LogRange.h" |
26 #include "base/ColumnOp.h" | 26 #include "base/ColumnOp.h" |
27 #include "base/Strings.h" | 27 #include "base/Strings.h" |
28 #include "base/StorageAdviser.h" | |
29 #include "base/Exceptions.h" | |
28 #include "widgets/CommandHistory.h" | 30 #include "widgets/CommandHistory.h" |
29 #include "data/model/Dense3DModelPeakCache.h" | 31 #include "data/model/Dense3DModelPeakCache.h" |
30 | 32 |
31 #include "ColourMapper.h" | 33 #include "ColourMapper.h" |
32 #include "PianoScale.h" | 34 #include "PianoScale.h" |
78 m_lastEmittedZoomStep(-1), | 80 m_lastEmittedZoomStep(-1), |
79 m_synchronous(false), | 81 m_synchronous(false), |
80 m_haveDetailedScale(false), | 82 m_haveDetailedScale(false), |
81 m_exiting(false), | 83 m_exiting(false), |
82 m_fftModel(0), | 84 m_fftModel(0), |
85 m_wholeCache(0), | |
83 m_peakCache(0), | 86 m_peakCache(0), |
84 m_peakCacheDivisor(8) | 87 m_peakCacheDivisor(8) |
85 { | 88 { |
86 QString colourConfigName = "spectrogram-colour"; | 89 QString colourConfigName = "spectrogram-colour"; |
87 int colourConfigDefault = int(ColourMapper::Green); | 90 int colourConfigDefault = int(ColourMapper::Green); |
88 | 91 |
89 if (config == FullRangeDb) { | 92 if (config == FullRangeDb) { |
90 m_initialMaxFrequency = 0; | 93 m_initialMaxFrequency = 0; |
91 setMaxFrequency(0); | 94 setMaxFrequency(0); |
92 } else if (config == MelodicRange) { | 95 } else if (config == MelodicRange) { |
93 setWindowSize(8192); | 96 setWindowSize(8192); |
94 setWindowHopLevel(4); | 97 setWindowHopLevel(4); |
95 m_initialMaxFrequency = 1500; | 98 m_initialMaxFrequency = 1500; |
96 setMaxFrequency(1500); | 99 setMaxFrequency(1500); |
97 setMinFrequency(40); | 100 setMinFrequency(40); |
98 setColourScale(ColourScaleType::Linear); | 101 setColourScale(ColourScaleType::Linear); |
99 setColourMap(ColourMapper::Sunset); | 102 setColourMap(ColourMapper::Sunset); |
100 setBinScale(BinScale::Log); | 103 setBinScale(BinScale::Log); |
101 colourConfigName = "spectrogram-melodic-colour"; | 104 colourConfigName = "spectrogram-melodic-colour"; |
102 colourConfigDefault = int(ColourMapper::Sunset); | 105 colourConfigDefault = int(ColourMapper::Sunset); |
103 // setGain(20); | 106 // setGain(20); |
104 } else if (config == MelodicPeaks) { | 107 } else if (config == MelodicPeaks) { |
105 setWindowSize(4096); | 108 setWindowSize(4096); |
106 setWindowHopLevel(5); | 109 setWindowHopLevel(5); |
107 m_initialMaxFrequency = 2000; | 110 m_initialMaxFrequency = 2000; |
108 setMaxFrequency(2000); | 111 setMaxFrequency(2000); |
109 setMinFrequency(40); | 112 setMinFrequency(40); |
110 setBinScale(BinScale::Log); | 113 setBinScale(BinScale::Log); |
111 setColourScale(ColourScaleType::Linear); | 114 setColourScale(ColourScaleType::Linear); |
112 setBinDisplay(BinDisplay::PeakFrequencies); | 115 setBinDisplay(BinDisplay::PeakFrequencies); |
113 setNormalization(ColumnNormalization::Max1); | 116 setNormalization(ColumnNormalization::Max1); |
114 colourConfigName = "spectrogram-melodic-colour"; | 117 colourConfigName = "spectrogram-melodic-colour"; |
115 colourConfigDefault = int(ColourMapper::Sunset); | 118 colourConfigDefault = int(ColourMapper::Sunset); |
116 } | 119 } |
117 | 120 |
127 } | 130 } |
128 | 131 |
129 SpectrogramLayer::~SpectrogramLayer() | 132 SpectrogramLayer::~SpectrogramLayer() |
130 { | 133 { |
131 invalidateRenderers(); | 134 invalidateRenderers(); |
132 invalidateFFTModel(); | 135 deleteDerivedModels(); |
136 } | |
137 | |
138 void | |
139 SpectrogramLayer::deleteDerivedModels() | |
140 { | |
141 if (m_fftModel) m_fftModel->aboutToDelete(); | |
142 if (m_peakCache) m_peakCache->aboutToDelete(); | |
143 if (m_wholeCache) m_wholeCache->aboutToDelete(); | |
144 | |
145 delete m_fftModel; | |
146 delete m_peakCache; | |
147 delete m_wholeCache; | |
148 | |
149 m_fftModel = 0; | |
150 m_peakCache = 0; | |
151 m_wholeCache = 0; | |
133 } | 152 } |
134 | 153 |
135 pair<ColourScaleType, double> | 154 pair<ColourScaleType, double> |
136 SpectrogramLayer::convertToColourScale(int value) | 155 SpectrogramLayer::convertToColourScale(int value) |
137 { | 156 { |
179 case ColumnNormalization::None: return 0; | 198 case ColumnNormalization::None: return 0; |
180 case ColumnNormalization::Max1: return 1; | 199 case ColumnNormalization::Max1: return 1; |
181 case ColumnNormalization::Hybrid: return 3; | 200 case ColumnNormalization::Hybrid: return 3; |
182 | 201 |
183 case ColumnNormalization::Sum1: | 202 case ColumnNormalization::Sum1: |
203 case ColumnNormalization::Range01: | |
184 default: return 0; | 204 default: return 0; |
185 } | 205 } |
186 } | 206 } |
187 | 207 |
188 void | 208 void |
191 // cerr << "SpectrogramLayer(" << this << "): setModel(" << model << ")" << endl; | 211 // cerr << "SpectrogramLayer(" << this << "): setModel(" << model << ")" << endl; |
192 | 212 |
193 if (model == m_model) return; | 213 if (model == m_model) return; |
194 | 214 |
195 m_model = model; | 215 m_model = model; |
196 invalidateFFTModel(); | 216 |
217 recreateFFTModel(); | |
197 | 218 |
198 if (!m_model || !m_model->isOK()) return; | 219 if (!m_model || !m_model->isOK()) return; |
199 | 220 |
200 connectSignals(m_model); | 221 connectSignals(m_model); |
201 | 222 |
202 connect(m_model, SIGNAL(modelChanged()), this, SLOT(cacheInvalid())); | 223 connect(m_model, SIGNAL(modelChanged()), this, SLOT(cacheInvalid())); |
203 connect(m_model, SIGNAL(modelChangedWithin(sv_frame_t, sv_frame_t)), | 224 connect(m_model, SIGNAL(modelChangedWithin(sv_frame_t, sv_frame_t)), |
204 this, SLOT(cacheInvalid(sv_frame_t, sv_frame_t))); | 225 this, SLOT(cacheInvalid(sv_frame_t, sv_frame_t))); |
205 | 226 |
206 emit modelReplaced(); | 227 emit modelReplaced(); |
207 } | 228 } |
208 | 229 |
209 Layer::PropertyList | 230 Layer::PropertyList |
253 SpectrogramLayer::getPropertyType(const PropertyName &name) const | 274 SpectrogramLayer::getPropertyType(const PropertyName &name) const |
254 { | 275 { |
255 if (name == "Gain") return RangeProperty; | 276 if (name == "Gain") return RangeProperty; |
256 if (name == "Colour Rotation") return RangeProperty; | 277 if (name == "Colour Rotation") return RangeProperty; |
257 if (name == "Threshold") return RangeProperty; | 278 if (name == "Threshold") return RangeProperty; |
279 if (name == "Colour") return ColourMapProperty; | |
258 return ValueProperty; | 280 return ValueProperty; |
259 } | 281 } |
260 | 282 |
261 QString | 283 QString |
262 SpectrogramLayer::getPropertyGroupName(const PropertyName &name) const | 284 SpectrogramLayer::getPropertyGroupName(const PropertyName &name) const |
263 { | 285 { |
264 if (name == "Bin Display" || | 286 if (name == "Bin Display" || |
265 name == "Frequency Scale") return tr("Bins"); | 287 name == "Frequency Scale") return tr("Bins"); |
266 if (name == "Window Size" || | 288 if (name == "Window Size" || |
267 name == "Window Increment") return tr("Window"); | 289 name == "Window Increment") return tr("Window"); |
268 if (name == "Colour" || | 290 if (name == "Colour" || |
269 name == "Threshold" || | 291 name == "Threshold" || |
270 name == "Colour Rotation") return tr("Colour"); | 292 name == "Colour Rotation") return tr("Colour"); |
271 if (name == "Normalization" || | 293 if (name == "Normalization" || |
272 name == "Gain" || | 294 name == "Gain" || |
273 name == "Colour Scale") return tr("Scale"); | 295 name == "Colour Scale") return tr("Scale"); |
274 return QString(); | 296 return QString(); |
275 } | 297 } |
276 | 298 |
277 int | 299 int |
278 SpectrogramLayer::getPropertyRangeAndValue(const PropertyName &name, | 300 SpectrogramLayer::getPropertyRangeAndValue(const PropertyName &name, |
279 int *min, int *max, int *deflt) const | 301 int *min, int *max, int *deflt) const |
280 { | 302 { |
281 int val = 0; | 303 int val = 0; |
282 | 304 |
283 int garbage0, garbage1, garbage2; | 305 int garbage0, garbage1, garbage2; |
284 if (!min) min = &garbage0; | 306 if (!min) min = &garbage0; |
285 if (!max) max = &garbage1; | 307 if (!max) max = &garbage1; |
286 if (!deflt) deflt = &garbage2; | 308 if (!deflt) deflt = &garbage2; |
287 | 309 |
288 if (name == "Gain") { | 310 if (name == "Gain") { |
289 | 311 |
290 *min = -50; | 312 *min = -50; |
291 *max = 50; | 313 *max = 50; |
292 | 314 |
293 *deflt = int(lrint(log10(m_initialGain) * 20.0)); | 315 *deflt = int(lrint(log10(m_initialGain) * 20.0)); |
294 if (*deflt < *min) *deflt = *min; | 316 if (*deflt < *min) *deflt = *min; |
295 if (*deflt > *max) *deflt = *max; | 317 if (*deflt > *max) *deflt = *max; |
296 | 318 |
297 val = int(lrint(log10(m_gain) * 20.0)); | 319 val = int(lrint(log10(m_gain) * 20.0)); |
298 if (val < *min) val = *min; | 320 if (val < *min) val = *min; |
299 if (val > *max) val = *max; | 321 if (val > *max) val = *max; |
300 | 322 |
301 } else if (name == "Threshold") { | 323 } else if (name == "Threshold") { |
302 | 324 |
303 *min = -81; | 325 *min = -81; |
304 *max = -1; | 326 *max = -1; |
305 | 327 |
306 *deflt = int(lrint(AudioLevel::multiplier_to_dB(m_initialThreshold))); | 328 *deflt = int(lrint(AudioLevel::multiplier_to_dB(m_initialThreshold))); |
307 if (*deflt < *min) *deflt = *min; | 329 if (*deflt < *min) *deflt = *min; |
308 if (*deflt > *max) *deflt = *max; | 330 if (*deflt > *max) *deflt = *max; |
309 | 331 |
310 val = int(lrint(AudioLevel::multiplier_to_dB(m_threshold))); | 332 val = int(lrint(AudioLevel::multiplier_to_dB(m_threshold))); |
311 if (val < *min) val = *min; | 333 if (val < *min) val = *min; |
312 if (val > *max) val = *max; | 334 if (val > *max) val = *max; |
313 | 335 |
314 } else if (name == "Colour Rotation") { | 336 } else if (name == "Colour Rotation") { |
315 | 337 |
316 *min = 0; | 338 *min = 0; |
317 *max = 256; | 339 *max = 256; |
318 *deflt = m_initialRotation; | 340 *deflt = m_initialRotation; |
319 | 341 |
320 val = m_colourRotation; | 342 val = m_colourRotation; |
321 | 343 |
322 } else if (name == "Colour Scale") { | 344 } else if (name == "Colour Scale") { |
323 | 345 |
324 // linear, meter, db^2, db, phase | 346 // linear, meter, db^2, db, phase |
325 *min = 0; | 347 *min = 0; |
326 *max = 4; | 348 *max = 4; |
327 *deflt = 2; | 349 *deflt = 2; |
328 | 350 |
329 val = convertFromColourScale(m_colourScale, m_colourScaleMultiple); | 351 val = convertFromColourScale(m_colourScale, m_colourScaleMultiple); |
330 | 352 |
331 } else if (name == "Colour") { | 353 } else if (name == "Colour") { |
332 | 354 |
333 *min = 0; | 355 *min = 0; |
334 *max = ColourMapper::getColourMapCount() - 1; | 356 *max = ColourMapper::getColourMapCount() - 1; |
335 *deflt = 0; | 357 *deflt = 0; |
336 | 358 |
337 val = m_colourMap; | 359 val = m_colourMap; |
338 | 360 |
339 } else if (name == "Window Size") { | 361 } else if (name == "Window Size") { |
340 | 362 |
341 *min = 0; | 363 *min = 0; |
342 *max = 10; | 364 *max = 10; |
343 *deflt = 5; | 365 *deflt = 5; |
344 | 366 |
345 val = 0; | 367 val = 0; |
346 int ws = m_windowSize; | 368 int ws = m_windowSize; |
347 while (ws > 32) { ws >>= 1; val ++; } | 369 while (ws > 32) { ws >>= 1; val ++; } |
348 | 370 |
349 } else if (name == "Window Increment") { | 371 } else if (name == "Window Increment") { |
350 | 372 |
351 *min = 0; | 373 *min = 0; |
352 *max = 5; | 374 *max = 5; |
353 *deflt = 2; | 375 *deflt = 2; |
354 | 376 |
355 val = m_windowHopLevel; | 377 val = m_windowHopLevel; |
356 | 378 |
357 } else if (name == "Min Frequency") { | 379 } else if (name == "Min Frequency") { |
358 | 380 |
359 *min = 0; | 381 *min = 0; |
360 *max = 9; | 382 *max = 9; |
361 *deflt = 1; | 383 *deflt = 1; |
362 | 384 |
363 switch (m_minFrequency) { | 385 switch (m_minFrequency) { |
364 case 0: default: val = 0; break; | 386 case 0: default: val = 0; break; |
365 case 10: val = 1; break; | 387 case 10: val = 1; break; |
366 case 20: val = 2; break; | 388 case 20: val = 2; break; |
367 case 40: val = 3; break; | 389 case 40: val = 3; break; |
368 case 100: val = 4; break; | 390 case 100: val = 4; break; |
369 case 250: val = 5; break; | 391 case 250: val = 5; break; |
370 case 500: val = 6; break; | 392 case 500: val = 6; break; |
371 case 1000: val = 7; break; | 393 case 1000: val = 7; break; |
372 case 4000: val = 8; break; | 394 case 4000: val = 8; break; |
373 case 10000: val = 9; break; | 395 case 10000: val = 9; break; |
374 } | 396 } |
375 | 397 |
376 } else if (name == "Max Frequency") { | 398 } else if (name == "Max Frequency") { |
377 | 399 |
378 *min = 0; | 400 *min = 0; |
379 *max = 9; | 401 *max = 9; |
380 *deflt = 6; | 402 *deflt = 6; |
381 | 403 |
382 switch (m_maxFrequency) { | 404 switch (m_maxFrequency) { |
383 case 500: val = 0; break; | 405 case 500: val = 0; break; |
384 case 1000: val = 1; break; | 406 case 1000: val = 1; break; |
385 case 1500: val = 2; break; | 407 case 1500: val = 2; break; |
386 case 2000: val = 3; break; | 408 case 2000: val = 3; break; |
387 case 4000: val = 4; break; | 409 case 4000: val = 4; break; |
388 case 6000: val = 5; break; | 410 case 6000: val = 5; break; |
389 case 8000: val = 6; break; | 411 case 8000: val = 6; break; |
390 case 12000: val = 7; break; | 412 case 12000: val = 7; break; |
391 case 16000: val = 8; break; | 413 case 16000: val = 8; break; |
392 default: val = 9; break; | 414 default: val = 9; break; |
393 } | 415 } |
394 | 416 |
395 } else if (name == "Frequency Scale") { | 417 } else if (name == "Frequency Scale") { |
396 | 418 |
397 *min = 0; | 419 *min = 0; |
398 *max = 1; | 420 *max = 1; |
399 *deflt = int(BinScale::Linear); | 421 *deflt = int(BinScale::Linear); |
400 val = (int)m_binScale; | 422 val = (int)m_binScale; |
401 | 423 |
402 } else if (name == "Bin Display") { | 424 } else if (name == "Bin Display") { |
403 | 425 |
404 *min = 0; | 426 *min = 0; |
405 *max = 2; | 427 *max = 2; |
406 *deflt = int(BinDisplay::AllBins); | 428 *deflt = int(BinDisplay::AllBins); |
407 val = (int)m_binDisplay; | 429 val = (int)m_binDisplay; |
408 | 430 |
409 } else if (name == "Normalization") { | 431 } else if (name == "Normalization") { |
410 | 432 |
411 *min = 0; | 433 *min = 0; |
412 *max = 3; | 434 *max = 3; |
413 *deflt = 0; | 435 *deflt = 0; |
414 | 436 |
415 val = convertFromColumnNorm(m_normalization, m_normalizeVisibleArea); | 437 val = convertFromColumnNorm(m_normalization, m_normalizeVisibleArea); |
416 | 438 |
417 } else { | 439 } else { |
418 val = Layer::getPropertyRangeAndValue(name, min, max, deflt); | 440 val = Layer::getPropertyRangeAndValue(name, min, max, deflt); |
419 } | 441 } |
420 | 442 |
421 return val; | 443 return val; |
422 } | 444 } |
423 | 445 |
424 QString | 446 QString |
425 SpectrogramLayer::getPropertyValueLabel(const PropertyName &name, | 447 SpectrogramLayer::getPropertyValueLabel(const PropertyName &name, |
426 int value) const | 448 int value) const |
427 { | 449 { |
428 if (name == "Colour") { | 450 if (name == "Colour") { |
429 return ColourMapper::getColourMapName(value); | 451 return ColourMapper::getColourMapName(value); |
430 } | 452 } |
431 if (name == "Colour Scale") { | 453 if (name == "Colour Scale") { |
432 switch (value) { | 454 switch (value) { |
433 default: | 455 default: |
434 case 0: return tr("Linear"); | 456 case 0: return tr("Linear"); |
435 case 1: return tr("Meter"); | 457 case 1: return tr("Meter"); |
436 case 2: return tr("dBV^2"); | 458 case 2: return tr("dBV^2"); |
437 case 3: return tr("dBV"); | 459 case 3: return tr("dBV"); |
438 case 4: return tr("Phase"); | 460 case 4: return tr("Phase"); |
439 } | 461 } |
440 } | 462 } |
441 if (name == "Normalization") { | 463 if (name == "Normalization") { |
442 return ""; // icon only | 464 switch(value) { |
465 default: | |
466 case 0: return tr("None"); | |
467 case 1: return tr("Col"); | |
468 case 2: return tr("View"); | |
469 case 3: return tr("Hybrid"); | |
470 } | |
471 // return ""; // icon only | |
443 } | 472 } |
444 if (name == "Window Size") { | 473 if (name == "Window Size") { |
445 return QString("%1").arg(32 << value); | 474 return QString("%1").arg(32 << value); |
446 } | 475 } |
447 if (name == "Window Increment") { | 476 if (name == "Window Increment") { |
448 switch (value) { | 477 switch (value) { |
449 default: | 478 default: |
450 case 0: return tr("None"); | 479 case 0: return tr("None"); |
451 case 1: return tr("25 %"); | 480 case 1: return tr("25 %"); |
452 case 2: return tr("50 %"); | 481 case 2: return tr("50 %"); |
453 case 3: return tr("75 %"); | 482 case 3: return tr("75 %"); |
454 case 4: return tr("87.5 %"); | 483 case 4: return tr("87.5 %"); |
455 case 5: return tr("93.75 %"); | 484 case 5: return tr("93.75 %"); |
456 } | 485 } |
457 } | 486 } |
458 if (name == "Min Frequency") { | 487 if (name == "Min Frequency") { |
459 switch (value) { | 488 switch (value) { |
460 default: | 489 default: |
461 case 0: return tr("No min"); | 490 case 0: return tr("No min"); |
462 case 1: return tr("10 Hz"); | 491 case 1: return tr("10 Hz"); |
463 case 2: return tr("20 Hz"); | 492 case 2: return tr("20 Hz"); |
464 case 3: return tr("40 Hz"); | 493 case 3: return tr("40 Hz"); |
465 case 4: return tr("100 Hz"); | 494 case 4: return tr("100 Hz"); |
466 case 5: return tr("250 Hz"); | 495 case 5: return tr("250 Hz"); |
467 case 6: return tr("500 Hz"); | 496 case 6: return tr("500 Hz"); |
468 case 7: return tr("1 KHz"); | 497 case 7: return tr("1 KHz"); |
469 case 8: return tr("4 KHz"); | 498 case 8: return tr("4 KHz"); |
470 case 9: return tr("10 KHz"); | 499 case 9: return tr("10 KHz"); |
471 } | 500 } |
472 } | 501 } |
473 if (name == "Max Frequency") { | 502 if (name == "Max Frequency") { |
474 switch (value) { | 503 switch (value) { |
475 default: | 504 default: |
476 case 0: return tr("500 Hz"); | 505 case 0: return tr("500 Hz"); |
477 case 1: return tr("1 KHz"); | 506 case 1: return tr("1 KHz"); |
478 case 2: return tr("1.5 KHz"); | 507 case 2: return tr("1.5 KHz"); |
479 case 3: return tr("2 KHz"); | 508 case 3: return tr("2 KHz"); |
480 case 4: return tr("4 KHz"); | 509 case 4: return tr("4 KHz"); |
481 case 5: return tr("6 KHz"); | 510 case 5: return tr("6 KHz"); |
482 case 6: return tr("8 KHz"); | 511 case 6: return tr("8 KHz"); |
483 case 7: return tr("12 KHz"); | 512 case 7: return tr("12 KHz"); |
484 case 8: return tr("16 KHz"); | 513 case 8: return tr("16 KHz"); |
485 case 9: return tr("No max"); | 514 case 9: return tr("No max"); |
486 } | 515 } |
487 } | 516 } |
488 if (name == "Frequency Scale") { | 517 if (name == "Frequency Scale") { |
489 switch (value) { | 518 switch (value) { |
490 default: | 519 default: |
491 case 0: return tr("Linear"); | 520 case 0: return tr("Linear"); |
492 case 1: return tr("Log"); | 521 case 1: return tr("Log"); |
493 } | 522 } |
494 } | 523 } |
495 if (name == "Bin Display") { | 524 if (name == "Bin Display") { |
496 switch (value) { | 525 switch (value) { |
497 default: | 526 default: |
498 case 0: return tr("All Bins"); | 527 case 0: return tr("All Bins"); |
499 case 1: return tr("Peak Bins"); | 528 case 1: return tr("Peak Bins"); |
500 case 2: return tr("Frequencies"); | 529 case 2: return tr("Frequencies"); |
501 } | 530 } |
502 } | 531 } |
503 return tr("<unknown>"); | 532 return tr("<unknown>"); |
504 } | 533 } |
505 | 534 |
506 QString | 535 QString |
534 | 563 |
535 void | 564 void |
536 SpectrogramLayer::setProperty(const PropertyName &name, int value) | 565 SpectrogramLayer::setProperty(const PropertyName &name, int value) |
537 { | 566 { |
538 if (name == "Gain") { | 567 if (name == "Gain") { |
539 setGain(float(pow(10, float(value)/20.0))); | 568 setGain(float(pow(10, float(value)/20.0))); |
540 } else if (name == "Threshold") { | 569 } else if (name == "Threshold") { |
541 if (value == -81) setThreshold(0.0); | 570 if (value == -81) setThreshold(0.0); |
542 else setThreshold(float(AudioLevel::dB_to_multiplier(value))); | 571 else setThreshold(float(AudioLevel::dB_to_multiplier(value))); |
543 } else if (name == "Colour Rotation") { | 572 } else if (name == "Colour Rotation") { |
544 setColourRotation(value); | 573 setColourRotation(value); |
545 } else if (name == "Colour") { | 574 } else if (name == "Colour") { |
546 setColourMap(value); | 575 setColourMap(value); |
547 } else if (name == "Window Size") { | 576 } else if (name == "Window Size") { |
548 setWindowSize(32 << value); | 577 setWindowSize(32 << value); |
549 } else if (name == "Window Increment") { | 578 } else if (name == "Window Increment") { |
550 setWindowHopLevel(value); | 579 setWindowHopLevel(value); |
551 } else if (name == "Min Frequency") { | 580 } else if (name == "Min Frequency") { |
552 switch (value) { | 581 switch (value) { |
553 default: | 582 default: |
554 case 0: setMinFrequency(0); break; | 583 case 0: setMinFrequency(0); break; |
555 case 1: setMinFrequency(10); break; | 584 case 1: setMinFrequency(10); break; |
556 case 2: setMinFrequency(20); break; | 585 case 2: setMinFrequency(20); break; |
557 case 3: setMinFrequency(40); break; | 586 case 3: setMinFrequency(40); break; |
558 case 4: setMinFrequency(100); break; | 587 case 4: setMinFrequency(100); break; |
559 case 5: setMinFrequency(250); break; | 588 case 5: setMinFrequency(250); break; |
560 case 6: setMinFrequency(500); break; | 589 case 6: setMinFrequency(500); break; |
561 case 7: setMinFrequency(1000); break; | 590 case 7: setMinFrequency(1000); break; |
562 case 8: setMinFrequency(4000); break; | 591 case 8: setMinFrequency(4000); break; |
563 case 9: setMinFrequency(10000); break; | 592 case 9: setMinFrequency(10000); break; |
564 } | 593 } |
565 int vs = getCurrentVerticalZoomStep(); | 594 int vs = getCurrentVerticalZoomStep(); |
566 if (vs != m_lastEmittedZoomStep) { | 595 if (vs != m_lastEmittedZoomStep) { |
567 emit verticalZoomChanged(); | 596 emit verticalZoomChanged(); |
568 m_lastEmittedZoomStep = vs; | 597 m_lastEmittedZoomStep = vs; |
569 } | 598 } |
570 } else if (name == "Max Frequency") { | 599 } else if (name == "Max Frequency") { |
571 switch (value) { | 600 switch (value) { |
572 case 0: setMaxFrequency(500); break; | 601 case 0: setMaxFrequency(500); break; |
573 case 1: setMaxFrequency(1000); break; | 602 case 1: setMaxFrequency(1000); break; |
574 case 2: setMaxFrequency(1500); break; | 603 case 2: setMaxFrequency(1500); break; |
575 case 3: setMaxFrequency(2000); break; | 604 case 3: setMaxFrequency(2000); break; |
576 case 4: setMaxFrequency(4000); break; | 605 case 4: setMaxFrequency(4000); break; |
577 case 5: setMaxFrequency(6000); break; | 606 case 5: setMaxFrequency(6000); break; |
578 case 6: setMaxFrequency(8000); break; | 607 case 6: setMaxFrequency(8000); break; |
579 case 7: setMaxFrequency(12000); break; | 608 case 7: setMaxFrequency(12000); break; |
580 case 8: setMaxFrequency(16000); break; | 609 case 8: setMaxFrequency(16000); break; |
581 default: | 610 default: |
582 case 9: setMaxFrequency(0); break; | 611 case 9: setMaxFrequency(0); break; |
583 } | 612 } |
584 int vs = getCurrentVerticalZoomStep(); | 613 int vs = getCurrentVerticalZoomStep(); |
585 if (vs != m_lastEmittedZoomStep) { | 614 if (vs != m_lastEmittedZoomStep) { |
586 emit verticalZoomChanged(); | 615 emit verticalZoomChanged(); |
587 m_lastEmittedZoomStep = vs; | 616 m_lastEmittedZoomStep = vs; |
588 } | 617 } |
589 } else if (name == "Colour Scale") { | 618 } else if (name == "Colour Scale") { |
590 setColourScaleMultiple(1.0); | 619 setColourScaleMultiple(1.0); |
591 switch (value) { | 620 switch (value) { |
592 default: | 621 default: |
593 case 0: setColourScale(ColourScaleType::Linear); break; | 622 case 0: setColourScale(ColourScaleType::Linear); break; |
594 case 1: setColourScale(ColourScaleType::Meter); break; | 623 case 1: setColourScale(ColourScaleType::Meter); break; |
595 case 2: | 624 case 2: |
596 setColourScale(ColourScaleType::Log); | 625 setColourScale(ColourScaleType::Log); |
597 setColourScaleMultiple(2.0); | 626 setColourScaleMultiple(2.0); |
598 break; | 627 break; |
599 case 3: setColourScale(ColourScaleType::Log); break; | 628 case 3: setColourScale(ColourScaleType::Log); break; |
600 case 4: setColourScale(ColourScaleType::Phase); break; | 629 case 4: setColourScale(ColourScaleType::Phase); break; |
601 } | 630 } |
602 } else if (name == "Frequency Scale") { | 631 } else if (name == "Frequency Scale") { |
603 switch (value) { | 632 switch (value) { |
604 default: | 633 default: |
605 case 0: setBinScale(BinScale::Linear); break; | 634 case 0: setBinScale(BinScale::Linear); break; |
606 case 1: setBinScale(BinScale::Log); break; | 635 case 1: setBinScale(BinScale::Log); break; |
607 } | 636 } |
608 } else if (name == "Bin Display") { | 637 } else if (name == "Bin Display") { |
609 switch (value) { | 638 switch (value) { |
610 default: | 639 default: |
611 case 0: setBinDisplay(BinDisplay::AllBins); break; | 640 case 0: setBinDisplay(BinDisplay::AllBins); break; |
612 case 1: setBinDisplay(BinDisplay::PeakBins); break; | 641 case 1: setBinDisplay(BinDisplay::PeakBins); break; |
613 case 2: setBinDisplay(BinDisplay::PeakFrequencies); break; | 642 case 2: setBinDisplay(BinDisplay::PeakFrequencies); break; |
614 } | 643 } |
615 } else if (name == "Normalization") { | 644 } else if (name == "Normalization") { |
616 auto n = convertToColumnNorm(value); | 645 auto n = convertToColumnNorm(value); |
617 setNormalization(n.first); | 646 setNormalization(n.first); |
618 setNormalizeVisibleArea(n.second); | 647 setNormalizeVisibleArea(n.second); |
619 } | 648 } |
663 { | 692 { |
664 if (m_channel == ch) return; | 693 if (m_channel == ch) return; |
665 | 694 |
666 invalidateRenderers(); | 695 invalidateRenderers(); |
667 m_channel = ch; | 696 m_channel = ch; |
668 invalidateFFTModel(); | 697 recreateFFTModel(); |
669 | 698 |
670 emit layerParametersChanged(); | 699 emit layerParametersChanged(); |
671 } | 700 } |
672 | 701 |
673 int | 702 int |
707 | 736 |
708 invalidateRenderers(); | 737 invalidateRenderers(); |
709 | 738 |
710 m_windowSize = ws; | 739 m_windowSize = ws; |
711 | 740 |
712 invalidateFFTModel(); | 741 recreateFFTModel(); |
713 | 742 |
714 emit layerParametersChanged(); | 743 emit layerParametersChanged(); |
715 } | 744 } |
716 | 745 |
717 int | 746 int |
727 | 756 |
728 invalidateRenderers(); | 757 invalidateRenderers(); |
729 | 758 |
730 m_windowHopLevel = v; | 759 m_windowHopLevel = v; |
731 | 760 |
732 invalidateFFTModel(); | 761 recreateFFTModel(); |
733 | 762 |
734 emit layerParametersChanged(); | 763 emit layerParametersChanged(); |
735 | |
736 // fillCache(); | |
737 } | 764 } |
738 | 765 |
739 int | 766 int |
740 SpectrogramLayer::getWindowHopLevel() const | 767 SpectrogramLayer::getWindowHopLevel() const |
741 { | 768 { |
749 | 776 |
750 invalidateRenderers(); | 777 invalidateRenderers(); |
751 | 778 |
752 m_windowType = w; | 779 m_windowType = w; |
753 | 780 |
754 invalidateFFTModel(); | 781 recreateFFTModel(); |
755 | 782 |
756 emit layerParametersChanged(); | 783 emit layerParametersChanged(); |
757 } | 784 } |
758 | 785 |
759 WindowType | 786 WindowType |
764 | 791 |
765 void | 792 void |
766 SpectrogramLayer::setGain(float gain) | 793 SpectrogramLayer::setGain(float gain) |
767 { | 794 { |
768 // SVDEBUG << "SpectrogramLayer::setGain(" << gain << ") (my gain is now " | 795 // SVDEBUG << "SpectrogramLayer::setGain(" << gain << ") (my gain is now " |
769 // << m_gain << ")" << endl; | 796 // << m_gain << ")" << endl; |
770 | 797 |
771 if (m_gain == gain) return; | 798 if (m_gain == gain) return; |
772 | 799 |
773 invalidateRenderers(); | 800 invalidateRenderers(); |
774 | 801 |
849 if (r < 0) r = 0; | 876 if (r < 0) r = 0; |
850 if (r > 256) r = 256; | 877 if (r > 256) r = 256; |
851 int distance = r - m_colourRotation; | 878 int distance = r - m_colourRotation; |
852 | 879 |
853 if (distance != 0) { | 880 if (distance != 0) { |
854 m_colourRotation = r; | 881 m_colourRotation = r; |
855 } | 882 } |
856 | 883 |
857 // Initially the idea with colour rotation was that we would just | 884 // Initially the idea with colour rotation was that we would just |
858 // rotate the palette of an already-generated cache. That's not | 885 // rotate the palette of an already-generated cache. That's not |
859 // really practical now that cacheing is handled in a separate | 886 // really practical now that cacheing is handled in a separate |
1001 return; | 1028 return; |
1002 } | 1029 } |
1003 | 1030 |
1004 Layer::setLayerDormant(v, true); | 1031 Layer::setLayerDormant(v, true); |
1005 | 1032 |
1006 invalidateRenderers(); | 1033 invalidateRenderers(); |
1007 | 1034 |
1008 } else { | 1035 } else { |
1009 | 1036 |
1010 Layer::setLayerDormant(v, false); | 1037 Layer::setLayerDormant(v, false); |
1011 } | 1038 } |
1039 } | |
1040 | |
1041 bool | |
1042 SpectrogramLayer::isLayerScrollable(const LayerGeometryProvider *) const | |
1043 { | |
1044 return false; | |
1012 } | 1045 } |
1013 | 1046 |
1014 void | 1047 void |
1015 SpectrogramLayer::cacheInvalid() | 1048 SpectrogramLayer::cacheInvalid() |
1016 { | 1049 { |
1056 { | 1089 { |
1057 sv_samplerate_t sr = m_model->getSampleRate(); | 1090 sv_samplerate_t sr = m_model->getSampleRate(); |
1058 double minf = double(sr) / getFFTSize(); | 1091 double minf = double(sr) / getFFTSize(); |
1059 | 1092 |
1060 if (m_minFrequency > 0.0) { | 1093 if (m_minFrequency > 0.0) { |
1061 int minbin = int((double(m_minFrequency) * getFFTSize()) / sr + 0.01); | 1094 int minbin = int((double(m_minFrequency) * getFFTSize()) / sr + 0.01); |
1062 if (minbin < 1) minbin = 1; | 1095 if (minbin < 1) minbin = 1; |
1063 minf = minbin * sr / getFFTSize(); | 1096 minf = minbin * sr / getFFTSize(); |
1064 } | 1097 } |
1065 | 1098 |
1066 return minf; | 1099 return minf; |
1067 } | 1100 } |
1068 | 1101 |
1071 { | 1104 { |
1072 sv_samplerate_t sr = m_model->getSampleRate(); | 1105 sv_samplerate_t sr = m_model->getSampleRate(); |
1073 double maxf = double(sr) / 2; | 1106 double maxf = double(sr) / 2; |
1074 | 1107 |
1075 if (m_maxFrequency > 0.0) { | 1108 if (m_maxFrequency > 0.0) { |
1076 int maxbin = int((double(m_maxFrequency) * getFFTSize()) / sr + 0.1); | 1109 int maxbin = int((double(m_maxFrequency) * getFFTSize()) / sr + 0.1); |
1077 if (maxbin > getFFTSize() / 2) maxbin = getFFTSize() / 2; | 1110 if (maxbin > getFFTSize() / 2) maxbin = getFFTSize() / 2; |
1078 maxf = maxbin * sr / getFFTSize(); | 1111 maxf = maxbin * sr / getFFTSize(); |
1079 } | 1112 } |
1080 | 1113 |
1081 return maxf; | 1114 return maxf; |
1082 } | 1115 } |
1083 | 1116 |
1133 // Each pixel column covers an exact range of sample frames: | 1166 // Each pixel column covers an exact range of sample frames: |
1134 sv_frame_t f0 = v->getFrameForX(x) - modelStart; | 1167 sv_frame_t f0 = v->getFrameForX(x) - modelStart; |
1135 sv_frame_t f1 = v->getFrameForX(x + 1) - modelStart - 1; | 1168 sv_frame_t f1 = v->getFrameForX(x + 1) - modelStart - 1; |
1136 | 1169 |
1137 if (f1 < int(modelStart) || f0 > int(modelEnd)) { | 1170 if (f1 < int(modelStart) || f0 > int(modelEnd)) { |
1138 return false; | 1171 return false; |
1139 } | 1172 } |
1140 | 1173 |
1141 // And that range may be drawn from a possibly non-integral | 1174 // And that range may be drawn from a possibly non-integral |
1142 // range of spectrogram windows: | 1175 // range of spectrogram windows: |
1143 | 1176 |
1158 int s1i = int(s1); | 1191 int s1i = int(s1); |
1159 | 1192 |
1160 int windowIncrement = getWindowIncrement(); | 1193 int windowIncrement = getWindowIncrement(); |
1161 int w0 = s0i * windowIncrement - (m_windowSize - windowIncrement)/2; | 1194 int w0 = s0i * windowIncrement - (m_windowSize - windowIncrement)/2; |
1162 int w1 = s1i * windowIncrement + windowIncrement + | 1195 int w1 = s1i * windowIncrement + windowIncrement + |
1163 (m_windowSize - windowIncrement)/2 - 1; | 1196 (m_windowSize - windowIncrement)/2 - 1; |
1164 | 1197 |
1165 min = RealTime::frame2RealTime(w0, m_model->getSampleRate()); | 1198 min = RealTime::frame2RealTime(w0, m_model->getSampleRate()); |
1166 max = RealTime::frame2RealTime(w1, m_model->getSampleRate()); | 1199 max = RealTime::frame2RealTime(w1, m_model->getSampleRate()); |
1167 return true; | 1200 return true; |
1168 } | 1201 } |
1178 int q1i = int(q1); | 1211 int q1i = int(q1); |
1179 | 1212 |
1180 sv_samplerate_t sr = m_model->getSampleRate(); | 1213 sv_samplerate_t sr = m_model->getSampleRate(); |
1181 | 1214 |
1182 for (int q = q0i; q <= q1i; ++q) { | 1215 for (int q = q0i; q <= q1i; ++q) { |
1183 if (q == q0i) freqMin = (sr * q) / getFFTSize(); | 1216 if (q == q0i) freqMin = (sr * q) / getFFTSize(); |
1184 if (q == q1i) freqMax = (sr * (q+1)) / getFFTSize(); | 1217 if (q == q1i) freqMax = (sr * (q+1)) / getFFTSize(); |
1185 } | 1218 } |
1186 return true; | 1219 return true; |
1187 } | 1220 } |
1188 | 1221 |
1189 bool | 1222 bool |
1190 SpectrogramLayer::getAdjustedYBinSourceRange(LayerGeometryProvider *v, int x, int y, | 1223 SpectrogramLayer::getAdjustedYBinSourceRange(LayerGeometryProvider *v, int x, int y, |
1191 double &freqMin, double &freqMax, | 1224 double &freqMin, double &freqMax, |
1192 double &adjFreqMin, double &adjFreqMax) | 1225 double &adjFreqMin, double &adjFreqMax) |
1193 const | 1226 const |
1194 { | 1227 { |
1195 if (!m_model || !m_model->isOK() || !m_model->isReady()) { | 1228 if (!m_model || !m_model->isOK() || !m_model->isReady()) { |
1196 return false; | 1229 return false; |
1197 } | 1230 } |
1198 | 1231 |
1199 FFTModel *fft = getFFTModel(); | 1232 FFTModel *fft = getFFTModel(); |
1200 if (!fft) return false; | 1233 if (!fft) return false; |
1201 | 1234 |
1214 sv_samplerate_t sr = m_model->getSampleRate(); | 1247 sv_samplerate_t sr = m_model->getSampleRate(); |
1215 | 1248 |
1216 bool haveAdj = false; | 1249 bool haveAdj = false; |
1217 | 1250 |
1218 bool peaksOnly = (m_binDisplay == BinDisplay::PeakBins || | 1251 bool peaksOnly = (m_binDisplay == BinDisplay::PeakBins || |
1219 m_binDisplay == BinDisplay::PeakFrequencies); | 1252 m_binDisplay == BinDisplay::PeakFrequencies); |
1220 | 1253 |
1221 for (int q = q0i; q <= q1i; ++q) { | 1254 for (int q = q0i; q <= q1i; ++q) { |
1222 | 1255 |
1223 for (int s = s0i; s <= s1i; ++s) { | 1256 for (int s = s0i; s <= s1i; ++s) { |
1224 | 1257 |
1225 double binfreq = (double(sr) * q) / m_windowSize; | 1258 double binfreq = (double(sr) * q) / m_windowSize; |
1226 if (q == q0i) freqMin = binfreq; | 1259 if (q == q0i) freqMin = binfreq; |
1227 if (q == q1i) freqMax = binfreq; | 1260 if (q == q1i) freqMax = binfreq; |
1228 | 1261 |
1229 if (peaksOnly && !fft->isLocalPeak(s, q)) continue; | 1262 if (peaksOnly && !fft->isLocalPeak(s, q)) continue; |
1230 | 1263 |
1231 if (!fft->isOverThreshold | 1264 if (!fft->isOverThreshold |
1232 (s, q, float(m_threshold * double(getFFTSize())/2.0))) { | 1265 (s, q, float(m_threshold * double(getFFTSize())/2.0))) { |
1233 continue; | 1266 continue; |
1234 } | 1267 } |
1235 | 1268 |
1236 double freq = binfreq; | 1269 double freq = binfreq; |
1237 | 1270 |
1238 if (s < int(fft->getWidth()) - 1) { | 1271 if (s < int(fft->getWidth()) - 1) { |
1239 | 1272 |
1240 fft->estimateStableFrequency(s, q, freq); | 1273 fft->estimateStableFrequency(s, q, freq); |
1241 | 1274 |
1242 if (!haveAdj || freq < adjFreqMin) adjFreqMin = freq; | 1275 if (!haveAdj || freq < adjFreqMin) adjFreqMin = freq; |
1243 if (!haveAdj || freq > adjFreqMax) adjFreqMax = freq; | 1276 if (!haveAdj || freq > adjFreqMax) adjFreqMax = freq; |
1244 | 1277 |
1245 haveAdj = true; | 1278 haveAdj = true; |
1246 } | 1279 } |
1247 } | 1280 } |
1248 } | 1281 } |
1249 | 1282 |
1250 if (!haveAdj) { | 1283 if (!haveAdj) { |
1251 adjFreqMin = adjFreqMax = 0.0; | 1284 adjFreqMin = adjFreqMax = 0.0; |
1252 } | 1285 } |
1253 | 1286 |
1254 return haveAdj; | 1287 return haveAdj; |
1255 } | 1288 } |
1256 | 1289 |
1257 bool | 1290 bool |
1258 SpectrogramLayer::getXYBinSourceRange(LayerGeometryProvider *v, int x, int y, | 1291 SpectrogramLayer::getXYBinSourceRange(LayerGeometryProvider *v, int x, int y, |
1259 double &min, double &max, | 1292 double &min, double &max, |
1260 double &phaseMin, double &phaseMax) const | 1293 double &phaseMin, double &phaseMax) const |
1261 { | 1294 { |
1262 if (!m_model || !m_model->isOK() || !m_model->isReady()) { | 1295 if (!m_model || !m_model->isOK() || !m_model->isReady()) { |
1263 return false; | 1296 return false; |
1264 } | 1297 } |
1265 | 1298 |
1266 double q0 = 0, q1 = 0; | 1299 double q0 = 0, q1 = 0; |
1267 if (!getYBinRange(v, y, q0, q1)) return false; | 1300 if (!getYBinRange(v, y, q0, q1)) return false; |
1268 | 1301 |
1303 value = fft->getMagnitudeAt(s, q) / (getFFTSize()/2.0); | 1336 value = fft->getMagnitudeAt(s, q) / (getFFTSize()/2.0); |
1304 if (!have || value < min) { min = value; } | 1337 if (!have || value < min) { min = value; } |
1305 if (!have || value > max) { max = value; } | 1338 if (!have || value > max) { max = value; } |
1306 | 1339 |
1307 have = true; | 1340 have = true; |
1308 } | 1341 } |
1309 } | 1342 } |
1310 } | 1343 } |
1311 | 1344 |
1312 if (have) { | 1345 if (have) { |
1313 rv = true; | 1346 rv = true; |
1314 } | 1347 } |
1315 } | 1348 } |
1316 | 1349 |
1317 return rv; | 1350 return rv; |
1318 } | 1351 } |
1319 | 1352 |
1320 FFTModel * | 1353 void |
1321 SpectrogramLayer::getFFTModel() const | 1354 SpectrogramLayer::recreateFFTModel() |
1322 { | 1355 { |
1323 if (!m_model) return 0; | 1356 SVDEBUG << "SpectrogramLayer::recreateFFTModel called" << endl; |
1324 | 1357 |
1325 int fftSize = getFFTSize(); | 1358 if (!m_model || !m_model->isOK()) { |
1326 | 1359 emit sliceableModelReplaced(m_fftModel, 0); |
1327 //!!! it is now surely slower to do this on every getFFTModel() | 1360 deleteDerivedModels(); |
1328 //!!! request than it would be to recreate the model immediately | 1361 return; |
1329 //!!! when something changes instead of just invalidating it | 1362 } |
1330 | 1363 |
1331 if (m_fftModel && | 1364 if (m_fftModel) m_fftModel->aboutToDelete(); |
1332 m_fftModel->getHeight() == fftSize / 2 + 1 && | 1365 |
1333 m_fftModel->getWindowIncrement() == getWindowIncrement()) { | 1366 if (m_peakCache) m_peakCache->aboutToDelete(); |
1334 return m_fftModel; | |
1335 } | |
1336 | |
1337 delete m_peakCache; | 1367 delete m_peakCache; |
1338 m_peakCache = 0; | 1368 m_peakCache = 0; |
1339 | 1369 |
1340 delete m_fftModel; | 1370 if (m_wholeCache) m_wholeCache->aboutToDelete(); |
1341 m_fftModel = new FFTModel(m_model, | 1371 delete m_wholeCache; |
1342 m_channel, | 1372 m_wholeCache = 0; |
1343 m_windowType, | 1373 |
1344 m_windowSize, | 1374 FFTModel *newModel = new FFTModel(m_model, |
1345 getWindowIncrement(), | 1375 m_channel, |
1346 fftSize); | 1376 m_windowType, |
1347 | 1377 m_windowSize, |
1348 if (!m_fftModel->isOK()) { | 1378 getWindowIncrement(), |
1379 getFFTSize()); | |
1380 | |
1381 if (!newModel->isOK()) { | |
1349 QMessageBox::critical | 1382 QMessageBox::critical |
1350 (0, tr("FFT cache failed"), | 1383 (0, tr("FFT cache failed"), |
1351 tr("Failed to create the FFT model for this spectrogram.\n" | 1384 tr("Failed to create the FFT model for this spectrogram.\n" |
1352 "There may be insufficient memory or disc space to continue.")); | 1385 "There may be insufficient memory or disc space to continue.")); |
1386 delete newModel; | |
1353 delete m_fftModel; | 1387 delete m_fftModel; |
1354 m_fftModel = 0; | 1388 m_fftModel = 0; |
1355 return 0; | 1389 return; |
1356 } | 1390 } |
1357 | 1391 |
1358 ((SpectrogramLayer *)this)->sliceableModelReplaced(0, m_fftModel); | 1392 FFTModel *oldModel = m_fftModel; |
1359 | 1393 m_fftModel = newModel; |
1360 return m_fftModel; | 1394 |
1361 } | 1395 if (canStoreWholeCache()) { // i.e. if enough memory |
1362 | 1396 m_wholeCache = new Dense3DModelPeakCache(m_fftModel, 1); |
1363 Dense3DModelPeakCache * | 1397 m_peakCache = new Dense3DModelPeakCache(m_wholeCache, m_peakCacheDivisor); |
1364 SpectrogramLayer::getPeakCache() const | 1398 } else { |
1365 { | 1399 m_peakCache = new Dense3DModelPeakCache(m_fftModel, m_peakCacheDivisor); |
1366 //!!! see comment in getFFTModel | 1400 } |
1367 | 1401 |
1368 if (!m_peakCache) { | 1402 emit sliceableModelReplaced(oldModel, m_fftModel); |
1369 FFTModel *f = getFFTModel(); | 1403 delete oldModel; |
1370 if (!f) return 0; | 1404 } |
1371 m_peakCache = new Dense3DModelPeakCache(f, m_peakCacheDivisor); | 1405 |
1372 } | 1406 bool |
1373 return m_peakCache; | 1407 SpectrogramLayer::canStoreWholeCache() const |
1408 { | |
1409 if (!m_fftModel) { | |
1410 return false; // or true, doesn't really matter | |
1411 } | |
1412 | |
1413 size_t sz = | |
1414 size_t(m_fftModel->getWidth()) * | |
1415 size_t(m_fftModel->getHeight()) * | |
1416 sizeof(float); | |
1417 | |
1418 try { | |
1419 SVDEBUG << "Requesting advice from StorageAdviser on whether to create whole-model cache" << endl; | |
1420 StorageAdviser::Recommendation recommendation = | |
1421 StorageAdviser::recommend | |
1422 (StorageAdviser::Criteria(StorageAdviser::SpeedCritical | | |
1423 StorageAdviser::PrecisionCritical | | |
1424 StorageAdviser::FrequentLookupLikely), | |
1425 sz / 1024, sz / 1024); | |
1426 if ((recommendation & StorageAdviser::UseDisc) || | |
1427 (recommendation & StorageAdviser::ConserveSpace)) { | |
1428 SVDEBUG << "Seems inadvisable to create whole-model cache" << endl; | |
1429 return false; | |
1430 } else { | |
1431 SVDEBUG << "Seems fine to create whole-model cache" << endl; | |
1432 return true; | |
1433 } | |
1434 } catch (const InsufficientDiscSpace &) { | |
1435 SVDEBUG << "Seems like a terrible idea to create whole-model cache" << endl; | |
1436 return false; | |
1437 } | |
1374 } | 1438 } |
1375 | 1439 |
1376 const Model * | 1440 const Model * |
1377 SpectrogramLayer::getSliceableModel() const | 1441 SpectrogramLayer::getSliceableModel() const |
1378 { | 1442 { |
1379 return m_fftModel; | 1443 return m_fftModel; |
1380 } | |
1381 | |
1382 void | |
1383 SpectrogramLayer::invalidateFFTModel() | |
1384 { | |
1385 #ifdef DEBUG_SPECTROGRAM | |
1386 cerr << "SpectrogramLayer::invalidateFFTModel called" << endl; | |
1387 #endif | |
1388 | |
1389 emit sliceableModelReplaced(m_fftModel, 0); | |
1390 | |
1391 delete m_fftModel; | |
1392 delete m_peakCache; | |
1393 | |
1394 m_fftModel = 0; | |
1395 m_peakCache = 0; | |
1396 } | 1444 } |
1397 | 1445 |
1398 void | 1446 void |
1399 SpectrogramLayer::invalidateMagnitudes() | 1447 SpectrogramLayer::invalidateMagnitudes() |
1400 { | 1448 { |
1419 | 1467 |
1420 Colour3DPlotRenderer::Sources sources; | 1468 Colour3DPlotRenderer::Sources sources; |
1421 sources.verticalBinLayer = this; | 1469 sources.verticalBinLayer = this; |
1422 sources.fft = getFFTModel(); | 1470 sources.fft = getFFTModel(); |
1423 sources.source = sources.fft; | 1471 sources.source = sources.fft; |
1424 sources.peakCache = getPeakCache(); | 1472 if (m_peakCache) sources.peakCaches.push_back(m_peakCache); |
1473 if (m_wholeCache) sources.peakCaches.push_back(m_wholeCache); | |
1425 | 1474 |
1426 ColourScale::Parameters cparams; | 1475 ColourScale::Parameters cparams; |
1427 cparams.colourMap = m_colourMap; | 1476 cparams.colourMap = m_colourMap; |
1428 cparams.scaleType = m_colourScale; | 1477 cparams.scaleType = m_colourScale; |
1429 cparams.multiple = m_colourScaleMultiple; | 1478 cparams.multiple = m_colourScaleMultiple; |
1431 if (m_colourScale != ColourScaleType::Phase) { | 1480 if (m_colourScale != ColourScaleType::Phase) { |
1432 cparams.gain = m_gain; | 1481 cparams.gain = m_gain; |
1433 cparams.threshold = m_threshold; | 1482 cparams.threshold = m_threshold; |
1434 } | 1483 } |
1435 | 1484 |
1436 float minValue = 0.0f; | 1485 double minValue = 0.0f; |
1437 float maxValue = 1.0f; | 1486 double maxValue = 1.0f; |
1438 | 1487 |
1439 if (m_normalizeVisibleArea && m_viewMags[viewId].isSet()) { | 1488 if (m_normalizeVisibleArea && m_viewMags[viewId].isSet()) { |
1440 minValue = m_viewMags[viewId].getMin(); | 1489 minValue = m_viewMags[viewId].getMin(); |
1441 maxValue = m_viewMags[viewId].getMax(); | 1490 maxValue = m_viewMags[viewId].getMax(); |
1442 } else if (m_colourScale == ColourScaleType::Linear && | 1491 } else if (m_colourScale == ColourScaleType::Linear && |
1452 } | 1501 } |
1453 | 1502 |
1454 cparams.minValue = minValue; | 1503 cparams.minValue = minValue; |
1455 cparams.maxValue = maxValue; | 1504 cparams.maxValue = maxValue; |
1456 | 1505 |
1457 m_lastRenderedMags[viewId] = MagnitudeRange(minValue, maxValue); | 1506 m_lastRenderedMags[viewId] = MagnitudeRange(float(minValue), |
1507 float(maxValue)); | |
1458 | 1508 |
1459 Colour3DPlotRenderer::Parameters params; | 1509 Colour3DPlotRenderer::Parameters params; |
1460 params.colourScale = ColourScale(cparams); | 1510 params.colourScale = ColourScale(cparams); |
1461 params.normalization = m_normalization; | 1511 params.normalization = m_normalization; |
1462 params.binDisplay = m_binDisplay; | 1512 params.binDisplay = m_binDisplay; |
1475 Preferences::getInstance()->getSpectrogramSmoothing(); | 1525 Preferences::getInstance()->getSpectrogramSmoothing(); |
1476 params.interpolate = | 1526 params.interpolate = |
1477 (smoothing == Preferences::SpectrogramInterpolated || | 1527 (smoothing == Preferences::SpectrogramInterpolated || |
1478 smoothing == Preferences::SpectrogramZeroPaddedAndInterpolated); | 1528 smoothing == Preferences::SpectrogramZeroPaddedAndInterpolated); |
1479 | 1529 |
1480 m_renderers[v->getId()] = new Colour3DPlotRenderer(sources, params); | 1530 m_renderers[viewId] = new Colour3DPlotRenderer(sources, params); |
1481 } | 1531 |
1482 | 1532 m_crosshairColour = |
1483 return m_renderers[v->getId()]; | 1533 ColourMapper(m_colourMap, 1.f, 255.f).getContrastingColour(); |
1534 } | |
1535 | |
1536 return m_renderers[viewId]; | |
1484 } | 1537 } |
1485 | 1538 |
1486 void | 1539 void |
1487 SpectrogramLayer::paintWithRenderer(LayerGeometryProvider *v, QPainter &paint, QRect rect) const | 1540 SpectrogramLayer::paintWithRenderer(LayerGeometryProvider *v, QPainter &paint, QRect rect) const |
1488 { | 1541 { |
1552 | 1605 |
1553 cerr << "SpectrogramLayer::paint(): rect is " << rect.x() << "," << rect.y() << " " << rect.width() << "x" << rect.height() << endl; | 1606 cerr << "SpectrogramLayer::paint(): rect is " << rect.x() << "," << rect.y() << " " << rect.width() << "x" << rect.height() << endl; |
1554 #endif | 1607 #endif |
1555 | 1608 |
1556 if (!m_model || !m_model->isOK() || !m_model->isReady()) { | 1609 if (!m_model || !m_model->isOK() || !m_model->isReady()) { |
1557 return; | 1610 return; |
1558 } | |
1559 | |
1560 if (isLayerDormant(v)) { | |
1561 SVDEBUG << "SpectrogramLayer::paint(): Layer is dormant, making it undormant again" << endl; | |
1562 } | 1611 } |
1563 | 1612 |
1564 paintWithRenderer(v, paint, rect); | 1613 paintWithRenderer(v, paint, rect); |
1565 | 1614 |
1566 illuminateLocalFeatures(v, paint); | 1615 illuminateLocalFeatures(v, paint); |
1611 | 1660 |
1612 double | 1661 double |
1613 SpectrogramLayer::getYForFrequency(const LayerGeometryProvider *v, double frequency) const | 1662 SpectrogramLayer::getYForFrequency(const LayerGeometryProvider *v, double frequency) const |
1614 { | 1663 { |
1615 return v->getYForFrequency(frequency, | 1664 return v->getYForFrequency(frequency, |
1616 getEffectiveMinFrequency(), | 1665 getEffectiveMinFrequency(), |
1617 getEffectiveMaxFrequency(), | 1666 getEffectiveMaxFrequency(), |
1618 m_binScale == BinScale::Log); | 1667 m_binScale == BinScale::Log); |
1619 } | 1668 } |
1620 | 1669 |
1621 double | 1670 double |
1622 SpectrogramLayer::getFrequencyForY(const LayerGeometryProvider *v, int y) const | 1671 SpectrogramLayer::getFrequencyForY(const LayerGeometryProvider *v, int y) const |
1623 { | 1672 { |
1624 return v->getFrequencyForY(y, | 1673 return v->getFrequencyForY(y, |
1625 getEffectiveMinFrequency(), | 1674 getEffectiveMinFrequency(), |
1626 getEffectiveMaxFrequency(), | 1675 getEffectiveMaxFrequency(), |
1627 m_binScale == BinScale::Log); | 1676 m_binScale == BinScale::Log); |
1628 } | 1677 } |
1629 | 1678 |
1630 int | 1679 int |
1631 SpectrogramLayer::getCompletion(LayerGeometryProvider *) const | 1680 SpectrogramLayer::getCompletion(LayerGeometryProvider *) const |
1632 { | 1681 { |
1712 } | 1761 } |
1713 | 1762 |
1714 bool | 1763 bool |
1715 SpectrogramLayer::snapToFeatureFrame(LayerGeometryProvider *, | 1764 SpectrogramLayer::snapToFeatureFrame(LayerGeometryProvider *, |
1716 sv_frame_t &frame, | 1765 sv_frame_t &frame, |
1717 int &resolution, | 1766 int &resolution, |
1718 SnapType snap) const | 1767 SnapType snap) const |
1719 { | 1768 { |
1720 resolution = getWindowIncrement(); | 1769 resolution = getWindowIncrement(); |
1721 sv_frame_t left = (frame / resolution) * resolution; | 1770 sv_frame_t left = (frame / resolution) * resolution; |
1722 sv_frame_t right = left + resolution; | 1771 sv_frame_t right = left + resolution; |
1723 | 1772 |
1724 switch (snap) { | 1773 switch (snap) { |
1725 case SnapLeft: frame = left; break; | 1774 case SnapLeft: frame = left; break; |
1726 case SnapRight: frame = right; break; | 1775 case SnapRight: frame = right; break; |
1727 case SnapNearest: | 1776 case SnapNearest: |
1728 case SnapNeighbouring: | 1777 case SnapNeighbouring: |
1729 if (frame - left > right - frame) frame = right; | 1778 if (frame - left > right - frame) frame = right; |
1730 else frame = left; | 1779 else frame = left; |
1731 break; | 1780 break; |
1732 } | 1781 } |
1733 | 1782 |
1734 return true; | 1783 return true; |
1735 } | 1784 } |
1736 | 1785 |
1882 RealTime rtMin, rtMax; | 1931 RealTime rtMin, rtMax; |
1883 | 1932 |
1884 bool haveValues = false; | 1933 bool haveValues = false; |
1885 | 1934 |
1886 if (!getXBinSourceRange(v, x, rtMin, rtMax)) { | 1935 if (!getXBinSourceRange(v, x, rtMin, rtMax)) { |
1887 return ""; | 1936 return ""; |
1888 } | 1937 } |
1889 if (getXYBinSourceRange(v, x, y, magMin, magMax, phaseMin, phaseMax)) { | 1938 if (getXYBinSourceRange(v, x, y, magMin, magMax, phaseMin, phaseMax)) { |
1890 haveValues = true; | 1939 haveValues = true; |
1891 } | 1940 } |
1892 | 1941 |
1893 QString adjFreqText = "", adjPitchText = ""; | 1942 QString adjFreqText = "", adjPitchText = ""; |
1894 | 1943 |
1895 if (m_binDisplay == BinDisplay::PeakFrequencies) { | 1944 if (m_binDisplay == BinDisplay::PeakFrequencies) { |
1896 | 1945 |
1897 if (!getAdjustedYBinSourceRange(v, x, y, freqMin, freqMax, | 1946 if (!getAdjustedYBinSourceRange(v, x, y, freqMin, freqMax, |
1898 adjFreqMin, adjFreqMax)) { | 1947 adjFreqMin, adjFreqMax)) { |
1899 return ""; | 1948 return ""; |
1900 } | 1949 } |
1901 | 1950 |
1902 if (adjFreqMin != adjFreqMax) { | 1951 if (adjFreqMin != adjFreqMax) { |
1903 adjFreqText = tr("Peak Frequency:\t%1 - %2 Hz\n") | 1952 adjFreqText = tr("Peak Frequency:\t%1 - %2 Hz\n") |
1904 .arg(adjFreqMin).arg(adjFreqMax); | 1953 .arg(adjFreqMin).arg(adjFreqMax); |
1905 } else { | 1954 } else { |
1906 adjFreqText = tr("Peak Frequency:\t%1 Hz\n") | 1955 adjFreqText = tr("Peak Frequency:\t%1 Hz\n") |
1907 .arg(adjFreqMin); | 1956 .arg(adjFreqMin); |
1908 } | 1957 } |
1909 | 1958 |
1910 QString pmin = Pitch::getPitchLabelForFrequency(adjFreqMin); | 1959 QString pmin = Pitch::getPitchLabelForFrequency(adjFreqMin); |
1911 QString pmax = Pitch::getPitchLabelForFrequency(adjFreqMax); | 1960 QString pmax = Pitch::getPitchLabelForFrequency(adjFreqMax); |
1912 | 1961 |
1913 if (pmin != pmax) { | 1962 if (pmin != pmax) { |
1914 adjPitchText = tr("Peak Pitch:\t%3 - %4\n").arg(pmin).arg(pmax); | 1963 adjPitchText = tr("Peak Pitch:\t%3 - %4\n").arg(pmin).arg(pmax); |
1915 } else { | 1964 } else { |
1916 adjPitchText = tr("Peak Pitch:\t%2\n").arg(pmin); | 1965 adjPitchText = tr("Peak Pitch:\t%2\n").arg(pmin); |
1917 } | 1966 } |
1918 | 1967 |
1919 } else { | 1968 } else { |
1920 | 1969 |
1921 if (!getYBinSourceRange(v, y, freqMin, freqMax)) return ""; | 1970 if (!getYBinSourceRange(v, y, freqMin, freqMax)) return ""; |
1922 } | 1971 } |
1923 | 1972 |
1924 QString text; | 1973 QString text; |
1925 | 1974 |
1926 if (rtMin != rtMax) { | 1975 if (rtMin != rtMax) { |
1927 text += tr("Time:\t%1 - %2\n") | 1976 text += tr("Time:\t%1 - %2\n") |
1928 .arg(rtMin.toText(true).c_str()) | 1977 .arg(rtMin.toText(true).c_str()) |
1929 .arg(rtMax.toText(true).c_str()); | 1978 .arg(rtMax.toText(true).c_str()); |
1930 } else { | 1979 } else { |
1931 text += tr("Time:\t%1\n") | 1980 text += tr("Time:\t%1\n") |
1932 .arg(rtMin.toText(true).c_str()); | 1981 .arg(rtMin.toText(true).c_str()); |
1933 } | 1982 } |
1934 | 1983 |
1935 if (freqMin != freqMax) { | 1984 if (freqMin != freqMax) { |
1936 text += tr("%1Bin Frequency:\t%2 - %3 Hz\n%4Bin Pitch:\t%5 - %6\n") | 1985 text += tr("%1Bin Frequency:\t%2 - %3 Hz\n%4Bin Pitch:\t%5 - %6\n") |
1937 .arg(adjFreqText) | 1986 .arg(adjFreqText) |
1938 .arg(freqMin) | 1987 .arg(freqMin) |
1939 .arg(freqMax) | 1988 .arg(freqMax) |
1940 .arg(adjPitchText) | 1989 .arg(adjPitchText) |
1941 .arg(Pitch::getPitchLabelForFrequency(freqMin)) | 1990 .arg(Pitch::getPitchLabelForFrequency(freqMin)) |
1942 .arg(Pitch::getPitchLabelForFrequency(freqMax)); | 1991 .arg(Pitch::getPitchLabelForFrequency(freqMax)); |
1943 } else { | 1992 } else { |
1944 text += tr("%1Bin Frequency:\t%2 Hz\n%3Bin Pitch:\t%4\n") | 1993 text += tr("%1Bin Frequency:\t%2 Hz\n%3Bin Pitch:\t%4\n") |
1945 .arg(adjFreqText) | 1994 .arg(adjFreqText) |
1946 .arg(freqMin) | 1995 .arg(freqMin) |
1947 .arg(adjPitchText) | 1996 .arg(adjPitchText) |
1948 .arg(Pitch::getPitchLabelForFrequency(freqMin)); | 1997 .arg(Pitch::getPitchLabelForFrequency(freqMin)); |
1949 } | 1998 } |
1950 | 1999 |
1951 if (haveValues) { | 2000 if (haveValues) { |
1952 double dbMin = AudioLevel::multiplier_to_dB(magMin); | 2001 double dbMin = AudioLevel::multiplier_to_dB(magMin); |
1953 double dbMax = AudioLevel::multiplier_to_dB(magMax); | 2002 double dbMax = AudioLevel::multiplier_to_dB(magMax); |
1954 QString dbMinString; | 2003 QString dbMinString; |
1955 QString dbMaxString; | 2004 QString dbMaxString; |
1956 if (dbMin == AudioLevel::DB_FLOOR) { | 2005 if (dbMin == AudioLevel::DB_FLOOR) { |
1957 dbMinString = Strings::minus_infinity; | 2006 dbMinString = Strings::minus_infinity; |
1958 } else { | 2007 } else { |
1959 dbMinString = QString("%1").arg(lrint(dbMin)); | 2008 dbMinString = QString("%1").arg(lrint(dbMin)); |
1960 } | 2009 } |
1961 if (dbMax == AudioLevel::DB_FLOOR) { | 2010 if (dbMax == AudioLevel::DB_FLOOR) { |
1962 dbMaxString = Strings::minus_infinity; | 2011 dbMaxString = Strings::minus_infinity; |
1963 } else { | 2012 } else { |
1964 dbMaxString = QString("%1").arg(lrint(dbMax)); | 2013 dbMaxString = QString("%1").arg(lrint(dbMax)); |
1965 } | 2014 } |
1966 if (lrint(dbMin) != lrint(dbMax)) { | 2015 if (lrint(dbMin) != lrint(dbMax)) { |
1967 text += tr("dB:\t%1 - %2").arg(dbMinString).arg(dbMaxString); | 2016 text += tr("dB:\t%1 - %2").arg(dbMinString).arg(dbMaxString); |
1968 } else { | 2017 } else { |
1969 text += tr("dB:\t%1").arg(dbMinString); | 2018 text += tr("dB:\t%1").arg(dbMinString); |
1970 } | 2019 } |
1971 if (phaseMin != phaseMax) { | 2020 if (phaseMin != phaseMax) { |
1972 text += tr("\nPhase:\t%1 - %2").arg(phaseMin).arg(phaseMax); | 2021 text += tr("\nPhase:\t%1 - %2").arg(phaseMin).arg(phaseMax); |
1973 } else { | 2022 } else { |
1974 text += tr("\nPhase:\t%1").arg(phaseMin); | 2023 text += tr("\nPhase:\t%1").arg(phaseMin); |
1975 } | 2024 } |
1976 } | 2025 } |
1977 | 2026 |
1978 return text; | 2027 return text; |
1979 } | 2028 } |
1980 | 2029 |
1995 | 2044 |
1996 int cw = 0; | 2045 int cw = 0; |
1997 if (detailed) cw = getColourScaleWidth(paint); | 2046 if (detailed) cw = getColourScaleWidth(paint); |
1998 | 2047 |
1999 int tw = paint.fontMetrics().width(QString("%1") | 2048 int tw = paint.fontMetrics().width(QString("%1") |
2000 .arg(m_maxFrequency > 0 ? | 2049 .arg(m_maxFrequency > 0 ? |
2001 m_maxFrequency - 1 : | 2050 m_maxFrequency - 1 : |
2002 m_model->getSampleRate() / 2)); | 2051 m_model->getSampleRate() / 2)); |
2003 | 2052 |
2004 int fw = paint.fontMetrics().width(tr("43Hz")); | 2053 int fw = paint.fontMetrics().width(tr("43Hz")); |
2005 if (tw < fw) tw = fw; | 2054 if (tw < fw) tw = fw; |
2006 | 2055 |
2007 int tickw = (m_binScale == BinScale::Log ? 10 : 4); | 2056 int tickw = (m_binScale == BinScale::Log ? 10 : 4); |
2012 void | 2061 void |
2013 SpectrogramLayer::paintVerticalScale(LayerGeometryProvider *v, bool detailed, | 2062 SpectrogramLayer::paintVerticalScale(LayerGeometryProvider *v, bool detailed, |
2014 QPainter &paint, QRect rect) const | 2063 QPainter &paint, QRect rect) const |
2015 { | 2064 { |
2016 if (!m_model || !m_model->isOK()) { | 2065 if (!m_model || !m_model->isOK()) { |
2017 return; | 2066 return; |
2018 } | 2067 } |
2019 | 2068 |
2020 Profiler profiler("SpectrogramLayer::paintVerticalScale"); | 2069 Profiler profiler("SpectrogramLayer::paintVerticalScale"); |
2021 | 2070 |
2022 //!!! cache this? | 2071 //!!! cache this? |
2034 | 2083 |
2035 int bins = getFFTSize() / 2; | 2084 int bins = getFFTSize() / 2; |
2036 sv_samplerate_t sr = m_model->getSampleRate(); | 2085 sv_samplerate_t sr = m_model->getSampleRate(); |
2037 | 2086 |
2038 if (m_maxFrequency > 0) { | 2087 if (m_maxFrequency > 0) { |
2039 bins = int((double(m_maxFrequency) * getFFTSize()) / sr + 0.1); | 2088 bins = int((double(m_maxFrequency) * getFFTSize()) / sr + 0.1); |
2040 if (bins > getFFTSize() / 2) bins = getFFTSize() / 2; | 2089 if (bins > getFFTSize() / 2) bins = getFFTSize() / 2; |
2041 } | 2090 } |
2042 | 2091 |
2043 int cw = 0; | 2092 int cw = 0; |
2044 if (detailed) cw = getColourScaleWidth(paint); | 2093 if (detailed) cw = getColourScaleWidth(paint); |
2045 | 2094 |
2050 | 2099 |
2051 int bin = -1; | 2100 int bin = -1; |
2052 | 2101 |
2053 for (int y = 0; y < v->getPaintHeight(); ++y) { | 2102 for (int y = 0; y < v->getPaintHeight(); ++y) { |
2054 | 2103 |
2055 double q0, q1; | 2104 double q0, q1; |
2056 if (!getYBinRange(v, v->getPaintHeight() - y, q0, q1)) continue; | 2105 if (!getYBinRange(v, v->getPaintHeight() - y, q0, q1)) continue; |
2057 | 2106 |
2058 int vy; | 2107 int vy; |
2059 | 2108 |
2060 if (int(q0) > bin) { | 2109 if (int(q0) > bin) { |
2061 vy = y; | 2110 vy = y; |
2062 bin = int(q0); | 2111 bin = int(q0); |
2063 } else { | 2112 } else { |
2064 continue; | 2113 continue; |
2065 } | 2114 } |
2066 | 2115 |
2067 int freq = int((sr * bin) / getFFTSize()); | 2116 int freq = int((sr * bin) / getFFTSize()); |
2068 | 2117 |
2069 if (py >= 0 && (vy - py) < textHeight - 1) { | 2118 if (py >= 0 && (vy - py) < textHeight - 1) { |
2070 if (m_binScale == BinScale::Linear) { | 2119 if (m_binScale == BinScale::Linear) { |
2071 paint.drawLine(w - tickw, h - vy, w, h - vy); | 2120 paint.drawLine(w - tickw, h - vy, w, h - vy); |
2072 } | 2121 } |
2073 continue; | 2122 continue; |
2074 } | 2123 } |
2075 | 2124 |
2076 QString text = QString("%1").arg(freq); | 2125 QString text = QString("%1").arg(freq); |
2077 if (bin == 1) text = tr("%1Hz").arg(freq); // bin 0 is DC | 2126 if (bin == 1) text = tr("%1Hz").arg(freq); // bin 0 is DC |
2078 paint.drawLine(cw + 7, h - vy, w - pkw - 1, h - vy); | 2127 paint.drawLine(cw + 7, h - vy, w - pkw - 1, h - vy); |
2079 | 2128 |
2080 if (h - vy - textHeight >= -2) { | 2129 if (h - vy - textHeight >= -2) { |
2081 int tx = w - 3 - paint.fontMetrics().width(text) - max(tickw, pkw); | 2130 int tx = w - 3 - paint.fontMetrics().width(text) - max(tickw, pkw); |
2082 paint.drawText(tx, h - vy + toff, text); | 2131 paint.drawText(tx, h - vy + toff, text); |
2083 } | 2132 } |
2084 | 2133 |
2085 py = vy; | 2134 py = vy; |
2086 } | 2135 } |
2087 | 2136 |
2088 if (m_binScale == BinScale::Log) { | 2137 if (m_binScale == BinScale::Log) { |
2089 | 2138 |
2090 // piano keyboard | 2139 // piano keyboard |
2116 int cbw = paint.fontMetrics().width("dB"); | 2165 int cbw = paint.fontMetrics().width("dB"); |
2117 | 2166 |
2118 int topLines = 2; | 2167 int topLines = 2; |
2119 | 2168 |
2120 int ch = h - textHeight * (topLines + 1) - 8; | 2169 int ch = h - textHeight * (topLines + 1) - 8; |
2121 // paint.drawRect(4, textHeight + 4, cw - 1, ch + 1); | 2170 // paint.drawRect(4, textHeight + 4, cw - 1, ch + 1); |
2122 paint.drawRect(4 + cw - cbw, textHeight * topLines + 4, cbw - 1, ch + 1); | 2171 paint.drawRect(4 + cw - cbw, textHeight * topLines + 4, cbw - 1, ch + 1); |
2123 | 2172 |
2124 QString top, bottom; | 2173 QString top, bottom; |
2125 double min = m_viewMags[v->getId()].getMin(); | 2174 double min = m_viewMags[v->getId()].getMin(); |
2126 double max = m_viewMags[v->getId()].getMax(); | 2175 double max = m_viewMags[v->getId()].getMax(); |
2444 QString indent, QString extraAttributes) const | 2493 QString indent, QString extraAttributes) const |
2445 { | 2494 { |
2446 QString s; | 2495 QString s; |
2447 | 2496 |
2448 s += QString("channel=\"%1\" " | 2497 s += QString("channel=\"%1\" " |
2449 "windowSize=\"%2\" " | 2498 "windowSize=\"%2\" " |
2450 "windowHopLevel=\"%3\" " | 2499 "windowHopLevel=\"%3\" " |
2451 "gain=\"%4\" " | 2500 "gain=\"%4\" " |
2452 "threshold=\"%5\" ") | 2501 "threshold=\"%5\" ") |
2453 .arg(m_channel) | 2502 .arg(m_channel) |
2454 .arg(m_windowSize) | 2503 .arg(m_windowSize) |
2455 .arg(m_windowHopLevel) | 2504 .arg(m_windowHopLevel) |
2456 .arg(m_gain) | 2505 .arg(m_gain) |
2457 .arg(m_threshold); | 2506 .arg(m_threshold); |
2458 | 2507 |
2459 s += QString("minFrequency=\"%1\" " | 2508 s += QString("minFrequency=\"%1\" " |
2460 "maxFrequency=\"%2\" " | 2509 "maxFrequency=\"%2\" " |
2461 "colourScale=\"%3\" " | 2510 "colourScale=\"%3\" " |
2462 "colourScheme=\"%4\" " | 2511 "colourScheme=\"%4\" " |
2463 "colourRotation=\"%5\" " | 2512 "colourRotation=\"%5\" " |
2464 "frequencyScale=\"%6\" " | 2513 "frequencyScale=\"%6\" " |
2465 "binDisplay=\"%7\" ") | 2514 "binDisplay=\"%7\" ") |
2466 .arg(m_minFrequency) | 2515 .arg(m_minFrequency) |
2467 .arg(m_maxFrequency) | 2516 .arg(m_maxFrequency) |
2468 .arg(convertFromColourScale(m_colourScale, m_colourScaleMultiple)) | 2517 .arg(convertFromColourScale(m_colourScale, m_colourScaleMultiple)) |
2469 .arg(m_colourMap) | 2518 .arg(m_colourMap) |
2470 .arg(m_colourRotation) | 2519 .arg(m_colourRotation) |
2471 .arg(int(m_binScale)) | 2520 .arg(int(m_binScale)) |
2472 .arg(int(m_binDisplay)); | 2521 .arg(int(m_binDisplay)); |
2473 | 2522 |
2474 // New-style normalization attributes, allowing for more types of | 2523 // New-style normalization attributes, allowing for more types of |
2475 // normalization in future: write out the column normalization | 2524 // normalization in future: write out the column normalization |
2476 // type separately, and then whether we are normalizing visible | 2525 // type separately, and then whether we are normalizing visible |
2477 // area as well afterwards | 2526 // area as well afterwards |
2485 // it (Tony v1.0) has a totally different scale factor for | 2534 // it (Tony v1.0) has a totally different scale factor for |
2486 // it. We'll just have to accept that session files from Tony | 2535 // it. We'll just have to accept that session files from Tony |
2487 // v2.0+ will look odd in Tony v1.0 | 2536 // v2.0+ will look odd in Tony v1.0 |
2488 | 2537 |
2489 s += QString("normalizeColumns=\"%1\" ") | 2538 s += QString("normalizeColumns=\"%1\" ") |
2490 .arg(m_normalization == ColumnNormalization::Max1 ? "true" : "false"); | 2539 .arg(m_normalization == ColumnNormalization::Max1 ? "true" : "false"); |
2491 | 2540 |
2492 // And this applies to both old- and new-style attributes | 2541 // And this applies to both old- and new-style attributes |
2493 | 2542 |
2494 s += QString("normalizeVisibleArea=\"%1\" ") | 2543 s += QString("normalizeVisibleArea=\"%1\" ") |
2495 .arg(m_normalizeVisibleArea ? "true" : "false"); | 2544 .arg(m_normalizeVisibleArea ? "true" : "false"); |
2552 | 2601 |
2553 int colourRotation = attributes.value("colourRotation").toInt(&ok); | 2602 int colourRotation = attributes.value("colourRotation").toInt(&ok); |
2554 if (ok) setColourRotation(colourRotation); | 2603 if (ok) setColourRotation(colourRotation); |
2555 | 2604 |
2556 BinScale binScale = (BinScale) | 2605 BinScale binScale = (BinScale) |
2557 attributes.value("frequencyScale").toInt(&ok); | 2606 attributes.value("frequencyScale").toInt(&ok); |
2558 if (ok) setBinScale(binScale); | 2607 if (ok) setBinScale(binScale); |
2559 | 2608 |
2560 BinDisplay binDisplay = (BinDisplay) | 2609 BinDisplay binDisplay = (BinDisplay) |
2561 attributes.value("binDisplay").toInt(&ok); | 2610 attributes.value("binDisplay").toInt(&ok); |
2562 if (ok) setBinDisplay(binDisplay); | 2611 if (ok) setBinDisplay(binDisplay); |
2563 | 2612 |
2564 bool haveNewStyleNormalization = false; | 2613 bool haveNewStyleNormalization = false; |
2565 | 2614 |
2566 QString columnNormalization = attributes.value("columnNormalization"); | 2615 QString columnNormalization = attributes.value("columnNormalization"); |
2574 } else if (columnNormalization == "hybrid") { | 2623 } else if (columnNormalization == "hybrid") { |
2575 setNormalization(ColumnNormalization::Hybrid); | 2624 setNormalization(ColumnNormalization::Hybrid); |
2576 } else if (columnNormalization == "none") { | 2625 } else if (columnNormalization == "none") { |
2577 setNormalization(ColumnNormalization::None); | 2626 setNormalization(ColumnNormalization::None); |
2578 } else { | 2627 } else { |
2579 cerr << "NOTE: Unknown or unsupported columnNormalization attribute \"" | 2628 SVCERR << "NOTE: Unknown or unsupported columnNormalization attribute \"" |
2580 << columnNormalization << "\"" << endl; | 2629 << columnNormalization << "\"" << endl; |
2581 } | 2630 } |
2582 } | 2631 } |
2583 | 2632 |
2584 if (!haveNewStyleNormalization) { | 2633 if (!haveNewStyleNormalization) { |