diff layer/SpectrogramLayer.cpp @ 36:c28ebb4ba4de

* Improvements to text layer editing, and implement file I/O for it * Start some fixes to spectrogram frequency computation
author Chris Cannam
date Mon, 20 Feb 2006 17:23:40 +0000
parents 10ba9276a315
children 21d061e66177
line wrap: on
line diff
--- a/layer/SpectrogramLayer.cpp	Mon Feb 20 13:33:36 2006 +0000
+++ b/layer/SpectrogramLayer.cpp	Mon Feb 20 17:23:40 2006 +0000
@@ -45,6 +45,7 @@
     m_colourScheme(DefaultColours),
     m_frequencyScale(LinearFrequencyScale),
     m_frequencyAdjustment(RawFrequency),
+    m_normalizeColumns(false),
     m_cache(0),
     m_phaseAdjustCache(0),
     m_cacheInvalid(true),
@@ -124,6 +125,7 @@
     list.push_back(tr("Window Type"));
     list.push_back(tr("Window Size"));
     list.push_back(tr("Window Overlap"));
+    list.push_back(tr("Normalize"));
     list.push_back(tr("Gain"));
     list.push_back(tr("Colour Rotation"));
     list.push_back(tr("Max Frequency"));
@@ -137,6 +139,7 @@
 {
     if (name == tr("Gain")) return RangeProperty;
     if (name == tr("Colour Rotation")) return RangeProperty;
+    if (name == tr("Normalize")) return ToggleProperty;
     return ValueProperty;
 }
 
@@ -149,6 +152,7 @@
     if (name == tr("Colour") ||
 	name == tr("Colour Rotation")) return tr("Colour");
     if (name == tr("Gain") ||
+	name == tr("Normalize") ||
 	name == tr("Colour Scale")) return tr("Scale");
     if (name == tr("Max Frequency") ||
 	name == tr("Frequency Scale") ||
@@ -250,6 +254,10 @@
 	*max = 2;
 	deft = (int)m_frequencyAdjustment;
 
+    } else if (name == tr("Normalize")) {
+	
+	deft = (m_normalizeColumns ? 1 : 0);
+
     } else {
 	deft = Layer::getPropertyRangeAndValue(name, min, max);
     }
@@ -399,6 +407,8 @@
 	case 1: setFrequencyAdjustment(PhaseAdjustedFrequency); break;
 	case 2: setFrequencyAdjustment(PhaseAdjustedPeaks); break;
 	}
+    } else if (name == "Normalize") {
+	setNormalizeColumns(value ? true : false);
     }
 }
 
@@ -660,6 +670,27 @@
 }
 
 void
