Mercurial > hg > svgui
comparison layer/SpectrogramLayer.cpp @ 38:beb801473743
* Rearrange spectrogram cacheing so that gain, normalization, instantaneous
frequency calculations etc can be done from the cached data (increasing the
size of the cache, but also the usability).
author | Chris Cannam |
---|---|
date | Thu, 23 Feb 2006 18:01:31 +0000 |
parents | 21d061e66177 |
children | 3be4438b186d |
comparison
equal
deleted
inserted
replaced
37:21d061e66177 | 38:beb801473743 |
---|---|
47 m_colourScheme(DefaultColours), | 47 m_colourScheme(DefaultColours), |
48 m_frequencyScale(LinearFrequencyScale), | 48 m_frequencyScale(LinearFrequencyScale), |
49 m_binDisplay(AllBins), | 49 m_binDisplay(AllBins), |
50 m_normalizeColumns(false), | 50 m_normalizeColumns(false), |
51 m_cache(0), | 51 m_cache(0), |
52 m_phaseAdjustCache(0), | |
53 m_cacheInvalid(true), | 52 m_cacheInvalid(true), |
54 m_pixmapCache(0), | 53 m_pixmapCache(0), |
55 m_pixmapCacheInvalid(true), | 54 m_pixmapCacheInvalid(true), |
56 m_fillThread(0), | 55 m_fillThread(0), |
57 m_updateTimer(0), | 56 m_updateTimer(0), |
89 m_condition.wakeAll(); | 88 m_condition.wakeAll(); |
90 if (m_fillThread) m_fillThread->wait(); | 89 if (m_fillThread) m_fillThread->wait(); |
91 delete m_fillThread; | 90 delete m_fillThread; |
92 | 91 |
93 delete m_cache; | 92 delete m_cache; |
94 delete m_phaseAdjustCache; | |
95 } | 93 } |
96 | 94 |
97 void | 95 void |
98 SpectrogramLayer::setModel(const DenseTimeValueModel *model) | 96 SpectrogramLayer::setModel(const DenseTimeValueModel *model) |
99 { | 97 { |
105 delete m_cache; //!!! hang on, this isn't safe to do here is it? | 103 delete m_cache; //!!! hang on, this isn't safe to do here is it? |
106 // we need some sort of guard against the fill | 104 // we need some sort of guard against the fill |
107 // thread trying to read the defunct model too. | 105 // thread trying to read the defunct model too. |
108 // should we use a scavenger? | 106 // should we use a scavenger? |
109 m_cache = 0; | 107 m_cache = 0; |
110 delete m_phaseAdjustCache; //!!! likewise | |
111 m_phaseAdjustCache = 0; | |
112 m_mutex.unlock(); | 108 m_mutex.unlock(); |
113 | 109 |
114 if (!m_model || !m_model->isOK()) return; | 110 if (!m_model || !m_model->isOK()) return; |
115 | 111 |
116 connect(m_model, SIGNAL(modelChanged()), this, SIGNAL(modelChanged())); | 112 connect(m_model, SIGNAL(modelChanged()), this, SIGNAL(modelChanged())); |
163 { | 159 { |
164 if (name == tr("Window Size") || | 160 if (name == tr("Window Size") || |
165 name == tr("Window Type") || | 161 name == tr("Window Type") || |
166 name == tr("Window Overlap")) return tr("Window"); | 162 name == tr("Window Overlap")) return tr("Window"); |
167 if (name == tr("Colour") || | 163 if (name == tr("Colour") || |
164 name == tr("Gain") || | |
165 name == tr("Threshold") || | |
168 name == tr("Colour Rotation")) return tr("Colour"); | 166 name == tr("Colour Rotation")) return tr("Colour"); |
169 if (name == tr("Gain") || | 167 if (name == tr("Normalize") || |
170 name == tr("Threshold") || | |
171 name == tr("Normalize") || | |
172 name == tr("Bin Display") || | 168 name == tr("Bin Display") || |
173 name == tr("Colour Scale")) return tr("Scale"); | 169 name == tr("Colour Scale")) return tr("Scale"); |
174 if (name == tr("Max Frequency") || | 170 if (name == tr("Max Frequency") || |
175 name == tr("Min Frequency") || | 171 name == tr("Min Frequency") || |
176 name == tr("Frequency Scale") || | 172 name == tr("Frequency Scale") || |
360 } | 356 } |
361 } | 357 } |
362 if (name == tr("Min Frequency")) { | 358 if (name == tr("Min Frequency")) { |
363 switch (value) { | 359 switch (value) { |
364 default: | 360 default: |
365 case 0: return tr("None"); | 361 case 0: return tr("No min"); |
366 case 1: return tr("10 Hz"); | 362 case 1: return tr("10 Hz"); |
367 case 2: return tr("20 Hz"); | 363 case 2: return tr("20 Hz"); |
368 case 3: return tr("40 Hz"); | 364 case 3: return tr("40 Hz"); |
369 case 4: return tr("100 Hz"); | 365 case 4: return tr("100 Hz"); |
370 case 5: return tr("250 Hz"); | 366 case 5: return tr("250 Hz"); |
384 case 4: return tr("4 KHz"); | 380 case 4: return tr("4 KHz"); |
385 case 5: return tr("6 KHz"); | 381 case 5: return tr("6 KHz"); |
386 case 6: return tr("8 KHz"); | 382 case 6: return tr("8 KHz"); |
387 case 7: return tr("12 KHz"); | 383 case 7: return tr("12 KHz"); |
388 case 8: return tr("16 KHz"); | 384 case 8: return tr("16 KHz"); |
389 case 9: return tr("All"); | 385 case 9: return tr("No max"); |
390 } | 386 } |
391 } | 387 } |
392 if (name == tr("Frequency Scale")) { | 388 if (name == tr("Frequency Scale")) { |
393 switch (value) { | 389 switch (value) { |
394 default: | 390 default: |
589 SpectrogramLayer::setGain(float gain) | 585 SpectrogramLayer::setGain(float gain) |
590 { | 586 { |
591 if (m_gain == gain) return; //!!! inadequate for floats! | 587 if (m_gain == gain) return; //!!! inadequate for floats! |
592 | 588 |
593 m_mutex.lock(); | 589 m_mutex.lock(); |
594 m_cacheInvalid = true; | |
595 m_pixmapCacheInvalid = true; | 590 m_pixmapCacheInvalid = true; |
596 | 591 |
597 m_gain = gain; | 592 m_gain = gain; |
598 | 593 |
599 m_mutex.unlock(); | 594 m_mutex.unlock(); |
613 SpectrogramLayer::setThreshold(float threshold) | 608 SpectrogramLayer::setThreshold(float threshold) |
614 { | 609 { |
615 if (m_threshold == threshold) return; //!!! inadequate for floats! | 610 if (m_threshold == threshold) return; //!!! inadequate for floats! |
616 | 611 |
617 m_mutex.lock(); | 612 m_mutex.lock(); |
618 m_cacheInvalid = true; | |
619 m_pixmapCacheInvalid = true; | 613 m_pixmapCacheInvalid = true; |
620 | 614 |
621 m_threshold = threshold; | 615 m_threshold = threshold; |
622 | 616 |
623 m_mutex.unlock(); | 617 m_mutex.unlock(); |
637 SpectrogramLayer::setMinFrequency(size_t mf) | 631 SpectrogramLayer::setMinFrequency(size_t mf) |
638 { | 632 { |
639 if (m_minFrequency == mf) return; | 633 if (m_minFrequency == mf) return; |
640 | 634 |
641 m_mutex.lock(); | 635 m_mutex.lock(); |
642 // don't need to invalidate main cache here | |
643 m_pixmapCacheInvalid = true; | 636 m_pixmapCacheInvalid = true; |
644 | 637 |
645 m_minFrequency = mf; | 638 m_minFrequency = mf; |
646 | 639 |
647 m_mutex.unlock(); | 640 m_mutex.unlock(); |
659 SpectrogramLayer::setMaxFrequency(size_t mf) | 652 SpectrogramLayer::setMaxFrequency(size_t mf) |
660 { | 653 { |
661 if (m_maxFrequency == mf) return; | 654 if (m_maxFrequency == mf) return; |
662 | 655 |
663 m_mutex.lock(); | 656 m_mutex.lock(); |
664 // don't need to invalidate main cache here | |
665 m_pixmapCacheInvalid = true; | 657 m_pixmapCacheInvalid = true; |
666 | 658 |
667 m_maxFrequency = mf; | 659 m_maxFrequency = mf; |
668 | 660 |
669 m_mutex.unlock(); | 661 m_mutex.unlock(); |
679 | 671 |
680 void | 672 void |
681 SpectrogramLayer::setColourRotation(int r) | 673 SpectrogramLayer::setColourRotation(int r) |
682 { | 674 { |
683 m_mutex.lock(); | 675 m_mutex.lock(); |
684 // don't need to invalidate main cache here | |
685 m_pixmapCacheInvalid = true; | 676 m_pixmapCacheInvalid = true; |
686 | 677 |
687 if (r < 0) r = 0; | 678 if (r < 0) r = 0; |
688 if (r > 256) r = 256; | 679 if (r > 256) r = 256; |
689 int distance = r - m_colourRotation; | 680 int distance = r - m_colourRotation; |
702 SpectrogramLayer::setColourScale(ColourScale colourScale) | 693 SpectrogramLayer::setColourScale(ColourScale colourScale) |
703 { | 694 { |
704 if (m_colourScale == colourScale) return; | 695 if (m_colourScale == colourScale) return; |
705 | 696 |
706 m_mutex.lock(); | 697 m_mutex.lock(); |
707 m_cacheInvalid = true; | |
708 m_pixmapCacheInvalid = true; | 698 m_pixmapCacheInvalid = true; |
709 | 699 |
710 m_colourScale = colourScale; | 700 m_colourScale = colourScale; |
711 | 701 |
712 m_mutex.unlock(); | 702 m_mutex.unlock(); |
725 SpectrogramLayer::setColourScheme(ColourScheme scheme) | 715 SpectrogramLayer::setColourScheme(ColourScheme scheme) |
726 { | 716 { |
727 if (m_colourScheme == scheme) return; | 717 if (m_colourScheme == scheme) return; |
728 | 718 |
729 m_mutex.lock(); | 719 m_mutex.lock(); |
730 // don't need to invalidate main cache here | |
731 m_pixmapCacheInvalid = true; | 720 m_pixmapCacheInvalid = true; |
732 | 721 |
733 m_colourScheme = scheme; | 722 m_colourScheme = scheme; |
734 setCacheColourmap(); | 723 setCacheColourmap(); |
735 | 724 |
749 { | 738 { |
750 if (m_frequencyScale == frequencyScale) return; | 739 if (m_frequencyScale == frequencyScale) return; |
751 | 740 |
752 m_mutex.lock(); | 741 m_mutex.lock(); |
753 | 742 |
754 // don't need to invalidate main cache here | |
755 m_pixmapCacheInvalid = true; | 743 m_pixmapCacheInvalid = true; |
756 | 744 |
757 m_frequencyScale = frequencyScale; | 745 m_frequencyScale = frequencyScale; |
758 | 746 |
759 m_mutex.unlock(); | 747 m_mutex.unlock(); |
772 { | 760 { |
773 if (m_binDisplay == binDisplay) return; | 761 if (m_binDisplay == binDisplay) return; |
774 | 762 |
775 m_mutex.lock(); | 763 m_mutex.lock(); |
776 | 764 |
777 m_cacheInvalid = true; | |
778 m_pixmapCacheInvalid = true; | 765 m_pixmapCacheInvalid = true; |
779 | 766 |
780 m_binDisplay = binDisplay; | 767 m_binDisplay = binDisplay; |
781 | 768 |
782 m_mutex.unlock(); | 769 m_mutex.unlock(); |
796 SpectrogramLayer::setNormalizeColumns(bool n) | 783 SpectrogramLayer::setNormalizeColumns(bool n) |
797 { | 784 { |
798 if (m_normalizeColumns == n) return; | 785 if (m_normalizeColumns == n) return; |
799 m_mutex.lock(); | 786 m_mutex.lock(); |
800 | 787 |
801 m_cacheInvalid = true; | |
802 m_pixmapCacheInvalid = true; | 788 m_pixmapCacheInvalid = true; |
803 m_normalizeColumns = n; | 789 m_normalizeColumns = n; |
804 m_mutex.unlock(); | 790 m_mutex.unlock(); |
805 | 791 |
806 fillCache(); | 792 fillCache(); |
826 // delete m_cache; | 812 // delete m_cache; |
827 // m_cache = 0; | 813 // m_cache = 0; |
828 | 814 |
829 m_cacheInvalid = true; | 815 m_cacheInvalid = true; |
830 m_pixmapCacheInvalid = true; | 816 m_pixmapCacheInvalid = true; |
831 m_cachedInitialVisibleArea = false; | |
832 delete m_pixmapCache; | 817 delete m_pixmapCache; |
833 m_pixmapCache = 0; | 818 m_pixmapCache = 0; |
834 | 819 |
835 m_mutex.unlock(); | 820 m_mutex.unlock(); |
836 | 821 |
844 void | 829 void |
845 SpectrogramLayer::cacheInvalid() | 830 SpectrogramLayer::cacheInvalid() |
846 { | 831 { |
847 m_cacheInvalid = true; | 832 m_cacheInvalid = true; |
848 m_pixmapCacheInvalid = true; | 833 m_pixmapCacheInvalid = true; |
849 m_cachedInitialVisibleArea = false; | |
850 fillCache(); | 834 fillCache(); |
851 } | 835 } |
852 | 836 |
853 void | 837 void |
854 SpectrogramLayer::cacheInvalid(size_t, size_t) | 838 SpectrogramLayer::cacheInvalid(size_t, size_t) |
929 { | 913 { |
930 if (m_cacheInvalid || !m_cache) return; | 914 if (m_cacheInvalid || !m_cache) return; |
931 | 915 |
932 int formerRotation = m_colourRotation; | 916 int formerRotation = m_colourRotation; |
933 | 917 |
934 m_cache->setColour(NO_VALUE, Qt::white); | 918 if (m_colourScheme == BlackOnWhite) { |
919 m_cache->setColour(NO_VALUE, Qt::white); | |
920 } else { | |
921 m_cache->setColour(NO_VALUE, Qt::black); | |
922 } | |
935 | 923 |
936 for (int pixel = 1; pixel < 256; ++pixel) { | 924 for (int pixel = 1; pixel < 256; ++pixel) { |
937 | 925 |
938 QColor colour; | 926 QColor colour; |
939 int hue, px; | 927 int hue, px; |
1001 for (int pixel = 0; pixel < 256; ++pixel) { | 989 for (int pixel = 0; pixel < 256; ++pixel) { |
1002 m_cache->setColour(pixel, newPixels[pixel]); | 990 m_cache->setColour(pixel, newPixels[pixel]); |
1003 } | 991 } |
1004 } | 992 } |
1005 | 993 |
1006 bool | 994 float |
995 SpectrogramLayer::calculateFrequency(size_t bin, | |
996 size_t windowSize, | |
997 size_t windowIncrement, | |
998 size_t sampleRate, | |
999 float oldPhase, | |
1000 float newPhase, | |
1001 bool &steadyState) | |
1002 { | |
1003 // At frequency f, phase shift of 2pi (one cycle) happens in 1/f sec. | |
1004 // At hopsize h and sample rate sr, one hop happens in h/sr sec. | |
1005 // At window size w, for bin b, f is b*sr/w. | |
1006 // thus 2pi phase shift happens in w/(b*sr) sec. | |
1007 // We need to know what phase shift we expect from h/sr sec. | |
1008 // -> 2pi * ((h/sr) / (w/(b*sr))) | |
1009 // = 2pi * ((h * b * sr) / (w * sr)) | |
1010 // = 2pi * (h * b) / w. | |
1011 | |
1012 float frequency = (float(bin) * sampleRate) / windowSize; | |
1013 | |
1014 float expectedPhase = | |
1015 oldPhase + (2.0 * M_PI * bin * windowIncrement) / windowSize; | |
1016 | |
1017 float phaseError = MathUtilities::princarg(newPhase - expectedPhase); | |
1018 | |
1019 if (fabs(phaseError) < (1.1 * (windowIncrement * M_PI) / windowSize)) { | |
1020 | |
1021 // The new frequency estimate based on the phase error | |
1022 // resulting from assuming the "native" frequency of this bin | |
1023 | |
1024 float newFrequency = | |
1025 (sampleRate * (expectedPhase + phaseError - oldPhase)) / | |
1026 (2 * M_PI * windowIncrement); | |
1027 | |
1028 steadyState = true; | |
1029 return newFrequency; | |
1030 } | |
1031 | |
1032 steadyState = false; | |
1033 return frequency; | |
1034 } | |
1035 | |
1036 void | |
1007 SpectrogramLayer::fillCacheColumn(int column, double *input, | 1037 SpectrogramLayer::fillCacheColumn(int column, double *input, |
1008 fftw_complex *output, | 1038 fftw_complex *output, |
1009 fftw_plan plan, | 1039 fftw_plan plan, |
1010 size_t windowSize, | 1040 size_t windowSize, |
1011 size_t increment, | 1041 size_t increment, |
1012 const Window<double> &windower, | 1042 const Window<double> &windower) const |
1013 bool resetStoredPhase) const | 1043 { |
1014 { | 1044 //!!! we _do_ need a lock for these references to the model |
1015 static std::vector<double> storedPhase; | 1045 // though, don't we? |
1016 | |
1017 bool phaseAdjust = (m_binDisplay == PeakFrequencies); | |
1018 bool haveStoredPhase = true; | |
1019 size_t sampleRate = 0; | |
1020 | |
1021 if (phaseAdjust) { | |
1022 if (resetStoredPhase || (storedPhase.size() != windowSize / 2)) { | |
1023 haveStoredPhase = false; | |
1024 storedPhase.clear(); | |
1025 for (size_t i = 0; i < windowSize / 2; ++i) { | |
1026 storedPhase.push_back(0.0); | |
1027 } | |
1028 } | |
1029 sampleRate = m_model->getSampleRate(); | |
1030 } | |
1031 | 1046 |
1032 int startFrame = increment * column; | 1047 int startFrame = increment * column; |
1033 int endFrame = startFrame + windowSize; | 1048 int endFrame = startFrame + windowSize; |
1034 | 1049 |
1035 startFrame -= int(windowSize - increment) / 2; | 1050 startFrame -= int(windowSize - increment) / 2; |
1046 size_t got = m_model->getValues(m_channel, startFrame + pfx, | 1061 size_t got = m_model->getValues(m_channel, startFrame + pfx, |
1047 endFrame, input + pfx); | 1062 endFrame, input + pfx); |
1048 while (got + pfx < windowSize) { | 1063 while (got + pfx < windowSize) { |
1049 input[got + pfx] = 0.0; | 1064 input[got + pfx] = 0.0; |
1050 ++got; | 1065 ++got; |
1051 } | |
1052 | |
1053 if (m_gain != 1.0) { | |
1054 for (size_t i = 0; i < windowSize; ++i) { | |
1055 input[i] *= m_gain; | |
1056 } | |
1057 } | 1066 } |
1058 | 1067 |
1059 if (m_channel == -1) { | 1068 if (m_channel == -1) { |
1060 int channels = m_model->getChannelCount(); | 1069 int channels = m_model->getChannelCount(); |
1061 if (channels > 1) { | 1070 if (channels > 1) { |
1073 input[i + windowSize/2] = temp; | 1082 input[i + windowSize/2] = temp; |
1074 } | 1083 } |
1075 | 1084 |
1076 fftw_execute(plan); | 1085 fftw_execute(plan); |
1077 | 1086 |
1078 bool interrupted = false; | 1087 double factor = 0.0; |
1079 | 1088 |
1080 double prevMag = 0.0; | 1089 // Calculate magnitude and phase from real and imaginary in |
1081 double maxMag = 0.0; | 1090 // output[i][0] and output[i][1] respectively, and store the phase |
1082 | 1091 // straight into cache and the magnitude back into output[i][0] |
1083 if (m_normalizeColumns) { | 1092 // (because we'll need to know the normalization factor, |
1084 for (size_t i = 0; i < windowSize/2; ++i) { | 1093 // i.e. maximum magnitude in this column, before we can store it) |
1085 double mag = sqrt(output[i][0] * output[i][0] + | 1094 |
1086 output[i][1] * output[i][1]); | 1095 for (size_t i = 0; i < windowSize/2; ++i) { |
1087 mag /= windowSize / 2; | |
1088 if (mag > maxMag) maxMag = mag; | |
1089 } | |
1090 } | |
1091 | |
1092 if (maxMag == 0.0) maxMag = 1.0; | |
1093 | |
1094 bool peaksOnly = (m_binDisplay == PeakBins || | |
1095 m_binDisplay == PeakFrequencies); | |
1096 | |
1097 for (size_t i = 0; i < windowSize / 2; ++i) { | |
1098 | |
1099 int value = 0; | |
1100 double phase = 0.0; | |
1101 | 1096 |
1102 double mag = sqrt(output[i][0] * output[i][0] + | 1097 double mag = sqrt(output[i][0] * output[i][0] + |
1103 output[i][1] * output[i][1]); | 1098 output[i][1] * output[i][1]); |
1104 | 1099 mag /= windowSize / 2; |
1105 mag /= (windowSize / 2); | 1100 |
1106 if (m_normalizeColumns) mag /= maxMag; | 1101 if (mag > factor) factor = mag; |
1107 | 1102 |
1108 bool showThis = true; | 1103 double phase = atan2(output[i][1], output[i][0]); |
1109 | 1104 phase = MathUtilities::princarg(phase); |
1110 if (peaksOnly) { | 1105 |
1111 if (mag < prevMag) showThis = false; | 1106 output[i][0] = mag; |
1112 else { | 1107 m_cache->setPhaseAt(column, i, phase); |
1113 double nextMag = 0.0; | 1108 } |
1114 if (i < windowSize / 2 - 1) { | 1109 |
1115 nextMag = sqrt(output[i+1][0] * output[i+1][0] + | 1110 m_cache->setNormalizationFactor(column, factor); |
1116 output[i+1][1] * output[i+1][1]); | 1111 |
1117 nextMag /= windowSize / 2; | 1112 for (size_t i = 0; i < windowSize/2; ++i) { |
1118 if (m_normalizeColumns) nextMag /= maxMag; | 1113 m_cache->setMagnitudeAt(column, i, output[i][0]); |
1119 } | 1114 } |
1120 if (mag < nextMag) showThis = false; | 1115 } |
1121 } | 1116 |
1122 prevMag = mag; | 1117 unsigned char |
1123 } | 1118 SpectrogramLayer::getDisplayValue(float input) const |
1124 | 1119 { |
1125 if (mag < m_threshold) showThis = false; | 1120 int value; |
1126 | 1121 |
1127 if (phaseAdjust || (m_colourScale == PhaseColourScale)) { | 1122 if (m_colourScale == PhaseColourScale) { |
1128 phase = atan2(output[i][1], output[i][0]); | 1123 |
1129 phase = MathUtilities::princarg(phase); | 1124 value = int((input * 127 / M_PI) + 128); |
1130 } | 1125 |
1131 | 1126 } else { |
1132 if (phaseAdjust && m_phaseAdjustCache) { | 1127 |
1133 m_phaseAdjustCache->setValueAt(column, i, 0); | 1128 switch (m_colourScale) { |
1134 } | |
1135 | |
1136 if (phaseAdjust && m_phaseAdjustCache && haveStoredPhase && showThis) { | |
1137 | |
1138 double freq = (double(i) * sampleRate) / m_windowSize; | |
1139 double prevPhase = storedPhase[i]; | |
1140 | |
1141 // At frequency f, phase shift of 2pi (one cycle) happens in 1/f sec. | |
1142 // At hopsize h and sample rate sr, one hop happens in h/sr sec. | |
1143 // At window size w, for bin i, f is i*sr/w. | |
1144 // thus 2pi phase shift happens in w/(b*sr) sec. | |
1145 // We need to know what phase shift we expect from h/sr sec. | |
1146 // -> 2pi * ((h/sr) / (w/(i*sr))) | |
1147 // = 2pi * ((h * i * sr) / (w * sr)) | |
1148 // = 2pi * (h * i) / w. | |
1149 | |
1150 double expectedPhase = | |
1151 prevPhase + (2.0 * M_PI * i * increment) / m_windowSize; | |
1152 | |
1153 double phaseError = MathUtilities::princarg(phase - expectedPhase); | |
1154 | 1129 |
1155 if (fabs(phaseError) < (1.1 * (increment * M_PI) / m_windowSize)) { | 1130 default: |
1156 | 1131 case LinearColourScale: |
1157 // The new frequency estimate based on the phase error | 1132 value = int(input * 50 * 255) + 1; |
1158 // resulting from assuming the "native" frequency of this bin | 1133 break; |
1159 | |
1160 double newFreq = | |
1161 (sampleRate * | |
1162 (expectedPhase + phaseError - prevPhase)) / | |
1163 (2 * M_PI * increment); | |
1164 | |
1165 // Convert the frequency difference to an unsigned char | |
1166 // for storage in the phase adjust cache | |
1167 | |
1168 double binRange = (double(i + 1) * sampleRate) / windowSize - freq; | |
1169 | 1134 |
1170 int offset = lrint(((newFreq - freq) / binRange) * 100); | 1135 case MeterColourScale: |
1171 | 1136 value = AudioLevel::multiplier_to_preview(input * 50, 255) + 1; |
1172 if (m_cacheInvalid || m_exiting) { | |
1173 interrupted = true; | |
1174 break; | |
1175 } | |
1176 | |
1177 if (offset >= SCHAR_MIN && offset <= SCHAR_MAX) { | |
1178 signed char coff = offset; | |
1179 m_phaseAdjustCache->setValueAt(column, i, (unsigned char)coff); | |
1180 } else { | |
1181 | |
1182 if (fabs(phaseError) < ((increment * M_PI) / m_windowSize)) { | |
1183 std::cerr << "WARNING: Phase error " << phaseError << " ( < " << ((increment * M_PI) / m_windowSize) << ") but offset " << offset << " out of range" << std::endl; | |
1184 } | |
1185 } | |
1186 } | |
1187 } | |
1188 | |
1189 if (phaseAdjust) storedPhase[i] = phase; | |
1190 | |
1191 if (m_colourScale == PhaseColourScale) { | |
1192 | |
1193 value = int((phase * 127 / M_PI) + 128); | |
1194 | |
1195 } else { | |
1196 | |
1197 switch (m_colourScale) { | |
1198 | |
1199 default: | |
1200 case LinearColourScale: | |
1201 value = int(mag * 50 * 255) + 1; | |
1202 break; | |
1203 | |
1204 case MeterColourScale: | |
1205 value = AudioLevel::multiplier_to_preview(mag * 50, 255) + 1; | |
1206 break; | 1137 break; |
1207 | 1138 |
1208 case dBColourScale: | 1139 case dBColourScale: |
1209 mag = 20.0 * log10(mag); | 1140 input = 20.0 * log10(input); |
1210 mag = (mag + 80.0) / 80.0; | 1141 input = (input + 80.0) / 80.0; |
1211 if (mag < 0.0) mag = 0.0; | 1142 if (input < 0.0) input = 0.0; |
1212 if (mag > 1.0) mag = 1.0; | 1143 if (input > 1.0) input = 1.0; |
1213 value = int(mag * 255) + 1; | 1144 value = int(input * 255) + 1; |
1214 } | 1145 } |
1215 } | 1146 } |
1216 | 1147 |
1217 if (value > UCHAR_MAX) value = UCHAR_MAX; | 1148 if (value > UCHAR_MAX) value = UCHAR_MAX; |
1218 if (value < 0) value = 0; | 1149 if (value < 0) value = 0; |
1219 | 1150 return value; |
1220 if (m_cacheInvalid || m_exiting) { | 1151 } |
1221 interrupted = true; | 1152 |
1222 break; | 1153 |
1223 } | 1154 SpectrogramLayer::Cache::Cache() : |
1224 | 1155 m_width(0), |
1225 if (showThis) { | 1156 m_height(0), |
1226 m_cache->setValueAt(column, i, value); | 1157 m_magnitude(0), |
1227 } else { | 1158 m_phase(0), |
1228 m_cache->setValueAt(column, i, NO_VALUE); | 1159 m_factor(0) |
1229 } | 1160 { |
1230 } | |
1231 | |
1232 return !interrupted; | |
1233 } | |
1234 | |
1235 SpectrogramLayer::Cache::Cache(size_t width, size_t height) : | |
1236 m_width(width), | |
1237 m_height(height) | |
1238 { | |
1239 // use malloc rather than new[], because we want to be able to use realloc | |
1240 m_values = (unsigned char *) | |
1241 malloc(m_width * m_height * sizeof(unsigned char)); | |
1242 if (!m_values) throw std::bad_alloc(); | |
1243 MUNLOCK(m_values, m_width * m_height * sizeof(unsigned char)); | |
1244 } | 1161 } |
1245 | 1162 |
1246 SpectrogramLayer::Cache::~Cache() | 1163 SpectrogramLayer::Cache::~Cache() |
1247 { | 1164 { |
1248 if (m_values) free(m_values); | 1165 for (size_t i = 0; i < m_height; ++i) { |
1166 if (m_magnitude && m_magnitude[i]) free(m_magnitude[i]); | |
1167 if (m_phase && m_phase[i]) free(m_phase[i]); | |
1168 } | |
1169 | |
1170 if (m_magnitude) free(m_magnitude); | |
1171 if (m_phase) free(m_phase); | |
1172 if (m_factor) free(m_factor); | |
1249 } | 1173 } |
1250 | 1174 |
1251 void | 1175 void |
1252 SpectrogramLayer::Cache::resize(size_t width, size_t height) | 1176 SpectrogramLayer::Cache::resize(size_t width, size_t height) |
1253 { | 1177 { |
1254 std::cerr << "SpectrogramLayer::Cache[" << this << "]::resize(" << width << "x" << height << ")" << std::endl; | 1178 std::cerr << "SpectrogramLayer::Cache[" << this << "]::resize(" << width << "x" << height << ")" << std::endl; |
1255 m_values = (unsigned char *) | 1179 |
1256 realloc(m_values, m_width * m_height * sizeof(unsigned char)); | 1180 if (m_width == width && m_height == height) return; |
1257 if (!m_values) throw std::bad_alloc(); | 1181 |
1258 MUNLOCK(m_values, m_width * m_height * sizeof(unsigned char)); | 1182 resize(m_magnitude, width, height); |
1259 } | 1183 resize(m_phase, width, height); |
1260 | 1184 |
1261 size_t | 1185 m_factor = (float *)realloc(m_factor, width * sizeof(float)); |
1262 SpectrogramLayer::Cache::getWidth() const | 1186 |
1263 { | 1187 m_width = width; |
1264 return m_width; | 1188 m_height = height; |
1265 } | 1189 } |
1266 | 1190 |
1267 size_t | 1191 void |
1268 SpectrogramLayer::Cache::getHeight() const | 1192 SpectrogramLayer::Cache::resize(uint16_t **&array, size_t width, size_t height) |
1269 { | 1193 { |
1270 return m_height; | 1194 for (size_t i = height; i < m_height; ++i) { |
1271 } | 1195 free(array[i]); |
1272 | 1196 } |
1273 unsigned char | 1197 |
1274 SpectrogramLayer::Cache::getValueAt(size_t x, size_t y) const | 1198 if (height != m_height) { |
1275 { | 1199 array = (uint16_t **)realloc(array, height * sizeof(uint16_t *)); |
1276 if (x >= m_width || y >= m_height) return 0; | 1200 if (!array) throw std::bad_alloc(); |
1277 return m_values[y * m_width + x]; | 1201 MUNLOCK(array, height * sizeof(uint16_t *)); |
1278 } | 1202 } |
1279 | 1203 |
1280 void | 1204 for (size_t i = m_height; i < height; ++i) { |
1281 SpectrogramLayer::Cache::setValueAt(size_t x, size_t y, unsigned char value) | 1205 array[i] = 0; |
1282 { | 1206 } |
1283 if (x >= m_width || y >= m_height) return; | 1207 |
1284 m_values[y * m_width + x] = value; | 1208 for (size_t i = 0; i < height; ++i) { |
1285 } | 1209 array[i] = (uint16_t *)realloc(array[i], width * sizeof(uint16_t)); |
1286 | 1210 if (!array[i]) throw std::bad_alloc(); |
1287 QColor | 1211 MUNLOCK(array[i], width * sizeof(uint16_t)); |
1288 SpectrogramLayer::Cache::getColour(unsigned char index) const | 1212 } |
1289 { | 1213 } |
1290 return m_colours[index]; | 1214 |
1291 } | 1215 void |
1292 | 1216 SpectrogramLayer::Cache::reset() |
1293 void | 1217 { |
1294 SpectrogramLayer::Cache::setColour(unsigned char index, QColor colour) | 1218 for (size_t x = 0; x < m_width; ++x) { |
1295 { | 1219 for (size_t y = 0; y < m_height; ++y) { |
1296 m_colours[index] = colour; | 1220 m_magnitude[y][x] = 0; |
1297 } | 1221 m_phase[y][x] = 0; |
1298 | 1222 } |
1299 void | 1223 m_factor[x] = 1.0f; |
1300 SpectrogramLayer::Cache::fill(unsigned char value) | 1224 } |
1301 { | 1225 } |
1302 std::cerr << "SpectrogramLayer::Cache[" << this << "]::fill(" << value << ")" << std::endl; | |
1303 for (size_t i = 0; i < m_width * m_height; ++i) { | |
1304 m_values[i] = value; | |
1305 } | |
1306 } | |
1307 | 1226 |
1308 void | 1227 void |
1309 SpectrogramLayer::CacheFillThread::run() | 1228 SpectrogramLayer::CacheFillThread::run() |
1310 { | 1229 { |
1311 // std::cerr << "SpectrogramLayer::CacheFillThread::run" << std::endl; | 1230 // std::cerr << "SpectrogramLayer::CacheFillThread::run" << std::endl; |
1320 | 1239 |
1321 if (m_layer.m_dormant) { | 1240 if (m_layer.m_dormant) { |
1322 | 1241 |
1323 if (m_layer.m_cacheInvalid) { | 1242 if (m_layer.m_cacheInvalid) { |
1324 delete m_layer.m_cache; | 1243 delete m_layer.m_cache; |
1325 delete m_layer.m_phaseAdjustCache; | |
1326 m_layer.m_cache = 0; | 1244 m_layer.m_cache = 0; |
1327 m_layer.m_phaseAdjustCache = 0; | |
1328 } | 1245 } |
1329 | 1246 |
1330 } else if (m_layer.m_model && m_layer.m_cacheInvalid) { | 1247 } else if (m_layer.m_model && m_layer.m_cacheInvalid) { |
1331 | 1248 |
1332 // std::cerr << "SpectrogramLayer::CacheFillThread::run: something to do" << std::endl; | 1249 // std::cerr << "SpectrogramLayer::CacheFillThread::run: something to do" << std::endl; |
1333 | 1250 |
1334 while (!m_layer.m_model->isReady()) { | 1251 while (!m_layer.m_model->isReady()) { |
1335 m_layer.m_condition.wait(&m_layer.m_mutex, 100); | 1252 m_layer.m_condition.wait(&m_layer.m_mutex, 100); |
1336 } | 1253 } |
1337 | 1254 |
1338 m_layer.m_cachedInitialVisibleArea = false; | |
1339 m_layer.m_cacheInvalid = false; | 1255 m_layer.m_cacheInvalid = false; |
1340 m_fillExtent = 0; | 1256 m_fillExtent = 0; |
1341 m_fillCompletion = 0; | 1257 m_fillCompletion = 0; |
1342 | 1258 |
1343 std::cerr << "SpectrogramLayer::CacheFillThread::run: model is ready" << std::endl; | 1259 std::cerr << "SpectrogramLayer::CacheFillThread::run: model is ready" << std::endl; |
1365 | 1281 |
1366 size_t width = (end - start) / windowIncrement + 1; | 1282 size_t width = (end - start) / windowIncrement + 1; |
1367 size_t height = windowSize / 2; | 1283 size_t height = windowSize / 2; |
1368 | 1284 |
1369 if (!m_layer.m_cache) { | 1285 if (!m_layer.m_cache) { |
1370 m_layer.m_cache = new Cache(width, height); | 1286 m_layer.m_cache = new Cache; |
1371 } else if (width != m_layer.m_cache->getWidth() || | |
1372 height != m_layer.m_cache->getHeight()) { | |
1373 m_layer.m_cache->resize(width, height); | |
1374 } | 1287 } |
1375 | 1288 |
1289 m_layer.m_cache->resize(width, height); | |
1376 m_layer.setCacheColourmap(); | 1290 m_layer.setCacheColourmap(); |
1377 m_layer.m_cache->fill(NO_VALUE); | 1291 m_layer.m_cache->reset(); |
1378 | |
1379 if (m_layer.m_binDisplay == PeakFrequencies) { | |
1380 | |
1381 if (!m_layer.m_phaseAdjustCache) { | |
1382 m_layer.m_phaseAdjustCache = new Cache(width, height); | |
1383 } else if (width != m_layer.m_phaseAdjustCache->getWidth() || | |
1384 height != m_layer.m_phaseAdjustCache->getHeight()) { | |
1385 m_layer.m_phaseAdjustCache->resize(width, height); | |
1386 } | |
1387 | |
1388 m_layer.m_phaseAdjustCache->fill(0); | |
1389 | |
1390 } else { | |
1391 delete m_layer.m_phaseAdjustCache; | |
1392 m_layer.m_phaseAdjustCache = 0; | |
1393 } | |
1394 | 1292 |
1395 // We don't need a lock when writing to or reading from | 1293 // We don't need a lock when writing to or reading from |
1396 // the pixels in the cache, because it's a fixed size | 1294 // the pixels in the cache. We do need to ensure we have |
1397 // array. We do need to ensure we have the width and | 1295 // the width and height of the cache and the FFT |
1398 // height of the cache and the FFT parameters known before | 1296 // parameters known before we unlock, in case they change |
1399 // we unlock, in case they change in the model while we | 1297 // in the model while we aren't holding a lock. It's safe |
1400 // aren't holding a lock. It's safe for us to continue to | 1298 // for us to continue to use the "old" values if that |
1401 // use the "old" values if that happens, because they will | 1299 // happens, because they will continue to match the |
1402 // continue to match the dimensions of the actual cache | 1300 // dimensions of the actual cache (which we manage, not |
1403 // (which we manage, not the model). | 1301 // the model). |
1404 m_layer.m_mutex.unlock(); | 1302 m_layer.m_mutex.unlock(); |
1405 | 1303 |
1406 double *input = (double *) | 1304 double *input = (double *) |
1407 fftw_malloc(windowSize * sizeof(double)); | 1305 fftw_malloc(windowSize * sizeof(double)); |
1408 | 1306 |
1433 for (size_t f = visibleStart; f < visibleEnd; f += windowIncrement) { | 1331 for (size_t f = visibleStart; f < visibleEnd; f += windowIncrement) { |
1434 | 1332 |
1435 m_layer.fillCacheColumn(int((f - start) / windowIncrement), | 1333 m_layer.fillCacheColumn(int((f - start) / windowIncrement), |
1436 input, output, plan, | 1334 input, output, plan, |
1437 windowSize, windowIncrement, | 1335 windowSize, windowIncrement, |
1438 //!!! actually if we're doing phase adjustment we also want to fill the column preceding the visible area so that we have the right values for the first visible one (also applies below) | 1336 windower); |
1439 windower, f == visibleStart); | |
1440 | 1337 |
1441 if (m_layer.m_cacheInvalid || m_layer.m_exiting) { | 1338 if (m_layer.m_cacheInvalid || m_layer.m_exiting) { |
1442 interrupted = true; | 1339 interrupted = true; |
1443 m_fillExtent = 0; | 1340 m_fillExtent = 0; |
1444 break; | 1341 break; |
1445 } | 1342 } |
1446 | 1343 |
1447 if (++counter == updateAt || f == visibleEnd - 1) { | 1344 if (++counter == updateAt || |
1345 (f >= visibleEnd - 1 && f < visibleEnd + windowIncrement)) { | |
1448 if (f < end) m_fillExtent = f; | 1346 if (f < end) m_fillExtent = f; |
1449 m_fillCompletion = size_t(100 * fabsf(float(f - visibleStart) / | 1347 m_fillCompletion = size_t(100 * fabsf(float(f - visibleStart) / |
1450 float(end - start))); | 1348 float(end - start))); |
1451 counter = 0; | 1349 counter = 0; |
1452 } | 1350 } |
1453 } | 1351 } |
1454 | 1352 |
1455 m_layer.m_cachedInitialVisibleArea = true; | |
1456 std::cerr << "SpectrogramLayer::CacheFillThread::run: visible bit done" << std::endl; | 1353 std::cerr << "SpectrogramLayer::CacheFillThread::run: visible bit done" << std::endl; |
1354 m_layer.m_view->update(); | |
1457 } | 1355 } |
1458 | 1356 |
1459 if (!interrupted && doVisibleFirst) { | 1357 if (!interrupted && doVisibleFirst) { |
1460 | 1358 |
1461 for (size_t f = visibleEnd; f < end; f += windowIncrement) { | 1359 for (size_t f = visibleEnd; f < end; f += windowIncrement) { |
1462 | 1360 |
1463 if (!m_layer.fillCacheColumn(int((f - start) / windowIncrement), | 1361 m_layer.fillCacheColumn(int((f - start) / windowIncrement), |
1464 input, output, plan, | 1362 input, output, plan, |
1465 windowSize, windowIncrement, | 1363 windowSize, windowIncrement, |
1466 windower, f == visibleEnd)) { | 1364 windower); |
1365 | |
1366 if (m_layer.m_cacheInvalid || m_layer.m_exiting) { | |
1467 interrupted = true; | 1367 interrupted = true; |
1468 m_fillExtent = 0; | 1368 m_fillExtent = 0; |
1469 break; | 1369 break; |
1470 } | 1370 } |
1471 | 1371 |
1472 | 1372 if (++counter == updateAt) { |
1473 if (++counter == updateAt || f == end - 1) { | |
1474 m_fillExtent = f; | 1373 m_fillExtent = f; |
1475 m_fillCompletion = size_t(100 * fabsf(float(f - visibleStart) / | 1374 m_fillCompletion = size_t(100 * fabsf(float(f - visibleStart) / |
1476 float(end - start))); | 1375 float(end - start))); |
1477 counter = 0; | 1376 counter = 0; |
1478 } | 1377 } |
1489 } | 1388 } |
1490 size_t baseCompletion = m_fillCompletion; | 1389 size_t baseCompletion = m_fillCompletion; |
1491 | 1390 |
1492 for (size_t f = start; f < remainingEnd; f += windowIncrement) { | 1391 for (size_t f = start; f < remainingEnd; f += windowIncrement) { |
1493 | 1392 |
1494 if (!m_layer.fillCacheColumn(int((f - start) / windowIncrement), | 1393 m_layer.fillCacheColumn(int((f - start) / windowIncrement), |
1495 input, output, plan, | 1394 input, output, plan, |
1496 windowSize, windowIncrement, | 1395 windowSize, windowIncrement, |
1497 windower, f == start)) { | 1396 windower); |
1397 | |
1398 if (m_layer.m_cacheInvalid || m_layer.m_exiting) { | |
1498 interrupted = true; | 1399 interrupted = true; |
1499 m_fillExtent = 0; | 1400 m_fillExtent = 0; |
1500 break; | 1401 break; |
1501 } | 1402 } |
1502 | 1403 |
1503 if (++counter == updateAt || | 1404 if (++counter == updateAt || |
1504 f == visibleEnd - 1 || | 1405 (f >= visibleEnd - 1 && f < visibleEnd + windowIncrement)) { |
1505 f == remainingEnd - 1) { | |
1506 m_fillExtent = f; | 1406 m_fillExtent = f; |
1507 m_fillCompletion = baseCompletion + | 1407 m_fillCompletion = baseCompletion + |
1508 size_t(100 * fabsf(float(f - start) / | 1408 size_t(100 * fabsf(float(f - start) / |
1509 float(end - start))); | 1409 float(end - start))); |
1510 counter = 0; | 1410 counter = 0; |
1532 SpectrogramLayer::getYBinRange(int y, float &q0, float &q1) const | 1432 SpectrogramLayer::getYBinRange(int y, float &q0, float &q1) const |
1533 { | 1433 { |
1534 int h = m_view->height(); | 1434 int h = m_view->height(); |
1535 if (y < 0 || y >= h) return false; | 1435 if (y < 0 || y >= h) return false; |
1536 | 1436 |
1537 // Each pixel in a column is drawn from a possibly non- | 1437 int sr = m_model->getSampleRate(); |
1538 // integral set of frequency bins. | 1438 float minf = float(sr) / m_windowSize; |
1539 | 1439 float maxf = float(sr) / 2; |
1540 if (m_frequencyScale == LinearFrequencyScale) { | 1440 |
1541 | 1441 if (m_minFrequency > 0.0) minf = m_minFrequency; |
1542 size_t bins = m_windowSize / 2; | 1442 if (m_maxFrequency > 0.0) maxf = m_maxFrequency; |
1543 | 1443 |
1544 if (m_maxFrequency > 0) { | 1444 bool logarithmic = (m_frequencyScale == LogFrequencyScale); |
1545 int sr = m_model->getSampleRate(); | 1445 |
1546 bins = int((double(m_maxFrequency) * m_windowSize) / sr + 0.1); | 1446 q0 = m_view->getFrequencyForY(y, minf, maxf, logarithmic); |
1547 if (bins > m_windowSize / 2) bins = m_windowSize / 2; | 1447 q1 = m_view->getFrequencyForY(y - 1, minf, maxf, logarithmic); |
1548 } | 1448 |
1549 | 1449 // Now map these on to actual bins |
1550 q0 = float(h - y - 1) * bins / h; | 1450 |
1551 q1 = float(h - y) * bins / h; | 1451 int b0 = (q0 * m_windowSize) / sr; |
1552 | 1452 int b1 = (q1 * m_windowSize) / sr; |
1553 } else { | 1453 |
1554 | 1454 q0 = b0; |
1555 // This is all most ad-hoc. I'm not at my brightest. | 1455 q1 = b1; |
1556 | 1456 |
1557 int sr = m_model->getSampleRate(); | 1457 // q0 = (b0 * sr) / m_windowSize; |
1558 | 1458 // q1 = (b1 * sr) / m_windowSize; |
1559 float maxf = m_maxFrequency; | |
1560 if (maxf == 0.0) maxf = float(sr) / 2; | |
1561 | |
1562 float minf = float(sr) / m_windowSize; | |
1563 | |
1564 float maxlogf = log10f(maxf); | |
1565 float minlogf = log10f(minf); | |
1566 | |
1567 float logf0 = minlogf + ((maxlogf - minlogf) * (h - y - 1)) / h; | |
1568 float logf1 = minlogf + ((maxlogf - minlogf) * (h - y)) / h; | |
1569 | |
1570 float f0 = pow(10.f, logf0); | |
1571 float f1 = pow(10.f, logf1); | |
1572 | |
1573 q0 = ((f0 * m_windowSize) / sr) - 1; | |
1574 q1 = ((f1 * m_windowSize) / sr) - 1; | |
1575 | |
1576 // std::cout << "y=" << y << " h=" << h << " maxf=" << maxf << " maxlogf=" | |
1577 // << maxlogf << " logf0=" << logf0 << " f0=" << f0 << " q0=" | |
1578 // << q0 << std::endl; | |
1579 } | |
1580 | 1459 |
1581 return true; | 1460 return true; |
1582 } | 1461 } |
1583 | 1462 |
1584 bool | 1463 bool |
1585 SpectrogramLayer::getXBinRange(int x, float &s0, float &s1) const | 1464 SpectrogramLayer::getXBinRange(int x, float &s0, float &s1) const |
1586 { | 1465 { |
1587 size_t modelStart = m_model->getStartFrame(); | 1466 size_t modelStart = m_model->getStartFrame(); |
1588 size_t modelEnd = m_model->getEndFrame(); | 1467 size_t modelEnd = m_model->getEndFrame(); |
1660 int q0i = int(q0 + 0.001); | 1539 int q0i = int(q0 + 0.001); |
1661 int q1i = int(q1); | 1540 int q1i = int(q1); |
1662 | 1541 |
1663 int sr = m_model->getSampleRate(); | 1542 int sr = m_model->getSampleRate(); |
1664 | 1543 |
1544 size_t windowSize = m_windowSize; | |
1545 size_t windowIncrement = getWindowIncrement(); | |
1546 | |
1665 bool haveAdj = false; | 1547 bool haveAdj = false; |
1666 | 1548 |
1667 bool peaksOnly = (m_binDisplay == PeakBins || | 1549 bool peaksOnly = (m_binDisplay == PeakBins || |
1668 m_binDisplay == PeakFrequencies); | 1550 m_binDisplay == PeakFrequencies); |
1669 | 1551 |
1673 | 1555 |
1674 float binfreq = (sr * q) / m_windowSize; | 1556 float binfreq = (sr * q) / m_windowSize; |
1675 if (q == q0i) freqMin = binfreq; | 1557 if (q == q0i) freqMin = binfreq; |
1676 if (q == q1i) freqMax = binfreq; | 1558 if (q == q1i) freqMax = binfreq; |
1677 | 1559 |
1678 if (m_cache->getValueAt(s, q) == NO_VALUE) { | 1560 if (!m_cache || m_cacheInvalid) break; //!!! lock? |
1679 continue; | 1561 |
1680 } | 1562 if (peaksOnly && !m_cache->isLocalPeak(s, q)) continue; |
1563 | |
1564 if (!m_cache->isOverThreshold(s, q, m_threshold)) continue; | |
1565 | |
1566 float freq = binfreq; | |
1567 bool steady = false; | |
1568 | |
1569 if (s < m_cache->getWidth() - 1) { | |
1570 | |
1571 freq = calculateFrequency(q, | |
1572 windowSize, | |
1573 windowIncrement, | |
1574 sr, | |
1575 m_cache->getPhaseAt(s, q), | |
1576 m_cache->getPhaseAt(s+1, q), | |
1577 steady); | |
1681 | 1578 |
1682 if (m_binDisplay == PeakFrequencies && | 1579 if (!haveAdj || freq < adjFreqMin) adjFreqMin = freq; |
1683 m_phaseAdjustCache) { | 1580 if (!haveAdj || freq > adjFreqMax) adjFreqMax = freq; |
1684 | 1581 |
1685 unsigned char cadj = m_phaseAdjustCache->getValueAt(s, q); | |
1686 int adjust = int((signed char)cadj); | |
1687 | |
1688 float nextBinFreq = (sr * (q + 1)) / m_windowSize; | |
1689 float fadjust = (adjust * (nextBinFreq - binfreq)) / 100.0;//!!! | |
1690 float f = binfreq + fadjust; | |
1691 if (!haveAdj || f < adjFreqMin) adjFreqMin = f; | |
1692 if (!haveAdj || f > adjFreqMax) adjFreqMax = f; | |
1693 haveAdj = true; | 1582 haveAdj = true; |
1694 } | 1583 } |
1695 } | 1584 } |
1696 } | 1585 } |
1697 | 1586 |
1701 | 1590 |
1702 return haveAdj; | 1591 return haveAdj; |
1703 } | 1592 } |
1704 | 1593 |
1705 bool | 1594 bool |
1706 SpectrogramLayer::getXYBinSourceRange(int x, int y, float &dbMin, float &dbMax) const | 1595 SpectrogramLayer::getXYBinSourceRange(int x, int y, |
1596 float &min, float &max, | |
1597 float &phaseMin, float &phaseMax) const | |
1707 { | 1598 { |
1708 float q0 = 0, q1 = 0; | 1599 float q0 = 0, q1 = 0; |
1709 if (!getYBinRange(y, q0, q1)) return false; | 1600 if (!getYBinRange(y, q0, q1)) return false; |
1710 | 1601 |
1711 float s0 = 0, s1 = 0; | 1602 float s0 = 0, s1 = 0; |
1723 if (m_cache && !m_cacheInvalid) { | 1614 if (m_cache && !m_cacheInvalid) { |
1724 | 1615 |
1725 int cw = m_cache->getWidth(); | 1616 int cw = m_cache->getWidth(); |
1726 int ch = m_cache->getHeight(); | 1617 int ch = m_cache->getHeight(); |
1727 | 1618 |
1728 int min = -1, max = -1; | 1619 min = 0.0; |
1620 max = 0.0; | |
1621 phaseMin = 0.0; | |
1622 phaseMax = 0.0; | |
1623 bool have = false; | |
1729 | 1624 |
1730 for (int q = q0i; q <= q1i; ++q) { | 1625 for (int q = q0i; q <= q1i; ++q) { |
1731 for (int s = s0i; s <= s1i; ++s) { | 1626 for (int s = s0i; s <= s1i; ++s) { |
1732 if (s >= 0 && q >= 0 && s < cw && q < ch) { | 1627 if (s >= 0 && q >= 0 && s < cw && q < ch) { |
1733 int value = int(m_cache->getValueAt(s, q)); | 1628 |
1734 if (value == NO_VALUE) continue; | 1629 float value; |
1735 if (min == -1 || value < min) min = value; | 1630 |
1736 if (max == -1 || value > max) max = value; | 1631 value = m_cache->getPhaseAt(s, q); |
1632 if (!have || value < phaseMin) { phaseMin = value; } | |
1633 if (!have || value > phaseMax) { phaseMax = value; } | |
1634 | |
1635 value = m_cache->getMagnitudeAt(s, q); | |
1636 if (!have || value < min) { min = value; } | |
1637 if (!have || value > max) { max = value; } | |
1638 | |
1639 have = true; | |
1737 } | 1640 } |
1738 } | 1641 } |
1739 } | 1642 } |
1740 | 1643 |
1741 if (min >= 0) { | 1644 if (have) { |
1742 dbMin = (float(min) / 256.0) * 80.0 - 80.0; | |
1743 dbMax = (float(max + 1) / 256.0) * 80.0 - 80.1; | |
1744 rv = true; | 1645 rv = true; |
1745 } | 1646 } |
1746 } | 1647 } |
1747 | 1648 |
1748 m_mutex.unlock(); | 1649 m_mutex.unlock(); |
1920 } | 1821 } |
1921 | 1822 |
1922 float minFreq = (float(minbin) * sr) / m_windowSize; | 1823 float minFreq = (float(minbin) * sr) / m_windowSize; |
1923 float maxFreq = (float(bins) * sr) / m_windowSize; | 1824 float maxFreq = (float(bins) * sr) / m_windowSize; |
1924 | 1825 |
1826 size_t increment = getWindowIncrement(); | |
1827 | |
1925 m_mutex.unlock(); | 1828 m_mutex.unlock(); |
1926 | 1829 |
1927 for (int x = 0; x < w; ++x) { | 1830 for (int x = 0; x < w; ++x) { |
1928 | 1831 |
1929 m_mutex.lock(); | 1832 m_mutex.lock(); |
1949 } | 1852 } |
1950 | 1853 |
1951 int s0i = int(s0 + 0.001); | 1854 int s0i = int(s0 + 0.001); |
1952 int s1i = int(s1); | 1855 int s1i = int(s1); |
1953 | 1856 |
1954 for (int q = minbin; q < bins; ++q) { | 1857 for (size_t q = minbin; q < bins; ++q) { |
1955 | 1858 |
1956 for (int s = s0i; s <= s1i; ++s) { | 1859 for (int s = s0i; s <= s1i; ++s) { |
1957 | 1860 |
1958 float sprop = 1.0; | 1861 float sprop = 1.0; |
1959 if (s == s0i) sprop *= (s + 1) - s0; | 1862 if (s == s0i) sprop *= (s + 1) - s0; |
1960 if (s == s1i) sprop *= s1 - s; | 1863 if (s == s1i) sprop *= s1 - s; |
1961 | 1864 |
1962 float f0 = (float(q) * sr) / m_windowSize; | 1865 float f0 = (float(q) * sr) / m_windowSize; |
1963 float f1 = (float(q + 1) * sr) / m_windowSize; | 1866 float f1 = (float(q + 1) * sr) / m_windowSize; |
1964 | 1867 |
1965 if ((m_binDisplay == PeakFrequencies) && | 1868 if (m_binDisplay == PeakFrequencies && |
1966 m_phaseAdjustCache) { | 1869 s < m_cache->getWidth() - 1) { |
1967 | 1870 |
1968 unsigned char cadj = m_phaseAdjustCache->getValueAt(s, q); | 1871 bool steady = false; |
1969 int adjust = int((signed char)cadj); | 1872 f0 = f1 = calculateFrequency(q, |
1970 float fadjust = (adjust * (f1 - f0)) / 100.0; | 1873 m_windowSize, |
1971 f0 = f1 = f0 + fadjust; | 1874 increment, |
1875 sr, | |
1876 m_cache->getPhaseAt(s, q), | |
1877 m_cache->getPhaseAt(s+1, q), | |
1878 steady); | |
1972 } | 1879 } |
1973 | 1880 |
1974 float y0 = h - (h * (f1 - minFreq)) / maxFreq; | 1881 float y0 = m_view->getYForFrequency |
1975 float y1 = h - (h * (f0 - minFreq)) / maxFreq; | 1882 (f1, minFreq, maxFreq, |
1976 | 1883 m_frequencyScale == LogFrequencyScale); |
1977 //!!! We want a general View::getYForFrequency and inverse | 1884 |
1978 // that can be passed min freq, max freq and log/linear. We | 1885 float y1 = m_view->getYForFrequency |
1979 // can then introduce a central correspondence between, say, | 1886 (f0, minFreq, maxFreq, |
1980 // spectrogram layer and a frequency-scaled MIDI layer on the | 1887 m_frequencyScale == LogFrequencyScale); |
1981 // same view. | 1888 |
1982 | |
1983 if (m_frequencyScale == LogFrequencyScale) { | |
1984 | |
1985 //!!! also, shouldn't be recalculating this every time! | |
1986 | |
1987 // float maxf = m_maxFrequency; | |
1988 // if (maxf == 0.0) maxf = float(sr) / 2; | |
1989 | |
1990 // float minf = float(sr) / m_windowSize; | |
1991 | |
1992 float maxlogf = log10f(maxFreq); | |
1993 float minlogf; | |
1994 | |
1995 if (minFreq > 0) minlogf = log10f(minFreq); | |
1996 else minlogf = log10f(float(sr) / m_windowSize); | |
1997 | |
1998 y0 = h - (h * (log10f(f1) - minlogf)) / (maxlogf - minlogf); | |
1999 y1 = h - (h * (log10f(f0) - minlogf)) / (maxlogf - minlogf); | |
2000 } | |
2001 | |
2002 int y0i = int(y0 + 0.001); | 1889 int y0i = int(y0 + 0.001); |
2003 int y1i = int(y1); | 1890 int y1i = int(y1); |
2004 | 1891 |
2005 for (int y = y0i; y <= y1i; ++y) { | 1892 for (int y = y0i; y <= y1i; ++y) { |
2006 | 1893 |
2008 | 1895 |
2009 float yprop = sprop; | 1896 float yprop = sprop; |
2010 if (y == y0i) yprop *= (y + 1) - y0; | 1897 if (y == y0i) yprop *= (y + 1) - y0; |
2011 if (y == y1i) yprop *= y1 - y; | 1898 if (y == y1i) yprop *= y1 - y; |
2012 | 1899 |
2013 int value = m_cache->getValueAt(s, q); | 1900 if (m_binDisplay == PeakBins || |
2014 if (value == NO_VALUE) continue; | 1901 m_binDisplay == PeakFrequencies) { |
1902 if (!m_cache->isLocalPeak(s, q)) continue; | |
1903 } | |
1904 | |
1905 if (!m_cache->isOverThreshold(s, q, m_threshold)) continue; | |
1906 | |
1907 float value; | |
1908 | |
1909 if (m_colourScale == PhaseColourScale) { | |
1910 value = m_cache->getPhaseAt(s, q); | |
1911 } else if (m_normalizeColumns) { | |
1912 value = m_cache->getNormalizedMagnitudeAt(s, q) * m_gain; | |
1913 } else { | |
1914 value = m_cache->getMagnitudeAt(s, q) * m_gain; | |
1915 } | |
2015 | 1916 |
2016 ymag[y] += yprop * value; | 1917 ymag[y] += yprop * value; |
2017 ydiv[y] += yprop; | 1918 ydiv[y] += yprop; |
2018 } | 1919 } |
2019 } | 1920 } |
2020 } | 1921 } |
2021 | 1922 |
2022 for (int y = 0; y < h; ++y) { | 1923 for (int y = 0; y < h; ++y) { |
2023 | 1924 |
2024 int pixel = 1; | 1925 unsigned char pixel = 0; |
2025 | 1926 |
2026 if (ydiv[y] > 0.0) { | 1927 if (ydiv[y] > 0.0) { |
2027 pixel = int(ymag[y] / ydiv[y]); | 1928 float avg = ymag[y] / ydiv[y]; |
2028 if (pixel > 255) pixel = 255; | 1929 pixel = getDisplayValue(avg); |
2029 if (pixel < 1) pixel = 1; | |
2030 } | 1930 } |
2031 | 1931 |
2032 assert(x <= scaled.width()); | 1932 assert(x <= scaled.width()); |
2033 QColor c = m_cache->getColour(pixel); | 1933 QColor c = m_cache->getColour(pixel); |
2034 scaled.setPixel(x, y, | 1934 scaled.setPixel(x, y, |
2035 qRgb(c.red(), c.green(), c.blue())); | 1935 qRgb(c.red(), c.green(), c.blue())); |
2036 } | 1936 } |
2037 | |
2038 | 1937 |
2039 m_mutex.unlock(); | 1938 m_mutex.unlock(); |
2040 } | 1939 } |
2041 | 1940 |
2042 paint.drawImage(x0, y0, scaled); | 1941 paint.drawImage(x0, y0, scaled); |
2096 int x = pos.x(); | 1995 int x = pos.x(); |
2097 int y = pos.y(); | 1996 int y = pos.y(); |
2098 | 1997 |
2099 if (!m_model || !m_model->isOK()) return ""; | 1998 if (!m_model || !m_model->isOK()) return ""; |
2100 | 1999 |
2101 float dbMin = 0, dbMax = 0; | 2000 float magMin = 0, magMax = 0; |
2001 float phaseMin = 0, phaseMax = 0; | |
2102 float freqMin = 0, freqMax = 0; | 2002 float freqMin = 0, freqMax = 0; |
2103 float adjFreqMin = 0, adjFreqMax = 0; | 2003 float adjFreqMin = 0, adjFreqMax = 0; |
2104 QString pitchMin, pitchMax; | 2004 QString pitchMin, pitchMax; |
2105 RealTime rtMin, rtMax; | 2005 RealTime rtMin, rtMax; |
2106 | 2006 |
2107 bool haveDb = false; | 2007 bool haveValues = false; |
2108 | 2008 |
2109 if (!getXBinSourceRange(x, rtMin, rtMax)) return ""; | 2009 if (!getXBinSourceRange(x, rtMin, rtMax)) { |
2110 if (getXYBinSourceRange(x, y, dbMin, dbMax)) haveDb = true; | 2010 return ""; |
2011 } | |
2012 if (getXYBinSourceRange(x, y, magMin, magMax, phaseMin, phaseMax)) { | |
2013 haveValues = true; | |
2014 } | |
2111 | 2015 |
2112 QString adjFreqText = "", adjPitchText = ""; | 2016 QString adjFreqText = "", adjPitchText = ""; |
2113 | 2017 |
2114 if ((m_binDisplay == PeakFrequencies) && | 2018 if (m_binDisplay == PeakFrequencies) { |
2115 m_phaseAdjustCache) { | |
2116 | 2019 |
2117 if (!getAdjustedYBinSourceRange(x, y, freqMin, freqMax, | 2020 if (!getAdjustedYBinSourceRange(x, y, freqMin, freqMax, |
2118 adjFreqMin, adjFreqMax)) return ""; | 2021 adjFreqMin, adjFreqMax)) { |
2022 return ""; | |
2023 } | |
2119 | 2024 |
2120 if (adjFreqMin != adjFreqMax) { | 2025 if (adjFreqMin != adjFreqMax) { |
2121 adjFreqText = tr("Adjusted Frequency:\t%1 - %2 Hz\n") | 2026 adjFreqText = tr("Adjusted Frequency:\t%1 - %2 Hz\n") |
2122 .arg(adjFreqMin).arg(adjFreqMax); | 2027 .arg(adjFreqMin).arg(adjFreqMax); |
2123 adjPitchText = tr("Adjusted Pitch:\t%3 - %4\n") | |
2124 .arg(Pitch::getPitchLabelForFrequency(adjFreqMin)) | |
2125 .arg(Pitch::getPitchLabelForFrequency(adjFreqMax)); | |
2126 } else { | 2028 } else { |
2127 adjFreqText = tr("Adjusted Frequency:\t%1 Hz\n") | 2029 adjFreqText = tr("Adjusted Frequency:\t%1 Hz\n") |
2128 .arg(adjFreqMin); | 2030 .arg(adjFreqMin); |
2129 adjPitchText = tr("Adjusted Pitch:\t%2\n") | 2031 } |
2130 .arg(Pitch::getPitchLabelForFrequency(adjFreqMin)); | 2032 |
2033 QString pmin = Pitch::getPitchLabelForFrequency(adjFreqMin); | |
2034 QString pmax = Pitch::getPitchLabelForFrequency(adjFreqMax); | |
2035 | |
2036 if (pmin != pmax) { | |
2037 adjPitchText = tr("Adjusted Pitch:\t%3 - %4\n").arg(pmin).arg(pmax); | |
2038 } else { | |
2039 adjPitchText = tr("Adjusted Pitch:\t%2\n").arg(pmin); | |
2131 } | 2040 } |
2132 | 2041 |
2133 } else { | 2042 } else { |
2134 | 2043 |
2135 if (!getYBinSourceRange(y, freqMin, freqMax)) return ""; | 2044 if (!getYBinSourceRange(y, freqMin, freqMax)) return ""; |
2136 } | 2045 } |
2137 | |
2138 //!!! want to actually do a one-off FFT to recalculate the dB value! | |
2139 | 2046 |
2140 QString text; | 2047 QString text; |
2141 | 2048 |
2142 if (rtMin != rtMax) { | 2049 if (rtMin != rtMax) { |
2143 text += tr("Time:\t%1 - %2\n") | 2050 text += tr("Time:\t%1 - %2\n") |
2162 .arg(adjFreqText) | 2069 .arg(adjFreqText) |
2163 .arg(Pitch::getPitchLabelForFrequency(freqMin)) | 2070 .arg(Pitch::getPitchLabelForFrequency(freqMin)) |
2164 .arg(adjPitchText); | 2071 .arg(adjPitchText); |
2165 } | 2072 } |
2166 | 2073 |
2167 if (haveDb) { | 2074 if (haveValues) { |
2075 float dbMin = AudioLevel::multiplier_to_dB(magMin); | |
2076 float dbMax = AudioLevel::multiplier_to_dB(magMax); | |
2168 if (lrintf(dbMin) != lrintf(dbMax)) { | 2077 if (lrintf(dbMin) != lrintf(dbMax)) { |
2169 text += tr("dB:\t%1 - %2").arg(lrintf(dbMin)).arg(lrintf(dbMax)); | 2078 text += tr("dB:\t%1 - %2").arg(lrintf(dbMin)).arg(lrintf(dbMax)); |
2170 } else { | 2079 } else { |
2171 text += tr("dB:\t%1").arg(lrintf(dbMin)); | 2080 text += tr("dB:\t%1").arg(lrintf(dbMin)); |
2081 } | |
2082 if (phaseMin != phaseMax) { | |
2083 text += tr("\nPhase:\t%1 - %2").arg(phaseMin).arg(phaseMax); | |
2084 } else { | |
2085 text += tr("\nPhase:\t%1").arg(phaseMin); | |
2172 } | 2086 } |
2173 } | 2087 } |
2174 | 2088 |
2175 return text; | 2089 return text; |
2176 } | 2090 } |