comparison layer/SpectrogramLayer.cpp @ 1057:218be6cf2d4f spectrogram-minor-refactor

Merge from default branch
author Chris Cannam
date Mon, 13 Jun 2016 12:46:36 +0100
parents b4fd6c67fce5
children 9a13bc339fa9
comparison
equal deleted inserted replaced
1040:25b035362c44 1057:218be6cf2d4f
46 46
47 #ifndef __GNUC__ 47 #ifndef __GNUC__
48 #include <alloca.h> 48 #include <alloca.h>
49 #endif 49 #endif
50 50
51 //#define DEBUG_SPECTROGRAM_REPAINT 1 51 #define DEBUG_SPECTROGRAM_REPAINT 1
52 52
53 using namespace std; 53 using namespace std;
54 54
55 SpectrogramLayer::SpectrogramLayer(Configuration config) : 55 SpectrogramLayer::SpectrogramLayer(Configuration config) :
56 m_model(0), 56 m_model(0),
76 m_normalization(NoNormalization), 76 m_normalization(NoNormalization),
77 m_lastEmittedZoomStep(-1), 77 m_lastEmittedZoomStep(-1),
78 m_synchronous(false), 78 m_synchronous(false),
79 m_haveDetailedScale(false), 79 m_haveDetailedScale(false),
80 m_exiting(false), 80 m_exiting(false),
81 m_peakCacheDivisor(8),
81 m_sliceableModel(0) 82 m_sliceableModel(0)
82 { 83 {
83 QString colourConfigName = "spectrogram-colour"; 84 QString colourConfigName = "spectrogram-colour";
84 int colourConfigDefault = int(ColourMapper::Green); 85 int colourConfigDefault = int(ColourMapper::Green);
85 86
580 } 581 }
581 582
582 void 583 void
583 SpectrogramLayer::invalidateImageCaches() 584 SpectrogramLayer::invalidateImageCaches()
584 { 585 {
586 #ifdef DEBUG_SPECTROGRAM
587 cerr << "SpectrogramLayer::invalidateImageCaches called" << endl;
588 #endif
585 for (ViewImageCache::iterator i = m_imageCaches.begin(); 589 for (ViewImageCache::iterator i = m_imageCaches.begin();
586 i != m_imageCaches.end(); ++i) { 590 i != m_imageCaches.end(); ++i) {
587 i->second.invalidate(); 591 i->second.invalidate();
588 } 592 }
589 } 593 }
1516 { 1520 {
1517 const View *view = v->getView(); 1521 const View *view = v->getView();
1518 if (!m_peakCaches[view->getId()]) { 1522 if (!m_peakCaches[view->getId()]) {
1519 FFTModel *f = getFFTModel(v); 1523 FFTModel *f = getFFTModel(v);
1520 if (!f) return 0; 1524 if (!f) return 0;
1521 m_peakCaches[view->getId()] = new Dense3DModelPeakCache(f, 8); 1525 m_peakCaches[view->getId()] =
1526 new Dense3DModelPeakCache(f, m_peakCacheDivisor);
1522 } 1527 }
1523 return m_peakCaches[view->getId()]; 1528 return m_peakCaches[view->getId()];
1524 } 1529 }
1525 1530
1526 const Model * 1531 const Model *
1533 } 1538 }
1534 1539
1535 void 1540 void
1536 SpectrogramLayer::invalidateFFTModels() 1541 SpectrogramLayer::invalidateFFTModels()
1537 { 1542 {
1543 #ifdef DEBUG_SPECTROGRAM
1544 cerr << "SpectrogramLayer::invalidateFFTModels called" << endl;
1545 #endif
1538 for (ViewFFTMap::iterator i = m_fftModels.begin(); 1546 for (ViewFFTMap::iterator i = m_fftModels.begin();
1539 i != m_fftModels.end(); ++i) { 1547 i != m_fftModels.end(); ++i) {
1540 delete i->second; 1548 delete i->second;
1541 } 1549 }
1542 for (PeakCacheMap::iterator i = m_peakCaches.begin(); 1550 for (PeakCacheMap::iterator i = m_peakCaches.begin();
1555 } 1563 }
1556 1564
1557 void 1565 void
1558 SpectrogramLayer::invalidateMagnitudes() 1566 SpectrogramLayer::invalidateMagnitudes()
1559 { 1567 {
1568 #ifdef DEBUG_SPECTROGRAM
1569 cerr << "SpectrogramLayer::invalidateMagnitudes called" << endl;
1570 #endif
1560 m_viewMags.clear(); 1571 m_viewMags.clear();
1561 for (vector<MagnitudeRange>::iterator i = m_columnMags.begin(); 1572 for (vector<MagnitudeRange>::iterator i = m_columnMags.begin();
1562 i != m_columnMags.end(); ++i) { 1573 i != m_columnMags.end(); ++i) {
1563 *i = MagnitudeRange(); 1574 *i = MagnitudeRange();
1564 } 1575 }
1595 } 1606 }
1596 } 1607 }
1597 1608
1598 #ifdef DEBUG_SPECTROGRAM_REPAINT 1609 #ifdef DEBUG_SPECTROGRAM_REPAINT
1599 cerr << "SpectrogramLayer::updateViewMagnitudes returning from cols " 1610 cerr << "SpectrogramLayer::updateViewMagnitudes returning from cols "
1600 << s0 << " -> " << s1 << " inclusive" << endl; 1611 << s0 << " -> " << s1 << " inclusive" << endl;
1612 cerr << "SpectrogramLayer::updateViewMagnitudes: for view id " << v->getId()
1613 << ": min is " << mag.getMin() << ", max is " << mag.getMax() << endl;
1601 #endif 1614 #endif
1602 1615
1603 if (!mag.isSet()) return false; 1616 if (!mag.isSet()) return false;
1604 if (mag == m_viewMags[v->getId()]) return false; 1617 if (mag == m_viewMags[v->getId()]) return false;
1605 m_viewMags[v->getId()] = mag; 1618 m_viewMags[v->getId()] = mag;
1917 } 1930 }
1918 } 1931 }
1919 if (m_drawBuffer.width() < bufwid || m_drawBuffer.height() != h) { 1932 if (m_drawBuffer.width() < bufwid || m_drawBuffer.height() != h) {
1920 m_drawBuffer = QImage(bufwid, h, QImage::Format_Indexed8); 1933 m_drawBuffer = QImage(bufwid, h, QImage::Format_Indexed8);
1921 } 1934 }
1922 usePeaksCache = (increment * 8) < zoomLevel; 1935 usePeaksCache = (increment * m_peakCacheDivisor) < zoomLevel;
1923 if (m_colourScale == PhaseColourScale) usePeaksCache = false; 1936 if (m_colourScale == PhaseColourScale) usePeaksCache = false;
1924 } 1937 }
1925 1938
1926 for (int pixel = 0; pixel < 256; ++pixel) { 1939 for (int pixel = 0; pixel < 256; ++pixel) {
1927 m_drawBuffer.setColor((unsigned char)pixel, 1940 m_drawBuffer.setColor((unsigned char)pixel,
2250 if (m_colourScale == PhaseColourScale) { 2263 if (m_colourScale == PhaseColourScale) {
2251 fft->getPhasesAt(sx, values, minbin, maxbin - minbin + 1); 2264 fft->getPhasesAt(sx, values, minbin, maxbin - minbin + 1);
2252 } else if (m_normalization == NormalizeColumns) { 2265 } else if (m_normalization == NormalizeColumns) {
2253 fft->getNormalizedMagnitudesAt(sx, values, minbin, maxbin - minbin + 1); 2266 fft->getNormalizedMagnitudesAt(sx, values, minbin, maxbin - minbin + 1);
2254 } else if (m_normalization == NormalizeHybrid) { 2267 } else if (m_normalization == NormalizeHybrid) {
2255 fft->getNormalizedMagnitudesAt(sx, values, minbin, maxbin - minbin + 1); 2268 float max = fft->getNormalizedMagnitudesAt
2256 double max = fft->getMaximumMagnitudeAt(sx); 2269 (sx, values, minbin, maxbin - minbin + 1);
2257 if (max > 0.f) { 2270 float scale = log10f(max + 1.f);
2258 for (int i = minbin; i <= maxbin; ++i) { 2271 for (int i = minbin; i <= maxbin; ++i) {
2259 values[i - minbin] = float(values[i - minbin] * log10(max)); 2272 values[i - minbin] *= scale;
2260 }
2261 } 2273 }
2262 } else { 2274 } else {
2263 fft->getMagnitudesAt(sx, values, minbin, maxbin - minbin + 1); 2275 fft->getMagnitudesAt(sx, values, minbin, maxbin - minbin + 1);
2264 } 2276 }
2265 psx = sx; 2277 psx = sx;
2367 FFTModel *fft = 0; 2379 FFTModel *fft = 0;
2368 int divisor = 1; 2380 int divisor = 1;
2369 #ifdef DEBUG_SPECTROGRAM_REPAINT 2381 #ifdef DEBUG_SPECTROGRAM_REPAINT
2370 cerr << "SpectrogramLayer::paintDrawBuffer: Note: bin display = " << m_binDisplay << ", w = " << w << ", binforx[" << w-1 << "] = " << binforx[w-1] << ", binforx[0] = " << binforx[0] << endl; 2382 cerr << "SpectrogramLayer::paintDrawBuffer: Note: bin display = " << m_binDisplay << ", w = " << w << ", binforx[" << w-1 << "] = " << binforx[w-1] << ", binforx[0] = " << binforx[0] << endl;
2371 #endif 2383 #endif
2372 if (usePeaksCache) { //!!! 2384 if (usePeaksCache) {
2373 sourceModel = getPeakCache(v); 2385 sourceModel = getPeakCache(v);
2374 divisor = 8;//!!! 2386 divisor = m_peakCacheDivisor;
2375 minbin = 0; 2387 minbin = 0;
2376 maxbin = sourceModel->getHeight(); 2388 maxbin = sourceModel->getHeight();
2377 } else { 2389 } else {
2378 sourceModel = fft = getFFTModel(v); 2390 sourceModel = fft = getFFTModel(v);
2379 } 2391 }
2422 2434
2423 int columnCount = 0; 2435 int columnCount = 0;
2424 2436
2425 for (int x = start; x != finish; x += step) { 2437 for (int x = start; x != finish; x += step) {
2426 2438
2439 // x is the on-canvas pixel coord; sx (later) will be the
2440 // source column index
2441
2427 ++columnCount; 2442 ++columnCount;
2428 2443
2429 if (binforx[x] < 0) continue; 2444 if (binforx[x] < 0) continue;
2430 2445
2431 // float columnGain = m_gain;
2432 float columnMax = 0.f; 2446 float columnMax = 0.f;
2433 2447
2434 int sx0 = binforx[x] / divisor; 2448 int sx0 = binforx[x] / divisor;
2435 int sx1 = sx0; 2449 int sx1 = sx0;
2436 if (x+1 < w) sx1 = binforx[x+1] / divisor; 2450 if (x+1 < w) sx1 = binforx[x+1] / divisor;
2458 if (m_colourScale == PhaseColourScale) { 2472 if (m_colourScale == PhaseColourScale) {
2459 fft->getPhasesAt(sx, autoarray, minbin, maxbin - minbin + 1); 2473 fft->getPhasesAt(sx, autoarray, minbin, maxbin - minbin + 1);
2460 } else if (m_normalization == NormalizeColumns) { 2474 } else if (m_normalization == NormalizeColumns) {
2461 fft->getNormalizedMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1); 2475 fft->getNormalizedMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1);
2462 } else if (m_normalization == NormalizeHybrid) { 2476 } else if (m_normalization == NormalizeHybrid) {
2463 fft->getNormalizedMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1); 2477 float max = fft->getNormalizedMagnitudesAt
2464 float max = fft->getMaximumMagnitudeAt(sx); 2478 (sx, autoarray, minbin, maxbin - minbin + 1);
2465 float scale = log10f(max + 1.f); 2479 float scale = log10f(max + 1.f);
2466 // cout << "sx = " << sx << ", max = " << max << ", log10(max) = " << log10(max) << ", scale = " << scale << endl;
2467 for (int i = minbin; i <= maxbin; ++i) { 2480 for (int i = minbin; i <= maxbin; ++i) {
2468 autoarray[i - minbin] *= scale; 2481 autoarray[i - minbin] *= scale;
2469 } 2482 }
2470 } else { 2483 } else {
2471 fft->getMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1); 2484 fft->getMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1);
2573 if (overallMag.sample(mag)) overallMagChanged = true; 2586 if (overallMag.sample(mag)) overallMagChanged = true;
2574 } 2587 }
2575 } 2588 }
2576 } 2589 }
2577 2590
2591 // at this point we have updated m_columnMags and overallMag
2592 // -- used elsewhere for calculating the overall view range
2593 // for NormalizeVisibleArea mode -- and calculated "peaks"
2594 // (the possibly scaled and interpolated value array from
2595 // which we actually draw the column) and "columnMax" (maximum
2596 // value used for normalisation)
2597
2578 for (int y = 0; y < h; ++y) { 2598 for (int y = 0; y < h; ++y) {
2579 2599
2580 double peak = peaks[y]; 2600 double peak = peaks[y];
2581 2601
2582 if (m_colourScale != PhaseColourScale && 2602 if (m_colourScale != PhaseColourScale &&
3122 double max = m_viewMags[v->getId()].getMax(); 3142 double max = m_viewMags[v->getId()].getMax();
3123 3143
3124 double dBmin = AudioLevel::multiplier_to_dB(min); 3144 double dBmin = AudioLevel::multiplier_to_dB(min);
3125 double dBmax = AudioLevel::multiplier_to_dB(max); 3145 double dBmax = AudioLevel::multiplier_to_dB(max);
3126 3146
3147 #ifdef DEBUG_SPECTROGRAM_REPAINT
3148 cerr << "paintVerticalScale: for view id " << v->getId()
3149 << ": min = " << min << ", max = " << max
3150 << ", dBmin = " << dBmin << ", dBmax = " << dBmax << endl;
3151 #endif
3152
3127 if (dBmax < -60.f) dBmax = -60.f; 3153 if (dBmax < -60.f) dBmax = -60.f;
3128 else top = QString("%1").arg(lrint(dBmax)); 3154 else top = QString("%1").arg(lrint(dBmax));
3129 3155
3130 if (dBmin < dBmax - 60.f) dBmin = dBmax - 60.f; 3156 if (dBmin < dBmax - 60.f) dBmin = dBmax - 60.f;
3131 bottom = QString("%1").arg(lrint(dBmin)); 3157 bottom = QString("%1").arg(lrint(dBmin));
3463 .arg(m_colourMap) 3489 .arg(m_colourMap)
3464 .arg(m_colourRotation) 3490 .arg(m_colourRotation)
3465 .arg(m_frequencyScale) 3491 .arg(m_frequencyScale)
3466 .arg(m_binDisplay); 3492 .arg(m_binDisplay);
3467 3493
3468 s += QString("normalizeColumns=\"%1\" " 3494 // New-style normalization attributes, allowing for more types of
3469 "normalizeVisibleArea=\"%2\" " 3495 // normalization in future: write out the column normalization
3470 "normalizeHybrid=\"%3\" ") 3496 // type separately, and then whether we are normalizing visible
3471 .arg(m_normalization == NormalizeColumns ? "true" : "false") 3497 // area as well afterwards
3472 .arg(m_normalization == NormalizeVisibleArea ? "true" : "false") 3498
3473 .arg(m_normalization == NormalizeHybrid ? "true" : "false"); 3499 s += QString("columnNormalization=\"%1\" ")
3474 3500 .arg(m_normalization == NormalizeColumns ? "peak" :
3501 m_normalization == NormalizeHybrid ? "hybrid" : "none");
3502
3503 // Old-style normalization attribute. We *don't* write out
3504 // normalizeHybrid here because the only release that would accept
3505 // it (Tony v1.0) has a totally different scale factor for
3506 // it. We'll just have to accept that session files from Tony
3507 // v2.0+ will look odd in Tony v1.0
3508
3509 s += QString("normalizeColumns=\"%1\" ")
3510 .arg(m_normalization == NormalizeColumns ? "true" : "false");
3511
3512 // And this applies to both old- and new-style attributes
3513
3514 s += QString("normalizeVisibleArea=\"%1\" ")
3515 .arg(m_normalization == NormalizeVisibleArea ? "true" : "false");
3516
3475 Layer::toXml(stream, indent, extraAttributes + " " + s); 3517 Layer::toXml(stream, indent, extraAttributes + " " + s);
3476 } 3518 }
3477 3519
3478 void 3520 void
3479 SpectrogramLayer::setProperties(const QXmlAttributes &attributes) 3521 SpectrogramLayer::setProperties(const QXmlAttributes &attributes)
3534 3576
3535 BinDisplay binDisplay = (BinDisplay) 3577 BinDisplay binDisplay = (BinDisplay)
3536 attributes.value("binDisplay").toInt(&ok); 3578 attributes.value("binDisplay").toInt(&ok);
3537 if (ok) setBinDisplay(binDisplay); 3579 if (ok) setBinDisplay(binDisplay);
3538 3580
3539 bool normalizeColumns = 3581 bool haveNewStyleNormalization = false;
3540 (attributes.value("normalizeColumns").trimmed() == "true"); 3582
3541 if (normalizeColumns) { 3583 QString columnNormalization = attributes.value("columnNormalization");
3542 setNormalization(NormalizeColumns); 3584
3585 if (columnNormalization != "") {
3586
3587 haveNewStyleNormalization = true;
3588
3589 if (columnNormalization == "peak") {
3590 setNormalization(NormalizeColumns);
3591 } else if (columnNormalization == "hybrid") {
3592 setNormalization(NormalizeHybrid);
3593 } else if (columnNormalization == "none") {
3594 // do nothing
3595 } else {
3596 cerr << "NOTE: Unknown or unsupported columnNormalization attribute \""
3597 << columnNormalization << "\"" << endl;
3598 }
3599 }
3600
3601 if (!haveNewStyleNormalization) {
3602
3603 bool normalizeColumns =
3604 (attributes.value("normalizeColumns").trimmed() == "true");
3605 if (normalizeColumns) {
3606 setNormalization(NormalizeColumns);
3607 }
3608
3609 bool normalizeHybrid =
3610 (attributes.value("normalizeHybrid").trimmed() == "true");
3611 if (normalizeHybrid) {
3612 setNormalization(NormalizeHybrid);
3613 }
3543 } 3614 }
3544 3615
3545 bool normalizeVisibleArea = 3616 bool normalizeVisibleArea =
3546 (attributes.value("normalizeVisibleArea").trimmed() == "true"); 3617 (attributes.value("normalizeVisibleArea").trimmed() == "true");
3547 if (normalizeVisibleArea) { 3618 if (normalizeVisibleArea) {
3548 setNormalization(NormalizeVisibleArea); 3619 setNormalization(NormalizeVisibleArea);
3549 } 3620 }
3550 3621
3551 bool normalizeHybrid = 3622 if (!haveNewStyleNormalization && m_normalization == NormalizeHybrid) {
3552 (attributes.value("normalizeHybrid").trimmed() == "true"); 3623 // Tony v1.0 is (and hopefully will remain!) the only released
3553 if (normalizeHybrid) { 3624 // SV-a-like to use old-style attributes when saving sessions
3554 setNormalization(NormalizeHybrid); 3625 // that ask for hybrid normalization. It saves them with the
3555 } 3626 // wrong gain factor, so hack in a fix for that here -- this
3556 } 3627 // gives us backward but not forward compatibility.
3557 3628 setGain(m_gain / float(m_fftSize / 2));
3629 }
3630 }
3631