18 #include "data/model/Model.h" 19 #include "data/model/SparseTimeValueModel.h" 20 #include "base/RealTime.h" 21 #include "base/Profiler.h" 22 #include "base/Pitch.h" 23 #include "base/LogRange.h" 24 #include "base/RangeMapper.h" 33 #include "data/model/NoteModel.h" 41 #include <QPainterPath> 42 #include <QMouseEvent> 43 #include <QTextStream> 44 #include <QMessageBox> 52 #define NOTE_HEIGHT 16 57 m_intelligentActions(true),
62 m_originalPoint(0, 0.0, 0, 1.f, tr(
"New Point")),
63 m_editingPoint(0, 0.0, 0, 1.f, tr(
"New Point")),
64 m_greatestLeftNeighbourFrame(0),
65 m_smallestRightNeighbourFrame(0),
66 m_editingCommand(nullptr),
67 m_verticalScale(AutoAlignScale),
77 auto newModel = ModelById::getAs<NoteModel>(modelId);
79 if (!modelId.isNone() && !newModel) {
80 throw std::logic_error(
"Not a NoteModel");
97 list.push_back(
"Vertical Scale");
98 list.push_back(
"Scale Units");
105 if (name ==
"Vertical Scale")
return tr(
"Vertical Scale");
106 if (name ==
"Scale Units")
return tr(
"Scale Units");
113 if (name ==
"Scale Units")
return UnitsProperty;
114 if (name ==
"Vertical Scale")
return ValueProperty;
121 if (name ==
"Vertical Scale" || name ==
"Scale Units") {
130 auto model = ModelById::getAs<NoteModel>(
m_model);
131 if (model)
return model->getScaleUnits();
137 int *min,
int *max,
int *deflt)
const 141 if (name ==
"Vertical Scale") {
149 }
else if (name ==
"Scale Units") {
151 if (deflt) *deflt = 0;
152 auto model = ModelById::getAs<NoteModel>(
m_model);
154 val = UnitDatabase::getInstance()->getUnitId
170 if (name ==
"Vertical Scale") {
173 case 0:
return tr(
"Auto-Align");
174 case 1:
return tr(
"Linear");
175 case 2:
return tr(
"Log");
176 case 3:
return tr(
"MIDI Notes");
185 if (name ==
"Vertical Scale") {
187 }
else if (name ==
"Scale Units") {
188 auto model = ModelById::getAs<NoteModel>(
m_model);
191 (UnitDatabase::getInstance()->getUnitById(value));
218 return (unit !=
"Hz");
228 auto model = ModelById::get(
m_model);
229 if (model)
return model->getCompletion();
235 bool &logarithmic, QString &unit)
const 237 auto model = ModelById::getAs<NoteModel>(
m_model);
238 if (!model)
return false;
239 min = model->getValueMinimum();
240 max = model->getValueMaximum();
244 min = Pitch::getFrequencyForPitch(
int(lrint(min)));
245 max = Pitch::getFrequencyForPitch(
int(lrint(max + 1)));
257 auto model = ModelById::getAs<NoteModel>(
m_model);
264 min = Pitch::getFrequencyForPitch(0);
265 max = Pitch::getFrequencyForPitch(127);
270 min = model->getValueMinimum();
271 max = model->getValueMaximum();
278 min = Pitch::getFrequencyForPitch(
int(lrint(min)));
279 max = Pitch::getFrequencyForPitch(
int(lrint(max + 1)));
282 #ifdef DEBUG_NOTE_LAYER 283 cerr <<
"NoteLayer::getDisplayExtents: min = " << min <<
", max = " << max <<
" (m_scaleMinimum = " <<
m_scaleMinimum <<
", m_scaleMaximum = " <<
m_scaleMaximum <<
")" << endl;
292 auto model = ModelById::getAs<NoteModel>(
m_model);
293 if (!model)
return false;
306 #ifdef DEBUG_NOTE_LAYER 307 cerr <<
"FlexiNoteLayer::setDisplayExtents: min = " << min <<
", max = " << max << endl;
318 auto model = ModelById::getAs<NoteModel>(
m_model);
319 if (!model)
return 0;
329 auto model = ModelById::getAs<NoteModel>(
m_model);
330 if (!model)
return 0;
333 if (!mapper)
return 0;
338 int nr = mapper->getPositionForValue(dmax - dmin);
351 auto model = ModelById::getAs<NoteModel>(
m_model);
365 double newdist = mapper->getValueForPosition(100 - step);
367 double newmin, newmax;
373 newmax = (newdist + sqrt(newdist*newdist + 4*dmin*dmax)) / 2;
374 newmin = newmax - newdist;
379 double dmid = (dmax + dmin) / 2;
380 newmin = dmid - newdist / 2;
381 newmax = dmid + newdist / 2;
385 newmax += (min - newmin);
392 #ifdef DEBUG_NOTE_LAYER 393 cerr <<
"FlexiNoteLayer::setVerticalZoomStep: " << step <<
": " << newmin <<
" -> " << newmax <<
" (range " << newdist <<
")" << endl;
402 auto model = ModelById::getAs<NoteModel>(
m_model);
403 if (!model)
return nullptr;
412 if (min == max)
return nullptr;
415 mapper =
new LogRangeMapper(0, 100, min, max, unit);
417 mapper =
new LinearRangeMapper(0, 100, min, max, unit);
426 auto model = ModelById::getAs<NoteModel>(
m_model);
427 if (!model)
return {};
431 EventVector local = model->getEventsCovering(frame);
432 if (!local.empty())
return local;
438 local = model->getEventsStartingWithin(frame, end - frame);
439 if (!local.empty())
return local;
441 local = model->getEventsSpanning(start, frame - start);
442 if (!local.empty())
return local;
450 auto model = ModelById::getAs<NoteModel>(
m_model);
451 if (!model)
return false;
455 EventVector onPoints = model->getEventsCovering(frame);
456 if (onPoints.empty())
return false;
458 int nearestDistance = -1;
459 for (
const auto &p: onPoints) {
461 if (distance < 0) distance = -distance;
462 if (nearestDistance == -1 || distance < nearestDistance) {
463 nearestDistance = distance;
475 auto model = ModelById::getAs<NoteModel>(
m_model);
476 if (!model)
return false;
480 EventVector onPoints = model->getEventsCovering(frame);
481 if (onPoints.empty())
return false;
483 int nearestDistance = -1;
484 for (
const auto &p: onPoints) {
486 if (distance < 0) distance = -distance;
487 if (nearestDistance == -1 || distance < nearestDistance) {
488 nearestDistance = distance;
501 auto model = ModelById::getAs<NoteModel>(
m_model);
502 if (!model || !model->getSampleRate())
return "";
506 if (points.empty()) {
507 if (!model->isReady()) {
508 return tr(
"In progress");
510 return tr(
"No local points");
515 EventVector::iterator i;
517 for (i = points.begin(); i != points.end(); ++i) {
522 if (model->getValueQuantization() != 0.0) {
524 (v, i->getValue() + model->getValueQuantization());
529 if (pos.y() >= y - 4 && pos.y() <= y + h) {
535 if (i == points.end())
return tr(
"No local points");
537 RealTime rt = RealTime::frame2RealTime(note.getFrame(),
538 model->getSampleRate());
539 RealTime rd = RealTime::frame2RealTime(note.getDuration(),
540 model->getSampleRate());
546 int mnote = int(lrint(note.getValue()));
547 int cents = int(lrint((note.getValue() - double(mnote)) * 100));
548 double freq = Pitch::getFrequencyForPitch(mnote, cents);
549 pitchText = tr(
"%1 (%2, %3 Hz)")
550 .arg(Pitch::getPitchLabel(mnote, cents))
556 pitchText = tr(
"%1 Hz (%2, %3)")
557 .arg(note.getValue())
558 .arg(Pitch::getPitchLabelForFrequency(note.getValue()))
559 .arg(Pitch::getPitchForFrequency(note.getValue()));
562 pitchText = tr(
"%1 %2")
568 if (note.getLabel() ==
"") {
569 text = QString(tr(
"Time:\t%1\nPitch:\t%2\nDuration:\t%3\nNo label"))
570 .arg(rt.toText(
true).c_str())
572 .arg(rd.toText(
true).c_str());
574 text = QString(tr(
"Time:\t%1\nPitch:\t%2\nDuration:\t%3\nLabel:\t%4"))
575 .arg(rt.toText(
true).c_str())
577 .arg(rd.toText(
true).c_str())
578 .arg(note.getLabel());
591 auto model = ModelById::getAs<NoteModel>(
m_model);
596 resolution = model->getResolution();
602 if (points.empty())
return false;
603 frame = points.begin()->getFrame();
607 points = model->getEventsCovering(frame);
608 sv_frame_t snapped = frame;
611 for (EventVector::const_iterator i = points.begin();
612 i != points.end(); ++i) {
616 if (i->getFrame() > frame) {
617 snapped = i->getFrame();
620 }
else if (i->getFrame() + i->getDuration() >= frame) {
621 snapped = i->getFrame() + i->getDuration();
628 if (i->getFrame() <= frame) {
629 snapped = i->getFrame();
637 EventVector::const_iterator j = i;
640 if (j == points.end()) {
642 snapped = i->getFrame();
646 }
else if (j->getFrame() >= frame) {
648 if (j->getFrame() - frame < frame - i->getFrame()) {
649 snapped = j->getFrame();
651 snapped = i->getFrame();
659 cerr <<
"snapToFeatureFrame: frame " << frame <<
" -> snapped " << snapped <<
", found = " << found << endl;
680 auto model = ModelById::getAs<NoteModel>(
m_model);
681 min = model->getValueMinimum();
682 max = model->getValueMaximum();
685 min = Pitch::getFrequencyForPitch(
int(lrint(min)));
686 max = Pitch::getFrequencyForPitch(
int(lrint(max + 1)));
689 #ifdef DEBUG_NOTE_LAYER 690 cerr <<
"FlexiNoteLayer[" <<
this <<
"]::getScaleExtents: min = " << min <<
", max = " << max <<
", log = " << log << endl;
695 LogRange::mapRange(min, max);
697 #ifdef DEBUG_NOTE_LAYER 698 cerr <<
"FlexiNoteLayer[" <<
this <<
"]::getScaleExtents: min = " << min <<
", max = " << max <<
", log = " << log << endl;
707 min = Pitch::getFrequencyForPitch(0);
708 max = Pitch::getFrequencyForPitch(70);
710 min = Pitch::getFrequencyForPitch(
int(lrint(min)));
711 max = Pitch::getFrequencyForPitch(
int(lrint(max + 1)));
715 LogRange::mapRange(min, max);
720 if (max == min) max = min + 1.0;
726 double min = 0.0, max = 0.0;
727 bool logarithmic =
false;
732 #ifdef DEBUG_NOTE_LAYER 733 cerr <<
"FlexiNoteLayer[" <<
this <<
"]::getYForValue(" << val <<
"): min = " << min <<
", max = " << max <<
", log = " << logarithmic << endl;
737 val = Pitch::getFrequencyForPitch(
int(lrint(val)),
738 int(lrint((val - floor(val)) * 100.0)));
739 #ifdef DEBUG_NOTE_LAYER 740 cerr <<
"shouldConvertMIDIToHz true, val now = " << val << endl;
745 val = LogRange::map(val);
746 #ifdef DEBUG_NOTE_LAYER 747 cerr <<
"logarithmic true, val now = " << val << endl;
751 int y = int(h - ((val - min) * h) / (max - min)) - 1;
752 #ifdef DEBUG_NOTE_LAYER 753 cerr <<
"y = " << y << endl;
761 double min = 0.0, max = 0.0;
762 bool logarithmic =
false;
767 double val = min + (double(h - y) * double(max - min)) / h;
770 val = pow(10.f, val);
774 val = Pitch::getPitchForFrequency(val);
789 auto model = ModelById::getAs<NoteModel>(
m_model);
790 if (!model || !model->isOK())
return;
792 sv_samplerate_t sampleRate = model->getSampleRate();
793 if (!sampleRate)
return;
797 int x0 = rect.left(), x1 = rect.right();
801 EventVector points(model->getEventsSpanning(frame0, frame1 - frame0));
802 if (points.empty())
return;
807 brushColour.setAlpha(80);
812 double min = model->getValueMinimum();
813 double max = model->getValueMaximum();
814 if (max == min) max = min + 1.0;
817 Event illuminatePoint(0);
818 bool shouldIlluminate =
false;
826 paint.setRenderHint(QPainter::Antialiasing,
false);
830 for (EventVector::const_iterator i = points.begin();
831 i != points.end(); ++i) {
835 if (noteNumber < 0) {
836 noteNumber = model->getIndexForEvent(p);
843 int w = v->
getXForFrame(p.getFrame() + p.getDuration()) - x;
846 if (model->getValueQuantization() != 0.0) {
847 h = y -
getYForValue(v, p.getValue() + model->getValueQuantization());
853 paint.setBrush(brushColour);
855 if (shouldIlluminate && illuminatePoint == p) {
862 QString vlabel = tr(
"freq: %1%2")
863 .arg(p.getValue()).arg(model->getScaleUnits());
867 y - h/2 - 2 - paint.fontMetrics().height()
868 - paint.fontMetrics().descent(),
871 QString hlabel = tr(
"dur: %1")
872 .arg(RealTime::frame2RealTime
873 (p.getDuration(), model->getSampleRate()).toText(
true)
878 y - h/2 - paint.fontMetrics().descent() - 2,
881 QString llabel = QString(
"%1").arg(p.getLabel());
885 y + h + 2 + paint.fontMetrics().descent(),
888 QString nlabel = QString(
"%1").arg(noteNumber);
891 x + paint.fontMetrics().averageCharWidth() / 2,
892 y + h/2 - paint.fontMetrics().descent(),
896 paint.drawRect(x, y - h/2, w, h);
919 auto model = ModelById::getAs<NoteModel>(
m_model);
920 if (!model || model->isEmpty())
return;
939 (v, paint, QRect(w - 10, 0, 10, h),
940 LogRange::unmap(min),
941 LogRange::unmap(max));
942 paint.drawLine(w, 0, w, h);
948 5 + paint.fontMetrics().ascent(),
960 auto model = ModelById::getAs<NoteModel>(
m_model);
964 if (frame < 0) frame = 0;
965 frame = frame / model->getResolution() * model->getResolution();
969 m_editingPoint = Event(frame,
float(value), 0, 0.8f, tr(
"New Point"));
984 auto model = ModelById::getAs<NoteModel>(
m_model);
988 if (frame < 0) frame = 0;
989 frame = frame / model->getResolution() * model->getResolution();
994 sv_frame_t newDuration = frame - newFrame;
995 if (newDuration < 0) {
997 newDuration = -newDuration;
998 }
else if (newDuration == 0) {
1004 .withFrame(newFrame)
1005 .withValue(
float(newValue))
1006 .withDuration(newDuration);
1014 auto model = ModelById::getAs<NoteModel>(
m_model);
1024 auto model = ModelById::getAs<NoteModel>(
m_model);
1064 std::cerr <<
"FlexiNoteLayer::editStart(" << e->x() <<
"," << e->y() <<
")" << std::endl;
1066 auto model = ModelById::getAs<NoteModel>(
m_model);
1098 EventVector allEvents = model->getAllEvents();
1100 for (
auto currentNote: allEvents) {
1103 if (currentNote.getFrame() + currentNote.getDuration() - 1 < onset) {
1105 currentNote.getFrame() + currentNote.getDuration() - 1;
1109 if (currentNote.getFrame() > offset) {
1122 std::cerr <<
"FlexiNoteLayer::editDrag(" << e->x() <<
"," << e->y() <<
")" << std::endl;
1124 auto model = ModelById::getAs<NoteModel>(
m_model);
1133 if (dragFrame < 0) dragFrame = 0;
1134 dragFrame = dragFrame / model->getResolution() * model->getResolution();
1140 new ChangeEventsCommand(
m_model.untyped, tr(
"Drag Point"));
1144 std::cerr <<
"edit mode: " <<
m_editMode <<
" intelligent actions = " 1161 .withFrame(dragFrame)
1195 .withFrame(dragFrame)
1196 .withValue(
float(value));
1200 int midiPitch = Pitch::getPitchForFrequency(
m_editingPoint.getValue(), ¢s);
1201 double lower = Pitch::getFrequencyForPitch(midiPitch - 1, cents);
1202 double higher = Pitch::getFrequencyForPitch(midiPitch + 1, cents);
1207 float(lower), float(higher));
1223 std::cerr <<
"FlexiNoteLayer::editEnd(" 1224 << e->x() <<
"," << e->y() <<
")" << std::endl;
1226 auto model = ModelById::getAs<NoteModel>(
m_model);
1244 newName = tr(
"Edit Point");
1246 newName = tr(
"Relocate Point");
1249 newName = tr(
"Change Point Value");
1263 auto model = ModelById::getAs<NoteModel>(
m_model);
1267 std::cerr <<
"splitStart (n.b. editStart will be called later, if the user drags the mouse)" << std::endl;
1288 auto model = ModelById::getAs<NoteModel>(
m_model);
1290 std::cerr <<
"splitEnd" << std::endl;
1295 if (xdist != 0 || ydist != 0) {
1296 std::cerr <<
"mouse moved" << std::endl;
1314 auto model = ModelById::getAs<NoteModel>(
m_model);
1317 EventVector onPoints = model->getEventsCovering(frame);
1318 if (onPoints.empty())
return;
1320 Event note(*onPoints.begin());
1322 auto command =
new ChangeEventsCommand(
m_model.untyped, tr(
"Edit Point"));
1323 command->remove(note);
1325 if (!e || !(e->modifiers() & Qt::ShiftModifier)) {
1329 Event newNote1(note.getFrame(), note.getValue(),
1330 frame - note.getFrame() - gap,
1331 note.getLevel(), note.getLabel());
1333 Event newNote2(frame, note.getValue(),
1334 note.getDuration() - newNote1.getDuration(),
1335 note.getLevel(), note.getLabel());
1339 command->add(newNote1);
1342 command->add(newNote2);
1345 command->add(newNote1);
1346 command->add(newNote2);
1356 auto model = ModelById::getAs<NoteModel>(
m_model);
1357 std::cerr <<
"addNote" << std::endl;
1360 sv_frame_t duration = 10000;
1365 EventVector noteList = model->getAllEvents();
1368 sv_frame_t smallestRightNeighbourFrame = 0;
1369 for (EventVector::const_iterator i = noteList.begin();
1370 i != noteList.end(); ++i) {
1371 Event currentNote = *i;
1372 if (currentNote.getFrame() > frame) {
1373 smallestRightNeighbourFrame = currentNote.getFrame();
1377 if (smallestRightNeighbourFrame > 0) {
1378 duration = std::min(smallestRightNeighbourFrame - frame + 1, duration);
1379 duration = (duration > 0) ? duration : 0;
1384 (model->getEventsCovering(frame).empty() && duration > 0)) {
1385 Event newNote(frame,
float(value), duration, 100.f, tr(
"new note"));
1386 auto command =
new ChangeEventsCommand(
m_model.untyped, tr(
"Add Point"));
1387 command->add(newNote);
1405 auto model = ModelById::getAs<SparseTimeValueModel>(modelId);
1406 if (model && model->getScaleUnits() ==
"Hz") {
1419 auto model = ModelById::getAs<NoteModel>(
m_model);
1422 EventVector points =
1423 model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
1425 auto command =
new ChangeEventsCommand(
m_model.untyped, tr(
"Snap Notes"));
1427 cerr <<
"snapSelectedNotesToPitchTrack: selection is from " << s.getStartFrame() <<
" to " << s.getEndFrame() << endl;
1429 for (EventVector::iterator i = points.begin();
1430 i != points.end(); ++i) {
1434 cerr <<
"snapSelectedNotesToPitchTrack: looking at note from " << note.getFrame() <<
" to " << note.getFrame() + note.getDuration() << endl;
1436 if (!s.contains(note.getFrame()) &&
1437 !s.contains(note.getFrame() + note.getDuration() - 1)) {
1441 cerr <<
"snapSelectedNotesToPitchTrack: making new note" << endl;
1442 Event newNote(note);
1444 command->remove(note);
1447 command->add(newNote);
1457 auto model = ModelById::getAs<NoteModel>(
m_model);
1462 points = model->getEventsSpanning(s.getStartFrame(), s.getDuration());
1464 points = model->getEventsWithin(s.getStartFrame(), s.getDuration());
1467 EventVector::iterator i = points.begin();
1468 if (i == points.end())
return;
1470 auto command =
new ChangeEventsCommand(
m_model.untyped, tr(
"Merge Notes"));
1474 while (i != points.end()) {
1477 if (i->getFrame() >= s.getEndFrame())
break;
1479 if (i->getFrame() + i->getDuration() > s.getEndFrame())
break;
1482 newNote = newNote.withDuration
1483 (i->getFrame() + i->getDuration() - newNote.getFrame());
1484 command->remove(*i);
1490 command->add(newNote);
1498 auto model = ModelById::getAs<SparseTimeValueModel>(modelId);
1499 if (!model)
return false;
1501 std::cerr << model->getTypeName() << std::endl;
1503 EventVector dataPoints =
1504 model->getEventsWithin(note.getFrame(), note.getDuration());
1506 std::cerr <<
"frame " << note.getFrame() <<
": " << dataPoints.size() <<
" candidate points" << std::endl;
1508 if (dataPoints.empty())
return false;
1510 std::vector<double> pitchValues;
1512 for (EventVector::const_iterator i =
1513 dataPoints.begin(); i != dataPoints.end(); ++i) {
1514 pitchValues.push_back(i->getValue());
1517 if (pitchValues.empty())
return false;
1519 sort(pitchValues.begin(), pitchValues.end());
1520 int size = int(pitchValues.size());
1523 if (size % 2 == 0) {
1524 median = (pitchValues[size/2 - 1] + pitchValues[size/2]) / 2;
1526 median = pitchValues[size/2];
1529 std::cerr <<
"updateNoteValueFromPitchCurve: corrected from " << note.getValue() <<
" to median " << median << std::endl;
1531 note = note.withValue(
float(median));
1547 bool closeToLeft =
false, closeToRight =
false,
1548 closeToTop =
false, closeToBottom =
false;
1550 closeToLeft, closeToRight,
1551 closeToTop, closeToBottom);
1554 v->
getView()->setCursor(Qt::SizeHorCursor);
1556 cerr <<
"edit mode -> LeftBoundary" << endl;
1557 }
else if (closeToRight) {
1558 v->
getView()->setCursor(Qt::SizeHorCursor);
1560 cerr <<
"edit mode -> RightBoundary" << endl;
1561 }
else if (closeToTop) {
1562 v->
getView()->setCursor(Qt::CrossCursor);
1564 cerr <<
"edit mode -> DragNote" << endl;
1565 }
else if (closeToBottom) {
1566 v->
getView()->setCursor(Qt::UpArrowCursor);
1568 cerr <<
"edit mode -> SplitNote" << endl;
1570 v->
getView()->setCursor(Qt::ArrowCursor);
1581 int noteEndX = v->
getXForFrame(note.getFrame() + note.getDuration());
1586 bool closeToNote =
false;
1588 if (y >= noteStartY-ctol && y <= noteEndY+ctol && x >= noteStartX-ctol && x <= noteEndX+ctol) closeToNote =
true;
1589 if (!closeToNote)
return;
1593 if (x >= noteStartX - tol && x <= noteStartX + tol) closeToLeft =
true;
1594 if (x >= noteEndX - tol && x <= noteEndX + tol) closeToRight =
true;
1595 if (y >= noteStartY - tol && y <= noteStartY + tol) closeToTop =
true;
1596 if (y >= noteEndY - tol && y <= noteEndY + tol) closeToBottom =
true;
1605 std::cerr <<
"Opening note editor dialog" << std::endl;
1606 auto model = ModelById::getAs<NoteModel>(
m_model);
1607 if (!model)
return false;
1613 (model->getSampleRate(),
1624 dialog->
setText(note.getLabel());
1626 if (dialog->exec() == QDialog::Accepted) {
1628 Event newNote = note
1632 .withLabel(dialog->
getText());
1634 auto command =
new ChangeEventsCommand(
m_model.untyped, tr(
"Edit Point"));
1635 command->remove(note);
1636 command->add(newNote);
1647 auto model = ModelById::getAs<NoteModel>(
m_model);
1650 auto command =
new ChangeEventsCommand(
m_model.untyped, tr(
"Drag Selection"));
1652 EventVector points =
1653 model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
1655 for (Event p: points) {
1657 Event moved = p.withFrame(p.getFrame() +
1658 newStartFrame - s.getStartFrame());
1659 command->add(moved);
1668 auto model = ModelById::getAs<NoteModel>(
m_model);
1669 if (!model || !s.getDuration())
return;
1671 auto command =
new ChangeEventsCommand(
m_model.untyped, tr(
"Resize Selection"));
1673 EventVector points =
1674 model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
1676 double ratio = double(newSize.getDuration()) /
double(s.getDuration());
1677 double oldStart = double(s.getStartFrame());
1678 double newStart = double(newSize.getStartFrame());
1680 for (Event p: points) {
1682 double newFrame = (double(p.getFrame()) - oldStart) * ratio + newStart;
1683 double newDuration = double(p.getDuration()) * ratio;
1686 .withFrame(lrint(newFrame))
1687 .withDuration(lrint(newDuration));
1689 command->add(newPoint);
1698 auto model = ModelById::getAs<NoteModel>(
m_model);
1702 new ChangeEventsCommand(
m_model.untyped, tr(
"Delete Selected Points"));
1704 EventVector points =
1705 model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
1707 for (Event p: points) {
1717 auto model = ModelById::getAs<NoteModel>(
m_model);
1721 new ChangeEventsCommand(
m_model.untyped, tr(
"Delete Selected Points"));
1723 EventVector points =
1724 model->getEventsSpanning(s.getStartFrame(), s.getDuration());
1726 for (Event p: points) {
1736 auto model = ModelById::getAs<NoteModel>(
m_model);
1739 EventVector points =
1740 model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
1742 for (Event p: points) {
1751 auto model = ModelById::getAs<NoteModel>(
m_model);
1752 if (!model)
return false;
1754 const EventVector &points = from.getPoints();
1756 bool realign =
false;
1760 QMessageBox::StandardButton button =
1761 QMessageBox::question(v->
getView(), tr(
"Re-align pasted items?"),
1762 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?"),
1763 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
1766 if (button == QMessageBox::Cancel) {
1770 if (button == QMessageBox::Yes) {
1775 auto command =
new ChangeEventsCommand(
m_model.untyped, tr(
"Paste"));
1777 for (EventVector::const_iterator i = points.begin();
1778 i != points.end(); ++i) {
1780 sv_frame_t frame = 0;
1784 frame = i->getFrame();
1788 if (i->hasReferenceFrame()) {
1789 frame = i->getReferenceFrame();
1792 frame = i->getFrame();
1796 Event p = i->withFrame(frame);
1799 if (!p.hasValue()) {
1800 newPoint = newPoint.withValue((model->getValueMinimum() +
1801 model->getValueMaximum()) / 2);
1803 if (!p.hasDuration()) {
1804 sv_frame_t nextFrame = frame;
1805 EventVector::const_iterator j = i;
1806 for (; j != points.end(); ++j) {
1809 if (j != points.end()) {
1810 nextFrame = j->getFrame();
1812 if (nextFrame == frame) {
1813 newPoint = newPoint.withDuration(model->getResolution());
1815 newPoint = newPoint.withDuration(nextFrame - frame);
1819 command->add(newPoint);
1830 float(velocity / 127.0),
""));
1841 if (lrintf(p.getValue()) == pitch) {
1843 Event note = p.withDuration(frame - p.getFrame());
1844 auto c =
new ChangeEventsCommand
1845 (
m_model.untyped, tr(
"Record Note"));
1865 (QString(darkbg ?
"White" :
"Black"));
1870 QString indent, QString extraAttributes)
const 1873 QString(
" verticalScale=\"%1\" scaleMinimum=\"%2\" scaleMaximum=\"%3\" ")
1886 attributes.value(
"verticalScale").toInt(&ok);
1893 auto model = ModelById::getAs<NoteModel>(
m_model);
1896 double minf = std::numeric_limits<double>::max();
1899 EventVector allPoints = model->getAllEvents();
1900 for (EventVector::const_iterator i = allPoints.begin();
1901 i != allPoints.end(); ++i) {
1904 if (note.getValue() < minf) minf = note.getValue();
1905 if (note.getValue() > maxf) maxf = note.getValue();
1908 std::cerr <<
"min frequency:" << minf <<
", max frequency: " << maxf << std::endl;
void paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect rect) const override
QString getFeatureDescription(LayerGeometryProvider *v, QPoint &) const override
void editEnd(LayerGeometryProvider *v, QMouseEvent *) override
virtual bool snapToFeatureFrame(LayerGeometryProvider *, sv_frame_t &, int &resolution, SnapType, int) const
Adjust the given frame to snap to the nearest feature, if possible.
int getPropertyRangeAndValue(const PropertyName &, int *min, int *max, int *deflt) const override
The base class for visual representations of the data found in a Model.
int getCompletion(LayerGeometryProvider *) const override
Return the proportion of background work complete in drawing this view, as a percentage – in most ca...
void setFrameDuration(sv_frame_t frame)
virtual QColor getForeground() const =0
bool getNoteToEdit(LayerGeometryProvider *v, int x, int y, Event &) const
int getVerticalScaleWidth(LayerGeometryProvider *v, bool, QPainter &) const override
int getDefaultColourHint(bool dark, bool &impose) override
void drawDrag(LayerGeometryProvider *v, QMouseEvent *) override
PropertyList getProperties() const override
virtual bool shouldIlluminateLocalFeatures(const Layer *, QPoint &) const =0
void splitStart(LayerGeometryProvider *v, QMouseEvent *) override
static int scalePixelSize(int pixels)
Take a "design pixel" size and scale it for the actual display.
bool setDisplayExtents(double min, double max) override
Set the displayed minimum and maximum values for the y axis to the given range, if supported...
static QString abbreviate(QString text, int maxLength, Policy policy=ElideEnd, bool fuzzy=true, QString ellipsis="")
Abbreviate the given text to the given maximum length (including ellipsis), using the given abbreviat...
virtual int getLayerCount() const
Return the number of layers, regardless of whether visible or dormant, i.e.
QString getPropertyValueLabel(const PropertyName &, int value) const override
void setVerticalRangeToNoteRange(LayerGeometryProvider *v)
void setProperty(const PropertyName &, int value) override
void setModel(ModelId model)
void moveSelection(Selection s, sv_frame_t newStartFrame) override
void addCommand(Command *command)
Add a command to the command history.
bool shouldConvertMIDIToHz() const
void setFrameTime(sv_frame_t frame)
void paintVertical(LayerGeometryProvider *v, const VerticalScaleLayer *layer, QPainter &paint, int x0, double minlog, double maxlog)
QString getPropertyGroupName(const PropertyName &) const override
void setVerticalScale(VerticalScale scale)
bool getValueExtents(double &min, double &max, bool &log, QString &unit) const override
Return the minimum and maximum values for the y axis of the model in this layer, as well as whether t...
sv_frame_t getFrameTime() const
virtual sv_frame_t getFrameForX(int x) const =0
Return the closest frame to the given pixel x-coordinate.
virtual void deleteSelectionInclusive(Selection s)
PropertyList getProperties() const override
void copy(LayerGeometryProvider *v, Selection s, Clipboard &to) override
void eraseEnd(LayerGeometryProvider *v, QMouseEvent *) override
void eraseStart(LayerGeometryProvider *v, QMouseEvent *) override
void drawEnd(LayerGeometryProvider *v, QMouseEvent *) override
sv_frame_t m_greatestLeftNeighbourFrame
int getWidth(LayerGeometryProvider *v, QPainter &paint)
virtual QColor getBaseQColor() const
void toXml(QTextStream &stream, QString indent="", QString extraAttributes="") const override
void paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const override
Paint the given rectangle of this layer onto the given view using the given painter, superimposing it on top of any existing material in that view.
int getWidth(LayerGeometryProvider *v, QPainter &paint)
void deleteSelection(Selection s) override
int getVerticalZoomSteps(int &defaultStep) const override
Get the number of vertical zoom steps available for this layer.
void modelChanged(ModelId)
void setText(QString text)
Interface for classes that provide geometry information (such as size, start frame, and a large number of other properties) about the disposition of a layer.
bool isLayerScrollable(const LayerGeometryProvider *v) const override
This should return true if the layer can safely be scrolled automatically by a given view (simply cop...
bool editOpen(LayerGeometryProvider *v, QMouseEvent *) override
Open an editor on the item under the mouse (e.g.
void splitEnd(LayerGeometryProvider *v, QMouseEvent *) override
int getColourIndex(QString name) const
Return the index of the colour with the given name, if found in the database.
virtual sv_frame_t alignFromReference(LayerGeometryProvider *v, sv_frame_t frame) const
QString getPropertyGroupName(const PropertyName &) const override
virtual QString getLayerPresentationName() const
void finish(ChangeEventsCommand *command)
void setProperties(const QXmlAttributes &attributes) override
Set the particular properties of a layer (those specific to the subclass) from a set of XML attribute...
int getCurrentVerticalZoomStep() const override
Get the current vertical zoom step.
void toXml(QTextStream &stream, QString indent="", QString extraAttributes="") const override
void layerParametersChanged()
bool snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame, int &resolution, SnapType snap, int ycoord) const override
Adjust the given frame to snap to the nearest feature, if possible.
void snapSelectedNotesToPitchTrack(LayerGeometryProvider *v, Selection s)
bool clipboardHasDifferentAlignment(LayerGeometryProvider *v, const Clipboard &clip) const
void addNote(LayerGeometryProvider *v, QMouseEvent *e) override
bool shouldAutoAlign() const
void paintPianoVertical(LayerGeometryProvider *v, QPainter &paint, QRect rect, double minf, double maxf)
int getPropertyRangeAndValue(const PropertyName &, int *min, int *max, int *deflt) const override
virtual sv_frame_t alignToReference(LayerGeometryProvider *v, sv_frame_t frame) const
void paintVertical(LayerGeometryProvider *v, const VerticalScaleLayer *layer, QPainter &paint, int x0, double minf, double maxf)
static CommandHistory * getInstance()
void connectSignals(ModelId)
QString getPropertyLabel(const PropertyName &) const override
virtual int getPaintHeight() const
bool paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t frameOffset, bool interactive) override
Paste from the given clipboard onto the layer at the given frame offset.
VerticalScale m_verticalScale
virtual Layer * getLayer(int n)
Return the nth layer, counted in stacking order.
PropertyType getPropertyType(const PropertyName &) const override
void abandonNoteOns()
Abandon all pending note-on events.
bool getPointToDrag(LayerGeometryProvider *v, int x, int y, Event &) const
void editStart(LayerGeometryProvider *v, QMouseEvent *) override
double getValueForY(LayerGeometryProvider *v, int y) const override
void setProperties(const QXmlAttributes &attributes) override
Set the particular properties of a layer (those specific to the subclass) from a set of XML attribute...
void setVerticalZoomStep(int) override
!! lots of duplication with TimeValueLayer
sv_frame_t m_smallestRightNeighbourFrame
virtual bool setDisplayExtents(double, double)
Set the displayed minimum and maximum values for the y axis to the given range, if supported...
void splitNotesAt(LayerGeometryProvider *v, sv_frame_t frame)
ModelId getAssociatedPitchModel(LayerGeometryProvider *v) const
void eraseDrag(LayerGeometryProvider *v, QMouseEvent *) override
void reAnalyseRegion(sv_frame_t, sv_frame_t, float, float)
virtual void mouseMoveEvent(LayerGeometryProvider *v, QMouseEvent *)
QString getPropertyValueLabel(const PropertyName &, int value) const override
void editDrag(LayerGeometryProvider *v, QMouseEvent *) override
virtual ModelId getModel() const =0
Return the ID of the model represented in this layer.
static void drawVisibleText(const LayerGeometryProvider *, QPainter &p, int x, int y, QString text, TextStyle style)
ChangeEventsCommand * m_editingCommand
void getScaleExtents(LayerGeometryProvider *, double &min, double &max, bool &log) const
bool getDisplayExtents(double &min, double &max) const override
Return the minimum and maximum values within the visible area for the y axis of this layer...
QString getScaleUnits() const override
void drawStart(LayerGeometryProvider *v, QMouseEvent *) override
virtual bool getVisibleExtentsForUnit(QString unit, double &min, double &max, bool &log) const =0
Return the visible vertical extents for the given unit, if any.
void resizeSelection(Selection s, Selection newSize) override
int getYForValue(LayerGeometryProvider *v, double value) const override
VerticalScaleLayer methods.
void addNoteOff(sv_frame_t frame, int pitch)
Add a note-off.
EventVector getLocalPoints(LayerGeometryProvider *v, int) const
void setProperty(const PropertyName &, int value) override
virtual int getXForFrame(sv_frame_t frame) const =0
Return the pixel x-coordinate corresponding to a given sample frame (which may be negative)...
void addNoteOn(sv_frame_t frame, int pitch, int velocity)
Add a note-on.
sv_frame_t getFrameDuration() const
bool updateNoteValueFromPitchCurve(LayerGeometryProvider *v, Event ¬e) const
bool m_intelligentActions
QString getPropertyLabel(const PropertyName &) const override
PropertyType getPropertyType(const PropertyName &) const override
RangeMapper * getNewVerticalZoomRangeMapper() const override
Create and return a range mapper for vertical zoom step values.
static ColourDatabase * getInstance()
virtual View * getView()=0
void mergeNotes(LayerGeometryProvider *v, Selection s, bool inclusive)
void setValue(float value)
void materialiseReAnalysis()
void getRelativeMousePosition(LayerGeometryProvider *v, Event ¬e, int x, int y, bool &closeToLeft, bool &closeToRight, bool &closeToTop, bool &closeToBottom) const