changeset 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
files layer/SpectrogramLayer.cpp layer/SpectrogramLayer.h layer/TextLayer.cpp layer/TextLayer.h widgets/Pane.cpp widgets/PropertyBox.cpp widgets/PropertyStack.cpp
diffstat 7 files changed, 187 insertions(+), 51 deletions(-) [+]
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);
 }
     
 
--- a/layer/SpectrogramLayer.h	Mon Feb 20 13:33:36 2006 +0000
+++ b/layer/SpectrogramLayer.h	Mon Feb 20 17:23:40 2006 +0000
@@ -130,6 +130,9 @@
     void setFrequencyAdjustment(FrequencyAdjustment);
     FrequencyAdjustment getFrequencyAdjustment() const;
 
+    void setNormalizeColumns(bool n);
+    bool getNormalizeColumns() const;
+
     enum ColourScheme { DefaultColours, WhiteOnBlack, BlackOnWhite,
 			RedOnBlue, YellowOnBlack, RedOnBlack };
 
@@ -177,6 +180,7 @@
     ColourScheme        m_colourScheme;
     FrequencyScale      m_frequencyScale;
     FrequencyAdjustment m_frequencyAdjustment;
+    bool                m_normalizeColumns;
 
     // A QImage would do just as well here, and we originally used
     // one: the problem is that we want to munlock() the memory it
--- a/layer/TextLayer.cpp	Mon Feb 20 13:33:36 2006 +0000
+++ b/layer/TextLayer.cpp	Mon Feb 20 17:23:40 2006 +0000
@@ -18,6 +18,7 @@
 
 #include <QPainter>
 #include <QMouseEvent>
+#include <QInputDialog>
 
 #include <iostream>
 #include <cmath>
@@ -47,7 +48,7 @@
     connect(m_model, SIGNAL(completionChanged()),
 	    this, SIGNAL(modelCompletionChanged()));
 
-    std::cerr << "TextLayer::setModel(" << model << ")" << std::endl;
+//    std::cerr << "TextLayer::setModel(" << model << ")" << std::endl;
 
     emit modelReplaced();
 }
@@ -329,13 +330,16 @@
     if (points.empty()) return;
 
     QColor brushColour(m_colour);
-    brushColour.setAlpha(80);
-    paint.setBrush(brushColour);
 
+    int h, s, v;
+    brushColour.getHsv(&h, &s, &v);
+    brushColour.setHsv(h, s, 255, 100);
+
+    QColor penColour;
     if (m_view->hasLightBackground()) {
-	paint.setPen(Qt::black);
+	penColour = Qt::black;
     } else {
-	paint.setPen(Qt::white);
+	penColour = Qt::white;
     }
 
 //    std::cerr << "TextLayer::paint: resolution is "
@@ -365,22 +369,15 @@
 	int y = getYForHeight(p.height);
 
 	if (illuminateFrame == p.frame) {
-/*
-	    //!!! aside from the problem of choosing a colour, it'd be
-	    //better to save the highlighted rects and draw them at
-	    //the end perhaps
-
-	    //!!! not equipped to illuminate the right section in line
-	    //or curve mode
-
-	    if (m_plotStyle != PlotCurve &&
-		m_plotStyle != PlotLines) {
-		paint.setPen(Qt::black);//!!!
-		if (m_plotStyle != PlotSegmentation) {
-		    paint.setBrush(Qt::black);//!!!
-		}
-	    }	 
-*/   
+	    paint.setBrush(penColour);
+	    if (m_view->hasLightBackground()) {
+		paint.setPen(Qt::white);
+	    } else {
+		paint.setPen(Qt::black);
+	    }
+	} else {
+	    paint.setPen(penColour);
+	    paint.setBrush(brushColour);
 	}
 
 	QString label = p.label;
