Mercurial > hg > svgui
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 |