annotate layer/FlexiNoteLayer.cpp @ 1447:8afea53332f3 single-point

Add option to make pane sizes auto-resize-only (i.e. remove user control via a splitter); also place alignment views above panes instead of below, meaning the extra bit of space that we currently have for the pane without one at least goes to the primary pane
author Chris Cannam
date Tue, 30 Apr 2019 15:53:21 +0100
parents 8a7c82282fbc
children 11a150e65ee1
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