@@ -428,7 +425,7 @@
 void
 TextLayer::drawStart(QMouseEvent *e)
 {
-    std::cerr << "TextLayer::drawStart(" << e->x() << "," << e->y() << ")" << std::endl;
+//    std::cerr << "TextLayer::drawStart(" << e->x() << "," << e->y() << ")" << std::endl;
 
     if (!m_model) {
 	std::cerr << "TextLayer::drawStart: no model" << std::endl;
@@ -454,7 +451,7 @@
 void
 TextLayer::drawDrag(QMouseEvent *e)
 {
-    std::cerr << "TextLayer::drawDrag(" << e->x() << "," << e->y() << ")" << std::endl;
+//    std::cerr << "TextLayer::drawDrag(" << e->x() << "," << e->y() << ")" << std::endl;
 
     if (!m_model || !m_editing) return;
 
@@ -473,8 +470,20 @@
 void
 TextLayer::drawEnd(QMouseEvent *e)
 {
-    std::cerr << "TextLayer::drawEnd(" << e->x() << "," << e->y() << ")" << std::endl;
+//    std::cerr << "TextLayer::drawEnd(" << e->x() << "," << e->y() << ")" << std::endl;
     if (!m_model || !m_editing) return;
+
+    bool ok = false;
+    QString label = QInputDialog::getText(m_view, tr("Enter label"),
+					  tr("Please enter a new label:"),
+					  QLineEdit::Normal, "", &ok);
+
+    if (ok) {
+	TextModel::RelabelCommand *command =
+	    new TextModel::RelabelCommand(m_model, m_editingPoint, label);
+	m_editingCommand->addCommand(command);
+    }
+
     m_editingCommand->finish();
     m_editingCommand = 0;
     m_editing = false;
@@ -483,13 +492,14 @@
 void
 TextLayer::editStart(QMouseEvent *e)
 {
-    std::cerr << "TextLayer::editStart(" << e->x() << "," << e->y() << ")" << std::endl;
+//    std::cerr << "TextLayer::editStart(" << e->x() << "," << e->y() << ")" << std::endl;
 
     if (!m_model) return;
 
     TextModel::PointList points = getLocalPoints(e->x(), e->y());
     if (points.empty()) return;
 
+    m_editOrigin = e->pos();
     m_editingPoint = *points.begin();
     m_originalPoint = m_editingPoint;
 
@@ -506,11 +516,17 @@
 {
     if (!m_model || !m_editing) return;
 
-    long frame = getFrameForX(e->x());
+    long frameDiff = getFrameForX(e->x()) - getFrameForX(m_editOrigin.x());
+    float heightDiff = getHeightForY(e->y()) - getHeightForY(m_editOrigin.y());
+
+    long frame = m_originalPoint.frame + frameDiff;
+    float height = m_originalPoint.height + heightDiff;
+
+//    long frame = getFrameForX(e->x());
     if (frame < 0) frame = 0;
-    frame = frame / m_model->getResolution() * m_model->getResolution();
+    frame = (frame / m_model->getResolution()) * m_model->getResolution();
 
-    float height = getHeightForY(e->y());
+//    float height = getHeightForY(e->y());
 
     if (!m_editingCommand) {
 	m_editingCommand = new TextModel::EditCommand(m_model, tr("Drag Label"));
@@ -525,7 +541,7 @@
 void
 TextLayer::editEnd(QMouseEvent *e)
 {
-    std::cerr << "TextLayer::editEnd(" << e->x() << "," << e->y() << ")" << std::endl;
+//    std::cerr << "TextLayer::editEnd(" << e->x() << "," << e->y() << ")" << std::endl;
     if (!m_model || !m_editing) return;
 
     if (m_editingCommand) {
@@ -536,10 +552,10 @@
 	    if (m_editingPoint.height != m_originalPoint.height) {
 		newName = tr("Move Label");
 	    } else {
-		newName = tr("Relocate Label");
+		newName = tr("Move Label Horizontally");
 	    }
 	} else {
-	    newName = tr("Change Point Height");
+	    newName = tr("Move Label Vertically");
 	}
 
 	m_editingCommand->setName(newName);
@@ -550,6 +566,29 @@
     m_editing = false;
 }
 
+void
+TextLayer::editOpen(QMouseEvent *e)
+{
+    std::cerr << "TextLayer::editOpen" << std::endl;
+
+    if (!m_model) return;
+
+    TextModel::PointList points = getLocalPoints(e->x(), e->y());
+    if (points.empty()) return;
+
+    QString label = points.begin()->label;
+
+    bool ok = false;
+    label = QInputDialog::getText(m_view, tr("Enter label"),
+				  tr("Please enter a new label:"),
+				  QLineEdit::Normal, label, &ok);
+    if (ok && label != points.begin()->label) {
+	TextModel::RelabelCommand *command =
+	    new TextModel::RelabelCommand(m_model, *points.begin(), label);
+	CommandHistory::getInstance()->addCommand(command, true);
+    }
+}    
+
 QString
 TextLayer::toXmlString(QString indent, QString extraAttributes) const
 {
--- a/layer/TextLayer.h	Mon Feb 20 13:33:36 2006 +0000
+++ b/layer/TextLayer.h	Mon Feb 20 17:23:40 2006 +0000
@@ -42,6 +42,8 @@
     virtual void editDrag(QMouseEvent *);
     virtual void editEnd(QMouseEvent *);
 
+    virtual void editOpen(QMouseEvent *); // on double-click
+
     virtual const Model *getModel() const { return m_model; }
     void setModel(TextModel *model);
 
@@ -75,6 +77,7 @@
 
     TextModel *m_model;
     bool m_editing;
+    QPoint m_editOrigin;
     TextModel::Point m_originalPoint;
     TextModel::Point m_editingPoint;
     TextModel::EditCommand *m_editingCommand;
--- a/widgets/Pane.cpp	Mon Feb 20 13:33:36 2006 +0000
+++ b/widgets/Pane.cpp	Mon Feb 20 17:23:40 2006 +0000
@@ -616,6 +616,22 @@
 Pane::mouseDoubleClickEvent(QMouseEvent *e)
 {
     std::cerr << "mouseDoubleClickEvent" << std::endl;
+
+    m_clickPos = e->pos();
+    m_clickedInRange = true;
+    m_shiftPressed = (e->modifiers() & Qt::ShiftModifier);
+    m_ctrlPressed = (e->modifiers() & Qt::ControlModifier);
+
+    ViewManager::ToolMode mode = ViewManager::NavigateMode;
+    if (m_manager) mode = m_manager->getToolMode();
+
+    if (mode == ViewManager::EditMode) {
+
+	Layer *layer = getSelectedLayer();
+	if (layer && layer->isLayerEditable()) {
+	    layer->editOpen(e);
+	}
+    }
 }
 
 void
@@ -713,10 +729,11 @@
     case ViewManager::DrawMode:
 	setCursor(Qt::CrossCursor);
 	break;
-	
+/*	
     case ViewManager::TextMode:
 	setCursor(Qt::IBeamCursor);
 	break;
+*/
     }
 }
 
--- a/widgets/PropertyBox.cpp	Mon Feb 20 13:33:36 2006 +0000
+++ b/widgets/PropertyBox.cpp	Mon Feb 20 17:23:40 2006 +0000
@@ -82,7 +82,9 @@
 void
 PropertyBox::populateViewPlayFrame()
 {
+#ifdef DEBUG_PROPERTY_BOX
     std::cerr << "PropertyBox(" << m_container << ")::populateViewPlayFrame" << std::endl;
+#endif
 
     if (m_viewPlayFrame) {
 	delete m_viewPlayFrame;
@@ -109,8 +111,10 @@
 
     layout->setMargin(layout->margin() / 2);
 
+#ifdef DEBUG_PROPERTY_BOX
     std::cerr << "PropertyBox::populateViewPlayFrame: container " << m_container << " (name " << m_container->getPropertyContainerName().toStdString() << ") params " << params << std::endl;
-    
+#endif
+
     if (layer) {
 	QLabel *showLabel = new QLabel(tr("Show"));
 	layout->addWidget(showLabel);
--- a/widgets/PropertyStack.cpp	Mon Feb 20 13:33:36 2006 +0000
+++ b/widgets/PropertyStack.cpp	Mon Feb 20 17:23:40 2006 +0000
@@ -17,7 +17,7 @@
 
 #include <iostream>
 
-#define DEBUG_PROPERTY_STACK 1
+//#define DEBUG_PROPERTY_STACK 1
 
 PropertyStack::PropertyStack(QWidget *parent, View *client) :
     QTabWidget(parent),