Mercurial > hg > svgui
view layer/FlexiNoteLayer.cpp @ 1447:8afea53332f3 single-point
Add option to make pane sizes auto-resize-only (i.e. remove user control via a splitter); also place alignment views above panes instead of below, meaning the extra bit of space that we currently have for the pane without one at least goes to the primary pane
author | Chris Cannam |
---|---|
date | Tue, 30 Apr 2019 15:53:21 +0100 |
parents | 8a7c82282fbc |
children | 11a150e65ee1 |
line wrap: on
line source
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ /* Sonic Visualiser An audio file viewer and annotation editor. Centre for Digital Music, Queen Mary, University of London. This file copyright 2006 Chris Cannam. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. See the file COPYING included with this distribution for more information. */ #include "FlexiNoteLayer.h" #include "data/model/Model.h" #include "data/model/SparseTimeValueModel.h" #include "base/RealTime.h" #include "base/Profiler.h" #include "base/Pitch.h" #include "base/LogRange.h" #include "base/RangeMapper.h" #include "ColourDatabase.h" #include "LayerGeometryProvider.h" #include "PianoScale.h" #include "LinearNumericalScale.h" #include "LogNumericalScale.h" #include "PaintAssistant.h" #include "data/model/NoteModel.h" #include "view/View.h" #include "widgets/ItemEditDialog.h" #include "widgets/TextAbbrev.h" #include <QPainter> #include <QPainterPath> #include <QMouseEvent> #include <QTextStream> #include <QMessageBox> #include <iostream> #include <cmath> #include <utility> #include <limits> // GF: included to compile std::numerical_limits on linux #include <vector> #define NOTE_HEIGHT 16 FlexiNoteLayer::FlexiNoteLayer() : SingleColourLayer(), m_model(nullptr), m_editing(false), m_intelligentActions(true), m_dragPointX(0), m_dragPointY(0), m_dragStartX(0), m_dragStartY(0), m_originalPoint(0, 0.0, 0, 1.f, tr("New Point")), m_editingPoint(0, 0.0, 0, 1.f, tr("New Point")), m_greatestLeftNeighbourFrame(0), m_smallestRightNeighbourFrame(0), m_editingCommand(nullptr), m_verticalScale(AutoAlignScale), m_editMode(DragNote), m_scaleMinimum(34), m_scaleMaximum(77) { } void FlexiNoteLayer::setModel(NoteModel *model) { if (m_model == model) return; m_model = model; connectSignals(m_model); // m_scaleMinimum = 0; // m_scaleMaximum = 0; emit modelReplaced(); } Layer::PropertyList FlexiNoteLayer::getProperties() const { PropertyList list = SingleColourLayer::getProperties(); list.push_back("Vertical Scale"); list.push_back("Scale Units"); return list; } QString FlexiNoteLayer::getPropertyLabel(const PropertyName &name) const { if (name == "Vertical Scale") return tr("Vertical Scale"); if (name == "Scale Units") return tr("Scale Units"); return SingleColourLayer::getPropertyLabel(name); } Layer::PropertyType FlexiNoteLayer::getPropertyType(const PropertyName &name) const { if (name == "Scale Units") return UnitsProperty; if (name == "Vertical Scale") return ValueProperty; return SingleColourLayer::getPropertyType(name); } QString FlexiNoteLayer::getPropertyGroupName(const PropertyName &name) const { if (name == "Vertical Scale" || name == "Scale Units") { return tr("Scale"); } return SingleColourLayer::getPropertyGroupName(name); } QString FlexiNoteLayer::getScaleUnits() const { if (m_model) return m_model->getScaleUnits(); else return ""; } int FlexiNoteLayer::getPropertyRangeAndValue(const PropertyName &name, int *min, int *max, int *deflt) const { int val = 0; if (name == "Vertical Scale") { if (min) *min = 0; if (max) *max = 3; if (deflt) *deflt = int(AutoAlignScale); val = int(m_verticalScale); } else if (name == "Scale Units") { if (deflt) *deflt = 0; if (m_model) { val = UnitDatabase::getInstance()->getUnitId (getScaleUnits()); } } else { val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt); } return val; } QString FlexiNoteLayer::getPropertyValueLabel(const PropertyName &name, int value) const { if (name == "Vertical Scale") { switch (value) { default: case 0: return tr("Auto-Align"); case 1: return tr("Linear"); case 2: return tr("Log"); case 3: return tr("MIDI Notes"); } } return SingleColourLayer::getPropertyValueLabel(name, value); } void FlexiNoteLayer::setProperty(const PropertyName &name, int value) { if (name == "Vertical Scale") { setVerticalScale(VerticalScale(value)); } else if (name == "Scale Units") { if (m_model) { m_model->setScaleUnits (UnitDatabase::getInstance()->getUnitById(value)); emit modelChanged(); } } else { return SingleColourLayer::setProperty(name, value); } } void FlexiNoteLayer::setVerticalScale(VerticalScale scale) { if (m_verticalScale == scale) return; m_verticalScale = scale; emit layerParametersChanged(); } bool FlexiNoteLayer::isLayerScrollable(const LayerGeometryProvider *v) const { QPoint discard; return !v->shouldIlluminateLocalFeatures(this, discard); } bool FlexiNoteLayer::shouldConvertMIDIToHz() const { QString unit = getScaleUnits(); return (unit != "Hz"); // if (unit == "" || // unit.startsWith("MIDI") || // unit.startsWith("midi")) return true; // return false; } bool FlexiNoteLayer::getValueExtents(double &min, double &max, bool &logarithmic, QString &unit) const { if (!m_model) return false; min = m_model->getValueMinimum(); max = m_model->getValueMaximum(); if (shouldConvertMIDIToHz()) { unit = "Hz"; min = Pitch::getFrequencyForPitch(int(lrint(min))); max = Pitch::getFrequencyForPitch(int(lrint(max + 1))); } else unit = getScaleUnits(); if (m_verticalScale == MIDIRangeScale || m_verticalScale == LogScale) logarithmic = true; return true; } bool FlexiNoteLayer::getDisplayExtents(double &min, double &max) const { if (!m_model || shouldAutoAlign()) { // std::cerr << "No model or shouldAutoAlign()" << std::endl; return false; } if (m_verticalScale == MIDIRangeScale) { min = Pitch::getFrequencyForPitch(0); max = Pitch::getFrequencyForPitch(127); return true; } if (m_scaleMinimum == m_scaleMaximum) { min = m_model->getValueMinimum(); max = m_model->getValueMaximum(); } else { min = m_scaleMinimum; max = m_scaleMaximum; } if (shouldConvertMIDIToHz()) { min = Pitch::getFrequencyForPitch(int(lrint(min))); max = Pitch::getFrequencyForPitch(int(lrint(max + 1))); } #ifdef DEBUG_NOTE_LAYER cerr << "NoteLayer::getDisplayExtents: min = " << min << ", max = " << max << " (m_scaleMinimum = " << m_scaleMinimum << ", m_scaleMaximum = " << m_scaleMaximum << ")" << endl; #endif return true; } bool FlexiNoteLayer::setDisplayExtents(double min, double max) { if (!m_model) return false; if (min == max) { if (min == 0.f) { max = 1.f; } else { max = min * 1.0001f; } } m_scaleMinimum = min; m_scaleMaximum = max; #ifdef DEBUG_NOTE_LAYER cerr << "FlexiNoteLayer::setDisplayExtents: min = " << min << ", max = " << max << endl; #endif emit layerParametersChanged(); return true; } int FlexiNoteLayer::getVerticalZoomSteps(int &defaultStep) const { if (shouldAutoAlign()) return 0; if (!m_model) return 0; defaultStep = 0; return 100; } int FlexiNoteLayer::getCurrentVerticalZoomStep() const { if (shouldAutoAlign()) return 0; if (!m_model) return 0; RangeMapper *mapper = getNewVerticalZoomRangeMapper(); if (!mapper) return 0; double dmin, dmax; getDisplayExtents(dmin, dmax); int nr = mapper->getPositionForValue(dmax - dmin); delete mapper; return 100 - nr; } //!!! lots of duplication with TimeValueLayer void FlexiNoteLayer::setVerticalZoomStep(int step) { if (shouldAutoAlign()) return; if (!m_model) return; RangeMapper *mapper = getNewVerticalZoomRangeMapper(); if (!mapper) return; double min, max; bool logarithmic; QString unit; getValueExtents(min, max, logarithmic, unit); double dmin, dmax; getDisplayExtents(dmin, dmax); double newdist = mapper->getValueForPosition(100 - step); double newmin, newmax; if (logarithmic) { // see SpectrogramLayer::setVerticalZoomStep newmax = (newdist + sqrt(newdist*newdist + 4*dmin*dmax)) / 2; newmin = newmax - newdist; // cerr << "newmin = " << newmin << ", newmax = " << newmax << endl; } else { double dmid = (dmax + dmin) / 2; newmin = dmid - newdist / 2; newmax = dmid + newdist / 2; } if (newmin < min) { newmax += (min - newmin); newmin = min; } if (newmax > max) { newmax = max; } #ifdef DEBUG_NOTE_LAYER cerr << "FlexiNoteLayer::setVerticalZoomStep: " << step << ": " << newmin << " -> " << newmax << " (range " << newdist << ")" << endl; #endif setDisplayExtents(newmin, newmax); } RangeMapper * FlexiNoteLayer::getNewVerticalZoomRangeMapper() const { if (!m_model) return nullptr; RangeMapper *mapper; double min, max; bool logarithmic; QString unit; getValueExtents(min, max, logarithmic, unit); if (min == max) return nullptr; if (logarithmic) { mapper = new LogRangeMapper(0, 100, min, max, unit); } else { mapper = new LinearRangeMapper(0, 100, min, max, unit); } return mapper; } EventVector FlexiNoteLayer::getLocalPoints(LayerGeometryProvider *v, int x) const { if (!m_model) return {}; sv_frame_t frame = v->getFrameForX(x); EventVector local = m_model->getEventsCovering(frame); if (!local.empty()) return local; int fuzz = ViewManager::scalePixelSize(2); sv_frame_t start = v->getFrameForX(x - fuzz); sv_frame_t end = v->getFrameForX(x + fuzz); local = m_model->getEventsStartingWithin(frame, end - frame); if (!local.empty()) return local; local = m_model->getEventsSpanning(start, frame - start); if (!local.empty()) return local; return {}; } bool FlexiNoteLayer::getPointToDrag(LayerGeometryProvider *v, int x, int y, Event &point) const { if (!m_model) return false; sv_frame_t frame = v->getFrameForX(x); EventVector onPoints = m_model->getEventsCovering(frame); if (onPoints.empty()) return false; int nearestDistance = -1; for (const auto &p: onPoints) { int distance = getYForValue(v, p.getValue()) - y; if (distance < 0) distance = -distance; if (nearestDistance == -1 || distance < nearestDistance) { nearestDistance = distance; point = p; } } return true; } bool FlexiNoteLayer::getNoteToEdit(LayerGeometryProvider *v, int x, int y, Event &point) const { // GF: find the note that is closest to the cursor if (!m_model) return false; sv_frame_t frame = v->getFrameForX(x); EventVector onPoints = m_model->getEventsCovering(frame); if (onPoints.empty()) return false; int nearestDistance = -1; for (const auto &p: onPoints) { int distance = getYForValue(v, p.getValue()) - y; if (distance < 0) distance = -distance; if (nearestDistance == -1 || distance < nearestDistance) { nearestDistance = distance; point = p; } } return true; } QString FlexiNoteLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &pos) const { int x = pos.x(); if (!m_model || !m_model->getSampleRate()) return ""; EventVector points = getLocalPoints(v, x); if (points.empty()) { if (!m_model->isReady()) { return tr("In progress"); } else { return tr("No local points"); } } Event note(0); EventVector::iterator i; for (i = points.begin(); i != points.end(); ++i) { int y = getYForValue(v, i->getValue()); int h = NOTE_HEIGHT; // GF: larger notes if (m_model->getValueQuantization() != 0.0) { h = y - getYForValue (v, i->getValue() + m_model->getValueQuantization()); if (h < NOTE_HEIGHT) h = NOTE_HEIGHT; } // GF: this is not quite correct if (pos.y() >= y - 4 && pos.y() <= y + h) { note = *i; break; } } if (i == points.end()) return tr("No local points"); RealTime rt = RealTime::frame2RealTime(note.getFrame(), m_model->getSampleRate()); RealTime rd = RealTime::frame2RealTime(note.getDuration(), m_model->getSampleRate()); QString pitchText; if (shouldConvertMIDIToHz()) { int mnote = int(lrint(note.getValue())); int cents = int(lrint((note.getValue() - double(mnote)) * 100)); double freq = Pitch::getFrequencyForPitch(mnote, cents); pitchText = tr("%1 (%2, %3 Hz)") .arg(Pitch::getPitchLabel(mnote, cents)) .arg(mnote) .arg(freq); } else if (getScaleUnits() == "Hz") { pitchText = tr("%1 Hz (%2, %3)") .arg(note.getValue()) .arg(Pitch::getPitchLabelForFrequency(note.getValue())) .arg(Pitch::getPitchForFrequency(note.getValue())); } else { pitchText = tr("%1 %2") .arg(note.getValue()).arg(getScaleUnits()); } QString text; if (note.getLabel() == "") { text = QString(tr("Time:\t%1\nPitch:\t%2\nDuration:\t%3\nNo label")) .arg(rt.toText(true).c_str()) .arg(pitchText) .arg(rd.toText(true).c_str()); } else { text = QString(tr("Time:\t%1\nPitch:\t%2\nDuration:\t%3\nLabel:\t%4")) .arg(rt.toText(true).c_str()) .arg(pitchText) .arg(rd.toText(true).c_str()) .arg(note.getLabel()); } pos = QPoint(v->getXForFrame(note.getFrame()), getYForValue(v, note.getValue())); return text; } bool FlexiNoteLayer::snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame, int &resolution, SnapType snap) const { if (!m_model) { return Layer::snapToFeatureFrame(v, frame, resolution, snap); } resolution = m_model->getResolution(); EventVector points; if (snap == SnapNeighbouring) { points = getLocalPoints(v, v->getXForFrame(frame)); if (points.empty()) return false; frame = points.begin()->getFrame(); return true; } points = m_model->getEventsCovering(frame); sv_frame_t snapped = frame; bool found = false; for (EventVector::const_iterator i = points.begin(); i != points.end(); ++i) { if (snap == SnapRight) { if (i->getFrame() > frame) { snapped = i->getFrame(); found = true; break; } else if (i->getFrame() + i->getDuration() >= frame) { snapped = i->getFrame() + i->getDuration(); found = true; break; } } else if (snap == SnapLeft) { if (i->getFrame() <= frame) { snapped = i->getFrame(); found = true; // don't break, as the next may be better } else { break; } } else { // nearest EventVector::const_iterator j = i; ++j; if (j == points.end()) { snapped = i->getFrame(); found = true; break; } else if (j->getFrame() >= frame) { if (j->getFrame() - frame < frame - i->getFrame()) { snapped = j->getFrame(); } else { snapped = i->getFrame(); } found = true; break; } } } cerr << "snapToFeatureFrame: frame " << frame << " -> snapped " << snapped << ", found = " << found << endl; frame = snapped; return found; } void FlexiNoteLayer::getScaleExtents(LayerGeometryProvider *v, double &min, double &max, bool &log) const { min = 0.0; max = 0.0; log = false; QString queryUnits; if (shouldConvertMIDIToHz()) queryUnits = "Hz"; else queryUnits = getScaleUnits(); if (shouldAutoAlign()) { if (!v->getValueExtents(queryUnits, min, max, log)) { min = m_model->getValueMinimum(); max = m_model->getValueMaximum(); if (shouldConvertMIDIToHz()) { min = Pitch::getFrequencyForPitch(int(lrint(min))); max = Pitch::getFrequencyForPitch(int(lrint(max + 1))); } #ifdef DEBUG_NOTE_LAYER cerr << "FlexiNoteLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << endl; #endif } else if (log) { LogRange::mapRange(min, max); #ifdef DEBUG_NOTE_LAYER cerr << "FlexiNoteLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << endl; #endif } } else { getDisplayExtents(min, max); if (m_verticalScale == MIDIRangeScale) { min = Pitch::getFrequencyForPitch(0); max = Pitch::getFrequencyForPitch(70); } else if (shouldConvertMIDIToHz()) { min = Pitch::getFrequencyForPitch(int(lrint(min))); max = Pitch::getFrequencyForPitch(int(lrint(max + 1))); } if (m_verticalScale == LogScale || m_verticalScale == MIDIRangeScale) { LogRange::mapRange(min, max); log = true; } } if (max == min) max = min + 1.0; } int FlexiNoteLayer::getYForValue(LayerGeometryProvider *v, double val) const { double min = 0.0, max = 0.0; bool logarithmic = false; int h = v->getPaintHeight(); getScaleExtents(v, min, max, logarithmic); #ifdef DEBUG_NOTE_LAYER cerr << "FlexiNoteLayer[" << this << "]::getYForValue(" << val << "): min = " << min << ", max = " << max << ", log = " << logarithmic << endl; #endif if (shouldConvertMIDIToHz()) { val = Pitch::getFrequencyForPitch(int(lrint(val)), int(lrint((val - floor(val)) * 100.0))); #ifdef DEBUG_NOTE_LAYER cerr << "shouldConvertMIDIToHz true, val now = " << val << endl; #endif } if (logarithmic) { val = LogRange::map(val); #ifdef DEBUG_NOTE_LAYER cerr << "logarithmic true, val now = " << val << endl; #endif } int y = int(h - ((val - min) * h) / (max - min)) - 1; #ifdef DEBUG_NOTE_LAYER cerr << "y = " << y << endl; #endif return y; } double FlexiNoteLayer::getValueForY(LayerGeometryProvider *v, int y) const { double min = 0.0, max = 0.0; bool logarithmic = false; int h = v->getPaintHeight(); getScaleExtents(v, min, max, logarithmic); double val = min + (double(h - y) * double(max - min)) / h; if (logarithmic) { val = pow(10.f, val); } if (shouldConvertMIDIToHz()) { val = Pitch::getPitchForFrequency(val); } return val; } bool FlexiNoteLayer::shouldAutoAlign() const { if (!m_model) return false; return (m_verticalScale == AutoAlignScale); } void FlexiNoteLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const { if (!m_model || !m_model->isOK()) return; sv_samplerate_t sampleRate = m_model->getSampleRate(); if (!sampleRate) return; // Profiler profiler("FlexiNoteLayer::paint", true); int x0 = rect.left(), x1 = rect.right(); sv_frame_t frame0 = v->getFrameForX(x0); sv_frame_t frame1 = v->getFrameForX(x1); EventVector points(m_model->getEventsSpanning(frame0, frame1 - frame0)); if (points.empty()) return; paint.setPen(getBaseQColor()); QColor brushColour(getBaseQColor()); brushColour.setAlpha(80); // SVDEBUG << "FlexiNoteLayer::paint: resolution is " // << m_model->getResolution() << " frames" << endl; double min = m_model->getValueMinimum(); double max = m_model->getValueMaximum(); if (max == min) max = min + 1.0; QPoint localPos; Event illuminatePoint(0); bool shouldIlluminate = false; if (v->shouldIlluminateLocalFeatures(this, localPos)) { shouldIlluminate = getPointToDrag(v, localPos.x(), localPos.y(), illuminatePoint); } paint.save(); paint.setRenderHint(QPainter::Antialiasing, false); int noteNumber = 0; for (EventVector::const_iterator i = points.begin(); i != points.end(); ++i) { ++noteNumber; const Event &p(*i); int x = v->getXForFrame(p.getFrame()); int y = getYForValue(v, p.getValue()); int w = v->getXForFrame(p.getFrame() + p.getDuration()) - x; int h = NOTE_HEIGHT; //GF: larger notes if (m_model->getValueQuantization() != 0.0) { h = y - getYForValue(v, p.getValue() + m_model->getValueQuantization()); if (h < NOTE_HEIGHT) h = NOTE_HEIGHT; //GF: larger notes } if (w < 1) w = 1; paint.setPen(getBaseQColor()); paint.setBrush(brushColour); if (shouldIlluminate && illuminatePoint == p) { paint.drawLine(x, -1, x, v->getPaintHeight() + 1); paint.drawLine(x+w, -1, x+w, v->getPaintHeight() + 1); paint.setPen(v->getForeground()); QString vlabel = tr("freq: %1%2") .arg(p.getValue()).arg(m_model->getScaleUnits()); PaintAssistant::drawVisibleText (v, paint, x, y - h/2 - 2 - paint.fontMetrics().height() - paint.fontMetrics().descent(), vlabel, PaintAssistant::OutlinedText); QString hlabel = tr("dur: %1") .arg(RealTime::frame2RealTime (p.getDuration(), m_model->getSampleRate()).toText(true) .c_str()); PaintAssistant::drawVisibleText (v, paint, x, y - h/2 - paint.fontMetrics().descent() - 2, hlabel, PaintAssistant::OutlinedText); QString llabel = QString("%1").arg(p.getLabel()); PaintAssistant::drawVisibleText (v, paint, x, y + h + 2 + paint.fontMetrics().descent(), llabel, PaintAssistant::OutlinedText); QString nlabel = QString("%1").arg(noteNumber); PaintAssistant::drawVisibleText (v, paint, x + paint.fontMetrics().averageCharWidth() / 2, y + h/2 - paint.fontMetrics().descent(), nlabel, PaintAssistant::OutlinedText); } paint.drawRect(x, y - h/2, w, h); } paint.restore(); } int FlexiNoteLayer::getVerticalScaleWidth(LayerGeometryProvider *v, bool, QPainter &paint) const { if (!m_model || shouldAutoAlign()) { return 0; } else { if (m_verticalScale == LogScale || m_verticalScale == MIDIRangeScale) { return LogNumericalScale().getWidth(v, paint) + 10; // for piano } else { return LinearNumericalScale().getWidth(v, paint); } } } void FlexiNoteLayer::paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect) const { if (!m_model || m_model->isEmpty()) return; QString unit; double min, max; bool logarithmic; int w = getVerticalScaleWidth(v, false, paint); int h = v->getPaintHeight(); getScaleExtents(v, min, max, logarithmic); if (logarithmic) { LogNumericalScale().paintVertical(v, this, paint, 0, min, max); } else { LinearNumericalScale().paintVertical(v, this, paint, 0, min, max); } if (logarithmic && (getScaleUnits() == "Hz")) { PianoScale().paintPianoVertical (v, paint, QRect(w - 10, 0, 10, h), LogRange::unmap(min), LogRange::unmap(max)); paint.drawLine(w, 0, w, h); } if (getScaleUnits() != "") { int mw = w - 5; paint.drawText(5, 5 + paint.fontMetrics().ascent(), TextAbbrev::abbreviate(getScaleUnits(), paint.fontMetrics(), mw)); } } void FlexiNoteLayer::drawStart(LayerGeometryProvider *v, QMouseEvent *e) { // SVDEBUG << "FlexiNoteLayer::drawStart(" << e->x() << "," << e->y() << ")" << endl; if (!m_model) return; sv_frame_t frame = v->getFrameForX(e->x()); if (frame < 0) frame = 0; frame = frame / m_model->getResolution() * m_model->getResolution(); double value = getValueForY(v, e->y()); m_editingPoint = Event(frame, float(value), 0, 0.8f, tr("New Point")); m_originalPoint = m_editingPoint; if (m_editingCommand) finish(m_editingCommand); m_editingCommand = new ChangeEventsCommand(m_model, tr("Draw Point")); m_editingCommand->add(m_editingPoint); m_editing = true; } void FlexiNoteLayer::drawDrag(LayerGeometryProvider *v, QMouseEvent *e) { // SVDEBUG << "FlexiNoteLayer::drawDrag(" << e->x() << "," << e->y() << ")" << endl; if (!m_model || !m_editing) return; sv_frame_t frame = v->getFrameForX(e->x()); if (frame < 0) frame = 0; frame = frame / m_model->getResolution() * m_model->getResolution(); double newValue = getValueForY(v, e->y()); sv_frame_t newFrame = m_editingPoint.getFrame(); sv_frame_t newDuration = frame - newFrame; if (newDuration < 0) { newFrame = frame; newDuration = -newDuration; } else if (newDuration == 0) { newDuration = 1; } m_editingCommand->remove(m_editingPoint); m_editingPoint = m_editingPoint .withFrame(newFrame) .withValue(float(newValue)) .withDuration(newDuration); m_editingCommand->add(m_editingPoint); } void FlexiNoteLayer::drawEnd(LayerGeometryProvider *, QMouseEvent *) { // SVDEBUG << "FlexiNoteLayer::drawEnd(" << e->x() << "," << e->y() << ")" << endl; if (!m_model || !m_editing) return; finish(m_editingCommand); m_editingCommand = nullptr; m_editing = false; } void FlexiNoteLayer::eraseStart(LayerGeometryProvider *v, QMouseEvent *e) { if (!m_model) return; if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return; if (m_editingCommand) { finish(m_editingCommand); m_editingCommand = nullptr; } m_editing = true; } void FlexiNoteLayer::eraseDrag(LayerGeometryProvider *, QMouseEvent *) { } void FlexiNoteLayer::eraseEnd(LayerGeometryProvider *v, QMouseEvent *e) { if (!m_model || !m_editing) return; m_editing = false; Event p(0); if (!getPointToDrag(v, e->x(), e->y(), p)) return; if (p.getFrame() != m_editingPoint.getFrame() || p.getValue() != m_editingPoint.getValue()) return; m_editingCommand = new ChangeEventsCommand(m_model, tr("Erase Point")); m_editingCommand->remove(m_editingPoint); finish(m_editingCommand); m_editingCommand = nullptr; m_editing = false; } void FlexiNoteLayer::editStart(LayerGeometryProvider *v, QMouseEvent *e) { // SVDEBUG << "FlexiNoteLayer::editStart(" << e->x() << "," << e->y() << ")" << endl; std::cerr << "FlexiNoteLayer::editStart(" << e->x() << "," << e->y() << ")" << std::endl; if (!m_model) return; if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return; m_originalPoint = m_editingPoint; if (m_editMode == RightBoundary) { m_dragPointX = v->getXForFrame (m_editingPoint.getFrame() + m_editingPoint.getDuration()); } else { m_dragPointX = v->getXForFrame (m_editingPoint.getFrame()); } m_dragPointY = getYForValue(v, m_editingPoint.getValue()); if (m_editingCommand) { finish(m_editingCommand); m_editingCommand = nullptr; } m_editing = true; m_dragStartX = e->x(); m_dragStartY = e->y(); sv_frame_t onset = m_originalPoint.getFrame(); sv_frame_t offset = m_originalPoint.getFrame() + m_originalPoint.getDuration() - 1; m_greatestLeftNeighbourFrame = -1; m_smallestRightNeighbourFrame = std::numeric_limits<int>::max(); EventVector allEvents = m_model->getAllEvents(); for (auto currentNote: allEvents) { // left boundary if (currentNote.getFrame() + currentNote.getDuration() - 1 < onset) { m_greatestLeftNeighbourFrame = currentNote.getFrame() + currentNote.getDuration() - 1; } // right boundary if (currentNote.getFrame() > offset) { m_smallestRightNeighbourFrame = currentNote.getFrame(); break; } } std::cerr << "editStart: mode is " << m_editMode << ", note frame: " << onset << ", left boundary: " << m_greatestLeftNeighbourFrame << ", right boundary: " << m_smallestRightNeighbourFrame << std::endl; } void FlexiNoteLayer::editDrag(LayerGeometryProvider *v, QMouseEvent *e) { // SVDEBUG << "FlexiNoteLayer::editDrag(" << e->x() << "," << e->y() << ")" << endl; std::cerr << "FlexiNoteLayer::editDrag(" << e->x() << "," << e->y() << ")" << std::endl; if (!m_model || !m_editing) return; int xdist = e->x() - m_dragStartX; int ydist = e->y() - m_dragStartY; int newx = m_dragPointX + xdist; int newy = m_dragPointY + ydist; sv_frame_t dragFrame = v->getFrameForX(newx); if (dragFrame < 0) dragFrame = 0; dragFrame = dragFrame / m_model->getResolution() * m_model->getResolution(); double value = getValueForY(v, newy); if (!m_editingCommand) { m_editingCommand = new ChangeEventsCommand(m_model, tr("Drag Point")); } m_editingCommand->remove(m_editingPoint); std::cerr << "edit mode: " << m_editMode << " intelligent actions = " << m_intelligentActions << std::endl; switch (m_editMode) { case LeftBoundary : { // left if (m_intelligentActions && dragFrame <= m_greatestLeftNeighbourFrame) { dragFrame = m_greatestLeftNeighbourFrame + 1; } // right if (m_intelligentActions && dragFrame >= m_originalPoint.getFrame() + m_originalPoint.getDuration()) { dragFrame = m_originalPoint.getFrame() + m_originalPoint.getDuration() - 1; } m_editingPoint = m_editingPoint .withFrame(dragFrame) .withDuration(m_originalPoint.getFrame() - dragFrame + m_originalPoint.getDuration()); break; } case RightBoundary : { // left if (m_intelligentActions && dragFrame <= m_greatestLeftNeighbourFrame) { dragFrame = m_greatestLeftNeighbourFrame + 1; } if (m_intelligentActions && dragFrame >= m_smallestRightNeighbourFrame) { dragFrame = m_smallestRightNeighbourFrame - 1; } m_editingPoint = m_editingPoint .withDuration(dragFrame - m_originalPoint.getFrame() + 1); break; } case DragNote : { // left if (m_intelligentActions && dragFrame <= m_greatestLeftNeighbourFrame) { dragFrame = m_greatestLeftNeighbourFrame + 1; } // right if (m_intelligentActions && dragFrame + m_originalPoint.getDuration() >= m_smallestRightNeighbourFrame) { dragFrame = m_smallestRightNeighbourFrame - m_originalPoint.getDuration(); } m_editingPoint = m_editingPoint .withFrame(dragFrame) .withValue(float(value)); // Re-analyse region within +/- 1 semitone of the dragged value float cents = 0; int midiPitch = Pitch::getPitchForFrequency(m_editingPoint.getValue(), ¢s); double lower = Pitch::getFrequencyForPitch(midiPitch - 1, cents); double higher = Pitch::getFrequencyForPitch(midiPitch + 1, cents); emit reAnalyseRegion(m_editingPoint.getFrame(), m_editingPoint.getFrame() + m_editingPoint.getDuration(), float(lower), float(higher)); break; } case SplitNote: // nothing break; } m_editingCommand->add(m_editingPoint); std::cerr << "added new point(" << m_editingPoint.getFrame() << "," << m_editingPoint.getDuration() << ")" << std::endl; } void FlexiNoteLayer::editEnd(LayerGeometryProvider *v, QMouseEvent *e) { std::cerr << "FlexiNoteLayer::editEnd(" << e->x() << "," << e->y() << ")" << std::endl; if (!m_model || !m_editing) return; if (m_editingCommand) { QString newName = m_editingCommand->getName(); if (m_editMode == DragNote) { //!!! command nesting is wrong? emit materialiseReAnalysis(); } m_editingCommand->remove(m_editingPoint); updateNoteValueFromPitchCurve(v, m_editingPoint); m_editingCommand->add(m_editingPoint); if (m_editingPoint.getFrame() != m_originalPoint.getFrame()) { if (m_editingPoint.getValue() != m_originalPoint.getValue()) { newName = tr("Edit Point"); } else { newName = tr("Relocate Point"); } } else { newName = tr("Change Point Value"); } m_editingCommand->setName(newName); finish(m_editingCommand); } m_editingCommand = nullptr; m_editing = false; } void FlexiNoteLayer::splitStart(LayerGeometryProvider *v, QMouseEvent *e) { // GF: note splitting starts (!! remove printing soon) std::cerr << "splitStart (n.b. editStart will be called later, if the user drags the mouse)" << std::endl; if (!m_model) return; if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return; // m_originalPoint = m_editingPoint; // // m_dragPointX = v->getXForFrame(m_editingPoint.getFrame()); // m_dragPointY = getYForValue(v, m_editingPoint.getValue()); if (m_editingCommand) { finish(m_editingCommand); m_editingCommand = nullptr; } m_editing = true; m_dragStartX = e->x(); m_dragStartY = e->y(); } void FlexiNoteLayer::splitEnd(LayerGeometryProvider *v, QMouseEvent *e) { // GF: note splitting ends. (!! remove printing soon) std::cerr << "splitEnd" << std::endl; if (!m_model || !m_editing || m_editMode != SplitNote) return; int xdist = e->x() - m_dragStartX; int ydist = e->y() - m_dragStartY; if (xdist != 0 || ydist != 0) { std::cerr << "mouse moved" << std::endl; return; } sv_frame_t frame = v->getFrameForX(e->x()); splitNotesAt(v, frame, e); } void FlexiNoteLayer::splitNotesAt(LayerGeometryProvider *v, sv_frame_t frame) { splitNotesAt(v, frame, nullptr); } void FlexiNoteLayer::splitNotesAt(LayerGeometryProvider *v, sv_frame_t frame, QMouseEvent *e) { EventVector onPoints = m_model->getEventsCovering(frame); if (onPoints.empty()) return; Event note(*onPoints.begin()); ChangeEventsCommand *command = new ChangeEventsCommand (m_model, tr("Edit Point")); command->remove(note); if (!e || !(e->modifiers() & Qt::ShiftModifier)) { int gap = 0; // MM: I prefer a gap of 0, but we can decide later Event newNote1(note.getFrame(), note.getValue(), frame - note.getFrame() - gap, note.getLevel(), note.getLabel()); Event newNote2(frame, note.getValue(), note.getDuration() - newNote1.getDuration(), note.getLevel(), note.getLabel()); if (m_intelligentActions) { if (updateNoteValueFromPitchCurve(v, newNote1)) { command->add(newNote1); } if (updateNoteValueFromPitchCurve(v, newNote2)) { command->add(newNote2); } } else { command->add(newNote1); command->add(newNote2); } } finish(command); } void FlexiNoteLayer::addNote(LayerGeometryProvider *v, QMouseEvent *e) { std::cerr << "addNote" << std::endl; if (!m_model) return; sv_frame_t duration = 10000; sv_frame_t frame = v->getFrameForX(e->x()); double value = getValueForY(v, e->y()); EventVector noteList = m_model->getAllEvents(); if (m_intelligentActions) { sv_frame_t smallestRightNeighbourFrame = 0; for (EventVector::const_iterator i = noteList.begin(); i != noteList.end(); ++i) { Event currentNote = *i; if (currentNote.getFrame() > frame) { smallestRightNeighbourFrame = currentNote.getFrame(); break; } } if (smallestRightNeighbourFrame > 0) { duration = std::min(smallestRightNeighbourFrame - frame + 1, duration); duration = (duration > 0) ? duration : 0; } } if (!m_intelligentActions || (m_model->getEventsCovering(frame).empty() && duration > 0)) { Event newNote(frame, float(value), duration, 100.f, tr("new note")); ChangeEventsCommand *command = new ChangeEventsCommand (m_model, tr("Add Point")); command->add(newNote); finish(command); } } SparseTimeValueModel * FlexiNoteLayer::getAssociatedPitchModel(LayerGeometryProvider *v) const { // Better than we used to do, but still not very satisfactory // cerr << "FlexiNoteLayer::getAssociatedPitchModel()" << endl; for (int i = 0; i < v->getView()->getLayerCount(); ++i) { Layer *layer = v->getView()->getLayer(i); if (layer && layer->getLayerPresentationName() != "candidate") { // cerr << "FlexiNoteLayer::getAssociatedPitchModel: looks like our layer is " << layer << endl; SparseTimeValueModel *model = qobject_cast<SparseTimeValueModel *> (layer->getModel()); // cerr << "FlexiNoteLayer::getAssociatedPitchModel: and its model is " << model << endl; if (model && model->getScaleUnits() == "Hz") { cerr << "FlexiNoteLayer::getAssociatedPitchModel: it's good, returning " << model << endl; return model; } } } cerr << "FlexiNoteLayer::getAssociatedPitchModel: failed to find a model" << endl; return nullptr; } void FlexiNoteLayer::snapSelectedNotesToPitchTrack(LayerGeometryProvider *v, Selection s) { if (!m_model) return; EventVector points = m_model->getEventsStartingWithin(s.getStartFrame(), s.getDuration()); ChangeEventsCommand *command = new ChangeEventsCommand (m_model, tr("Snap Notes")); cerr << "snapSelectedNotesToPitchTrack: selection is from " << s.getStartFrame() << " to " << s.getEndFrame() << endl; for (EventVector::iterator i = points.begin(); i != points.end(); ++i) { Event note(*i); cerr << "snapSelectedNotesToPitchTrack: looking at note from " << note.getFrame() << " to " << note.getFrame() + note.getDuration() << endl; if (!s.contains(note.getFrame()) && !s.contains(note.getFrame() + note.getDuration() - 1)) { continue; } cerr << "snapSelectedNotesToPitchTrack: making new note" << endl; Event newNote(note); command->remove(note); if (updateNoteValueFromPitchCurve(v, newNote)) { command->add(newNote); } } finish(command); } void FlexiNoteLayer::mergeNotes(LayerGeometryProvider *v, Selection s, bool inclusive) { EventVector points; if (inclusive) { points = m_model->getEventsSpanning(s.getStartFrame(), s.getDuration()); } else { points = m_model->getEventsWithin(s.getStartFrame(), s.getDuration()); } EventVector::iterator i = points.begin(); if (i == points.end()) return; ChangeEventsCommand *command = new ChangeEventsCommand(m_model, tr("Merge Notes")); Event newNote(*i); while (i != points.end()) { if (inclusive) { if (i->getFrame() >= s.getEndFrame()) break; } else { if (i->getFrame() + i->getDuration() > s.getEndFrame()) break; } newNote = newNote.withDuration (i->getFrame() + i->getDuration() - newNote.getFrame()); command->remove(*i); ++i; } updateNoteValueFromPitchCurve(v, newNote); command->add(newNote); finish(command); } bool FlexiNoteLayer::updateNoteValueFromPitchCurve(LayerGeometryProvider *v, Event ¬e) const { SparseTimeValueModel *model = getAssociatedPitchModel(v); if (!model) return false; std::cerr << model->getTypeName() << std::endl; EventVector dataPoints = model->getEventsWithin(note.getFrame(), note.getDuration()); std::cerr << "frame " << note.getFrame() << ": " << dataPoints.size() << " candidate points" << std::endl; if (dataPoints.empty()) return false; std::vector<double> pitchValues; for (EventVector::const_iterator i = dataPoints.begin(); i != dataPoints.end(); ++i) { pitchValues.push_back(i->getValue()); } if (pitchValues.empty()) return false; sort(pitchValues.begin(), pitchValues.end()); int size = int(pitchValues.size()); double median; if (size % 2 == 0) { median = (pitchValues[size/2 - 1] + pitchValues[size/2]) / 2; } else { median = pitchValues[size/2]; } std::cerr << "updateNoteValueFromPitchCurve: corrected from " << note.getValue() << " to median " << median << std::endl; note = note.withValue(float(median)); return true; } void FlexiNoteLayer::mouseMoveEvent(LayerGeometryProvider *v, QMouseEvent *e) { // GF: context sensitive cursors // v->getView()->setCursor(Qt::ArrowCursor); Event note(0); if (!getNoteToEdit(v, e->x(), e->y(), note)) { // v->getView()->setCursor(Qt::UpArrowCursor); return; } bool closeToLeft = false, closeToRight = false, closeToTop = false, closeToBottom = false; getRelativeMousePosition(v, note, e->x(), e->y(), closeToLeft, closeToRight, closeToTop, closeToBottom); if (closeToLeft) { v->getView()->setCursor(Qt::SizeHorCursor); m_editMode = LeftBoundary; cerr << "edit mode -> LeftBoundary" << endl; } else if (closeToRight) { v->getView()->setCursor(Qt::SizeHorCursor); m_editMode = RightBoundary; cerr << "edit mode -> RightBoundary" << endl; } else if (closeToTop) { v->getView()->setCursor(Qt::CrossCursor); m_editMode = DragNote; cerr << "edit mode -> DragNote" << endl; } else if (closeToBottom) { v->getView()->setCursor(Qt::UpArrowCursor); m_editMode = SplitNote; cerr << "edit mode -> SplitNote" << endl; } else { v->getView()->setCursor(Qt::ArrowCursor); } } void FlexiNoteLayer::getRelativeMousePosition(LayerGeometryProvider *v, Event ¬e, int x, int y, bool &closeToLeft, bool &closeToRight, bool &closeToTop, bool &closeToBottom) const { // GF: TODO: consoloidate the tolerance values if (!m_model) return; int ctol = 0; int noteStartX = v->getXForFrame(note.getFrame()); int noteEndX = v->getXForFrame(note.getFrame() + note.getDuration()); int noteValueY = getYForValue(v,note.getValue()); int noteStartY = noteValueY - (NOTE_HEIGHT / 2); int noteEndY = noteValueY + (NOTE_HEIGHT / 2); bool closeToNote = false; if (y >= noteStartY-ctol && y <= noteEndY+ctol && x >= noteStartX-ctol && x <= noteEndX+ctol) closeToNote = true; if (!closeToNote) return; int tol = NOTE_HEIGHT / 2; if (x >= noteStartX - tol && x <= noteStartX + tol) closeToLeft = true; if (x >= noteEndX - tol && x <= noteEndX + tol) closeToRight = true; if (y >= noteStartY - tol && y <= noteStartY + tol) closeToTop = true; if (y >= noteEndY - tol && y <= noteEndY + tol) closeToBottom = true; // cerr << "FlexiNoteLayer::getRelativeMousePosition: close to: left " << closeToLeft << " right " << closeToRight << " top " << closeToTop << " bottom " << closeToBottom << endl; } bool FlexiNoteLayer::editOpen(LayerGeometryProvider *v, QMouseEvent *e) { std::cerr << "Opening note editor dialog" << std::endl; if (!m_model) return false; Event note(0); if (!getPointToDrag(v, e->x(), e->y(), note)) return false; // Event note = *points.begin(); ItemEditDialog *dialog = new ItemEditDialog (m_model->getSampleRate(), ItemEditDialog::ShowTime | ItemEditDialog::ShowDuration | ItemEditDialog::ShowValue | ItemEditDialog::ShowText, getScaleUnits()); dialog->setFrameTime(note.getFrame()); dialog->setValue(note.getValue()); dialog->setFrameDuration(note.getDuration()); dialog->setText(note.getLabel()); if (dialog->exec() == QDialog::Accepted) { Event newNote = note .withFrame(dialog->getFrameTime()) .withValue(dialog->getValue()) .withDuration(dialog->getFrameDuration()) .withLabel(dialog->getText()); ChangeEventsCommand *command = new ChangeEventsCommand (m_model, tr("Edit Point")); command->remove(note); command->add(newNote); finish(command); } delete dialog; return true; } void FlexiNoteLayer::moveSelection(Selection s, sv_frame_t newStartFrame) { if (!m_model) return; ChangeEventsCommand *command = new ChangeEventsCommand(m_model, tr("Drag Selection")); EventVector points = m_model->getEventsStartingWithin(s.getStartFrame(), s.getDuration()); for (Event p: points) { command->remove(p); Event moved = p.withFrame(p.getFrame() + newStartFrame - s.getStartFrame()); command->add(moved); } finish(command); } void FlexiNoteLayer::resizeSelection(Selection s, Selection newSize) { if (!m_model || !s.getDuration()) return; ChangeEventsCommand *command = new ChangeEventsCommand(m_model, tr("Resize Selection")); EventVector points = m_model->getEventsStartingWithin(s.getStartFrame(), s.getDuration()); double ratio = double(newSize.getDuration()) / double(s.getDuration()); double oldStart = double(s.getStartFrame()); double newStart = double(newSize.getStartFrame()); for (Event p: points) { double newFrame = (double(p.getFrame()) - oldStart) * ratio + newStart; double newDuration = double(p.getDuration()) * ratio; Event newPoint = p .withFrame(lrint(newFrame)) .withDuration(lrint(newDuration)); command->remove(p); command->add(newPoint); } finish(command); } void FlexiNoteLayer::deleteSelection(Selection s) { if (!m_model) return; ChangeEventsCommand *command = new ChangeEventsCommand(m_model, tr("Delete Selected Points")); EventVector points = m_model->getEventsStartingWithin(s.getStartFrame(), s.getDuration()); for (Event p: points) { command->remove(p); } finish(command); } void FlexiNoteLayer::deleteSelectionInclusive(Selection s) { if (!m_model) return; ChangeEventsCommand *command = new ChangeEventsCommand(m_model, tr("Delete Selected Points")); EventVector points = m_model->getEventsSpanning(s.getStartFrame(), s.getDuration()); for (Event p: points) { command->remove(p); } finish(command); } void FlexiNoteLayer::copy(LayerGeometryProvider *v, Selection s, Clipboard &to) { if (!m_model) return; EventVector points = m_model->getEventsStartingWithin(s.getStartFrame(), s.getDuration()); for (Event p: points) { to.addPoint(p.withReferenceFrame(alignToReference(v, p.getFrame()))); } } bool FlexiNoteLayer::paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t /*frameOffset */, bool /* interactive */) { if (!m_model) return false; const EventVector &points = from.getPoints(); bool realign = false; if (clipboardHasDifferentAlignment(v, from)) { QMessageBox::StandardButton button = QMessageBox::question(v->getView(), tr("Re-align pasted items?"), tr("The items you are pasting came from a layer with different source material from this one. Do you want to re-align them in time, to match the source material for this layer?"), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes); if (button == QMessageBox::Cancel) { return false; } if (button == QMessageBox::Yes) { realign = true; } } ChangeEventsCommand *command = new ChangeEventsCommand(m_model, tr("Paste")); for (EventVector::const_iterator i = points.begin(); i != points.end(); ++i) { sv_frame_t frame = 0; if (!realign) { frame = i->getFrame(); } else { if (i->hasReferenceFrame()) { frame = i->getReferenceFrame(); frame = alignFromReference(v, frame); } else { frame = i->getFrame(); } } Event p = *i; Event newPoint = p; if (!p.hasValue()) { newPoint = newPoint.withValue((m_model->getValueMinimum() + m_model->getValueMaximum()) / 2); } if (!p.hasDuration()) { sv_frame_t nextFrame = frame; EventVector::const_iterator j = i; for (; j != points.end(); ++j) { if (j != i) break; } if (j != points.end()) { nextFrame = j->getFrame(); } if (nextFrame == frame) { newPoint = newPoint.withDuration(m_model->getResolution()); } else { newPoint = newPoint.withDuration(nextFrame - frame); } } command->add(newPoint); } finish(command); return true; } void FlexiNoteLayer::addNoteOn(sv_frame_t frame, int pitch, int velocity) { m_pendingNoteOns.insert(Event(frame, float(pitch), 0, float(velocity / 127.0), "")); } void FlexiNoteLayer::addNoteOff(sv_frame_t frame, int pitch) { for (NoteSet::iterator i = m_pendingNoteOns.begin(); i != m_pendingNoteOns.end(); ++i) { Event p = *i; if (lrintf(p.getValue()) == pitch) { m_pendingNoteOns.erase(i); Event note = p.withDuration(frame - p.getFrame()); if (m_model) { ChangeEventsCommand *c = new ChangeEventsCommand (m_model, tr("Record Note")); c->add(note); // execute and bundle: CommandHistory::getInstance()->addCommand(c, true, true); } break; } } } void FlexiNoteLayer::abandonNoteOns() { m_pendingNoteOns.clear(); } int FlexiNoteLayer::getDefaultColourHint(bool darkbg, bool &impose) { impose = false; return ColourDatabase::getInstance()->getColourIndex (QString(darkbg ? "White" : "Black")); } void FlexiNoteLayer::toXml(QTextStream &stream, QString indent, QString extraAttributes) const { SingleColourLayer::toXml(stream, indent, extraAttributes + QString(" verticalScale=\"%1\" scaleMinimum=\"%2\" scaleMaximum=\"%3\" ") .arg(m_verticalScale) .arg(m_scaleMinimum) .arg(m_scaleMaximum)); } void FlexiNoteLayer::setProperties(const QXmlAttributes &attributes) { SingleColourLayer::setProperties(attributes); bool ok; VerticalScale scale = (VerticalScale) attributes.value("verticalScale").toInt(&ok); if (ok) setVerticalScale(scale); } void FlexiNoteLayer::setVerticalRangeToNoteRange(LayerGeometryProvider *v) { double minf = std::numeric_limits<double>::max(); double maxf = 0; bool hasNotes = 0; EventVector allPoints = m_model->getAllEvents(); for (EventVector::const_iterator i = allPoints.begin(); i != allPoints.end(); ++i) { hasNotes = 1; Event note = *i; if (note.getValue() < minf) minf = note.getValue(); if (note.getValue() > maxf) maxf = note.getValue(); } std::cerr << "min frequency:" << minf << ", max frequency: " << maxf << std::endl; if (hasNotes) { v->getView()->getLayer(1)->setDisplayExtents(minf*0.66,maxf*1.5); // MM: this is a hack because we rely on // * this layer being automatically aligned to layer 1 // * layer one is a log frequency layer. } }