comparison layer/SpectrogramLayer.cpp @ 9:561be41ad083

* Tweaked up spectrogram and added a colourmap rotation option * Fleshed out the SV file reader code -- now we just need to hook it up and watch it fall over
author Chris Cannam
date Mon, 16 Jan 2006 18:04:09 +0000
parents 02aaea1ffaf7
children 8f5b812baaee
comparison
equal deleted inserted replaced
8:06bba0b79b1c 9:561be41ad083
34 m_channel(0), 34 m_channel(0),
35 m_windowSize(1024), 35 m_windowSize(1024),
36 m_windowType(HanningWindow), 36 m_windowType(HanningWindow),
37 m_windowOverlap(50), 37 m_windowOverlap(50),
38 m_gain(1.0), 38 m_gain(1.0),
39 m_colourRotation(0),
39 m_maxFrequency(8000), 40 m_maxFrequency(8000),
40 m_colourScale(dBColourScale), 41 m_colourScale(dBColourScale),
41 m_colourScheme(DefaultColours), 42 m_colourScheme(DefaultColours),
42 m_frequencyScale(LinearFrequencyScale), 43 m_frequencyScale(LinearFrequencyScale),
43 m_cache(0), 44 m_cache(0),
106 list.push_back(tr("Colour Scale")); 107 list.push_back(tr("Colour Scale"));
107 list.push_back(tr("Window Type")); 108 list.push_back(tr("Window Type"));
108 list.push_back(tr("Window Size")); 109 list.push_back(tr("Window Size"));
109 list.push_back(tr("Window Overlap")); 110 list.push_back(tr("Window Overlap"));
110 list.push_back(tr("Gain")); 111 list.push_back(tr("Gain"));
112 list.push_back(tr("Colour Rotation"));
111 list.push_back(tr("Max Frequency")); 113 list.push_back(tr("Max Frequency"));
112 list.push_back(tr("Frequency Scale")); 114 list.push_back(tr("Frequency Scale"));
113 return list; 115 return list;
114 } 116 }
115 117
116 Layer::PropertyType 118 Layer::PropertyType
117 SpectrogramLayer::getPropertyType(const PropertyName &name) const 119 SpectrogramLayer::getPropertyType(const PropertyName &name) const
118 { 120 {
119 if (name == tr("Gain")) return RangeProperty; 121 if (name == tr("Gain")) return RangeProperty;
122 if (name == tr("Colour Rotation")) return RangeProperty;
120 return ValueProperty; 123 return ValueProperty;
121 } 124 }
122 125
123 QString 126 QString
124 SpectrogramLayer::getPropertyGroupName(const PropertyName &name) const 127 SpectrogramLayer::getPropertyGroupName(const PropertyName &name) const
125 { 128 {
126 if (name == tr("Window Size") || 129 if (name == tr("Window Size") ||
127 name == tr("Window Overlap")) return tr("Window"); 130 name == tr("Window Overlap")) return tr("Window");
128 if (name == tr("Gain") || 131 if (name == tr("Gain") ||
132 name == tr("Colour Rotation") ||
129 name == tr("Colour Scale")) return tr("Scale"); 133 name == tr("Colour Scale")) return tr("Scale");
130 if (name == tr("Max Frequency") || 134 if (name == tr("Max Frequency") ||
131 name == tr("Frequency Scale")) return tr("Frequency"); 135 name == tr("Frequency Scale")) return tr("Frequency");
132 return QString(); 136 return QString();
133 } 137 }
144 *max = 50; 148 *max = 50;
145 149
146 deft = lrint(log10(m_gain) * 20.0); 150 deft = lrint(log10(m_gain) * 20.0);
147 if (deft < *min) deft = *min; 151 if (deft < *min) deft = *min;
148 if (deft > *max) deft = *max; 152 if (deft > *max) deft = *max;
153
154 } else if (name == tr("Colour Rotation")) {
155
156 *min = 0;
157 *max = 256;
158
159 deft = m_colourRotation;
149 160
150 } else if (name == tr("Colour Scale")) { 161 } else if (name == tr("Colour Scale")) {
151 162
152 *min = 0; 163 *min = 0;
153 *max = 3; 164 *max = 3;
216 return deft; 227 return deft;
217 } 228 }
218 229
219 QString 230 QString
220 SpectrogramLayer::getPropertyValueLabel(const PropertyName &name, 231 SpectrogramLayer::getPropertyValueLabel(const PropertyName &name,
221 int value) const 232 int value) const
222 { 233 {
223 if (name == tr("Colour")) { 234 if (name == tr("Colour")) {
224 switch (value) { 235 switch (value) {
225 default: 236 default:
226 case 0: return tr("Default"); 237 case 0: return tr("Default");
293 void 304 void
294 SpectrogramLayer::setProperty(const PropertyName &name, int value) 305 SpectrogramLayer::setProperty(const PropertyName &name, int value)
295 { 306 {
296 if (name == tr("Gain")) { 307 if (name == tr("Gain")) {
297 setGain(pow(10, float(value)/20.0)); 308 setGain(pow(10, float(value)/20.0));
309 } else if (name == tr("Colour Rotation")) {
310 setColourRotation(value);
298 } else if (name == tr("Colour")) { 311 } else if (name == tr("Colour")) {
299 if (m_view) m_view->setLightBackground(value == 2); 312 if (m_view) m_view->setLightBackground(value == 2);
300 switch (value) { 313 switch (value) {
301 default: 314 default:
302 case 0: setColourScheme(DefaultColours); break; 315 case 0: setColourScheme(DefaultColours); break;
352 m_mutex.lock(); 365 m_mutex.lock();
353 m_cacheInvalid = true; 366 m_cacheInvalid = true;
354 m_pixmapCacheInvalid = true; 367 m_pixmapCacheInvalid = true;
355 368
356 m_channel = ch; 369 m_channel = ch;
370
371 m_mutex.unlock();
372
357 emit layerParametersChanged(); 373 emit layerParametersChanged();
358 374
359 m_mutex.unlock();
360 fillCache(); 375 fillCache();
361
362 } 376 }
363 377
364 int 378 int
365 SpectrogramLayer::getChannel() const 379 SpectrogramLayer::getChannel() const
366 { 380 {
375 m_mutex.lock(); 389 m_mutex.lock();
376 m_cacheInvalid = true; 390 m_cacheInvalid = true;
377 m_pixmapCacheInvalid = true; 391 m_pixmapCacheInvalid = true;
378 392
379 m_windowSize = ws; 393 m_windowSize = ws;
394
395 m_mutex.unlock();
396
380 emit layerParametersChanged(); 397 emit layerParametersChanged();
381 398
382 m_mutex.unlock();
383 fillCache(); 399 fillCache();
384
385 } 400 }
386 401
387 size_t 402 size_t
388 SpectrogramLayer::getWindowSize() const 403 SpectrogramLayer::getWindowSize() const
389 { 404 {
398 m_mutex.lock(); 413 m_mutex.lock();
399 m_cacheInvalid = true; 414 m_cacheInvalid = true;
400 m_pixmapCacheInvalid = true; 415 m_pixmapCacheInvalid = true;
401 416
402 m_windowOverlap = wi; 417 m_windowOverlap = wi;
418
419 m_mutex.unlock();
420
403 emit layerParametersChanged(); 421 emit layerParametersChanged();
404 422
405 m_mutex.unlock();
406 fillCache(); 423 fillCache();
407 } 424 }
408 425
409 size_t 426 size_t
410 SpectrogramLayer::getWindowOverlap() const 427 SpectrogramLayer::getWindowOverlap() const
420 m_mutex.lock(); 437 m_mutex.lock();
421 m_cacheInvalid = true; 438 m_cacheInvalid = true;
422 m_pixmapCacheInvalid = true; 439 m_pixmapCacheInvalid = true;
423 440
424 m_windowType = w; 441 m_windowType = w;
442
443 m_mutex.unlock();
444
425 emit layerParametersChanged(); 445 emit layerParametersChanged();
426 446
427 m_mutex.unlock();
428 fillCache(); 447 fillCache();
429 } 448 }
430 449
431 WindowType 450 WindowType
432 SpectrogramLayer::getWindowType() const 451 SpectrogramLayer::getWindowType() const
442 m_mutex.lock(); 461 m_mutex.lock();
443 m_cacheInvalid = true; 462 m_cacheInvalid = true;
444 m_pixmapCacheInvalid = true; 463 m_pixmapCacheInvalid = true;
445 464
446 m_gain = gain; 465 m_gain = gain;
466
467 m_mutex.unlock();
468
447 emit layerParametersChanged(); 469 emit layerParametersChanged();
448 470
449 m_mutex.unlock();
450 fillCache(); 471 fillCache();
451 } 472 }
452 473
453 float 474 float
454 SpectrogramLayer::getGain() const 475 SpectrogramLayer::getGain() const
464 m_mutex.lock(); 485 m_mutex.lock();
465 // don't need to invalidate main cache here 486 // don't need to invalidate main cache here
466 m_pixmapCacheInvalid = true; 487 m_pixmapCacheInvalid = true;
467 488
468 m_maxFrequency = mf; 489 m_maxFrequency = mf;
490
491 m_mutex.unlock();
492
469 emit layerParametersChanged(); 493 emit layerParametersChanged();
470
471 m_mutex.unlock();
472 } 494 }
473 495
474 size_t 496 size_t
475 SpectrogramLayer::getMaxFrequency() const 497 SpectrogramLayer::getMaxFrequency() const
476 { 498 {
477 return m_maxFrequency; 499 return m_maxFrequency;
500 }
501
502 void
503 SpectrogramLayer::setColourRotation(int r)
504 {
505 m_mutex.lock();
506 // don't need to invalidate main cache here
507 m_pixmapCacheInvalid = true;
508
509 if (r < 0) r = 0;
510 if (r > 256) r = 256;
511 int distance = r - m_colourRotation;
512
513 if (distance != 0) {
514 rotateCacheColourmap(-distance);
515 m_colourRotation = r;
516 }
517
518 m_mutex.unlock();
519
520 emit layerParametersChanged();
478 } 521 }
479 522
480 void 523 void
481 SpectrogramLayer::setColourScale(ColourScale colourScale) 524 SpectrogramLayer::setColourScale(ColourScale colourScale)
482 { 525 {
485 m_mutex.lock(); 528 m_mutex.lock();
486 m_cacheInvalid = true; 529 m_cacheInvalid = true;
487 m_pixmapCacheInvalid = true; 530 m_pixmapCacheInvalid = true;
488 531
489 m_colourScale = colourScale; 532 m_colourScale = colourScale;
490 emit layerParametersChanged();
491 533
492 m_mutex.unlock(); 534 m_mutex.unlock();
493 fillCache(); 535 fillCache();
536
537 emit layerParametersChanged();
494 } 538 }
495 539
496 SpectrogramLayer::ColourScale 540 SpectrogramLayer::ColourScale
497 SpectrogramLayer::getColourScale() const 541 SpectrogramLayer::getColourScale() const
498 { 542 {
506 550
507 m_mutex.lock(); 551 m_mutex.lock();
508 // don't need to invalidate main cache here 552 // don't need to invalidate main cache here
509 m_pixmapCacheInvalid = true; 553 m_pixmapCacheInvalid = true;
510 554
555 int formerColourRotation = m_colourRotation;
556
511 m_colourScheme = scheme; 557 m_colourScheme = scheme;
512 setCacheColourmap(); 558 setCacheColourmap();
559
560 int distance = formerColourRotation - m_colourRotation;
561
562 if (distance != 0) {
563 rotateCacheColourmap(-distance);
564 m_colourRotation = formerColourRotation;
565 }
566
567 m_mutex.unlock();
568
513 emit layerParametersChanged(); 569 emit layerParametersChanged();
514
515 m_mutex.unlock();
516 } 570 }
517 571
518 SpectrogramLayer::ColourScheme 572 SpectrogramLayer::ColourScheme
519 SpectrogramLayer::getColourScheme() const 573 SpectrogramLayer::getColourScheme() const
520 { 574 {
529 m_mutex.lock(); 583 m_mutex.lock();
530 // don't need to invalidate main cache here 584 // don't need to invalidate main cache here
531 m_pixmapCacheInvalid = true; 585 m_pixmapCacheInvalid = true;
532 586
533 m_frequencyScale = frequencyScale; 587 m_frequencyScale = frequencyScale;
588
589 m_mutex.unlock();
590
534 emit layerParametersChanged(); 591 emit layerParametersChanged();
535
536 m_mutex.unlock();
537 } 592 }
538 593
539 SpectrogramLayer::FrequencyScale 594 SpectrogramLayer::FrequencyScale
540 SpectrogramLayer::getFrequencyScale() const 595 SpectrogramLayer::getFrequencyScale() const
541 { 596 {
676 } 731 }
677 732
678 m_cache->setColor 733 m_cache->setColor
679 (pixel, qRgb(colour.red(), colour.green(), colour.blue())); 734 (pixel, qRgb(colour.red(), colour.green(), colour.blue()));
680 } 735 }
736
737 m_colourRotation = 0;
738 }
739
740 void
741 SpectrogramLayer::rotateCacheColourmap(int distance)
742 {
743 QRgb newPixels[256];
744
745 newPixels[0] = m_cache->color(0);
746
747 for (int pixel = 1; pixel < 256; ++pixel) {
748 int target = pixel + distance;
749 while (target < 1) target += 255;
750 while (target > 255) target -= 255;
751 newPixels[target] = m_cache->color(pixel);
752 }
753
754 for (int pixel = 0; pixel < 256; ++pixel) {
755 m_cache->setColor(pixel, newPixels[pixel]);
756 }
681 } 757 }
682 758
683 bool 759 bool
684 SpectrogramLayer::fillCacheColumn(int column, double *input, 760 SpectrogramLayer::fillCacheColumn(int column, double *input,
685 fftw_complex *output, 761 fftw_complex *output,
686 fftw_plan plan, 762 fftw_plan plan,
763 size_t windowSize,
764 size_t increment,
687 const Window<double> &windower, 765 const Window<double> &windower,
688 bool lock) const 766 bool lock) const
689 { 767 {
690 size_t increment = m_windowSize - m_windowSize * m_windowOverlap / 100;
691 int startFrame = increment * column; 768 int startFrame = increment * column;
692 int endFrame = startFrame + m_windowSize; 769 int endFrame = startFrame + windowSize;
693 770
694 startFrame -= int(m_windowSize - increment) / 2; 771 startFrame -= int(windowSize - increment) / 2;
695 endFrame -= int(m_windowSize - increment) / 2; 772 endFrame -= int(windowSize - increment) / 2;
696 size_t pfx = 0; 773 size_t pfx = 0;
697 774
698 if (startFrame < 0) { 775 if (startFrame < 0) {
699 pfx = size_t(-startFrame); 776 pfx = size_t(-startFrame);
700 for (size_t i = 0; i < pfx; ++i) { 777 for (size_t i = 0; i < pfx; ++i) {
702 } 779 }
703 } 780 }
704 781
705 size_t got = m_model->getValues(m_channel, startFrame + pfx, 782 size_t got = m_model->getValues(m_channel, startFrame + pfx,
706 endFrame, input + pfx); 783 endFrame, input + pfx);
707 while (got + pfx < m_windowSize) { 784 while (got + pfx < windowSize) {
708 input[got + pfx] = 0.0; 785 input[got + pfx] = 0.0;
709 ++got; 786 ++got;
710 } 787 }
711 788
712 if (m_gain != 1.0) { 789 if (m_gain != 1.0) {
713 for (size_t i = 0; i < m_windowSize; ++i) { 790 for (size_t i = 0; i < windowSize; ++i) {
714 input[i] *= m_gain; 791 input[i] *= m_gain;
715 } 792 }
716 } 793 }
717 794
718 windower.cut(input); 795 windower.cut(input);
720 fftw_execute(plan); 797 fftw_execute(plan);
721 798
722 if (lock) m_mutex.lock(); 799 if (lock) m_mutex.lock();
723 bool interrupted = false; 800 bool interrupted = false;
724 801
725 for (size_t i = 0; i < m_windowSize / 2; ++i) { 802 for (size_t i = 0; i < windowSize / 2; ++i) {
726 803
727 int value = 0; 804 int value = 0;
728 805
729 if (m_colourScale == PhaseColourScale) { 806 if (m_colourScale == PhaseColourScale) {
730 807
733 810
734 } else { 811 } else {
735 812
736 double mag = sqrt(output[i][0] * output[i][0] + 813 double mag = sqrt(output[i][0] * output[i][0] +
737 output[i][1] * output[i][1]); 814 output[i][1] * output[i][1]);
738 mag /= m_windowSize / 2; 815 mag /= windowSize / 2;
739 816
740 switch (m_colourScale) { 817 switch (m_colourScale) {
741 818
742 default: 819 default:
743 case LinearColourScale: 820 case LinearColourScale:
802 879
803 std::cerr << "SpectrogramLayer::CacheFillThread::run: model is ready" << std::endl; 880 std::cerr << "SpectrogramLayer::CacheFillThread::run: model is ready" << std::endl;
804 881
805 size_t start = m_layer.m_model->getStartFrame(); 882 size_t start = m_layer.m_model->getStartFrame();
806 size_t end = m_layer.m_model->getEndFrame(); 883 size_t end = m_layer.m_model->getEndFrame();
884
885 WindowType windowType = m_layer.m_windowType;
807 size_t windowSize = m_layer.m_windowSize; 886 size_t windowSize = m_layer.m_windowSize;
808 size_t windowIncrement = m_layer.getWindowIncrement(); 887 size_t windowIncrement = m_layer.getWindowIncrement();
809 888
810 size_t visibleStart = start; 889 size_t visibleStart = start;
811 size_t visibleEnd = end; 890 size_t visibleEnd = end;
820 } 899 }
821 visibleEnd = m_layer.m_view->getEndFrame(); 900 visibleEnd = m_layer.m_view->getEndFrame();
822 } 901 }
823 902
824 delete m_layer.m_cache; 903 delete m_layer.m_cache;
825 m_layer.m_cache = new QImage((end - start) / windowIncrement + 1, 904 size_t width = (end - start) / windowIncrement + 1;
826 windowSize / 2, 905 size_t height = windowSize / 2;
827 QImage::Format_Indexed8); 906 m_layer.m_cache = new QImage(width, height,
907 QImage::Format_Indexed8);
908
909 // If we're using JACK in mlock mode, this will be locked
910 // and we ought to unlock to avoid memory exhaustion.
911 // Shame it doesn't appear to be possible to allocate
912 // unlocked in the first place.
913 //!!! hm, I don't think this is working.
914 MUNLOCK((void *)m_layer.m_cache, width * height);
828 915
829 m_layer.setCacheColourmap(); 916 m_layer.setCacheColourmap();
830 917
831 m_layer.m_cache->fill(0); 918 m_layer.m_cache->fill(0);
832 m_layer.m_mutex.unlock(); 919 m_layer.m_mutex.unlock();
838 fftw_malloc(windowSize * sizeof(fftw_complex)); 925 fftw_malloc(windowSize * sizeof(fftw_complex));
839 926
840 fftw_plan plan = fftw_plan_dft_r2c_1d(windowSize, input, 927 fftw_plan plan = fftw_plan_dft_r2c_1d(windowSize, input,
841 output, FFTW_ESTIMATE); 928 output, FFTW_ESTIMATE);
842 929
843 Window<double> windower(m_layer.m_windowType, m_layer.m_windowSize); 930 Window<double> windower(windowType, windowSize);
844 931
845 if (!plan) { 932 if (!plan) {
846 std::cerr << "WARNING: fftw_plan_dft_r2c_1d(" << windowSize << ") failed!" << std::endl; 933 std::cerr << "WARNING: fftw_plan_dft_r2c_1d(" << windowSize << ") failed!" << std::endl;
847 fftw_free(input); 934 fftw_free(input);
848 fftw_free(output); 935 fftw_free(output);
861 m_layer.m_mutex.lock(); 948 m_layer.m_mutex.lock();
862 949
863 for (size_t f = visibleStart; f < visibleEnd; f += windowIncrement) { 950 for (size_t f = visibleStart; f < visibleEnd; f += windowIncrement) {
864 951
865 m_layer.fillCacheColumn(int((f - start) / windowIncrement), 952 m_layer.fillCacheColumn(int((f - start) / windowIncrement),
866 input, output, plan, windower, false); 953 input, output, plan,
954 windowSize, windowIncrement,
955 windower, false);
867 956
868 m_layer.m_mutex.unlock(); 957 m_layer.m_mutex.unlock();
869 m_layer.m_mutex.lock(); 958 m_layer.m_mutex.lock();
870 959
871 if (m_layer.m_cacheInvalid || m_layer.m_exiting) { 960 if (m_layer.m_cacheInvalid || m_layer.m_exiting) {
890 if (!interrupted && doVisibleFirst) { 979 if (!interrupted && doVisibleFirst) {
891 980
892 for (size_t f = visibleEnd; f < end; f += windowIncrement) { 981 for (size_t f = visibleEnd; f < end; f += windowIncrement) {
893 982
894 if (!m_layer.fillCacheColumn(int((f - start) / windowIncrement), 983 if (!m_layer.fillCacheColumn(int((f - start) / windowIncrement),
895 input, output, plan, windower, true)) { 984 input, output, plan,
985 windowSize, windowIncrement,
986 windower, true)) {
896 interrupted = true; 987 interrupted = true;
897 m_fillExtent = 0; 988 m_fillExtent = 0;
898 break; 989 break;
899 } 990 }
900 991
919 size_t baseCompletion = m_fillCompletion; 1010 size_t baseCompletion = m_fillCompletion;
920 1011
921 for (size_t f = start; f < remainingEnd; f += windowIncrement) { 1012 for (size_t f = start; f < remainingEnd; f += windowIncrement) {
922 1013
923 if (!m_layer.fillCacheColumn(int((f - start) / windowIncrement), 1014 if (!m_layer.fillCacheColumn(int((f - start) / windowIncrement),
924 input, output, plan, windower, true)) { 1015 input, output, plan,
1016 windowSize, windowIncrement,
1017 windower, true)) {
925 interrupted = true; 1018 interrupted = true;
926 m_fillExtent = 0; 1019 m_fillExtent = 0;
927 break; 1020 break;
928 } 1021 }
929 1022
1346 } 1439 }
1347 } 1440 }
1348 } 1441 }
1349 1442
1350 if (divisor > 0.0) { 1443 if (divisor > 0.0) {
1444
1351 int pixel = int(total / divisor); 1445 int pixel = int(total / divisor);
1352 if (pixel > 255) pixel = 255; 1446 if (pixel > 255) pixel = 255;
1353 if (pixel < 1) pixel = 1; 1447 if (pixel < 1) pixel = 1;
1354 assert(x <= scaled.width()); 1448 assert(x <= scaled.width());
1355 scaled.setPixel(x, y, m_cache->color(pixel)); 1449 scaled.setPixel(x, y, m_cache->color(pixel));
1450 /*
1451 float pixel = total / divisor;
1452 float lq = pixel - int(pixel);
1453 float hq = int(pixel) + 1 - pixel;
1454 int pixNum = int(pixel);
1455 QRgb low = m_cache->color(pixNum > 255 ? 255 : pixNum);
1456 QRgb high = m_cache->color(pixNum > 254 ? 255 : pixNum + 1);
1457 QRgb mixed = qRgb
1458 (qRed(low) * lq + qRed(high) * hq + 0.01,
1459 qGreen(low) * lq + qGreen(high) * hq + 0.01,
1460 qBlue(low) * lq + qBlue(high) * hq + 0.01);
1461 scaled.setPixel(x, y, mixed);
1462 */
1356 } else { 1463 } else {
1357 assert(x <= scaled.width()); 1464 assert(x <= scaled.width());
1358 scaled.setPixel(x, y, qRgb(0, 0, 0)); 1465 scaled.setPixel(x, y, qRgb(0, 0, 0));
1359 } 1466 }
1360 } 1467 }