comparison layer/SpectrogramLayer.cpp @ 1063:a0f234acd6e7 spectrogram-minor-refactor

Pull out column ops into ColumnOp
author Chris Cannam
date Mon, 20 Jun 2016 11:30:15 +0100
parents 38ecdd5924ac
children 77564d4fff43
comparison
equal deleted inserted replaced
1062:38ecdd5924ac 1063:a0f234acd6e7
21 #include "base/Window.h" 21 #include "base/Window.h"
22 #include "base/Pitch.h" 22 #include "base/Pitch.h"
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 "widgets/CommandHistory.h" 27 #include "widgets/CommandHistory.h"
27 #include "ColourMapper.h" 28 #include "ColourMapper.h"
28 #include "ImageRegionFinder.h" 29 #include "ImageRegionFinder.h"
29 #include "data/model/Dense3DModelPeakCache.h" 30 #include "data/model/Dense3DModelPeakCache.h"
30 #include "PianoScale.h" 31 #include "PianoScale.h"
71 m_initialMaxFrequency(8000), 72 m_initialMaxFrequency(8000),
72 m_colourScale(dBColourScale), 73 m_colourScale(dBColourScale),
73 m_colourMap(0), 74 m_colourMap(0),
74 m_frequencyScale(LinearFrequencyScale), 75 m_frequencyScale(LinearFrequencyScale),
75 m_binDisplay(AllBins), 76 m_binDisplay(AllBins),
76 m_normalization(NoNormalization), 77 m_normalization(ColumnOp::NoNormalization),
77 m_lastEmittedZoomStep(-1), 78 m_lastEmittedZoomStep(-1),
78 m_synchronous(false), 79 m_synchronous(false),
79 m_haveDetailedScale(false), 80 m_haveDetailedScale(false),
80 m_exiting(false), 81 m_exiting(false),
81 m_peakCacheDivisor(8), 82 m_peakCacheDivisor(8),
106 setMaxFrequency(2000); 107 setMaxFrequency(2000);
107 setMinFrequency(40); 108 setMinFrequency(40);
108 setFrequencyScale(LogFrequencyScale); 109 setFrequencyScale(LogFrequencyScale);
109 setColourScale(LinearColourScale); 110 setColourScale(LinearColourScale);
110 setBinDisplay(PeakFrequencies); 111 setBinDisplay(PeakFrequencies);
111 setNormalization(NormalizeColumns); 112 setNormalization(ColumnOp::NormalizeColumns);
112 colourConfigName = "spectrogram-melodic-colour"; 113 colourConfigName = "spectrogram-melodic-colour";
113 colourConfigDefault = int(ColourMapper::Sunset); 114 colourConfigDefault = int(ColourMapper::Sunset);
114 } 115 }
115 116
116 QSettings settings; 117 QSettings settings;
365 366
366 } else if (name == "Normalization") { 367 } else if (name == "Normalization") {
367 368
368 *min = 0; 369 *min = 0;
369 *max = 3; 370 *max = 3;
370 *deflt = int(NoNormalization); 371 *deflt = int(ColumnOp::NoNormalization);
371 val = (int)m_normalization; 372 val = (int)m_normalization;
372 373
373 } else { 374 } else {
374 val = Layer::getPropertyRangeAndValue(name, min, max, deflt); 375 val = Layer::getPropertyRangeAndValue(name, min, max, deflt);
375 } 376 }
570 case 2: setBinDisplay(PeakFrequencies); break; 571 case 2: setBinDisplay(PeakFrequencies); break;
571 } 572 }
572 } else if (name == "Normalization") { 573 } else if (name == "Normalization") {
573 switch (value) { 574 switch (value) {
574 default: 575 default:
575 case 0: setNormalization(NoNormalization); break; 576 case 0: setNormalization(ColumnOp::NoNormalization); break;
576 case 1: setNormalization(NormalizeColumns); break; 577 case 1: setNormalization(ColumnOp::NormalizeColumns); break;
577 case 2: setNormalization(NormalizeVisibleArea); break; 578 case 2: setNormalization(ColumnOp::NormalizeVisibleArea); break;
578 case 3: setNormalization(NormalizeHybrid); break; 579 case 3: setNormalization(ColumnOp::NormalizeHybrid); break;
579 } 580 }
580 } 581 }
581 } 582 }
582 583
583 void 584 void
886 { 887 {
887 return m_binDisplay; 888 return m_binDisplay;
888 } 889 }
889 890
890 void 891 void
891 SpectrogramLayer::setNormalization(Normalization n) 892 SpectrogramLayer::setNormalization(ColumnOp::Normalization n)
892 { 893 {
893 if (m_normalization == n) return; 894 if (m_normalization == n) return;
894 895
895 invalidateImageCaches(); 896 invalidateImageCaches();
896 invalidateMagnitudes(); 897 invalidateMagnitudes();
897 m_normalization = n; 898 m_normalization = n;
898 899
899 emit layerParametersChanged(); 900 emit layerParametersChanged();
900 } 901 }
901 902
902 SpectrogramLayer::Normalization 903 ColumnOp::Normalization
903 SpectrogramLayer::getNormalization() const 904 SpectrogramLayer::getNormalization() const
904 { 905 {
905 return m_normalization; 906 return m_normalization;
906 } 907 }
907 908
1048 int value; 1049 int value;
1049 1050
1050 double min = 0.0; 1051 double min = 0.0;
1051 double max = 1.0; 1052 double max = 1.0;
1052 1053
1053 if (m_normalization == NormalizeVisibleArea) { 1054 if (m_normalization == ColumnOp::NormalizeVisibleArea) {
1054 min = m_viewMags[v->getId()].getMin(); 1055 min = m_viewMags[v->getId()].getMin();
1055 max = m_viewMags[v->getId()].getMax(); 1056 max = m_viewMags[v->getId()].getMax();
1056 } else if (m_normalization != NormalizeColumns) { 1057 } else if (m_normalization != ColumnOp::NormalizeColumns) {
1057 if (m_colourScale == LinearColourScale //|| 1058 if (m_colourScale == LinearColourScale //||
1058 // m_colourScale == MeterColourScale) { 1059 // m_colourScale == MeterColourScale) {
1059 ) { 1060 ) {
1060 max = 0.1; 1061 max = 0.1;
1061 } 1062 }
1684 1685
1685 if (updateViewMagnitudes(v)) { 1686 if (updateViewMagnitudes(v)) {
1686 #ifdef DEBUG_SPECTROGRAM_REPAINT 1687 #ifdef DEBUG_SPECTROGRAM_REPAINT
1687 cerr << "SpectrogramLayer: magnitude range changed to [" << m_viewMags[v->getId()].getMin() << "->" << m_viewMags[v->getId()].getMax() << "]" << endl; 1688 cerr << "SpectrogramLayer: magnitude range changed to [" << m_viewMags[v->getId()].getMin() << "->" << m_viewMags[v->getId()].getMax() << "]" << endl;
1688 #endif 1689 #endif
1689 if (m_normalization == NormalizeVisibleArea) { 1690 if (m_normalization == ColumnOp::NormalizeVisibleArea) {
1690 cache.invalidate(); 1691 cache.invalidate();
1691 } 1692 }
1692 } 1693 }
1693 1694
1694 if (cache.getZoomLevel() != zoomLevel || 1695 if (cache.getZoomLevel() != zoomLevel ||
2127 paint.drawImage(pr.x(), pr.y(), cache.getImage(), 2128 paint.drawImage(pr.x(), pr.y(), cache.getImage(),
2128 pr.x(), pr.y(), pr.width(), pr.height()); 2129 pr.x(), pr.y(), pr.width(), pr.height());
2129 2130
2130 if (!m_synchronous) { 2131 if (!m_synchronous) {
2131 2132
2132 if ((m_normalization != NormalizeVisibleArea) || !overallMagChanged) { 2133 if ((m_normalization != ColumnOp::NormalizeVisibleArea) || !overallMagChanged) {
2133 2134
2134 QRect areaLeft(0, 0, cache.getValidLeft(), h); 2135 QRect areaLeft(0, 0, cache.getValidLeft(), h);
2135 QRect areaRight(cache.getValidRight(), 0, 2136 QRect areaRight(cache.getValidRight(), 0,
2136 cache.getSize().width() - cache.getValidRight(), h); 2137 cache.getSize().width() - cache.getValidRight(), h);
2137 2138
2261 peakfreqs = fft->getPeakFrequencies(FFTModel::AllPeaks, sx, 2262 peakfreqs = fft->getPeakFrequencies(FFTModel::AllPeaks, sx,
2262 minbin, maxbin - 1); 2263 minbin, maxbin - 1);
2263 if (m_colourScale == PhaseColourScale) { 2264 if (m_colourScale == PhaseColourScale) {
2264 fft->getPhasesAt(sx, values, minbin, maxbin - minbin + 1); 2265 fft->getPhasesAt(sx, values, minbin, maxbin - minbin + 1);
2265 /*!!! 2266 /*!!!
2266 } else if (m_normalization == NormalizeColumns) { 2267 } else if (m_normalization == ColumnOp::NormalizeColumns) {
2267 fft->getNormalizedMagnitudesAt(sx, values, minbin, maxbin - minbin + 1); 2268 fft->getNormalizedMagnitudesAt(sx, values, minbin, maxbin - minbin + 1);
2268 } else if (m_normalization == NormalizeHybrid) { 2269 } else if (m_normalization == ColumnOp::NormalizeHybrid) {
2269 float max = fft->getNormalizedMagnitudesAt 2270 float max = fft->getNormalizedMagnitudesAt
2270 (sx, values, minbin, maxbin - minbin + 1); 2271 (sx, values, minbin, maxbin - minbin + 1);
2271 float scale = log10f(max + 1.f); 2272 float scale = log10f(max + 1.f);
2272 for (int i = minbin; i <= maxbin; ++i) { 2273 for (int i = minbin; i <= maxbin; ++i) {
2273 values[i - minbin] *= scale; 2274 values[i - minbin] *= scale;
2288 if (bin > maxbin) break; 2289 if (bin > maxbin) break;
2289 2290
2290 double value = values[bin - minbin]; 2291 double value = values[bin - minbin];
2291 2292
2292 if (m_colourScale != PhaseColourScale) { 2293 if (m_colourScale != PhaseColourScale) {
2293 if (m_normalization != NormalizeColumns) { 2294 if (m_normalization != ColumnOp::NormalizeColumns) {
2294 value /= (m_fftSize/2.0); 2295 value /= (m_fftSize/2.0);
2295 } 2296 }
2296 mag.sample(float(value)); 2297 mag.sample(float(value));
2297 value *= m_gain; 2298 value *= m_gain;
2298 } 2299 }
2384 2385
2385 return vector<float>(col.data() + minbin, 2386 return vector<float>(col.data() + minbin,
2386 col.data() + minbin + bincount); 2387 col.data() + minbin + bincount);
2387 } 2388 }
2388 2389
2389 vector<float>
2390 SpectrogramLayer::scaleColumn(const vector<float> &in) const
2391 {
2392 if (m_normalization == NormalizeColumns ||
2393 m_normalization == NormalizeHybrid) {
2394 return in;
2395 }
2396 vector<float> out;
2397 out.reserve(in.size());
2398 float scale = 2.f / float(m_fftSize);
2399 for (auto v: in) {
2400 out.push_back(v * scale);
2401 }
2402 return out;
2403 }
2404
2405 static bool
2406 is_peak(const vector<float> &values, int ix)
2407 {
2408 if (!in_range_for(values, ix-1)) return false;
2409 if (!in_range_for(values, ix+1)) return false;
2410 if (values[ix] < values[ix+1]) return false;
2411 if (values[ix] < values[ix-1]) return false;
2412 return true;
2413 }
2414
2415 vector<float>
2416 SpectrogramLayer::distributeColumn(const vector<float> &in,
2417 int h,
2418 const vector<double> &binfory,
2419 int minbin,
2420 bool interpolate) const
2421 {
2422 vector<float> out(h, 0.f);
2423 int bins = int(in.size());
2424
2425 for (int y = 0; y < h; ++y) {
2426
2427 double sy0 = binfory[y] - minbin;
2428 double sy1 = sy0 + 1;
2429 if (y+1 < h) {
2430 sy1 = binfory[y+1] - minbin;
2431 }
2432
2433 if (interpolate && fabs(sy1 - sy0) < 1.0) {
2434
2435 double centre = (sy0 + sy1) / 2;
2436 double dist = (centre - 0.5) - rint(centre - 0.5);
2437 int bin = int(centre);
2438
2439 int other = (dist < 0 ? (bin-1) : (bin+1));
2440
2441 if (bin < 0) bin = 0;
2442 if (bin >= bins) bin = bins-1;
2443
2444 if (other < 0 || other >= bins) {
2445 other = bin;
2446 }
2447
2448 double prop = 1.0 - fabs(dist);
2449
2450 double v0 = in[bin];
2451 double v1 = in[other];
2452
2453 out[y] = float(prop * v0 + (1.0 - prop) * v1);
2454
2455 } else { // not interpolating this one
2456
2457 int by0 = int(sy0 + 0.0001);
2458 int by1 = int(sy1 + 0.0001);
2459 if (by1 < by0 + 1) by1 = by0 + 1;
2460
2461 for (int bin = by0; bin < by1; ++bin) {
2462
2463 float value = in[bin];
2464
2465 if (value > out[y] || m_colourScale == PhaseColourScale) {
2466 out[y] = value;
2467 }
2468 }
2469 }
2470 }
2471
2472 return out;
2473 }
2474
2475 void 2390 void
2476 SpectrogramLayer::recordColumnExtents(const vector<float> &col, 2391 SpectrogramLayer::recordColumnExtents(const vector<float> &col,
2477 int sx, // column index, for m_columnMags 2392 int sx, // column index, for m_columnMags
2478 MagnitudeRange &overallMag, 2393 MagnitudeRange &overallMag,
2479 bool &overallMagChanged) const 2394 bool &overallMagChanged) const
2480 { 2395 {
2481 if (!in_range_for(m_columnMags, sx)) { 2396 if (!in_range_for(m_columnMags, sx)) {
2482 throw logic_error("sx out of range for m_columnMags"); 2397 m_columnMags.resize(sx + 1);
2483 } 2398 }
2484 MagnitudeRange mr; 2399 MagnitudeRange mr;
2485 for (auto v: col) { 2400 for (auto v: col) {
2486 mr.sample(v); 2401 mr.sample(v);
2487 } 2402 }
2488 m_columnMags[sx] = mr; 2403 m_columnMags[sx] = mr;
2489 if (overallMag.sample(mr)) { 2404 if (overallMag.sample(mr)) {
2490 overallMagChanged = true; 2405 overallMagChanged = true;
2491 } 2406 }
2492 } 2407 }
2493
2494 vector<float>
2495 SpectrogramLayer::normalizeColumn(const vector<float> &in) const
2496 {
2497 if (m_normalization == NoNormalization ||
2498 m_normalization == NormalizeVisibleArea) {
2499 // NormalizeVisibleArea is handled through adjustment to m_gain
2500 return in;
2501 }
2502
2503 float max = *max_element(in.begin(), in.end());
2504
2505 if (m_normalization == NormalizeColumns && max == 0.f) {
2506 return in;
2507 }
2508
2509 if (m_normalization == NormalizeHybrid && max <= 0.f) {
2510 return in;
2511 }
2512
2513 vector<float> out;
2514 out.reserve(in.size());
2515
2516 float scale;
2517 if (m_normalization == NormalizeHybrid) {
2518 scale = log10f(max + 1.f) / max;
2519 } else {
2520 scale = 1.f / max;
2521 }
2522
2523 for (auto v: in) {
2524 out.push_back(v * scale);
2525 }
2526 return out;
2527 }
2528
2529 vector<float>
2530 SpectrogramLayer::peakPickColumn(const vector<float> &in) const
2531 {
2532 if (m_binDisplay == AllBins) return in;
2533
2534 vector<float> out(in.size(), 0.f);
2535
2536 for (int i = 0; in_range_for(in, i); ++i) {
2537 if (is_peak(in, i)) {
2538 out[i] = in[i];
2539 }
2540 }
2541
2542 return out;
2543 }
2544
2545 vector<float>
2546 SpectrogramLayer::applyDisplayGain(const vector<float> &in) const
2547 {
2548 if (m_gain == 1.0) return in;
2549
2550 vector<float> out;
2551 out.reserve(in.size());
2552 for (auto v: in) {
2553 out.push_back(v * m_gain);
2554 }
2555 return out;
2556 }
2557
2558 // order:
2559 // get column -> scale -> record extents -> normalise -> peak pick -> apply display gain -> distribute/interpolate
2560 2408
2561 int 2409 int
2562 SpectrogramLayer::paintDrawBuffer(LayerGeometryProvider *v, 2410 SpectrogramLayer::paintDrawBuffer(LayerGeometryProvider *v,
2563 int w, 2411 int w,
2564 int h, 2412 int h,
2662 2510
2663 MagnitudeRange mag; 2511 MagnitudeRange mag;
2664 2512
2665 if (sx != psx) { 2513 if (sx != psx) {
2666 2514
2667 vector<float> column; 2515 // order: get column -> scale -> record extents ->
2516 // normalise -> peak pick -> apply display gain ->
2517 // distribute/interpolate
2518
2519 ColumnOp::Column column;
2520
2668 if (peakCacheModel) { 2521 if (peakCacheModel) {
2669 column = getColumnFromGenericModel(peakCacheModel, 2522 column = getColumnFromGenericModel(peakCacheModel,
2670 sx, 2523 sx,
2671 minbin, 2524 minbin,
2672 maxbin - minbin + 1); 2525 maxbin - minbin + 1);
2675 sx, 2528 sx,
2676 minbin, 2529 minbin,
2677 maxbin - minbin + 1); 2530 maxbin - minbin + 1);
2678 } 2531 }
2679 2532
2680 column = scaleColumn(column); 2533 column = ColumnOp::fftScale(column, m_fftSize);
2681 2534
2682 recordColumnExtents(column, 2535 recordColumnExtents(column,
2683 sx, 2536 sx,
2684 overallMag, 2537 overallMag,
2685 overallMagChanged); 2538 overallMagChanged);
2686 2539
2540 column = ColumnOp::normalize(column, m_normalization);
2541
2542 if (m_binDisplay == PeakBins) {
2543 column = ColumnOp::peakPick(column);
2544 }
2545
2687 preparedColumn = 2546 preparedColumn =
2688 distributeColumn(applyDisplayGain 2547 ColumnOp::distribute
2689 (peakPickColumn 2548 (ColumnOp::applyGain(column, m_gain),
2690 (normalizeColumn 2549 h,
2691 (column))), 2550 binfory,
2692 h, 2551 minbin,
2693 binfory, 2552 interpolate);
2694 minbin,
2695 interpolate);
2696 2553
2697 psx = sx; 2554 psx = sx;
2698 } 2555 }
2699 2556
2700 //!!! now peak of all preparedColumns for this pixel 2557 //!!! now peak of all preparedColumns for this pixel
3586 // normalization in future: write out the column normalization 3443 // normalization in future: write out the column normalization
3587 // type separately, and then whether we are normalizing visible 3444 // type separately, and then whether we are normalizing visible
3588 // area as well afterwards 3445 // area as well afterwards
3589 3446
3590 s += QString("columnNormalization=\"%1\" ") 3447 s += QString("columnNormalization=\"%1\" ")
3591 .arg(m_normalization == NormalizeColumns ? "peak" : 3448 .arg(m_normalization == ColumnOp::NormalizeColumns ? "peak" :
3592 m_normalization == NormalizeHybrid ? "hybrid" : "none"); 3449 m_normalization == ColumnOp::NormalizeHybrid ? "hybrid" : "none");
3593 3450
3594 // Old-style normalization attribute. We *don't* write out 3451 // Old-style normalization attribute. We *don't* write out
3595 // normalizeHybrid here because the only release that would accept 3452 // normalizeHybrid here because the only release that would accept
3596 // it (Tony v1.0) has a totally different scale factor for 3453 // it (Tony v1.0) has a totally different scale factor for
3597 // it. We'll just have to accept that session files from Tony 3454 // it. We'll just have to accept that session files from Tony
3598 // v2.0+ will look odd in Tony v1.0 3455 // v2.0+ will look odd in Tony v1.0
3599 3456
3600 s += QString("normalizeColumns=\"%1\" ") 3457 s += QString("normalizeColumns=\"%1\" ")
3601 .arg(m_normalization == NormalizeColumns ? "true" : "false"); 3458 .arg(m_normalization == ColumnOp::NormalizeColumns ? "true" : "false");
3602 3459
3603 // And this applies to both old- and new-style attributes 3460 // And this applies to both old- and new-style attributes
3604 3461
3605 s += QString("normalizeVisibleArea=\"%1\" ") 3462 s += QString("normalizeVisibleArea=\"%1\" ")
3606 .arg(m_normalization == NormalizeVisibleArea ? "true" : "false"); 3463 .arg(m_normalization == ColumnOp::NormalizeVisibleArea ? "true" : "false");
3607 3464
3608 Layer::toXml(stream, indent, extraAttributes + " " + s); 3465 Layer::toXml(stream, indent, extraAttributes + " " + s);
3609 } 3466 }
3610 3467
3611 void 3468 void
3676 if (columnNormalization != "") { 3533 if (columnNormalization != "") {
3677 3534
3678 haveNewStyleNormalization = true; 3535 haveNewStyleNormalization = true;
3679 3536
3680 if (columnNormalization == "peak") { 3537 if (columnNormalization == "peak") {
3681 setNormalization(NormalizeColumns); 3538 setNormalization(ColumnOp::NormalizeColumns);
3682 } else if (columnNormalization == "hybrid") { 3539 } else if (columnNormalization == "hybrid") {
3683 setNormalization(NormalizeHybrid); 3540 setNormalization(ColumnOp::NormalizeHybrid);
3684 } else if (columnNormalization == "none") { 3541 } else if (columnNormalization == "none") {
3685 // do nothing 3542 // do nothing
3686 } else { 3543 } else {
3687 cerr << "NOTE: Unknown or unsupported columnNormalization attribute \"" 3544 cerr << "NOTE: Unknown or unsupported columnNormalization attribute \""
3688 << columnNormalization << "\"" << endl; 3545 << columnNormalization << "\"" << endl;
3692 if (!haveNewStyleNormalization) { 3549 if (!haveNewStyleNormalization) {
3693 3550
3694 bool normalizeColumns = 3551 bool normalizeColumns =
3695 (attributes.value("normalizeColumns").trimmed() == "true"); 3552 (attributes.value("normalizeColumns").trimmed() == "true");
3696 if (normalizeColumns) { 3553 if (normalizeColumns) {
3697 setNormalization(NormalizeColumns); 3554 setNormalization(ColumnOp::NormalizeColumns);
3698 } 3555 }
3699 3556
3700 bool normalizeHybrid = 3557 bool normalizeHybrid =
3701 (attributes.value("normalizeHybrid").trimmed() == "true"); 3558 (attributes.value("normalizeHybrid").trimmed() == "true");
3702 if (normalizeHybrid) { 3559 if (normalizeHybrid) {
3703 setNormalization(NormalizeHybrid); 3560 setNormalization(ColumnOp::NormalizeHybrid);
3704 } 3561 }
3705 } 3562 }
3706 3563
3707 bool normalizeVisibleArea = 3564 bool normalizeVisibleArea =
3708 (attributes.value("normalizeVisibleArea").trimmed() == "true"); 3565 (attributes.value("normalizeVisibleArea").trimmed() == "true");
3709 if (normalizeVisibleArea) { 3566 if (normalizeVisibleArea) {
3710 setNormalization(NormalizeVisibleArea); 3567 setNormalization(ColumnOp::NormalizeVisibleArea);
3711 } 3568 }
3712 3569
3713 if (!haveNewStyleNormalization && m_normalization == NormalizeHybrid) { 3570 if (!haveNewStyleNormalization && m_normalization == ColumnOp::NormalizeHybrid) {
3714 // Tony v1.0 is (and hopefully will remain!) the only released 3571 // Tony v1.0 is (and hopefully will remain!) the only released
3715 // SV-a-like to use old-style attributes when saving sessions 3572 // SV-a-like to use old-style attributes when saving sessions
3716 // that ask for hybrid normalization. It saves them with the 3573 // that ask for hybrid normalization. It saves them with the
3717 // wrong gain factor, so hack in a fix for that here -- this 3574 // wrong gain factor, so hack in a fix for that here -- this
3718 // gives us backward but not forward compatibility. 3575 // gives us backward but not forward compatibility.