annotate layer/FlexiNoteLayer.cpp @ 1024:3bce4c45b681 spectrogram-minor-refactor

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