+SpectrogramLayer::setNormalizeColumns(bool n)
+{
+    if (m_normalizeColumns == n) return;
+    m_mutex.lock();
+
+    m_cacheInvalid = true;
+    m_pixmapCacheInvalid = true;
+    m_normalizeColumns = n;
+    m_mutex.unlock();
+
+    fillCache();
+    emit layerParametersChanged();
+}
+
+bool
+SpectrogramLayer::getNormalizeColumns() const
+{
+    return m_normalizeColumns;
+}
+
+void
 SpectrogramLayer::setLayerDormant(bool dormant)
 {
     if (dormant == m_dormant) return;
@@ -922,14 +953,28 @@
 
     double prevMag = 0.0;
 
+    double maxMag = 0.0;
+    if (m_normalizeColumns) {
+	for (size_t i = 0; i < windowSize/2; ++i) {
+	    double mag = sqrt(output[i][0] * output[i][0] +
+			      output[i][1] * output[i][1]);
+	    mag /= windowSize / 2;
+	    if (mag > maxMag) maxMag = mag;
+	}
+    }
+
     for (size_t i = 0; i < windowSize / 2; ++i) {
 
 	int value = 0;
 	double phase = 0.0;
 
-	    double mag = sqrt(output[i][0] * output[i][0] +
-			      output[i][1] * output[i][1]);
-	    mag /= windowSize / 2;
+	double mag = sqrt(output[i][0] * output[i][0] +
+			  output[i][1] * output[i][1]);
+	mag /= windowSize / 2;
+
+	if (m_normalizeColumns && maxMag > 0.0) {
+	    mag /= maxMag;
+	}
 
 	if (phaseAdjust || (m_colourScale == PhaseColourScale)) {
 
@@ -942,7 +987,8 @@
 	if (phaseAdjust && m_phaseAdjustCache && haveStoredPhase) {
 
 	    bool peak = true;
-	    if (m_frequencyAdjustment == PhaseAdjustedPeaks) {
+//	    if (m_frequencyAdjustment == PhaseAdjustedPeaks) {
+	    if (true) { //!!!
 		if (mag < prevMag) peak = false;
 		else {
 		    double nextMag = 0.0;
@@ -950,6 +996,9 @@
 			nextMag = sqrt(output[i+1][0] * output[i+1][0] +
 				       output[i+1][1] * output[i+1][1]);
 			nextMag /= windowSize / 2;
+			if (m_normalizeColumns && maxMag > 0.0) {
+			    nextMag /= maxMag;
+			}
 		    }
 		    if (mag < nextMag) peak = false;
 		}
@@ -964,16 +1013,33 @@
 		m_phaseAdjustCache->setValueAt(column, i, SCHAR_MIN);
 	    } else {
 
-//	    if (i > 45 && i < 55 && counter == 10)  {
+	    if (i < 100 && counter == 10)  {
 	    
+//		if (counter == 10) {
+
 	    double freq = (double(i) * sampleRate) / m_windowSize;
 //	    std::cout << "\nbin = " << i << " initial estimate freq = " << freq
 //		      << " mag = " << mag << std::endl;
 
 	    double prevPhase = storedPhase[i];
 
+	    // If the frequency is 100Hz and the sample rate is
+	    // 10000Hz and we have 1000 samples (1/10sec) per bin,
+	    // then we expect phase 0 
+
+	    // 2pi happens in 1/freq sec
+	    // one hop happens in hopsize/sr sec
+	    // freq is bin*sr / windowsize
+	    // thus 2pi happens in windowsize/(bin*sr) sec
+	    
+	    // need to know what phase increment we expect from
+	    // hopsize/sr sec
+	    // must be 2pi * ((hopsize/sr) / (windowsize/(bin*sr)))
+	    // = 2pi * ((hopsize * bin * sr) / (windowsize * sr))
+	    // = 2pi * (hopsize * bin) / windowsize
+
 	    double expectedPhase =
-		prevPhase + (2 * M_PI * i * increment) / m_windowSize;
+		prevPhase + (2.0 * M_PI * i * increment) / m_windowSize;
 
 	    double phaseError = MathUtilities::princarg(phase - expectedPhase);
 	    
@@ -981,9 +1047,9 @@
 //		std::cout << "error > " << (1.2 * (increment * M_PI) / m_windowSize) << std::endl;
 //	    }// else {
 
-//	    std::cout << "prevPhase = " << prevPhase << ", phase = " << phase
-//		      << ", expected = " << MathUtilities::princarg(expectedPhase) << ", error = "
-//		      << phaseError << std::endl;
+	    std::cout << "prev = " << prevPhase << ", phase = " << phase
+		      << ", exp = " << expectedPhase << " prin = " << MathUtilities::princarg(expectedPhase) << ", error = "
+		      << phaseError << " inc = " << increment << " i = " << i << ", pi = " << M_PI <<  std::endl;
 
 	    double newFreq =
 		(sampleRate *
@@ -991,9 +1057,8 @@
 		//(prevPhase - (expectedPhase + phaseError))) /
 		(2 * M_PI * increment);
 
-//	    std::cout << freq << " (" << Pitch::getPitchLabelForFrequency(freq).toStdString() <<  ") -> " << newFreq << " (" << Pitch::getPitchLabelForFrequency(newFreq).toStdString() << ")" << std::endl;
+	    std::cout << freq << " (" << Pitch::getPitchLabelForFrequency(freq).toStdString() <<  ") -> " << newFreq << " (" << Pitch::getPitchLabelForFrequency(newFreq).toStdString() << ")" << std::endl;
 //	    }
-//}
 
 	    double binRange = (double(i + 1) * sampleRate) / windowSize - freq;
 	    
@@ -1010,6 +1075,8 @@
 		m_phaseAdjustCache->setValueAt(column, i, 0);
 	    }
 	    }
+}
+
 	    storedPhase[i] = phase;
 	}
 
@@ -1020,10 +1087,6 @@
 
 	} else {
 
-	    double mag = sqrt(output[i][0] * output[i][0] +
-			      output[i][1] * output[i][1]);
-	    mag /= windowSize / 2;
-
 	    switch (m_colourScale) {
 		
 	    default:
@@ -2173,12 +2236,14 @@
 		 "colourScale=\"%2\" "
 		 "colourScheme=\"%3\" "
 		 "frequencyScale=\"%4\" "
-		 "frequencyAdjustment=\"%5\"")
+		 "frequencyAdjustment=\"%5\" "
+		 "normalizeColumns=\"%6\"")
 	.arg(m_maxFrequency)
 	.arg(m_colourScale)
 	.arg(m_colourScheme)
 	.arg(m_frequencyScale)
-	.arg(m_frequencyAdjustment);
+	.arg(m_frequencyAdjustment)
+	.arg(m_normalizeColumns ? "true" : "false");
 
     return Layer::toXmlString(indent, extraAttributes + " " + s);
 }
@@ -2222,6 +2287,10 @@
     FrequencyAdjustment frequencyAdjustment = (FrequencyAdjustment)
 	attributes.value("frequencyAdjustment").toInt(&ok);
     if (ok) setFrequencyAdjustment(frequencyAdjustment);
+
+    bool normalizeColumns =
+	(attributes.value("normalizeColumns").trimmed() == "true");
+    setNormalizeColumns(normalizeColumns);
 }