# HG changeset patch # User Chris Cannam # Date 1140456220 0 # Node ID c28ebb4ba4de34323ccfec370cc78ec81a0a5d52 # Parent 10ba9276a3153029cfadafa243260415475b4e2d * Improvements to text layer editing, and implement file I/O for it * Start some fixes to spectrogram frequency computation diff -r 10ba9276a315 -r c28ebb4ba4de layer/SpectrogramLayer.cpp --- 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); } diff -r 10ba9276a315 -r c28ebb4ba4de layer/SpectrogramLayer.h --- 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 diff -r 10ba9276a315 -r c28ebb4ba4de layer/TextLayer.cpp --- 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 #include +#include #include #include @@ -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 { diff -r 10ba9276a315 -r c28ebb4ba4de layer/TextLayer.h --- 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; diff -r 10ba9276a315 -r c28ebb4ba4de widgets/Pane.cpp --- 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; +*/ } } diff -r 10ba9276a315 -r c28ebb4ba4de widgets/PropertyBox.cpp --- 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); diff -r 10ba9276a315 -r c28ebb4ba4de widgets/PropertyStack.cpp --- 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 -#define DEBUG_PROPERTY_STACK 1 +//#define DEBUG_PROPERTY_STACK 1 PropertyStack::PropertyStack(QWidget *parent, View *client) : QTabWidget(parent),