annotate layer/FlexiNoteLayer.cpp @ 1466:0ecfaa463c1b

Merge
author Chris Cannam
date Fri, 24 May 2019 15:14:09 +0100
parents 8a7c82282fbc
children 11a150e65ee1
rev   line source
Chris@58 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@30 2
Chris@30 3 /*
Chris@59 4 Sonic Visualiser
Chris@59 5 An audio file viewer and annotation editor.
Chris@59 6 Centre for Digital Music, Queen Mary, University of London.
Chris@59 7 This file copyright 2006 Chris Cannam.
Chris@30 8
Chris@59 9 This program is free software; you can redistribute it and/or
Chris@59 10 modify it under the terms of the GNU General Public License as
Chris@59 11 published by the Free Software Foundation; either version 2 of the
Chris@59 12 License, or (at your option) any later version. See the file
Chris@59 13 COPYING included with this distribution for more information.
Chris@30 14 */
Chris@30 15
matthiasm@620 16 #include "FlexiNoteLayer.h"
Chris@30 17
Chris@128 18 #include "data/model/Model.h"
gyorgyf@655 19 #include "data/model/SparseTimeValueModel.h"
Chris@30 20 #include "base/RealTime.h"
Chris@30 21 #include "base/Profiler.h"
Chris@30 22 #include "base/Pitch.h"
Chris@197 23 #include "base/LogRange.h"
Chris@439 24 #include "base/RangeMapper.h"
Chris@1078 25
Chris@376 26 #include "ColourDatabase.h"
Chris@1077 27 #include "LayerGeometryProvider.h"
Chris@692 28 #include "PianoScale.h"
Chris@701 29 #include "LinearNumericalScale.h"
Chris@701 30 #include "LogNumericalScale.h"
Chris@1078 31 #include "PaintAssistant.h"
Chris@30 32
Chris@1426 33 #include "data/model/NoteModel.h"
Chris@30 34
Chris@1077 35 #include "view/View.h"
Chris@1077 36
Chris@70 37 #include "widgets/ItemEditDialog.h"
Chris@701 38 #include "widgets/TextAbbrev.h"
Chris@70 39
Chris@30 40 #include <QPainter>
Chris@30 41 #include <QPainterPath>
Chris@30 42 #include <QMouseEvent>
Chris@316 43 #include <QTextStream>
Chris@360 44 #include <QMessageBox>
Chris@30 45
Chris@30 46 #include <iostream>
Chris@30 47 #include <cmath>
Chris@551 48 #include <utility>
gyorgyf@655 49 #include <limits> // GF: included to compile std::numerical_limits on linux
gyorgyf@655 50 #include <vector>
gyorgyf@655 51
Chris@1426 52 #define NOTE_HEIGHT 16
Chris@30 53
matthiasm@620 54 FlexiNoteLayer::FlexiNoteLayer() :
gyorgyf@646 55 SingleColourLayer(),
Chris@1408 56 m_model(nullptr),
gyorgyf@628 57 m_editing(false),
Chris@805 58 m_intelligentActions(true),
Chris@844 59 m_dragPointX(0),
Chris@844 60 m_dragPointY(0),
Chris@844 61 m_dragStartX(0),
Chris@844 62 m_dragStartY(0),
gyorgyf@627 63 m_originalPoint(0, 0.0, 0, 1.f, tr("New Point")),
gyorgyf@627 64 m_editingPoint(0, 0.0, 0, 1.f, tr("New Point")),
Chris@844 65 m_greatestLeftNeighbourFrame(0),
Chris@844 66 m_smallestRightNeighbourFrame(0),
Chris@1408 67 m_editingCommand(nullptr),
matthiasm@634 68 m_verticalScale(AutoAlignScale),
Chris@688 69 m_editMode(DragNote),
gyorgyf@628 70 m_scaleMinimum(34),
Chris@805 71 m_scaleMaximum(77)
Chris@30 72 {
Chris@30 73 }
Chris@30 74
Chris@30 75 void
Chris@1426 76 FlexiNoteLayer::setModel(NoteModel *model)
Chris@30 77 {
Chris@30 78 if (m_model == model) return;
Chris@30 79 m_model = model;
Chris@30 80
Chris@320 81 connectSignals(m_model);
Chris@30 82
gyorgyf@628 83 // m_scaleMinimum = 0;
gyorgyf@628 84 // m_scaleMaximum = 0;
Chris@439 85
Chris@30 86 emit modelReplaced();
Chris@30 87 }
Chris@30 88
Chris@30 89 Layer::PropertyList
matthiasm@620 90 FlexiNoteLayer::getProperties() const
Chris@30 91 {
Chris@287 92 PropertyList list = SingleColourLayer::getProperties();
Chris@87 93 list.push_back("Vertical Scale");
Chris@100 94 list.push_back("Scale Units");
Chris@30 95 return list;
Chris@30 96 }
Chris@30 97
Chris@87 98 QString
matthiasm@620 99 FlexiNoteLayer::getPropertyLabel(const PropertyName &name) const
Chris@87 100 {
Chris@87 101 if (name == "Vertical Scale") return tr("Vertical Scale");
Chris@116 102 if (name == "Scale Units") return tr("Scale Units");
Chris@287 103 return SingleColourLayer::getPropertyLabel(name);
Chris@87 104 }
Chris@87 105
Chris@30 106 Layer::PropertyType
matthiasm@620 107 FlexiNoteLayer::getPropertyType(const PropertyName &name) const
Chris@30 108 {
Chris@100 109 if (name == "Scale Units") return UnitsProperty;
Chris@287 110 if (name == "Vertical Scale") return ValueProperty;
Chris@287 111 return SingleColourLayer::getPropertyType(name);
Chris@30 112 }
Chris@30 113
Chris@198 114 QString
matthiasm@620 115 FlexiNoteLayer::getPropertyGroupName(const PropertyName &name) const
Chris@198 116 {
Chris@198 117 if (name == "Vertical Scale" || name == "Scale Units") {
Chris@198 118 return tr("Scale");
Chris@198 119 }
Chris@287 120 return SingleColourLayer::getPropertyGroupName(name);
Chris@198 121 }
Chris@198 122
Chris@701 123 QString
Chris@703 124 FlexiNoteLayer::getScaleUnits() const
Chris@701 125 {
Chris@701 126 if (m_model) return m_model->getScaleUnits();
Chris@701 127 else return "";
Chris@701 128 }
Chris@701 129
Chris@30 130 int
matthiasm@620 131 FlexiNoteLayer::getPropertyRangeAndValue(const PropertyName &name,
Chris@714 132 int *min, int *max, int *deflt) const
Chris@30 133 {
Chris@216 134 int val = 0;
Chris@30 135
Chris@287 136 if (name == "Vertical Scale") {
gyorgyf@646 137
Chris@714 138 if (min) *min = 0;
Chris@714 139 if (max) *max = 3;
Chris@216 140 if (deflt) *deflt = int(AutoAlignScale);
gyorgyf@646 141
Chris@714 142 val = int(m_verticalScale);
Chris@30 143
Chris@100 144 } else if (name == "Scale Units") {
Chris@100 145
Chris@216 146 if (deflt) *deflt = 0;
Chris@100 147 if (m_model) {
Chris@216 148 val = UnitDatabase::getInstance()->getUnitId
Chris@701 149 (getScaleUnits());
Chris@100 150 }
Chris@100 151
Chris@30 152 } else {
Chris@216 153
Chris@714 154 val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt);
Chris@30 155 }
Chris@30 156
Chris@216 157 return val;
Chris@30 158 }
Chris@30 159
Chris@30 160 QString
matthiasm@620 161 FlexiNoteLayer::getPropertyValueLabel(const PropertyName &name,
Chris@714 162 int value) const
Chris@30 163 {
Chris@287 164 if (name == "Vertical Scale") {
Chris@714 165 switch (value) {
Chris@714 166 default:
Chris@714 167 case 0: return tr("Auto-Align");
Chris@714 168 case 1: return tr("Linear");
Chris@714 169 case 2: return tr("Log");
Chris@714 170 case 3: return tr("MIDI Notes");
Chris@714 171 }
Chris@30 172 }
Chris@287 173 return SingleColourLayer::getPropertyValueLabel(name, value);
Chris@30 174 }
Chris@30 175
Chris@30 176 void
matthiasm@620 177 FlexiNoteLayer::setProperty(const PropertyName &name, int value)
Chris@30 178 {
Chris@287 179 if (name == "Vertical Scale") {
Chris@714 180 setVerticalScale(VerticalScale(value));
Chris@100 181 } else if (name == "Scale Units") {
Chris@100 182 if (m_model) {
Chris@100 183 m_model->setScaleUnits
Chris@100 184 (UnitDatabase::getInstance()->getUnitById(value));
Chris@100 185 emit modelChanged();
Chris@100 186 }
Chris@287 187 } else {
Chris@287 188 return SingleColourLayer::setProperty(name, value);
Chris@30 189 }
Chris@30 190 }
Chris@30 191
Chris@30 192 void
matthiasm@620 193 FlexiNoteLayer::setVerticalScale(VerticalScale scale)
Chris@30 194 {
Chris@30 195 if (m_verticalScale == scale) return;
Chris@30 196 m_verticalScale = scale;
Chris@30 197 emit layerParametersChanged();
Chris@30 198 }
Chris@30 199
Chris@30 200 bool
Chris@916 201 FlexiNoteLayer::isLayerScrollable(const LayerGeometryProvider *v) const
Chris@30 202 {
Chris@30 203 QPoint discard;
Chris@44 204 return !v->shouldIlluminateLocalFeatures(this, discard);
Chris@30 205 }
Chris@30 206
Chris@79 207 bool
matthiasm@620 208 FlexiNoteLayer::shouldConvertMIDIToHz() const
Chris@101 209 {
Chris@701 210 QString unit = getScaleUnits();
Chris@101 211 return (unit != "Hz");
Chris@101 212 // if (unit == "" ||
Chris@101 213 // unit.startsWith("MIDI") ||
Chris@101 214 // unit.startsWith("midi")) return true;
Chris@101 215 // return false;
Chris@101 216 }
Chris@101 217
Chris@101 218 bool
Chris@904 219 FlexiNoteLayer::getValueExtents(double &min, double &max,
Chris@714 220 bool &logarithmic, QString &unit) const
Chris@79 221 {
Chris@79 222 if (!m_model) return false;
Chris@79 223 min = m_model->getValueMinimum();
Chris@79 224 max = m_model->getValueMaximum();
Chris@101 225
Chris@105 226 if (shouldConvertMIDIToHz()) {
Chris@105 227 unit = "Hz";
Chris@904 228 min = Pitch::getFrequencyForPitch(int(lrint(min)));
Chris@904 229 max = Pitch::getFrequencyForPitch(int(lrint(max + 1)));
Chris@701 230 } else unit = getScaleUnits();
Chris@101 231
Chris@101 232 if (m_verticalScale == MIDIRangeScale ||
Chris@101 233 m_verticalScale == LogScale) logarithmic = true;
Chris@101 234
Chris@101 235 return true;
Chris@101 236 }
Chris@101 237
Chris@101 238 bool
Chris@904 239 FlexiNoteLayer::getDisplayExtents(double &min, double &max) const
Chris@101 240 {
matthiasm@623 241 if (!m_model || shouldAutoAlign()) {
Chris@695 242 // std::cerr << "No model or shouldAutoAlign()" << std::endl;
gyorgyf@646 243 return false;
gyorgyf@646 244 }
Chris@101 245
Chris@101 246 if (m_verticalScale == MIDIRangeScale) {
Chris@101 247 min = Pitch::getFrequencyForPitch(0);
matthiasm@634 248 max = Pitch::getFrequencyForPitch(127);
Chris@101 249 return true;
Chris@101 250 }
Chris@101 251
Chris@439 252 if (m_scaleMinimum == m_scaleMaximum) {
Chris@455 253 min = m_model->getValueMinimum();
Chris@455 254 max = m_model->getValueMaximum();
Chris@455 255 } else {
Chris@455 256 min = m_scaleMinimum;
Chris@455 257 max = m_scaleMaximum;
Chris@439 258 }
Chris@439 259
Chris@101 260 if (shouldConvertMIDIToHz()) {
Chris@904 261 min = Pitch::getFrequencyForPitch(int(lrint(min)));
Chris@904 262 max = Pitch::getFrequencyForPitch(int(lrint(max + 1)));
Chris@101 263 }
Chris@101 264
Chris@667 265 #ifdef DEBUG_NOTE_LAYER
Chris@682 266 cerr << "NoteLayer::getDisplayExtents: min = " << min << ", max = " << max << " (m_scaleMinimum = " << m_scaleMinimum << ", m_scaleMaximum = " << m_scaleMaximum << ")" << endl;
Chris@667 267 #endif
Chris@667 268
Chris@79 269 return true;
Chris@79 270 }
Chris@79 271
Chris@439 272 bool
Chris@904 273 FlexiNoteLayer::setDisplayExtents(double min, double max)
Chris@439 274 {
Chris@439 275 if (!m_model) return false;
Chris@439 276
Chris@439 277 if (min == max) {
Chris@439 278 if (min == 0.f) {
Chris@439 279 max = 1.f;
Chris@439 280 } else {
Chris@904 281 max = min * 1.0001f;
Chris@439 282 }
Chris@439 283 }
Chris@439 284
Chris@439 285 m_scaleMinimum = min;
Chris@439 286 m_scaleMaximum = max;
Chris@439 287
Chris@667 288 #ifdef DEBUG_NOTE_LAYER
Chris@684 289 cerr << "FlexiNoteLayer::setDisplayExtents: min = " << min << ", max = " << max << endl;
Chris@667 290 #endif
Chris@439 291
Chris@439 292 emit layerParametersChanged();
Chris@439 293 return true;
Chris@439 294 }
Chris@439 295
Chris@439 296 int
matthiasm@620 297 FlexiNoteLayer::getVerticalZoomSteps(int &defaultStep) const
Chris@439 298 {
Chris@439 299 if (shouldAutoAlign()) return 0;
Chris@439 300 if (!m_model) return 0;
Chris@439 301
Chris@439 302 defaultStep = 0;
Chris@439 303 return 100;
Chris@439 304 }
Chris@439 305
Chris@439 306 int
matthiasm@620 307 FlexiNoteLayer::getCurrentVerticalZoomStep() const
Chris@439 308 {
Chris@439 309 if (shouldAutoAlign()) return 0;
Chris@439 310 if (!m_model) return 0;
Chris@439 311
Chris@439 312 RangeMapper *mapper = getNewVerticalZoomRangeMapper();
Chris@439 313 if (!mapper) return 0;
Chris@439 314
Chris@904 315 double dmin, dmax;
Chris@439 316 getDisplayExtents(dmin, dmax);
Chris@439 317
Chris@439 318 int nr = mapper->getPositionForValue(dmax - dmin);
Chris@439 319
Chris@439 320 delete mapper;
Chris@439 321
Chris@439 322 return 100 - nr;
Chris@439 323 }
Chris@439 324
Chris@439 325 //!!! lots of duplication with TimeValueLayer
Chris@439 326
Chris@439 327 void
matthiasm@620 328 FlexiNoteLayer::setVerticalZoomStep(int step)
Chris@439 329 {
Chris@439 330 if (shouldAutoAlign()) return;
Chris@439 331 if (!m_model) return;
Chris@439 332
Chris@439 333 RangeMapper *mapper = getNewVerticalZoomRangeMapper();
Chris@439 334 if (!mapper) return;
Chris@439 335
Chris@904 336 double min, max;
Chris@439 337 bool logarithmic;
Chris@439 338 QString unit;
Chris@439 339 getValueExtents(min, max, logarithmic, unit);
Chris@439 340
Chris@904 341 double dmin, dmax;
Chris@439 342 getDisplayExtents(dmin, dmax);
Chris@439 343
Chris@904 344 double newdist = mapper->getValueForPosition(100 - step);
Chris@439 345
Chris@904 346 double newmin, newmax;
Chris@439 347
Chris@439 348 if (logarithmic) {
Chris@439 349
Chris@439 350 // see SpectrogramLayer::setVerticalZoomStep
Chris@439 351
Chris@904 352 newmax = (newdist + sqrt(newdist*newdist + 4*dmin*dmax)) / 2;
Chris@439 353 newmin = newmax - newdist;
Chris@439 354
Chris@682 355 // cerr << "newmin = " << newmin << ", newmax = " << newmax << endl;
Chris@439 356
Chris@439 357 } else {
Chris@904 358 double dmid = (dmax + dmin) / 2;
Chris@439 359 newmin = dmid - newdist / 2;
Chris@439 360 newmax = dmid + newdist / 2;
Chris@439 361 }
Chris@439 362
Chris@439 363 if (newmin < min) {
Chris@439 364 newmax += (min - newmin);
Chris@439 365 newmin = min;
Chris@439 366 }
Chris@439 367 if (newmax > max) {
Chris@439 368 newmax = max;
Chris@439 369 }
Chris@439 370
Chris@667 371 #ifdef DEBUG_NOTE_LAYER
Chris@684 372 cerr << "FlexiNoteLayer::setVerticalZoomStep: " << step << ": " << newmin << " -> " << newmax << " (range " << newdist << ")" << endl;
Chris@667 373 #endif
Chris@439 374
Chris@439 375 setDisplayExtents(newmin, newmax);
Chris@439 376 }
Chris@439 377
Chris@439 378 RangeMapper *
matthiasm@620 379 FlexiNoteLayer::getNewVerticalZoomRangeMapper() const
Chris@439 380 {
Chris@1408 381 if (!m_model) return nullptr;
Chris@439 382
Chris@439 383 RangeMapper *mapper;
Chris@439 384
Chris@904 385 double min, max;
Chris@439 386 bool logarithmic;
Chris@439 387 QString unit;
Chris@439 388 getValueExtents(min, max, logarithmic, unit);
Chris@439 389
Chris@1408 390 if (min == max) return nullptr;
Chris@439 391
Chris@439 392 if (logarithmic) {
Chris@439 393 mapper = new LogRangeMapper(0, 100, min, max, unit);
Chris@439 394 } else {
Chris@439 395 mapper = new LinearRangeMapper(0, 100, min, max, unit);
Chris@439 396 }
Chris@439 397
Chris@439 398 return mapper;
Chris@439 399 }
Chris@439 400
Chris@1426 401 EventVector
Chris@916 402 FlexiNoteLayer::getLocalPoints(LayerGeometryProvider *v, int x) const
Chris@30 403 {
Chris@1426 404 if (!m_model) return {};
Chris@1426 405
Chris@904 406 sv_frame_t frame = v->getFrameForX(x);
Chris@30 407
Chris@1426 408 EventVector local = m_model->getEventsCovering(frame);
Chris@1426 409 if (!local.empty()) return local;
Chris@30 410
Chris@1426 411 int fuzz = ViewManager::scalePixelSize(2);
Chris@1426 412 sv_frame_t start = v->getFrameForX(x - fuzz);
Chris@1426 413 sv_frame_t end = v->getFrameForX(x + fuzz);
Chris@30 414
Chris@1426 415 local = m_model->getEventsStartingWithin(frame, end - frame);
Chris@1426 416 if (!local.empty()) return local;
Chris@30 417
Chris@1426 418 local = m_model->getEventsSpanning(start, frame - start);
Chris@1426 419 if (!local.empty()) return local;
Chris@30 420
Chris@1426 421 return {};
Chris@30 422 }
Chris@30 423
Chris@550 424 bool
Chris@1426 425 FlexiNoteLayer::getPointToDrag(LayerGeometryProvider *v, int x, int y, Event &point) const
Chris@550 426 {
Chris@550 427 if (!m_model) return false;
Chris@550 428
Chris@904 429 sv_frame_t frame = v->getFrameForX(x);
Chris@550 430
Chris@1426 431 EventVector onPoints = m_model->getEventsCovering(frame);
Chris@550 432 if (onPoints.empty()) return false;
Chris@550 433
Chris@550 434 int nearestDistance = -1;
Chris@1426 435 for (const auto &p: onPoints) {
Chris@1426 436 int distance = getYForValue(v, p.getValue()) - y;
Chris@550 437 if (distance < 0) distance = -distance;
Chris@550 438 if (nearestDistance == -1 || distance < nearestDistance) {
Chris@550 439 nearestDistance = distance;
Chris@1426 440 point = p;
Chris@550 441 }
Chris@550 442 }
Chris@550 443
Chris@550 444 return true;
Chris@550 445 }
Chris@550 446
gyorgyf@646 447 bool
Chris@1426 448 FlexiNoteLayer::getNoteToEdit(LayerGeometryProvider *v, int x, int y, Event &point) const
gyorgyf@646 449 {
gyorgyf@647 450 // GF: find the note that is closest to the cursor
gyorgyf@646 451 if (!m_model) return false;
gyorgyf@646 452
Chris@904 453 sv_frame_t frame = v->getFrameForX(x);
gyorgyf@646 454
Chris@1426 455 EventVector onPoints = m_model->getEventsCovering(frame);
gyorgyf@646 456 if (onPoints.empty()) return false;
gyorgyf@646 457
gyorgyf@646 458 int nearestDistance = -1;
Chris@1426 459 for (const auto &p: onPoints) {
Chris@1426 460 int distance = getYForValue(v, p.getValue()) - y;
gyorgyf@646 461 if (distance < 0) distance = -distance;
gyorgyf@646 462 if (nearestDistance == -1 || distance < nearestDistance) {
gyorgyf@646 463 nearestDistance = distance;
Chris@1426 464 point = p;
gyorgyf@646 465 }
gyorgyf@646 466 }
gyorgyf@646 467
gyorgyf@646 468 return true;
gyorgyf@646 469 }
gyorgyf@646 470
Chris@30 471 QString
Chris@916 472 FlexiNoteLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &pos) const
Chris@30 473 {
Chris@30 474 int x = pos.x();
Chris@30 475
Chris@30 476 if (!m_model || !m_model->getSampleRate()) return "";
Chris@30 477
Chris@1426 478 EventVector points = getLocalPoints(v, x);
Chris@30 479
Chris@30 480 if (points.empty()) {
Chris@714 481 if (!m_model->isReady()) {
Chris@714 482 return tr("In progress");
Chris@714 483 } else {
Chris@714 484 return tr("No local points");
Chris@714 485 }
Chris@30 486 }
Chris@30 487
Chris@1426 488 Event note(0);
Chris@1426 489 EventVector::iterator i;
Chris@30 490
Chris@30 491 for (i = points.begin(); i != points.end(); ++i) {
Chris@30 492
Chris@1426 493 int y = getYForValue(v, i->getValue());
Chris@714 494 int h = NOTE_HEIGHT; // GF: larger notes
Chris@30 495
Chris@714 496 if (m_model->getValueQuantization() != 0.0) {
Chris@1426 497 h = y - getYForValue
Chris@1426 498 (v, i->getValue() + m_model->getValueQuantization());
Chris@714 499 if (h < NOTE_HEIGHT) h = NOTE_HEIGHT;
Chris@714 500 }
Chris@30 501
Chris@714 502 // GF: this is not quite correct
Chris@714 503 if (pos.y() >= y - 4 && pos.y() <= y + h) {
Chris@714 504 note = *i;
Chris@714 505 break;
Chris@714 506 }
Chris@30 507 }
Chris@30 508
Chris@30 509 if (i == points.end()) return tr("No local points");
Chris@30 510
Chris@1426 511 RealTime rt = RealTime::frame2RealTime(note.getFrame(),
Chris@714 512 m_model->getSampleRate());
Chris@1426 513 RealTime rd = RealTime::frame2RealTime(note.getDuration(),
Chris@714 514 m_model->getSampleRate());
Chris@30 515
Chris@101 516 QString pitchText;
Chris@101 517
Chris@101 518 if (shouldConvertMIDIToHz()) {
Chris@101 519
Chris@1426 520 int mnote = int(lrint(note.getValue()));
Chris@1426 521 int cents = int(lrint((note.getValue() - double(mnote)) * 100));
Chris@904 522 double freq = Pitch::getFrequencyForPitch(mnote, cents);
Chris@544 523 pitchText = tr("%1 (%2, %3 Hz)")
Chris@544 524 .arg(Pitch::getPitchLabel(mnote, cents))
Chris@544 525 .arg(mnote)
Chris@544 526 .arg(freq);
Chris@101 527
Chris@701 528 } else if (getScaleUnits() == "Hz") {
Chris@101 529
Chris@544 530 pitchText = tr("%1 Hz (%2, %3)")
Chris@1426 531 .arg(note.getValue())
Chris@1426 532 .arg(Pitch::getPitchLabelForFrequency(note.getValue()))
Chris@1426 533 .arg(Pitch::getPitchForFrequency(note.getValue()));
Chris@101 534
Chris@101 535 } else {
Chris@234 536 pitchText = tr("%1 %2")
Chris@1426 537 .arg(note.getValue()).arg(getScaleUnits());
Chris@101 538 }
Chris@101 539
Chris@30 540 QString text;
Chris@30 541
Chris@1426 542 if (note.getLabel() == "") {
Chris@714 543 text = QString(tr("Time:\t%1\nPitch:\t%2\nDuration:\t%3\nNo label"))
Chris@714 544 .arg(rt.toText(true).c_str())
Chris@714 545 .arg(pitchText)
Chris@714 546 .arg(rd.toText(true).c_str());
Chris@30 547 } else {
Chris@714 548 text = QString(tr("Time:\t%1\nPitch:\t%2\nDuration:\t%3\nLabel:\t%4"))
Chris@714 549 .arg(rt.toText(true).c_str())
Chris@714 550 .arg(pitchText)
Chris@714 551 .arg(rd.toText(true).c_str())
Chris@1426 552 .arg(note.getLabel());
Chris@30 553 }
Chris@30 554
Chris@1426 555 pos = QPoint(v->getXForFrame(note.getFrame()),
Chris@1426 556 getYForValue(v, note.getValue()));
Chris@30 557 return text;
Chris@30 558 }
Chris@30 559
Chris@30 560 bool
Chris@916 561 FlexiNoteLayer::snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame,
Chris@805 562 int &resolution,
Chris@714 563 SnapType snap) const
Chris@30 564 {
Chris@30 565 if (!m_model) {
Chris@714 566 return Layer::snapToFeatureFrame(v, frame, resolution, snap);
Chris@30 567 }
Chris@30 568
Chris@30 569 resolution = m_model->getResolution();
Chris@1426 570 EventVector points;
Chris@30 571
Chris@30 572 if (snap == SnapNeighbouring) {
gyorgyf@646 573
Chris@714 574 points = getLocalPoints(v, v->getXForFrame(frame));
Chris@714 575 if (points.empty()) return false;
Chris@1426 576 frame = points.begin()->getFrame();
Chris@714 577 return true;
Chris@30 578 }
Chris@30 579
Chris@1426 580 points = m_model->getEventsCovering(frame);
Chris@904 581 sv_frame_t snapped = frame;
Chris@30 582 bool found = false;
Chris@30 583
Chris@1426 584 for (EventVector::const_iterator i = points.begin();
Chris@714 585 i != points.end(); ++i) {
Chris@30 586
Chris@714 587 if (snap == SnapRight) {
Chris@30 588
Chris@1426 589 if (i->getFrame() > frame) {
Chris@1426 590 snapped = i->getFrame();
Chris@714 591 found = true;
Chris@714 592 break;
Chris@1426 593 } else if (i->getFrame() + i->getDuration() >= frame) {
Chris@1426 594 snapped = i->getFrame() + i->getDuration();
Chris@715 595 found = true;
Chris@715 596 break;
Chris@714 597 }
Chris@714 598
Chris@714 599 } else if (snap == SnapLeft) {
Chris@714 600
Chris@1426 601 if (i->getFrame() <= frame) {
Chris@1426 602 snapped = i->getFrame();
Chris@714 603 found = true; // don't break, as the next may be better
Chris@714 604 } else {
Chris@714 605 break;
Chris@714 606 }
Chris@714 607
Chris@714 608 } else { // nearest
Chris@714 609
Chris@1426 610 EventVector::const_iterator j = i;
Chris@714 611 ++j;
Chris@714 612
Chris@714 613 if (j == points.end()) {
Chris@714 614
Chris@1426 615 snapped = i->getFrame();
Chris@714 616 found = true;
Chris@714 617 break;
Chris@714 618
Chris@1426 619 } else if (j->getFrame() >= frame) {
Chris@714 620
Chris@1426 621 if (j->getFrame() - frame < frame - i->getFrame()) {
Chris@1426 622 snapped = j->getFrame();
Chris@714 623 } else {
Chris@1426 624 snapped = i->getFrame();
Chris@714 625 }
Chris@714 626 found = true;
Chris@714 627 break;
Chris@714 628 }
gyorgyf@646 629 }
Chris@30 630 }
Chris@30 631
Chris@715 632 cerr << "snapToFeatureFrame: frame " << frame << " -> snapped " << snapped << ", found = " << found << endl;
Chris@715 633
Chris@30 634 frame = snapped;
Chris@30 635 return found;
Chris@30 636 }
Chris@30 637
Chris@101 638 void
Chris@916 639 FlexiNoteLayer::getScaleExtents(LayerGeometryProvider *v, double &min, double &max, bool &log) const
Chris@30 640 {
Chris@101 641 min = 0.0;
Chris@101 642 max = 0.0;
Chris@101 643 log = false;
Chris@42 644
Chris@101 645 QString queryUnits;
Chris@101 646 if (shouldConvertMIDIToHz()) queryUnits = "Hz";
Chris@701 647 else queryUnits = getScaleUnits();
Chris@30 648
Chris@439 649 if (shouldAutoAlign()) {
Chris@30 650
Chris@101 651 if (!v->getValueExtents(queryUnits, min, max, log)) {
Chris@30 652
Chris@101 653 min = m_model->getValueMinimum();
Chris@101 654 max = m_model->getValueMaximum();
Chris@42 655
Chris@101 656 if (shouldConvertMIDIToHz()) {
Chris@904 657 min = Pitch::getFrequencyForPitch(int(lrint(min)));
Chris@904 658 max = Pitch::getFrequencyForPitch(int(lrint(max + 1)));
Chris@101 659 }
Chris@42 660
Chris@667 661 #ifdef DEBUG_NOTE_LAYER
Chris@684 662 cerr << "FlexiNoteLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << endl;
Chris@667 663 #endif
Chris@105 664
Chris@101 665 } else if (log) {
Chris@101 666
Chris@197 667 LogRange::mapRange(min, max);
Chris@105 668
Chris@667 669 #ifdef DEBUG_NOTE_LAYER
Chris@684 670 cerr << "FlexiNoteLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << endl;
Chris@667 671 #endif
Chris@101 672 }
Chris@101 673
Chris@101 674 } else {
Chris@101 675
Chris@439 676 getDisplayExtents(min, max);
Chris@101 677
Chris@101 678 if (m_verticalScale == MIDIRangeScale) {
Chris@101 679 min = Pitch::getFrequencyForPitch(0);
matthiasm@623 680 max = Pitch::getFrequencyForPitch(70);
Chris@101 681 } else if (shouldConvertMIDIToHz()) {
Chris@904 682 min = Pitch::getFrequencyForPitch(int(lrint(min)));
Chris@904 683 max = Pitch::getFrequencyForPitch(int(lrint(max + 1)));
Chris@101 684 }
Chris@101 685
Chris@101 686 if (m_verticalScale == LogScale || m_verticalScale == MIDIRangeScale) {
Chris@197 687 LogRange::mapRange(min, max);
Chris@101 688 log = true;
Chris@101 689 }
Chris@30 690 }
Chris@30 691
Chris@101 692 if (max == min) max = min + 1.0;
Chris@101 693 }
Chris@30 694
Chris@101 695 int
Chris@916 696 FlexiNoteLayer::getYForValue(LayerGeometryProvider *v, double val) const
Chris@101 697 {
Chris@904 698 double min = 0.0, max = 0.0;
Chris@101 699 bool logarithmic = false;
Chris@916 700 int h = v->getPaintHeight();
Chris@101 701
Chris@101 702 getScaleExtents(v, min, max, logarithmic);
Chris@101 703
Chris@667 704 #ifdef DEBUG_NOTE_LAYER
Chris@684 705 cerr << "FlexiNoteLayer[" << this << "]::getYForValue(" << val << "): min = " << min << ", max = " << max << ", log = " << logarithmic << endl;
Chris@667 706 #endif
Chris@101 707
Chris@101 708 if (shouldConvertMIDIToHz()) {
Chris@904 709 val = Pitch::getFrequencyForPitch(int(lrint(val)),
Chris@904 710 int(lrint((val - floor(val)) * 100.0)));
Chris@667 711 #ifdef DEBUG_NOTE_LAYER
Chris@682 712 cerr << "shouldConvertMIDIToHz true, val now = " << val << endl;
Chris@667 713 #endif
Chris@101 714 }
Chris@101 715
Chris@101 716 if (logarithmic) {
Chris@197 717 val = LogRange::map(val);
Chris@667 718 #ifdef DEBUG_NOTE_LAYER
Chris@682 719 cerr << "logarithmic true, val now = " << val << endl;
Chris@667 720 #endif
Chris@101 721 }
Chris@101 722
Chris@101 723 int y = int(h - ((val - min) * h) / (max - min)) - 1;
Chris@667 724 #ifdef DEBUG_NOTE_LAYER
Chris@682 725 cerr << "y = " << y << endl;
Chris@667 726 #endif
Chris@101 727 return y;
Chris@30 728 }
Chris@30 729
Chris@904 730 double
Chris@916 731 FlexiNoteLayer::getValueForY(LayerGeometryProvider *v, int y) const
Chris@30 732 {
Chris@904 733 double min = 0.0, max = 0.0;
Chris@101 734 bool logarithmic = false;
Chris@916 735 int h = v->getPaintHeight();
Chris@30 736
Chris@101 737 getScaleExtents(v, min, max, logarithmic);
Chris@101 738
Chris@904 739 double val = min + (double(h - y) * double(max - min)) / h;
Chris@101 740
Chris@101 741 if (logarithmic) {
Chris@904 742 val = pow(10.f, val);
Chris@101 743 }
Chris@101 744
Chris@101 745 if (shouldConvertMIDIToHz()) {
Chris@101 746 val = Pitch::getPitchForFrequency(val);
Chris@101 747 }
Chris@101 748
Chris@101 749 return val;
Chris@30 750 }
Chris@30 751
Chris@439 752 bool
matthiasm@620 753 FlexiNoteLayer::shouldAutoAlign() const
Chris@439 754 {
Chris@439 755 if (!m_model) return false;
Chris@439 756 return (m_verticalScale == AutoAlignScale);
Chris@439 757 }
Chris@439 758
Chris@30 759 void
Chris@916 760 FlexiNoteLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const
Chris@30 761 {
Chris@30 762 if (!m_model || !m_model->isOK()) return;
Chris@30 763
Chris@904 764 sv_samplerate_t sampleRate = m_model->getSampleRate();
Chris@30 765 if (!sampleRate) return;
Chris@30 766
matthiasm@620 767 // Profiler profiler("FlexiNoteLayer::paint", true);
Chris@30 768
Chris@1426 769 int x0 = rect.left(), x1 = rect.right();
Chris@1426 770 sv_frame_t frame0 = v->getFrameForX(x0);
Chris@904 771 sv_frame_t frame1 = v->getFrameForX(x1);
Chris@30 772
Chris@1426 773 EventVector points(m_model->getEventsSpanning(frame0, frame1 - frame0));
Chris@30 774 if (points.empty()) return;
Chris@30 775
Chris@287 776 paint.setPen(getBaseQColor());
Chris@30 777
Chris@287 778 QColor brushColour(getBaseQColor());
Chris@30 779 brushColour.setAlpha(80);
Chris@30 780
matthiasm@620 781 // SVDEBUG << "FlexiNoteLayer::paint: resolution is "
gyorgyf@646 782 // << m_model->getResolution() << " frames" << endl;
Chris@30 783
Chris@904 784 double min = m_model->getValueMinimum();
Chris@904 785 double max = m_model->getValueMaximum();
Chris@30 786 if (max == min) max = min + 1.0;
Chris@30 787
Chris@30 788 QPoint localPos;
Chris@1426 789 Event illuminatePoint(0);
Chris@551 790 bool shouldIlluminate = false;
Chris@30 791
Chris@44 792 if (v->shouldIlluminateLocalFeatures(this, localPos)) {
Chris@551 793 shouldIlluminate = getPointToDrag(v, localPos.x(), localPos.y(),
Chris@551 794 illuminatePoint);
Chris@30 795 }
Chris@30 796
Chris@30 797 paint.save();
Chris@30 798 paint.setRenderHint(QPainter::Antialiasing, false);
Chris@30 799
matthiasm@819 800 int noteNumber = 0;
matthiasm@819 801
Chris@1426 802 for (EventVector::const_iterator i = points.begin();
Chris@714 803 i != points.end(); ++i) {
Chris@30 804
matthiasm@819 805 ++noteNumber;
Chris@1426 806 const Event &p(*i);
Chris@30 807
Chris@1426 808 int x = v->getXForFrame(p.getFrame());
Chris@1426 809 int y = getYForValue(v, p.getValue());
Chris@1426 810 int w = v->getXForFrame(p.getFrame() + p.getDuration()) - x;
Chris@714 811 int h = NOTE_HEIGHT; //GF: larger notes
gyorgyf@646 812
Chris@714 813 if (m_model->getValueQuantization() != 0.0) {
Chris@1426 814 h = y - getYForValue(v, p.getValue() + m_model->getValueQuantization());
Chris@714 815 if (h < NOTE_HEIGHT) h = NOTE_HEIGHT; //GF: larger notes
Chris@714 816 }
Chris@30 817
Chris@714 818 if (w < 1) w = 1;
Chris@714 819 paint.setPen(getBaseQColor());
Chris@714 820 paint.setBrush(brushColour);
Chris@30 821
Chris@1426 822 if (shouldIlluminate && illuminatePoint == p) {
matthiasm@784 823
Chris@1426 824 paint.drawLine(x, -1, x, v->getPaintHeight() + 1);
Chris@1426 825 paint.drawLine(x+w, -1, x+w, v->getPaintHeight() + 1);
matthiasm@784 826
Chris@1426 827 paint.setPen(v->getForeground());
matthiasm@784 828
Chris@1426 829 QString vlabel = tr("freq: %1%2")
Chris@1426 830 .arg(p.getValue()).arg(m_model->getScaleUnits());
Chris@1426 831 PaintAssistant::drawVisibleText
Chris@1426 832 (v, paint,
Chris@1426 833 x,
Chris@1426 834 y - h/2 - 2 - paint.fontMetrics().height()
Chris@1426 835 - paint.fontMetrics().descent(),
Chris@1426 836 vlabel, PaintAssistant::OutlinedText);
matthiasm@793 837
Chris@1426 838 QString hlabel = tr("dur: %1")
Chris@1426 839 .arg(RealTime::frame2RealTime
Chris@1426 840 (p.getDuration(), m_model->getSampleRate()).toText(true)
Chris@1426 841 .c_str());
Chris@1426 842 PaintAssistant::drawVisibleText
Chris@1426 843 (v, paint,
Chris@1426 844 x,
Chris@1426 845 y - h/2 - paint.fontMetrics().descent() - 2,
Chris@1426 846 hlabel, PaintAssistant::OutlinedText);
matthiasm@793 847
Chris@1426 848 QString llabel = QString("%1").arg(p.getLabel());
Chris@1426 849 PaintAssistant::drawVisibleText
Chris@1426 850 (v, paint,
Chris@1426 851 x,
Chris@1426 852 y + h + 2 + paint.fontMetrics().descent(),
Chris@1426 853 llabel, PaintAssistant::OutlinedText);
Chris@1426 854
Chris@1426 855 QString nlabel = QString("%1").arg(noteNumber);
Chris@1426 856 PaintAssistant::drawVisibleText
Chris@1426 857 (v, paint,
Chris@1426 858 x + paint.fontMetrics().averageCharWidth() / 2,
Chris@1426 859 y + h/2 - paint.fontMetrics().descent(),
Chris@1426 860 nlabel, PaintAssistant::OutlinedText);
matthiasm@784 861 }
gyorgyf@646 862
Chris@714 863 paint.drawRect(x, y - h/2, w, h);
Chris@30 864 }
Chris@30 865
Chris@30 866 paint.restore();
Chris@30 867 }
Chris@30 868
Chris@692 869 int
Chris@916 870 FlexiNoteLayer::getVerticalScaleWidth(LayerGeometryProvider *v, bool, QPainter &paint) const
Chris@692 871 {
Chris@697 872 if (!m_model || shouldAutoAlign()) {
Chris@696 873 return 0;
Chris@701 874 } else {
Chris@701 875 if (m_verticalScale == LogScale || m_verticalScale == MIDIRangeScale) {
Chris@701 876 return LogNumericalScale().getWidth(v, paint) + 10; // for piano
Chris@701 877 } else {
Chris@701 878 return LinearNumericalScale().getWidth(v, paint);
Chris@701 879 }
Chris@696 880 }
Chris@692 881 }
Chris@692 882
Chris@692 883 void
Chris@916 884 FlexiNoteLayer::paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect) const
Chris@692 885 {
Chris@1426 886 if (!m_model || m_model->isEmpty()) return;
Chris@701 887
Chris@701 888 QString unit;
Chris@904 889 double min, max;
Chris@701 890 bool logarithmic;
Chris@701 891
Chris@701 892 int w = getVerticalScaleWidth(v, false, paint);
Chris@916 893 int h = v->getPaintHeight();
Chris@701 894
Chris@701 895 getScaleExtents(v, min, max, logarithmic);
Chris@701 896
Chris@701 897 if (logarithmic) {
Chris@701 898 LogNumericalScale().paintVertical(v, this, paint, 0, min, max);
Chris@696 899 } else {
Chris@701 900 LinearNumericalScale().paintVertical(v, this, paint, 0, min, max);
Chris@701 901 }
Chris@701 902
Chris@701 903 if (logarithmic && (getScaleUnits() == "Hz")) {
Chris@696 904 PianoScale().paintPianoVertical
Chris@701 905 (v, paint, QRect(w - 10, 0, 10, h),
Chris@701 906 LogRange::unmap(min),
Chris@701 907 LogRange::unmap(max));
Chris@701 908 paint.drawLine(w, 0, w, h);
Chris@701 909 }
Chris@701 910
Chris@701 911 if (getScaleUnits() != "") {
Chris@701 912 int mw = w - 5;
Chris@701 913 paint.drawText(5,
Chris@701 914 5 + paint.fontMetrics().ascent(),
Chris@701 915 TextAbbrev::abbreviate(getScaleUnits(),
Chris@701 916 paint.fontMetrics(),
Chris@701 917 mw));
Chris@696 918 }
Chris@692 919 }
Chris@692 920
Chris@30 921 void
Chris@916 922 FlexiNoteLayer::drawStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@30 923 {
matthiasm@620 924 // SVDEBUG << "FlexiNoteLayer::drawStart(" << e->x() << "," << e->y() << ")" << endl;
Chris@30 925
Chris@30 926 if (!m_model) return;
Chris@30 927
Chris@904 928 sv_frame_t frame = v->getFrameForX(e->x());
Chris@30 929 if (frame < 0) frame = 0;
Chris@30 930 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@30 931
Chris@904 932 double value = getValueForY(v, e->y());
Chris@30 933
Chris@1426 934 m_editingPoint = Event(frame, float(value), 0, 0.8f, tr("New Point"));
Chris@30 935 m_originalPoint = m_editingPoint;
Chris@30 936
Chris@376 937 if (m_editingCommand) finish(m_editingCommand);
Chris@1427 938 m_editingCommand = new ChangeEventsCommand(m_model, tr("Draw Point"));
Chris@1426 939 m_editingCommand->add(m_editingPoint);
Chris@30 940
Chris@30 941 m_editing = true;
Chris@30 942 }
Chris@30 943
Chris@30 944 void
Chris@916 945 FlexiNoteLayer::drawDrag(LayerGeometryProvider *v, QMouseEvent *e)
Chris@30 946 {
matthiasm@620 947 // SVDEBUG << "FlexiNoteLayer::drawDrag(" << e->x() << "," << e->y() << ")" << endl;
Chris@30 948
Chris@30 949 if (!m_model || !m_editing) return;
Chris@30 950
Chris@904 951 sv_frame_t frame = v->getFrameForX(e->x());
Chris@30 952 if (frame < 0) frame = 0;
Chris@30 953 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@30 954
Chris@904 955 double newValue = getValueForY(v, e->y());
Chris@101 956
Chris@1426 957 sv_frame_t newFrame = m_editingPoint.getFrame();
Chris@904 958 sv_frame_t newDuration = frame - newFrame;
Chris@101 959 if (newDuration < 0) {
Chris@101 960 newFrame = frame;
Chris@101 961 newDuration = -newDuration;
Chris@101 962 } else if (newDuration == 0) {
Chris@101 963 newDuration = 1;
Chris@101 964 }
Chris@30 965
Chris@1426 966 m_editingCommand->remove(m_editingPoint);
Chris@1426 967 m_editingPoint = m_editingPoint
Chris@1426 968 .withFrame(newFrame)
Chris@1426 969 .withValue(float(newValue))
Chris@1426 970 .withDuration(newDuration);
Chris@1426 971 m_editingCommand->add(m_editingPoint);
Chris@30 972 }
Chris@30 973
Chris@30 974 void
Chris@916 975 FlexiNoteLayer::drawEnd(LayerGeometryProvider *, QMouseEvent *)
Chris@30 976 {
matthiasm@620 977 // SVDEBUG << "FlexiNoteLayer::drawEnd(" << e->x() << "," << e->y() << ")" << endl;
Chris@30 978 if (!m_model || !m_editing) return;
Chris@376 979 finish(m_editingCommand);
Chris@1408 980 m_editingCommand = nullptr;
Chris@30 981 m_editing = false;
Chris@30 982 }
Chris@30 983
Chris@30 984 void
Chris@916 985 FlexiNoteLayer::eraseStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@335 986 {
Chris@335 987 if (!m_model) return;
Chris@335 988
Chris@550 989 if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return;
Chris@335 990
Chris@335 991 if (m_editingCommand) {
Chris@714 992 finish(m_editingCommand);
Chris@1408 993 m_editingCommand = nullptr;
Chris@335 994 }
Chris@335 995
Chris@335 996 m_editing = true;
Chris@335 997 }
Chris@335 998
Chris@335 999 void
Chris@916 1000 FlexiNoteLayer::eraseDrag(LayerGeometryProvider *, QMouseEvent *)
Chris@335 1001 {
Chris@335 1002 }
Chris@335 1003
Chris@335 1004 void
Chris@916 1005 FlexiNoteLayer::eraseEnd(LayerGeometryProvider *v, QMouseEvent *e)
Chris@335 1006 {
Chris@335 1007 if (!m_model || !m_editing) return;
Chris@335 1008
Chris@335 1009 m_editing = false;
Chris@335 1010
Chris@1426 1011 Event p(0);
Chris@550 1012 if (!getPointToDrag(v, e->x(), e->y(), p)) return;
Chris@1426 1013 if (p.getFrame() != m_editingPoint.getFrame() || p.getValue() != m_editingPoint.getValue()) return;
Chris@550 1014
Chris@1427 1015 m_editingCommand = new ChangeEventsCommand(m_model, tr("Erase Point"));
Chris@1426 1016 m_editingCommand->remove(m_editingPoint);
Chris@376 1017 finish(m_editingCommand);
Chris@1408 1018 m_editingCommand = nullptr;
Chris@335 1019 m_editing = false;
Chris@335 1020 }
Chris@335 1021
Chris@335 1022 void
Chris@916 1023 FlexiNoteLayer::editStart(LayerGeometryProvider *v, QMouseEvent *e)
Chris@30 1024 {
matthiasm@620 1025 // SVDEBUG << "FlexiNoteLayer::editStart(" << e->x() << "," << e->y() << ")" << endl;
gyorgyf@635 1026 std::cerr << "FlexiNoteLayer::editStart(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@30 1027
Chris@30 1028 if (!m_model) return;
Chris@30 1029
Chris@550 1030 if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return;
Chris@1426 1031 m_originalPoint = m_editingPoint;
gyorgyf@649 1032
matthiasm@651 1033 if (m_editMode == RightBoundary) {
Chris@1426 1034 m_dragPointX = v->getXForFrame
Chris@1426 1035 (m_editingPoint.getFrame() + m_editingPoint.getDuration());
gyorgyf@649 1036 } else {
Chris@1426 1037 m_dragPointX = v->getXForFrame
Chris@1426 1038 (m_editingPoint.getFrame());
gyorgyf@649 1039 }
Chris@1426 1040 m_dragPointY = getYForValue(v, m_editingPoint.getValue());
Chris@551 1041
Chris@30 1042 if (m_editingCommand) {
Chris@714 1043 finish(m_editingCommand);
Chris@1408 1044 m_editingCommand = nullptr;
Chris@30 1045 }
Chris@30 1046
Chris@30 1047 m_editing = true;
Chris@551 1048 m_dragStartX = e->x();
Chris@551 1049 m_dragStartY = e->y();
matthiasm@651 1050
Chris@1426 1051 sv_frame_t onset = m_originalPoint.getFrame();
Chris@1426 1052 sv_frame_t offset =
Chris@1426 1053 m_originalPoint.getFrame() +
Chris@1426 1054 m_originalPoint.getDuration() - 1;
matthiasm@651 1055
matthiasm@651 1056 m_greatestLeftNeighbourFrame = -1;
Chris@806 1057 m_smallestRightNeighbourFrame = std::numeric_limits<int>::max();
Chris@1426 1058
Chris@1426 1059 EventVector allEvents = m_model->getAllEvents();
matthiasm@651 1060
Chris@1426 1061 for (auto currentNote: allEvents) {
matthiasm@651 1062
matthiasm@651 1063 // left boundary
Chris@1426 1064 if (currentNote.getFrame() + currentNote.getDuration() - 1 < onset) {
Chris@1426 1065 m_greatestLeftNeighbourFrame =
Chris@1426 1066 currentNote.getFrame() + currentNote.getDuration() - 1;
matthiasm@651 1067 }
matthiasm@651 1068
matthiasm@651 1069 // right boundary
Chris@1426 1070 if (currentNote.getFrame() > offset) {
Chris@1426 1071 m_smallestRightNeighbourFrame = currentNote.getFrame();
matthiasm@651 1072 break;
matthiasm@651 1073 }
matthiasm@651 1074 }
Chris@1426 1075
Chris@753 1076 std::cerr << "editStart: mode is " << m_editMode << ", note frame: " << onset << ", left boundary: " << m_greatestLeftNeighbourFrame << ", right boundary: " << m_smallestRightNeighbourFrame << std::endl;
Chris@30 1077 }
Chris@30 1078
Chris@30 1079 void
Chris@916 1080 FlexiNoteLayer::editDrag(LayerGeometryProvider *v, QMouseEvent *e)
Chris@30 1081 {
matthiasm@620 1082 // SVDEBUG << "FlexiNoteLayer::editDrag(" << e->x() << "," << e->y() << ")" << endl;
gyorgyf@635 1083 std::cerr << "FlexiNoteLayer::editDrag(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@30 1084
Chris@30 1085 if (!m_model || !m_editing) return;
Chris@30 1086
Chris@551 1087 int xdist = e->x() - m_dragStartX;
Chris@551 1088 int ydist = e->y() - m_dragStartY;
Chris@551 1089 int newx = m_dragPointX + xdist;
Chris@551 1090 int newy = m_dragPointY + ydist;
Chris@551 1091
Chris@904 1092 sv_frame_t dragFrame = v->getFrameForX(newx);
matthiasm@657 1093 if (dragFrame < 0) dragFrame = 0;
matthiasm@657 1094 dragFrame = dragFrame / m_model->getResolution() * m_model->getResolution();
matthiasm@651 1095
Chris@904 1096 double value = getValueForY(v, newy);
Chris@30 1097
Chris@30 1098 if (!m_editingCommand) {
Chris@1427 1099 m_editingCommand = new ChangeEventsCommand(m_model, tr("Drag Point"));
Chris@30 1100 }
Chris@1426 1101 m_editingCommand->remove(m_editingPoint);
matthiasm@651 1102
Chris@874 1103 std::cerr << "edit mode: " << m_editMode << " intelligent actions = "
Chris@874 1104 << m_intelligentActions << std::endl;
matthiasm@657 1105
matthiasm@651 1106 switch (m_editMode) {
Chris@1426 1107
Chris@714 1108 case LeftBoundary : {
Chris@714 1109 // left
Chris@1426 1110 if (m_intelligentActions &&
Chris@1426 1111 dragFrame <= m_greatestLeftNeighbourFrame) {
Chris@1426 1112 dragFrame = m_greatestLeftNeighbourFrame + 1;
Chris@1426 1113 }
Chris@714 1114 // right
Chris@1426 1115 if (m_intelligentActions &&
Chris@1426 1116 dragFrame >= m_originalPoint.getFrame() + m_originalPoint.getDuration()) {
Chris@1426 1117 dragFrame = m_originalPoint.getFrame() + m_originalPoint.getDuration() - 1;
matthiasm@651 1118 }
Chris@1426 1119 m_editingPoint = m_editingPoint
Chris@1426 1120 .withFrame(dragFrame)
Chris@1426 1121 .withDuration(m_originalPoint.getFrame() -
Chris@1426 1122 dragFrame + m_originalPoint.getDuration());
Chris@714 1123 break;
Chris@714 1124 }
Chris@1426 1125
Chris@714 1126 case RightBoundary : {
Chris@714 1127 // left
Chris@1426 1128 if (m_intelligentActions &&
Chris@1426 1129 dragFrame <= m_greatestLeftNeighbourFrame) {
Chris@1426 1130 dragFrame = m_greatestLeftNeighbourFrame + 1;
Chris@1426 1131 }
Chris@1426 1132 if (m_intelligentActions &&
Chris@1426 1133 dragFrame >= m_smallestRightNeighbourFrame) {
Chris@1426 1134 dragFrame = m_smallestRightNeighbourFrame - 1;
Chris@1426 1135 }
Chris@1426 1136 m_editingPoint = m_editingPoint
Chris@1426 1137 .withDuration(dragFrame - m_originalPoint.getFrame() + 1);
Chris@714 1138 break;
Chris@714 1139 }
Chris@1426 1140
Chris@714 1141 case DragNote : {
Chris@714 1142 // left
Chris@1426 1143 if (m_intelligentActions &&
Chris@1426 1144 dragFrame <= m_greatestLeftNeighbourFrame) {
Chris@1426 1145 dragFrame = m_greatestLeftNeighbourFrame + 1;
Chris@1426 1146 }
Chris@714 1147 // right
Chris@1426 1148 if (m_intelligentActions &&
Chris@1426 1149 dragFrame + m_originalPoint.getDuration() >= m_smallestRightNeighbourFrame) {
Chris@1426 1150 dragFrame = m_smallestRightNeighbourFrame - m_originalPoint.getDuration();
matthiasm@651 1151 }
Chris@1426 1152
Chris@1426 1153 m_editingPoint = m_editingPoint
Chris@1426 1154 .withFrame(dragFrame)
Chris@1426 1155 .withValue(float(value));
Chris@875 1156
Chris@875 1157 // Re-analyse region within +/- 1 semitone of the dragged value
Chris@875 1158 float cents = 0;
Chris@1426 1159 int midiPitch = Pitch::getPitchForFrequency(m_editingPoint.getValue(), &cents);
Chris@922 1160 double lower = Pitch::getFrequencyForPitch(midiPitch - 1, cents);
Chris@922 1161 double higher = Pitch::getFrequencyForPitch(midiPitch + 1, cents);
Chris@875 1162
Chris@1426 1163 emit reAnalyseRegion(m_editingPoint.getFrame(),
Chris@1426 1164 m_editingPoint.getFrame() +
Chris@1426 1165 m_editingPoint.getDuration(),
Chris@922 1166 float(lower), float(higher));
Chris@714 1167 break;
Chris@714 1168 }
Chris@1426 1169
Chris@805 1170 case SplitNote: // nothing
Chris@805 1171 break;
gyorgyf@649 1172 }
Chris@875 1173
Chris@1426 1174 m_editingCommand->add(m_editingPoint);
Chris@875 1175
Chris@1426 1176 std::cerr << "added new point(" << m_editingPoint.getFrame() << "," << m_editingPoint.getDuration() << ")" << std::endl;
Chris@30 1177 }
Chris@30 1178
Chris@30 1179 void
Chris@948 1180 FlexiNoteLayer::editEnd(LayerGeometryProvider *v, QMouseEvent *e)
Chris@30 1181 {
Chris@1426 1182 std::cerr << "FlexiNoteLayer::editEnd("
Chris@1426 1183 << e->x() << "," << e->y() << ")" << std::endl;
matthiasm@656 1184
Chris@30 1185 if (!m_model || !m_editing) return;
Chris@30 1186
Chris@30 1187 if (m_editingCommand) {
Chris@30 1188
Chris@714 1189 QString newName = m_editingCommand->getName();
Chris@30 1190
Chris@876 1191 if (m_editMode == DragNote) {
Chris@876 1192 //!!! command nesting is wrong?
Chris@876 1193 emit materialiseReAnalysis();
Chris@876 1194 }
Chris@876 1195
Chris@1426 1196 m_editingCommand->remove(m_editingPoint);
Chris@875 1197 updateNoteValueFromPitchCurve(v, m_editingPoint);
Chris@1426 1198 m_editingCommand->add(m_editingPoint);
Chris@875 1199
Chris@1426 1200 if (m_editingPoint.getFrame() != m_originalPoint.getFrame()) {
Chris@1426 1201 if (m_editingPoint.getValue() != m_originalPoint.getValue()) {
Chris@714 1202 newName = tr("Edit Point");
Chris@714 1203 } else {
Chris@714 1204 newName = tr("Relocate Point");
Chris@714 1205 }
gyorgyf@646 1206 } else {
Chris@714 1207 newName = tr("Change Point Value");
gyorgyf@646 1208 }
Chris@30 1209
Chris@714 1210 m_editingCommand->setName(newName);
Chris@714 1211 finish(m_editingCommand);
Chris@30 1212 }
Chris@30 1213
Chris@1408 1214 m_editingCommand = nullptr;
Chris@30 1215 m_editing = false;
Chris@30 1216 }
Chris@30 1217
gyorgyf@635 1218 void
Chris@916 1219 FlexiNoteLayer::splitStart(LayerGeometryProvider *v, QMouseEvent *e)
gyorgyf@635 1220 {
gyorgyf@646 1221 // GF: note splitting starts (!! remove printing soon)
Chris@874 1222 std::cerr << "splitStart (n.b. editStart will be called later, if the user drags the mouse)" << std::endl;
gyorgyf@646 1223 if (!m_model) return;
gyorgyf@635 1224
gyorgyf@635 1225 if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return;
gyorgyf@635 1226 // m_originalPoint = m_editingPoint;
gyorgyf@635 1227 //
Chris@1426 1228 // m_dragPointX = v->getXForFrame(m_editingPoint.getFrame());
Chris@1426 1229 // m_dragPointY = getYForValue(v, m_editingPoint.getValue());
gyorgyf@635 1230
gyorgyf@635 1231 if (m_editingCommand) {
Chris@714 1232 finish(m_editingCommand);
Chris@1408 1233 m_editingCommand = nullptr;
gyorgyf@635 1234 }
gyorgyf@635 1235
gyorgyf@635 1236 m_editing = true;
gyorgyf@635 1237 m_dragStartX = e->x();
gyorgyf@635 1238 m_dragStartY = e->y();
gyorgyf@635 1239 }
gyorgyf@635 1240
gyorgyf@635 1241 void
Chris@916 1242 FlexiNoteLayer::splitEnd(LayerGeometryProvider *v, QMouseEvent *e)
gyorgyf@635 1243 {
gyorgyf@646 1244 // GF: note splitting ends. (!! remove printing soon)
gyorgyf@646 1245 std::cerr << "splitEnd" << std::endl;
matthiasm@651 1246 if (!m_model || !m_editing || m_editMode != SplitNote) return;
gyorgyf@635 1247
gyorgyf@635 1248 int xdist = e->x() - m_dragStartX;
gyorgyf@635 1249 int ydist = e->y() - m_dragStartY;
gyorgyf@635 1250 if (xdist != 0 || ydist != 0) {
gyorgyf@646 1251 std::cerr << "mouse moved" << std::endl;
gyorgyf@635 1252 return;
gyorgyf@635 1253 }
gyorgyf@635 1254
Chris@904 1255 sv_frame_t frame = v->getFrameForX(e->x());
gyorgyf@635 1256
Chris@753 1257 splitNotesAt(v, frame, e);
Chris@746 1258 }
Chris@746 1259
Chris@746 1260 void
Chris@916 1261 FlexiNoteLayer::splitNotesAt(LayerGeometryProvider *v, sv_frame_t frame)
Chris@746 1262 {
Chris@1408 1263 splitNotesAt(v, frame, nullptr);
Chris@753 1264 }
Chris@753 1265
Chris@753 1266 void
Chris@916 1267 FlexiNoteLayer::splitNotesAt(LayerGeometryProvider *v, sv_frame_t frame, QMouseEvent *e)
Chris@753 1268 {
Chris@1426 1269 EventVector onPoints = m_model->getEventsCovering(frame);
Chris@746 1270 if (onPoints.empty()) return;
Chris@746 1271
Chris@1426 1272 Event note(*onPoints.begin());
gyorgyf@635 1273
Chris@1427 1274 ChangeEventsCommand *command = new ChangeEventsCommand
gyorgyf@635 1275 (m_model, tr("Edit Point"));
Chris@1426 1276 command->remove(note);
Chris@753 1277
Chris@753 1278 if (!e || !(e->modifiers() & Qt::ShiftModifier)) {
Chris@753 1279
Chris@753 1280 int gap = 0; // MM: I prefer a gap of 0, but we can decide later
Chris@753 1281
Chris@1426 1282 Event newNote1(note.getFrame(), note.getValue(),
Chris@1426 1283 frame - note.getFrame() - gap,
Chris@1426 1284 note.getLevel(), note.getLabel());
Chris@753 1285
Chris@1426 1286 Event newNote2(frame, note.getValue(),
Chris@1426 1287 note.getDuration() - newNote1.getDuration(),
Chris@1426 1288 note.getLevel(), note.getLabel());
Chris@747 1289
Chris@753 1290 if (m_intelligentActions) {
Chris@875 1291 if (updateNoteValueFromPitchCurve(v, newNote1)) {
Chris@1426 1292 command->add(newNote1);
Chris@753 1293 }
Chris@875 1294 if (updateNoteValueFromPitchCurve(v, newNote2)) {
Chris@1426 1295 command->add(newNote2);
Chris@753 1296 }
Chris@753 1297 } else {
Chris@1426 1298 command->add(newNote1);
Chris@1426 1299 command->add(newNote2);
Chris@747 1300 }
Chris@747 1301 }
Chris@746 1302
gyorgyf@635 1303 finish(command);
gyorgyf@646 1304 }
gyorgyf@646 1305
gyorgyf@655 1306 void
Chris@916 1307 FlexiNoteLayer::addNote(LayerGeometryProvider *v, QMouseEvent *e)
matthiasm@660 1308 {
matthiasm@660 1309 std::cerr << "addNote" << std::endl;
matthiasm@660 1310 if (!m_model) return;
matthiasm@660 1311
Chris@904 1312 sv_frame_t duration = 10000;
matthiasm@660 1313
Chris@904 1314 sv_frame_t frame = v->getFrameForX(e->x());
Chris@904 1315 double value = getValueForY(v, e->y());
matthiasm@660 1316
Chris@1426 1317 EventVector noteList = m_model->getAllEvents();
matthiasm@792 1318
matthiasm@660 1319 if (m_intelligentActions) {
Chris@904 1320 sv_frame_t smallestRightNeighbourFrame = 0;
Chris@1426 1321 for (EventVector::const_iterator i = noteList.begin();
matthiasm@792 1322 i != noteList.end(); ++i) {
Chris@1426 1323 Event currentNote = *i;
Chris@1426 1324 if (currentNote.getFrame() > frame) {
Chris@1426 1325 smallestRightNeighbourFrame = currentNote.getFrame();
matthiasm@660 1326 break;
matthiasm@660 1327 }
matthiasm@660 1328 }
matthiasm@792 1329 if (smallestRightNeighbourFrame > 0) {
matthiasm@792 1330 duration = std::min(smallestRightNeighbourFrame - frame + 1, duration);
matthiasm@792 1331 duration = (duration > 0) ? duration : 0;
matthiasm@792 1332 }
matthiasm@660 1333 }
matthiasm@660 1334
matthiasm@660 1335 if (!m_intelligentActions ||
Chris@1426 1336 (m_model->getEventsCovering(frame).empty() && duration > 0)) {
Chris@1426 1337 Event newNote(frame, float(value), duration, 100.f, tr("new note"));
Chris@1427 1338 ChangeEventsCommand *command = new ChangeEventsCommand
Chris@714 1339 (m_model, tr("Add Point"));
Chris@1426 1340 command->add(newNote);
matthiasm@660 1341 finish(command);
matthiasm@660 1342 }
matthiasm@660 1343 }
matthiasm@660 1344
Chris@745 1345 SparseTimeValueModel *
Chris@916 1346 FlexiNoteLayer::getAssociatedPitchModel(LayerGeometryProvider *v) const
Chris@745 1347 {
Chris@745 1348 // Better than we used to do, but still not very satisfactory
Chris@745 1349
Chris@874 1350 // cerr << "FlexiNoteLayer::getAssociatedPitchModel()" << endl;
Chris@746 1351
Chris@918 1352 for (int i = 0; i < v->getView()->getLayerCount(); ++i) {
Chris@918 1353 Layer *layer = v->getView()->getLayer(i);
Chris@795 1354 if (layer &&
Chris@748 1355 layer->getLayerPresentationName() != "candidate") {
Chris@874 1356 // cerr << "FlexiNoteLayer::getAssociatedPitchModel: looks like our layer is " << layer << endl;
Chris@745 1357 SparseTimeValueModel *model = qobject_cast<SparseTimeValueModel *>
Chris@745 1358 (layer->getModel());
Chris@874 1359 // cerr << "FlexiNoteLayer::getAssociatedPitchModel: and its model is " << model << endl;
Chris@745 1360 if (model && model->getScaleUnits() == "Hz") {
Chris@746 1361 cerr << "FlexiNoteLayer::getAssociatedPitchModel: it's good, returning " << model << endl;
Chris@745 1362 return model;
Chris@745 1363 }
Chris@745 1364 }
Chris@745 1365 }
Chris@874 1366 cerr << "FlexiNoteLayer::getAssociatedPitchModel: failed to find a model" << endl;
Chris@1408 1367 return nullptr;
Chris@745 1368 }
matthiasm@660 1369
matthiasm@660 1370 void
Chris@916 1371 FlexiNoteLayer::snapSelectedNotesToPitchTrack(LayerGeometryProvider *v, Selection s)
Chris@746 1372 {
Chris@746 1373 if (!m_model) return;
Chris@746 1374
Chris@1426 1375 EventVector points =
Chris@1426 1376 m_model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
Chris@746 1377
Chris@1427 1378 ChangeEventsCommand *command = new ChangeEventsCommand
Chris@746 1379 (m_model, tr("Snap Notes"));
Chris@746 1380
Chris@746 1381 cerr << "snapSelectedNotesToPitchTrack: selection is from " << s.getStartFrame() << " to " << s.getEndFrame() << endl;
Chris@746 1382
Chris@1426 1383 for (EventVector::iterator i = points.begin();
Chris@746 1384 i != points.end(); ++i) {
Chris@746 1385
Chris@1426 1386 Event note(*i);
Chris@746 1387
Chris@1426 1388 cerr << "snapSelectedNotesToPitchTrack: looking at note from " << note.getFrame() << " to " << note.getFrame() + note.getDuration() << endl;
Chris@746 1389
Chris@1426 1390 if (!s.contains(note.getFrame()) &&
Chris@1426 1391 !s.contains(note.getFrame() + note.getDuration() - 1)) {
Chris@746 1392 continue;
Chris@746 1393 }
Chris@746 1394
matthiasm@773 1395 cerr << "snapSelectedNotesToPitchTrack: making new note" << endl;
Chris@1426 1396 Event newNote(note);
Chris@746 1397
Chris@1426 1398 command->remove(note);
Chris@746 1399
Chris@875 1400 if (updateNoteValueFromPitchCurve(v, newNote)) {
Chris@1426 1401 command->add(newNote);
matthiasm@775 1402 }
Chris@746 1403 }
Chris@747 1404
Chris@746 1405 finish(command);
Chris@746 1406 }
Chris@746 1407
Chris@746 1408 void
Chris@916 1409 FlexiNoteLayer::mergeNotes(LayerGeometryProvider *v, Selection s, bool inclusive)
Chris@747 1410 {
Chris@1426 1411 EventVector points;
Chris@749 1412 if (inclusive) {
Chris@1426 1413 points = m_model->getEventsSpanning(s.getStartFrame(), s.getDuration());
Chris@749 1414 } else {
Chris@1426 1415 points = m_model->getEventsWithin(s.getStartFrame(), s.getDuration());
Chris@747 1416 }
Chris@749 1417
Chris@1426 1418 EventVector::iterator i = points.begin();
Chris@747 1419 if (i == points.end()) return;
Chris@747 1420
Chris@1427 1421 ChangeEventsCommand *command =
Chris@1427 1422 new ChangeEventsCommand(m_model, tr("Merge Notes"));
Chris@747 1423
Chris@1426 1424 Event newNote(*i);
Chris@747 1425
Chris@747 1426 while (i != points.end()) {
Chris@747 1427
Chris@749 1428 if (inclusive) {
Chris@1426 1429 if (i->getFrame() >= s.getEndFrame()) break;
Chris@749 1430 } else {
Chris@1426 1431 if (i->getFrame() + i->getDuration() > s.getEndFrame()) break;
Chris@749 1432 }
Chris@747 1433
Chris@1426 1434 newNote = newNote.withDuration
Chris@1426 1435 (i->getFrame() + i->getDuration() - newNote.getFrame());
Chris@1426 1436 command->remove(*i);
Chris@747 1437
Chris@747 1438 ++i;
Chris@747 1439 }
Chris@747 1440
Chris@875 1441 updateNoteValueFromPitchCurve(v, newNote);
Chris@1426 1442 command->add(newNote);
Chris@747 1443 finish(command);
Chris@747 1444 }
Chris@747 1445
Chris@747 1446 bool
Chris@1426 1447 FlexiNoteLayer::updateNoteValueFromPitchCurve(LayerGeometryProvider *v, Event &note) const
gyorgyf@655 1448 {
Chris@745 1449 SparseTimeValueModel *model = getAssociatedPitchModel(v);
Chris@747 1450 if (!model) return false;
gyorgyf@655 1451
gyorgyf@655 1452 std::cerr << model->getTypeName() << std::endl;
gyorgyf@655 1453
Chris@1429 1454 EventVector dataPoints =
Chris@1429 1455 model->getEventsWithin(note.getFrame(), note.getDuration());
Chris@746 1456
Chris@1426 1457 std::cerr << "frame " << note.getFrame() << ": " << dataPoints.size() << " candidate points" << std::endl;
Chris@746 1458
Chris@747 1459 if (dataPoints.empty()) return false;
Chris@746 1460
Chris@904 1461 std::vector<double> pitchValues;
gyorgyf@655 1462
Chris@1429 1463 for (EventVector::const_iterator i =
Chris@747 1464 dataPoints.begin(); i != dataPoints.end(); ++i) {
Chris@1429 1465 pitchValues.push_back(i->getValue());
gyorgyf@655 1466 }
Chris@747 1467
Chris@747 1468 if (pitchValues.empty()) return false;
Chris@747 1469
gyorgyf@655 1470 sort(pitchValues.begin(), pitchValues.end());
Chris@904 1471 int size = int(pitchValues.size());
gyorgyf@655 1472 double median;
gyorgyf@655 1473
gyorgyf@655 1474 if (size % 2 == 0) {
gyorgyf@655 1475 median = (pitchValues[size/2 - 1] + pitchValues[size/2]) / 2;
gyorgyf@655 1476 } else {
gyorgyf@655 1477 median = pitchValues[size/2];
gyorgyf@655 1478 }
Chris@875 1479
Chris@1426 1480 std::cerr << "updateNoteValueFromPitchCurve: corrected from " << note.getValue() << " to median " << median << std::endl;
gyorgyf@655 1481
Chris@1426 1482 note = note.withValue(float(median));
Chris@747 1483
Chris@747 1484 return true;
gyorgyf@655 1485 }
gyorgyf@655 1486
gyorgyf@646 1487 void
Chris@916 1488 FlexiNoteLayer::mouseMoveEvent(LayerGeometryProvider *v, QMouseEvent *e)
gyorgyf@646 1489 {
gyorgyf@646 1490 // GF: context sensitive cursors
Chris@918 1491 // v->getView()->setCursor(Qt::ArrowCursor);
Chris@1426 1492 Event note(0);
gyorgyf@646 1493 if (!getNoteToEdit(v, e->x(), e->y(), note)) {
Chris@918 1494 // v->getView()->setCursor(Qt::UpArrowCursor);
gyorgyf@646 1495 return;
gyorgyf@646 1496 }
gyorgyf@646 1497
Chris@874 1498 bool closeToLeft = false, closeToRight = false,
Chris@874 1499 closeToTop = false, closeToBottom = false;
Chris@874 1500 getRelativeMousePosition(v, note, e->x(), e->y(),
Chris@874 1501 closeToLeft, closeToRight,
Chris@874 1502 closeToTop, closeToBottom);
gyorgyf@649 1503
Chris@874 1504 if (closeToLeft) {
Chris@945 1505 v->getView()->setCursor(Qt::SizeHorCursor);
Chris@874 1506 m_editMode = LeftBoundary;
Chris@874 1507 cerr << "edit mode -> LeftBoundary" << endl;
Chris@874 1508 } else if (closeToRight) {
Chris@945 1509 v->getView()->setCursor(Qt::SizeHorCursor);
Chris@874 1510 m_editMode = RightBoundary;
Chris@874 1511 cerr << "edit mode -> RightBoundary" << endl;
Chris@874 1512 } else if (closeToTop) {
Chris@945 1513 v->getView()->setCursor(Qt::CrossCursor);
Chris@874 1514 m_editMode = DragNote;
Chris@874 1515 cerr << "edit mode -> DragNote" << endl;
Chris@874 1516 } else if (closeToBottom) {
Chris@945 1517 v->getView()->setCursor(Qt::UpArrowCursor);
Chris@874 1518 m_editMode = SplitNote;
Chris@874 1519 cerr << "edit mode -> SplitNote" << endl;
Chris@874 1520 } else {
Chris@945 1521 v->getView()->setCursor(Qt::ArrowCursor);
Chris@874 1522 }
gyorgyf@646 1523 }
gyorgyf@646 1524
gyorgyf@646 1525 void
Chris@1426 1526 FlexiNoteLayer::getRelativeMousePosition(LayerGeometryProvider *v, Event &note, int x, int y, bool &closeToLeft, bool &closeToRight, bool &closeToTop, bool &closeToBottom) const
gyorgyf@646 1527 {
gyorgyf@649 1528 // GF: TODO: consoloidate the tolerance values
gyorgyf@646 1529 if (!m_model) return;
gyorgyf@646 1530
matthiasm@651 1531 int ctol = 0;
Chris@1426 1532 int noteStartX = v->getXForFrame(note.getFrame());
Chris@1426 1533 int noteEndX = v->getXForFrame(note.getFrame() + note.getDuration());
Chris@1426 1534 int noteValueY = getYForValue(v,note.getValue());
gyorgyf@646 1535 int noteStartY = noteValueY - (NOTE_HEIGHT / 2);
gyorgyf@646 1536 int noteEndY = noteValueY + (NOTE_HEIGHT / 2);
gyorgyf@646 1537
gyorgyf@646 1538 bool closeToNote = false;
gyorgyf@646 1539
gyorgyf@646 1540 if (y >= noteStartY-ctol && y <= noteEndY+ctol && x >= noteStartX-ctol && x <= noteEndX+ctol) closeToNote = true;
gyorgyf@646 1541 if (!closeToNote) return;
gyorgyf@646 1542
matthiasm@651 1543 int tol = NOTE_HEIGHT / 2;
gyorgyf@646 1544
gyorgyf@646 1545 if (x >= noteStartX - tol && x <= noteStartX + tol) closeToLeft = true;
gyorgyf@646 1546 if (x >= noteEndX - tol && x <= noteEndX + tol) closeToRight = true;
gyorgyf@646 1547 if (y >= noteStartY - tol && y <= noteStartY + tol) closeToTop = true;
gyorgyf@646 1548 if (y >= noteEndY - tol && y <= noteEndY + tol) closeToBottom = true;
Chris@688 1549
Chris@688 1550 // cerr << "FlexiNoteLayer::getRelativeMousePosition: close to: left " << closeToLeft << " right " << closeToRight << " top " << closeToTop << " bottom " << closeToBottom << endl;
gyorgyf@635 1551 }
gyorgyf@635 1552
gyorgyf@635 1553
Chris@255 1554 bool
Chris@916 1555 FlexiNoteLayer::editOpen(LayerGeometryProvider *v, QMouseEvent *e)
Chris@70 1556 {
gyorgyf@633 1557 std::cerr << "Opening note editor dialog" << std::endl;
Chris@255 1558 if (!m_model) return false;
Chris@70 1559
Chris@1426 1560 Event note(0);
Chris@550 1561 if (!getPointToDrag(v, e->x(), e->y(), note)) return false;
Chris@550 1562
Chris@1426 1563 // Event note = *points.begin();
Chris@70 1564
Chris@70 1565 ItemEditDialog *dialog = new ItemEditDialog
Chris@70 1566 (m_model->getSampleRate(),
Chris@70 1567 ItemEditDialog::ShowTime |
Chris@70 1568 ItemEditDialog::ShowDuration |
Chris@70 1569 ItemEditDialog::ShowValue |
Chris@100 1570 ItemEditDialog::ShowText,
Chris@701 1571 getScaleUnits());
Chris@70 1572
Chris@1426 1573 dialog->setFrameTime(note.getFrame());
Chris@1426 1574 dialog->setValue(note.getValue());
Chris@1426 1575 dialog->setFrameDuration(note.getDuration());
Chris@1426 1576 dialog->setText(note.getLabel());
Chris@70 1577
Chris@70 1578 if (dialog->exec() == QDialog::Accepted) {
Chris@70 1579
Chris@1426 1580 Event newNote = note
Chris@1426 1581 .withFrame(dialog->getFrameTime())
Chris@1426 1582 .withValue(dialog->getValue())
Chris@1426 1583 .withDuration(dialog->getFrameDuration())
Chris@1426 1584 .withLabel(dialog->getText());
Chris@70 1585
Chris@1427 1586 ChangeEventsCommand *command = new ChangeEventsCommand
Chris@70 1587 (m_model, tr("Edit Point"));
Chris@1426 1588 command->remove(note);
Chris@1426 1589 command->add(newNote);
Chris@376 1590 finish(command);
Chris@70 1591 }
Chris@70 1592
Chris@70 1593 delete dialog;
Chris@255 1594 return true;
Chris@70 1595 }
Chris@70 1596
Chris@70 1597 void
Chris@905 1598 FlexiNoteLayer::moveSelection(Selection s, sv_frame_t newStartFrame)
Chris@43 1599 {
Chris@99 1600 if (!m_model) return;
Chris@99 1601
Chris@1427 1602 ChangeEventsCommand *command =
Chris@1427 1603 new ChangeEventsCommand(m_model, tr("Drag Selection"));
Chris@43 1604
Chris@1426 1605 EventVector points =
Chris@1426 1606 m_model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
Chris@43 1607
Chris@1426 1608 for (Event p: points) {
Chris@1426 1609 command->remove(p);
Chris@1426 1610 Event moved = p.withFrame(p.getFrame() +
Chris@1426 1611 newStartFrame - s.getStartFrame());
Chris@1426 1612 command->add(moved);
Chris@43 1613 }
Chris@43 1614
Chris@376 1615 finish(command);
Chris@43 1616 }
Chris@43 1617
Chris@43 1618 void
matthiasm@620 1619 FlexiNoteLayer::resizeSelection(Selection s, Selection newSize)
Chris@43 1620 {
Chris@1426 1621 if (!m_model || !s.getDuration()) return;
Chris@99 1622
Chris@1427 1623 ChangeEventsCommand *command =
Chris@1427 1624 new ChangeEventsCommand(m_model, tr("Resize Selection"));
Chris@43 1625
Chris@1426 1626 EventVector points =
Chris@1426 1627 m_model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
Chris@43 1628
Chris@1426 1629 double ratio = double(newSize.getDuration()) / double(s.getDuration());
Chris@1426 1630 double oldStart = double(s.getStartFrame());
Chris@1426 1631 double newStart = double(newSize.getStartFrame());
Chris@1426 1632
Chris@1426 1633 for (Event p: points) {
Chris@43 1634
Chris@1426 1635 double newFrame = (double(p.getFrame()) - oldStart) * ratio + newStart;
Chris@1426 1636 double newDuration = double(p.getDuration()) * ratio;
Chris@43 1637
Chris@1426 1638 Event newPoint = p
Chris@1426 1639 .withFrame(lrint(newFrame))
Chris@1426 1640 .withDuration(lrint(newDuration));
Chris@1426 1641 command->remove(p);
Chris@1426 1642 command->add(newPoint);
Chris@43 1643 }
Chris@43 1644
Chris@376 1645 finish(command);
Chris@43 1646 }
Chris@43 1647
Chris@76 1648 void
matthiasm@620 1649 FlexiNoteLayer::deleteSelection(Selection s)
Chris@76 1650 {
Chris@99 1651 if (!m_model) return;
Chris@99 1652
Chris@1427 1653 ChangeEventsCommand *command =
Chris@1427 1654 new ChangeEventsCommand(m_model, tr("Delete Selected Points"));
Chris@76 1655
Chris@1426 1656 EventVector points =
Chris@1426 1657 m_model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
Chris@76 1658
Chris@1426 1659 for (Event p: points) {
Chris@1426 1660 command->remove(p);
Chris@76 1661 }
Chris@76 1662
Chris@376 1663 finish(command);
Chris@76 1664 }
Chris@76 1665
Chris@76 1666 void
matthiasm@784 1667 FlexiNoteLayer::deleteSelectionInclusive(Selection s)
matthiasm@784 1668 {
matthiasm@784 1669 if (!m_model) return;
matthiasm@784 1670
Chris@1427 1671 ChangeEventsCommand *command =
Chris@1427 1672 new ChangeEventsCommand(m_model, tr("Delete Selected Points"));
matthiasm@784 1673
Chris@1426 1674 EventVector points =
Chris@1426 1675 m_model->getEventsSpanning(s.getStartFrame(), s.getDuration());
matthiasm@784 1676
Chris@1426 1677 for (Event p: points) {
Chris@1426 1678 command->remove(p);
matthiasm@784 1679 }
matthiasm@784 1680
matthiasm@784 1681 finish(command);
matthiasm@784 1682 }
matthiasm@784 1683
matthiasm@784 1684 void
Chris@916 1685 FlexiNoteLayer::copy(LayerGeometryProvider *v, Selection s, Clipboard &to)
Chris@76 1686 {
Chris@99 1687 if (!m_model) return;
Chris@99 1688
Chris@1426 1689 EventVector points =
Chris@1426 1690 m_model->getEventsStartingWithin(s.getStartFrame(), s.getDuration());
Chris@76 1691
Chris@1426 1692 for (Event p: points) {
Chris@1426 1693 to.addPoint(p.withReferenceFrame(alignToReference(v, p.getFrame())));
Chris@76 1694 }
Chris@76 1695 }
Chris@76 1696
Chris@125 1697 bool
Chris@916 1698 FlexiNoteLayer::paste(LayerGeometryProvider *v, const Clipboard &from, sv_frame_t /*frameOffset */, bool /* interactive */)
Chris@76 1699 {
Chris@125 1700 if (!m_model) return false;
Chris@99 1701
Chris@1423 1702 const EventVector &points = from.getPoints();
Chris@76 1703
Chris@360 1704 bool realign = false;
Chris@360 1705
Chris@360 1706 if (clipboardHasDifferentAlignment(v, from)) {
Chris@360 1707
Chris@360 1708 QMessageBox::StandardButton button =
Chris@918 1709 QMessageBox::question(v->getView(), tr("Re-align pasted items?"),
Chris@360 1710 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?"),
Chris@360 1711 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
Chris@360 1712 QMessageBox::Yes);
Chris@360 1713
Chris@360 1714 if (button == QMessageBox::Cancel) {
Chris@360 1715 return false;
Chris@360 1716 }
Chris@360 1717
Chris@360 1718 if (button == QMessageBox::Yes) {
Chris@360 1719 realign = true;
Chris@360 1720 }
Chris@360 1721 }
Chris@360 1722
Chris@1427 1723 ChangeEventsCommand *command =
Chris@1427 1724 new ChangeEventsCommand(m_model, tr("Paste"));
Chris@76 1725
Chris@1423 1726 for (EventVector::const_iterator i = points.begin();
Chris@76 1727 i != points.end(); ++i) {
Chris@76 1728
Chris@904 1729 sv_frame_t frame = 0;
Chris@360 1730
Chris@360 1731 if (!realign) {
Chris@360 1732
Chris@360 1733 frame = i->getFrame();
Chris@360 1734
Chris@360 1735 } else {
Chris@360 1736
Chris@1423 1737 if (i->hasReferenceFrame()) {
Chris@360 1738 frame = i->getReferenceFrame();
Chris@360 1739 frame = alignFromReference(v, frame);
Chris@360 1740 } else {
Chris@360 1741 frame = i->getFrame();
Chris@360 1742 }
Chris@76 1743 }
Chris@360 1744
Chris@1426 1745 Event p = *i;
Chris@1426 1746 Event newPoint = p;
Chris@1426 1747 if (!p.hasValue()) {
Chris@1426 1748 newPoint = newPoint.withValue((m_model->getValueMinimum() +
Chris@1426 1749 m_model->getValueMaximum()) / 2);
Chris@1426 1750 }
Chris@1426 1751 if (!p.hasDuration()) {
Chris@904 1752 sv_frame_t nextFrame = frame;
Chris@1423 1753 EventVector::const_iterator j = i;
Chris@125 1754 for (; j != points.end(); ++j) {
Chris@125 1755 if (j != i) break;
Chris@125 1756 }
Chris@125 1757 if (j != points.end()) {
Chris@125 1758 nextFrame = j->getFrame();
Chris@125 1759 }
Chris@125 1760 if (nextFrame == frame) {
Chris@1426 1761 newPoint = newPoint.withDuration(m_model->getResolution());
Chris@125 1762 } else {
Chris@1426 1763 newPoint = newPoint.withDuration(nextFrame - frame);
Chris@125 1764 }
Chris@125 1765 }
Chris@76 1766
Chris@1426 1767 command->add(newPoint);
Chris@76 1768 }
Chris@76 1769
Chris@376 1770 finish(command);
Chris@125 1771 return true;
Chris@76 1772 }
Chris@76 1773
Chris@507 1774 void
Chris@904 1775 FlexiNoteLayer::addNoteOn(sv_frame_t frame, int pitch, int velocity)
Chris@507 1776 {
Chris@1426 1777 m_pendingNoteOns.insert(Event(frame, float(pitch), 0,
Chris@1426 1778 float(velocity / 127.0), ""));
Chris@507 1779 }
Chris@507 1780
Chris@507 1781 void
Chris@904 1782 FlexiNoteLayer::addNoteOff(sv_frame_t frame, int pitch)
Chris@507 1783 {
Chris@1426 1784 for (NoteSet::iterator i = m_pendingNoteOns.begin();
Chris@507 1785 i != m_pendingNoteOns.end(); ++i) {
Chris@1426 1786
Chris@1426 1787 Event p = *i;
Chris@1426 1788
Chris@1426 1789 if (lrintf(p.getValue()) == pitch) {
Chris@507 1790 m_pendingNoteOns.erase(i);
Chris@1426 1791 Event note = p.withDuration(frame - p.getFrame());
Chris@507 1792 if (m_model) {
Chris@1427 1793 ChangeEventsCommand *c = new ChangeEventsCommand
Chris@1426 1794 (m_model, tr("Record Note"));
Chris@1426 1795 c->add(note);
Chris@507 1796 // execute and bundle:
Chris@507 1797 CommandHistory::getInstance()->addCommand(c, true, true);
Chris@507 1798 }
Chris@507 1799 break;
Chris@507 1800 }
Chris@507 1801 }
Chris@507 1802 }
Chris@507 1803
Chris@507 1804 void
matthiasm@620 1805 FlexiNoteLayer::abandonNoteOns()
Chris@507 1806 {
Chris@507 1807 m_pendingNoteOns.clear();
Chris@507 1808 }
Chris@507 1809
Chris@287 1810 int
matthiasm@620 1811 FlexiNoteLayer::getDefaultColourHint(bool darkbg, bool &impose)
Chris@287 1812 {
Chris@287 1813 impose = false;
Chris@287 1814 return ColourDatabase::getInstance()->getColourIndex
Chris@287 1815 (QString(darkbg ? "White" : "Black"));
Chris@287 1816 }
Chris@287 1817
Chris@316 1818 void
matthiasm@620 1819 FlexiNoteLayer::toXml(QTextStream &stream,
Chris@714 1820 QString indent, QString extraAttributes) const
Chris@30 1821 {
Chris@316 1822 SingleColourLayer::toXml(stream, indent, extraAttributes +
Chris@445 1823 QString(" verticalScale=\"%1\" scaleMinimum=\"%2\" scaleMaximum=\"%3\" ")
Chris@445 1824 .arg(m_verticalScale)
Chris@445 1825 .arg(m_scaleMinimum)
Chris@445 1826 .arg(m_scaleMaximum));
Chris@30 1827 }
Chris@30 1828
Chris@30 1829 void
matthiasm@620 1830 FlexiNoteLayer::setProperties(const QXmlAttributes &attributes)
Chris@30 1831 {
Chris@287 1832 SingleColourLayer::setProperties(attributes);
Chris@30 1833
Chris@805 1834 bool ok;
Chris@30 1835 VerticalScale scale = (VerticalScale)
Chris@714 1836 attributes.value("verticalScale").toInt(&ok);
Chris@30 1837 if (ok) setVerticalScale(scale);
Chris@30 1838 }
Chris@30 1839
matthiasm@651 1840 void
Chris@916 1841 FlexiNoteLayer::setVerticalRangeToNoteRange(LayerGeometryProvider *v)
matthiasm@651 1842 {
Chris@904 1843 double minf = std::numeric_limits<double>::max();
Chris@904 1844 double maxf = 0;
matthiasm@656 1845 bool hasNotes = 0;
Chris@1426 1846 EventVector allPoints = m_model->getAllEvents();
Chris@1426 1847 for (EventVector::const_iterator i = allPoints.begin();
Chris@1426 1848 i != allPoints.end(); ++i) {
Chris@714 1849 hasNotes = 1;
Chris@1426 1850 Event note = *i;
Chris@1426 1851 if (note.getValue() < minf) minf = note.getValue();
Chris@1426 1852 if (note.getValue() > maxf) maxf = note.getValue();
matthiasm@651 1853 }
matthiasm@651 1854
matthiasm@656 1855 std::cerr << "min frequency:" << minf << ", max frequency: " << maxf << std::endl;
matthiasm@656 1856
matthiasm@656 1857 if (hasNotes) {
Chris@918 1858 v->getView()->getLayer(1)->setDisplayExtents(minf*0.66,maxf*1.5);
matthiasm@656 1859 // MM: this is a hack because we rely on
matthiasm@656 1860 // * this layer being automatically aligned to layer 1
matthiasm@656 1861 // * layer one is a log frequency layer.
matthiasm@651 1862 }
matthiasm@651 1863 }
Chris@30 1864
matthiasm@651 1865