annotate layer/FlexiNoteLayer.cpp @ 959:2633a1d73e39

Address #1214, layer import produces wrong layer type. We needed a more principled approach to making sure the format gets updated properly and the dialog elements are consistent (basically separating making the dialog elements consistent from subsequently updating the format). This change should provide that, though there may be gotchas still.
author Chris Cannam
date Tue, 12 May 2015 12:31:37 +0100
parents 26da827e8fb5
children bb80983c9e61
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
matthiasm@620 207 FlexiNoteLayer::isLayerScrollable(const View *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
matthiasm@620 408 FlexiNoteLayer::getLocalPoints(View *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
matthiasm@622 451 FlexiNoteLayer::getPointToDrag(View *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
gyorgyf@646 479 FlexiNoteLayer::getNoteToEdit(View *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
matthiasm@620 508 FlexiNoteLayer::getFeatureDescription(View *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@904 596 FlexiNoteLayer::snapToFeatureFrame(View *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@904 676 FlexiNoteLayer::getScaleExtents(View *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@904 733 FlexiNoteLayer::getYForValue(View *v, double val) const
Chris@101 734 {
Chris@904 735 double min = 0.0, max = 0.0;
Chris@101 736 bool logarithmic = false;
Chris@101 737 int h = v->height();
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
matthiasm@620 768 FlexiNoteLayer::getValueForY(View *v, int y) const
Chris@30 769 {
Chris@904 770 double min = 0.0, max = 0.0;
Chris@101 771 bool logarithmic = false;
Chris@44 772 int h = v->height();
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
matthiasm@620 797 FlexiNoteLayer::paint(View *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
matthiasm@784 863 paint.drawLine(x, -1, x, v->height() + 1);
matthiasm@784 864 paint.drawLine(x+w, -1, x+w, v->height() + 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@702 907 FlexiNoteLayer::getVerticalScaleWidth(View *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@695 921 FlexiNoteLayer::paintVerticalScale(View *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@701 930 int h = v->height();
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
matthiasm@620 959 FlexiNoteLayer::drawStart(View *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
matthiasm@620 983 FlexiNoteLayer::drawDrag(View *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
matthiasm@620 1012 FlexiNoteLayer::drawEnd(View *, 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
matthiasm@620 1022 FlexiNoteLayer::eraseStart(View *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@805 1037 FlexiNoteLayer::eraseDrag(View *, QMouseEvent *)
Chris@335 1038 {
Chris@335 1039 }
Chris@335 1040
Chris@335 1041 void
matthiasm@620 1042 FlexiNoteLayer::eraseEnd(View *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
matthiasm@620 1062 FlexiNoteLayer::editStart(View *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
matthiasm@620 1113 FlexiNoteLayer::editDrag(View *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@875 1193 FlexiNoteLayer::editEnd(View *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
gyorgyf@635 1232 FlexiNoteLayer::splitStart(View *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
gyorgyf@635 1255 FlexiNoteLayer::splitEnd(View *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@904 1274 FlexiNoteLayer::splitNotesAt(View *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@904 1280 FlexiNoteLayer::splitNotesAt(View *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
matthiasm@660 1320 FlexiNoteLayer::addNote(View *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@745 1359 FlexiNoteLayer::getAssociatedPitchModel(View *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@745 1365 for (int i = 0; i < v->getLayerCount(); ++i) {
Chris@745 1366 Layer *layer = v->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@746 1384 FlexiNoteLayer::snapSelectedNotesToPitchTrack(View *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@749 1422 FlexiNoteLayer::mergeNotes(View *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@875 1465 FlexiNoteLayer::updateNoteValueFromPitchCurve(View *v,
Chris@875 1466 FlexiNoteModel::Point &note) const
gyorgyf@655 1467 {
Chris@745 1468 SparseTimeValueModel *model = getAssociatedPitchModel(v);
Chris@747 1469 if (!model) return false;
gyorgyf@655 1470
gyorgyf@655 1471 std::cerr << model->getTypeName() << std::endl;
gyorgyf@655 1472
Chris@747 1473 SparseModel<TimeValuePoint>::PointList dataPoints =
Chris@747 1474 model->getPoints(note.frame, note.frame + note.duration);
Chris@746 1475
Chris@746 1476 std::cerr << "frame " << note.frame << ": " << dataPoints.size() << " candidate points" << std::endl;
Chris@746 1477
Chris@747 1478 if (dataPoints.empty()) return false;
Chris@746 1479
Chris@904 1480 std::vector<double> pitchValues;
gyorgyf@655 1481
Chris@747 1482 for (SparseModel<TimeValuePoint>::PointList::const_iterator i =
Chris@747 1483 dataPoints.begin(); i != dataPoints.end(); ++i) {
Chris@747 1484 if (i->frame >= note.frame &&
Chris@747 1485 i->frame < note.frame + note.duration) {
Chris@748 1486 pitchValues.push_back(i->value);
Chris@747 1487 }
gyorgyf@655 1488 }
Chris@747 1489
Chris@747 1490 if (pitchValues.empty()) return false;
Chris@747 1491
gyorgyf@655 1492 sort(pitchValues.begin(), pitchValues.end());
Chris@904 1493 int size = int(pitchValues.size());
gyorgyf@655 1494 double median;
gyorgyf@655 1495
gyorgyf@655 1496 if (size % 2 == 0) {
gyorgyf@655 1497 median = (pitchValues[size/2 - 1] + pitchValues[size/2]) / 2;
gyorgyf@655 1498 } else {
gyorgyf@655 1499 median = pitchValues[size/2];
gyorgyf@655 1500 }
Chris@875 1501
Chris@875 1502 std::cerr << "updateNoteValueFromPitchCurve: corrected from " << note.value << " to median " << median << std::endl;
gyorgyf@655 1503
Chris@904 1504 note.value = float(median);
Chris@747 1505
Chris@747 1506 return true;
gyorgyf@655 1507 }
gyorgyf@655 1508
gyorgyf@646 1509 void
gyorgyf@646 1510 FlexiNoteLayer::mouseMoveEvent(View *v, QMouseEvent *e)
gyorgyf@646 1511 {
gyorgyf@646 1512 // GF: context sensitive cursors
gyorgyf@646 1513 // v->setCursor(Qt::ArrowCursor);
gyorgyf@646 1514 FlexiNoteModel::Point note(0);
gyorgyf@646 1515 if (!getNoteToEdit(v, e->x(), e->y(), note)) {
gyorgyf@646 1516 // v->setCursor(Qt::UpArrowCursor);
gyorgyf@646 1517 return;
gyorgyf@646 1518 }
gyorgyf@646 1519
Chris@874 1520 bool closeToLeft = false, closeToRight = false,
Chris@874 1521 closeToTop = false, closeToBottom = false;
Chris@874 1522 getRelativeMousePosition(v, note, e->x(), e->y(),
Chris@874 1523 closeToLeft, closeToRight,
Chris@874 1524 closeToTop, closeToBottom);
gyorgyf@649 1525
Chris@874 1526 if (closeToLeft) {
Chris@874 1527 v->setCursor(Qt::SizeHorCursor);
Chris@874 1528 m_editMode = LeftBoundary;
Chris@874 1529 cerr << "edit mode -> LeftBoundary" << endl;
Chris@874 1530 } else if (closeToRight) {
Chris@874 1531 v->setCursor(Qt::SizeHorCursor);
Chris@874 1532 m_editMode = RightBoundary;
Chris@874 1533 cerr << "edit mode -> RightBoundary" << endl;
Chris@874 1534 } else if (closeToTop) {
Chris@874 1535 v->setCursor(Qt::CrossCursor);
Chris@874 1536 m_editMode = DragNote;
Chris@874 1537 cerr << "edit mode -> DragNote" << endl;
Chris@874 1538 } else if (closeToBottom) {
Chris@874 1539 v->setCursor(Qt::UpArrowCursor);
Chris@874 1540 m_editMode = SplitNote;
Chris@874 1541 cerr << "edit mode -> SplitNote" << endl;
Chris@874 1542 } else {
Chris@874 1543 v->setCursor(Qt::ArrowCursor);
Chris@874 1544 }
gyorgyf@646 1545 }
gyorgyf@646 1546
gyorgyf@646 1547 void
gyorgyf@646 1548 FlexiNoteLayer::getRelativeMousePosition(View *v, FlexiNoteModel::Point &note, int x, int y, bool &closeToLeft, bool &closeToRight, bool &closeToTop, bool &closeToBottom) const
gyorgyf@646 1549 {
gyorgyf@649 1550 // GF: TODO: consoloidate the tolerance values
gyorgyf@646 1551 if (!m_model) return;
gyorgyf@646 1552
matthiasm@651 1553 int ctol = 0;
gyorgyf@646 1554 int noteStartX = v->getXForFrame(note.frame);
gyorgyf@646 1555 int noteEndX = v->getXForFrame(note.frame + note.duration);
gyorgyf@646 1556 int noteValueY = getYForValue(v,note.value);
gyorgyf@646 1557 int noteStartY = noteValueY - (NOTE_HEIGHT / 2);
gyorgyf@646 1558 int noteEndY = noteValueY + (NOTE_HEIGHT / 2);
gyorgyf@646 1559
gyorgyf@646 1560 bool closeToNote = false;
gyorgyf@646 1561
gyorgyf@646 1562 if (y >= noteStartY-ctol && y <= noteEndY+ctol && x >= noteStartX-ctol && x <= noteEndX+ctol) closeToNote = true;
gyorgyf@646 1563 if (!closeToNote) return;
gyorgyf@646 1564
matthiasm@651 1565 int tol = NOTE_HEIGHT / 2;
gyorgyf@646 1566
gyorgyf@646 1567 if (x >= noteStartX - tol && x <= noteStartX + tol) closeToLeft = true;
gyorgyf@646 1568 if (x >= noteEndX - tol && x <= noteEndX + tol) closeToRight = true;
gyorgyf@646 1569 if (y >= noteStartY - tol && y <= noteStartY + tol) closeToTop = true;
gyorgyf@646 1570 if (y >= noteEndY - tol && y <= noteEndY + tol) closeToBottom = true;
Chris@688 1571
Chris@688 1572 // cerr << "FlexiNoteLayer::getRelativeMousePosition: close to: left " << closeToLeft << " right " << closeToRight << " top " << closeToTop << " bottom " << closeToBottom << endl;
gyorgyf@635 1573 }
gyorgyf@635 1574
gyorgyf@635 1575
Chris@255 1576 bool
matthiasm@620 1577 FlexiNoteLayer::editOpen(View *v, QMouseEvent *e)
Chris@70 1578 {
gyorgyf@633 1579 std::cerr << "Opening note editor dialog" << std::endl;
Chris@255 1580 if (!m_model) return false;
Chris@70 1581
matthiasm@620 1582 FlexiNoteModel::Point note(0);
Chris@550 1583 if (!getPointToDrag(v, e->x(), e->y(), note)) return false;
Chris@550 1584
matthiasm@620 1585 // FlexiNoteModel::Point note = *points.begin();
Chris@70 1586
Chris@70 1587 ItemEditDialog *dialog = new ItemEditDialog
Chris@70 1588 (m_model->getSampleRate(),
Chris@70 1589 ItemEditDialog::ShowTime |
Chris@70 1590 ItemEditDialog::ShowDuration |
Chris@70 1591 ItemEditDialog::ShowValue |
Chris@100 1592 ItemEditDialog::ShowText,
Chris@701 1593 getScaleUnits());
Chris@70 1594
Chris@70 1595 dialog->setFrameTime(note.frame);
Chris@70 1596 dialog->setValue(note.value);
Chris@70 1597 dialog->setFrameDuration(note.duration);
Chris@70 1598 dialog->setText(note.label);
Chris@70 1599
Chris@70 1600 if (dialog->exec() == QDialog::Accepted) {
Chris@70 1601
matthiasm@620 1602 FlexiNoteModel::Point newNote = note;
Chris@70 1603 newNote.frame = dialog->getFrameTime();
Chris@70 1604 newNote.value = dialog->getValue();
Chris@70 1605 newNote.duration = dialog->getFrameDuration();
Chris@70 1606 newNote.label = dialog->getText();
Chris@70 1607
matthiasm@620 1608 FlexiNoteModel::EditCommand *command = new FlexiNoteModel::EditCommand
Chris@70 1609 (m_model, tr("Edit Point"));
Chris@70 1610 command->deletePoint(note);
Chris@70 1611 command->addPoint(newNote);
Chris@376 1612 finish(command);
Chris@70 1613 }
Chris@70 1614
Chris@70 1615 delete dialog;
Chris@255 1616 return true;
Chris@70 1617 }
Chris@70 1618
Chris@70 1619 void
Chris@905 1620 FlexiNoteLayer::moveSelection(Selection s, sv_frame_t newStartFrame)
Chris@43 1621 {
Chris@99 1622 if (!m_model) return;
Chris@99 1623
matthiasm@620 1624 FlexiNoteModel::EditCommand *command =
Chris@714 1625 new FlexiNoteModel::EditCommand(m_model, tr("Drag Selection"));
Chris@43 1626
matthiasm@620 1627 FlexiNoteModel::PointList points =
Chris@714 1628 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@43 1629
matthiasm@620 1630 for (FlexiNoteModel::PointList::iterator i = points.begin();
Chris@714 1631 i != points.end(); ++i) {
Chris@43 1632
Chris@714 1633 if (s.contains(i->frame)) {
Chris@714 1634 FlexiNoteModel::Point newPoint(*i);
Chris@714 1635 newPoint.frame = i->frame + newStartFrame - s.getStartFrame();
Chris@714 1636 command->deletePoint(*i);
Chris@714 1637 command->addPoint(newPoint);
Chris@714 1638 }
Chris@43 1639 }
Chris@43 1640
Chris@376 1641 finish(command);
Chris@43 1642 }
Chris@43 1643
Chris@43 1644 void
matthiasm@620 1645 FlexiNoteLayer::resizeSelection(Selection s, Selection newSize)
Chris@43 1646 {
Chris@99 1647 if (!m_model) return;
Chris@99 1648
matthiasm@620 1649 FlexiNoteModel::EditCommand *command =
Chris@714 1650 new FlexiNoteModel::EditCommand(m_model, tr("Resize Selection"));
Chris@43 1651
matthiasm@620 1652 FlexiNoteModel::PointList points =
Chris@714 1653 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@43 1654
Chris@43 1655 double ratio =
Chris@714 1656 double(newSize.getEndFrame() - newSize.getStartFrame()) /
Chris@714 1657 double(s.getEndFrame() - s.getStartFrame());
Chris@43 1658
matthiasm@620 1659 for (FlexiNoteModel::PointList::iterator i = points.begin();
Chris@714 1660 i != points.end(); ++i) {
Chris@43 1661
Chris@714 1662 if (s.contains(i->frame)) {
Chris@43 1663
Chris@904 1664 double targetStart = double(i->frame);
Chris@904 1665 targetStart = double(newSize.getStartFrame()) +
Chris@904 1666 targetStart - double(s.getStartFrame()) * ratio;
Chris@43 1667
Chris@904 1668 double targetEnd = double(i->frame + i->duration);
Chris@904 1669 targetEnd = double(newSize.getStartFrame()) +
Chris@904 1670 targetEnd - double(s.getStartFrame()) * ratio;
Chris@43 1671
Chris@714 1672 FlexiNoteModel::Point newPoint(*i);
Chris@714 1673 newPoint.frame = lrint(targetStart);
Chris@714 1674 newPoint.duration = lrint(targetEnd - targetStart);
Chris@714 1675 command->deletePoint(*i);
Chris@714 1676 command->addPoint(newPoint);
Chris@714 1677 }
Chris@43 1678 }
Chris@43 1679
Chris@376 1680 finish(command);
Chris@43 1681 }
Chris@43 1682
Chris@76 1683 void
matthiasm@620 1684 FlexiNoteLayer::deleteSelection(Selection s)
Chris@76 1685 {
Chris@99 1686 if (!m_model) return;
Chris@99 1687
matthiasm@620 1688 FlexiNoteModel::EditCommand *command =
Chris@714 1689 new FlexiNoteModel::EditCommand(m_model, tr("Delete Selected Points"));
Chris@76 1690
matthiasm@620 1691 FlexiNoteModel::PointList points =
Chris@714 1692 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@76 1693
matthiasm@620 1694 for (FlexiNoteModel::PointList::iterator i = points.begin();
Chris@714 1695 i != points.end(); ++i) {
Chris@76 1696
Chris@76 1697 if (s.contains(i->frame)) {
Chris@76 1698 command->deletePoint(*i);
Chris@76 1699 }
Chris@76 1700 }
Chris@76 1701
Chris@376 1702 finish(command);
Chris@76 1703 }
Chris@76 1704
Chris@76 1705 void
matthiasm@784 1706 FlexiNoteLayer::deleteSelectionInclusive(Selection s)
matthiasm@784 1707 {
matthiasm@784 1708 if (!m_model) return;
matthiasm@784 1709
matthiasm@784 1710 FlexiNoteModel::EditCommand *command =
matthiasm@784 1711 new FlexiNoteModel::EditCommand(m_model, tr("Delete Selected Points"));
matthiasm@784 1712
matthiasm@784 1713 FlexiNoteModel::PointList points =
matthiasm@784 1714 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
matthiasm@784 1715
matthiasm@784 1716 for (FlexiNoteModel::PointList::iterator i = points.begin();
matthiasm@784 1717 i != points.end(); ++i) {
matthiasm@796 1718 bool overlap = !(
matthiasm@799 1719 ((s.getStartFrame() <= i->frame) && (s.getEndFrame() <= i->frame)) || // selection is left of note
matthiasm@799 1720 ((s.getStartFrame() >= (i->frame+i->duration)) && (s.getEndFrame() >= (i->frame+i->duration))) // selection is right of note
matthiasm@796 1721 );
matthiasm@784 1722 if (overlap) {
matthiasm@784 1723 command->deletePoint(*i);
matthiasm@784 1724 }
matthiasm@784 1725 }
matthiasm@784 1726
matthiasm@784 1727 finish(command);
matthiasm@784 1728 }
matthiasm@784 1729
matthiasm@784 1730 void
matthiasm@620 1731 FlexiNoteLayer::copy(View *v, Selection s, Clipboard &to)
Chris@76 1732 {
Chris@99 1733 if (!m_model) return;
Chris@99 1734
matthiasm@620 1735 FlexiNoteModel::PointList points =
Chris@714 1736 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@76 1737
matthiasm@620 1738 for (FlexiNoteModel::PointList::iterator i = points.begin();
Chris@714 1739 i != points.end(); ++i) {
Chris@714 1740 if (s.contains(i->frame)) {
Chris@335 1741 Clipboard::Point point(i->frame, i->value, i->duration, i->level, i->label);
Chris@360 1742 point.setReferenceFrame(alignToReference(v, i->frame));
Chris@76 1743 to.addPoint(point);
Chris@76 1744 }
Chris@76 1745 }
Chris@76 1746 }
Chris@76 1747
Chris@125 1748 bool
Chris@904 1749 FlexiNoteLayer::paste(View *v, const Clipboard &from, sv_frame_t /*frameOffset */, bool /* interactive */)
Chris@76 1750 {
Chris@125 1751 if (!m_model) return false;
Chris@99 1752
Chris@76 1753 const Clipboard::PointList &points = from.getPoints();
Chris@76 1754
Chris@360 1755 bool realign = false;
Chris@360 1756
Chris@360 1757 if (clipboardHasDifferentAlignment(v, from)) {
Chris@360 1758
Chris@360 1759 QMessageBox::StandardButton button =
Chris@360 1760 QMessageBox::question(v, tr("Re-align pasted items?"),
Chris@360 1761 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 1762 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
Chris@360 1763 QMessageBox::Yes);
Chris@360 1764
Chris@360 1765 if (button == QMessageBox::Cancel) {
Chris@360 1766 return false;
Chris@360 1767 }
Chris@360 1768
Chris@360 1769 if (button == QMessageBox::Yes) {
Chris@360 1770 realign = true;
Chris@360 1771 }
Chris@360 1772 }
Chris@360 1773
matthiasm@620 1774 FlexiNoteModel::EditCommand *command =
Chris@714 1775 new FlexiNoteModel::EditCommand(m_model, tr("Paste"));
Chris@76 1776
Chris@76 1777 for (Clipboard::PointList::const_iterator i = points.begin();
Chris@76 1778 i != points.end(); ++i) {
Chris@76 1779
Chris@76 1780 if (!i->haveFrame()) continue;
Chris@904 1781 sv_frame_t frame = 0;
Chris@360 1782
Chris@360 1783 if (!realign) {
Chris@360 1784
Chris@360 1785 frame = i->getFrame();
Chris@360 1786
Chris@360 1787 } else {
Chris@360 1788
Chris@360 1789 if (i->haveReferenceFrame()) {
Chris@360 1790 frame = i->getReferenceFrame();
Chris@360 1791 frame = alignFromReference(v, frame);
Chris@360 1792 } else {
Chris@360 1793 frame = i->getFrame();
Chris@360 1794 }
Chris@76 1795 }
Chris@360 1796
matthiasm@620 1797 FlexiNoteModel::Point newPoint(frame);
Chris@76 1798
Chris@76 1799 if (i->haveLabel()) newPoint.label = i->getLabel();
Chris@76 1800 if (i->haveValue()) newPoint.value = i->getValue();
Chris@76 1801 else newPoint.value = (m_model->getValueMinimum() +
Chris@76 1802 m_model->getValueMaximum()) / 2;
Chris@335 1803 if (i->haveLevel()) newPoint.level = i->getLevel();
Chris@76 1804 if (i->haveDuration()) newPoint.duration = i->getDuration();
Chris@125 1805 else {
Chris@904 1806 sv_frame_t nextFrame = frame;
Chris@125 1807 Clipboard::PointList::const_iterator j = i;
Chris@125 1808 for (; j != points.end(); ++j) {
Chris@125 1809 if (!j->haveFrame()) continue;
Chris@125 1810 if (j != i) break;
Chris@125 1811 }
Chris@125 1812 if (j != points.end()) {
Chris@125 1813 nextFrame = j->getFrame();
Chris@125 1814 }
Chris@125 1815 if (nextFrame == frame) {
Chris@125 1816 newPoint.duration = m_model->getResolution();
Chris@125 1817 } else {
Chris@125 1818 newPoint.duration = nextFrame - frame;
Chris@125 1819 }
Chris@125 1820 }
Chris@76 1821
Chris@76 1822 command->addPoint(newPoint);
Chris@76 1823 }
Chris@76 1824
Chris@376 1825 finish(command);
Chris@125 1826 return true;
Chris@76 1827 }
Chris@76 1828
Chris@507 1829 void
Chris@904 1830 FlexiNoteLayer::addNoteOn(sv_frame_t frame, int pitch, int velocity)
Chris@507 1831 {
Chris@904 1832 m_pendingNoteOns.insert(FlexiNote(frame, float(pitch), 0, float(velocity / 127.0), ""));
Chris@507 1833 }
Chris@507 1834
Chris@507 1835 void
Chris@904 1836 FlexiNoteLayer::addNoteOff(sv_frame_t frame, int pitch)
Chris@507 1837 {
matthiasm@620 1838 for (FlexiNoteSet::iterator i = m_pendingNoteOns.begin();
Chris@507 1839 i != m_pendingNoteOns.end(); ++i) {
Chris@904 1840 if (lrint((*i).value) == pitch) {
matthiasm@620 1841 FlexiNote note(*i);
Chris@507 1842 m_pendingNoteOns.erase(i);
Chris@507 1843 note.duration = frame - note.frame;
Chris@507 1844 if (m_model) {
matthiasm@620 1845 FlexiNoteModel::AddPointCommand *c = new FlexiNoteModel::AddPointCommand
matthiasm@620 1846 (m_model, note, tr("Record FlexiNote"));
Chris@507 1847 // execute and bundle:
Chris@507 1848 CommandHistory::getInstance()->addCommand(c, true, true);
Chris@507 1849 }
Chris@507 1850 break;
Chris@507 1851 }
Chris@507 1852 }
Chris@507 1853 }
Chris@507 1854
Chris@507 1855 void
matthiasm@620 1856 FlexiNoteLayer::abandonNoteOns()
Chris@507 1857 {
Chris@507 1858 m_pendingNoteOns.clear();
Chris@507 1859 }
Chris@507 1860
Chris@287 1861 int
matthiasm@620 1862 FlexiNoteLayer::getDefaultColourHint(bool darkbg, bool &impose)
Chris@287 1863 {
Chris@287 1864 impose = false;
Chris@287 1865 return ColourDatabase::getInstance()->getColourIndex
Chris@287 1866 (QString(darkbg ? "White" : "Black"));
Chris@287 1867 }
Chris@287 1868
Chris@316 1869 void
matthiasm@620 1870 FlexiNoteLayer::toXml(QTextStream &stream,
Chris@714 1871 QString indent, QString extraAttributes) const
Chris@30 1872 {
Chris@316 1873 SingleColourLayer::toXml(stream, indent, extraAttributes +
Chris@445 1874 QString(" verticalScale=\"%1\" scaleMinimum=\"%2\" scaleMaximum=\"%3\" ")
Chris@445 1875 .arg(m_verticalScale)
Chris@445 1876 .arg(m_scaleMinimum)
Chris@445 1877 .arg(m_scaleMaximum));
Chris@30 1878 }
Chris@30 1879
Chris@30 1880 void
matthiasm@620 1881 FlexiNoteLayer::setProperties(const QXmlAttributes &attributes)
Chris@30 1882 {
Chris@287 1883 SingleColourLayer::setProperties(attributes);
Chris@30 1884
Chris@805 1885 bool ok;
Chris@30 1886 VerticalScale scale = (VerticalScale)
Chris@714 1887 attributes.value("verticalScale").toInt(&ok);
Chris@30 1888 if (ok) setVerticalScale(scale);
Chris@445 1889
Chris@805 1890 // bool alsoOk;
Chris@904 1891 // double min = attributes.value("scaleMinimum").toDouble(&ok);
Chris@904 1892 // double max = attributes.value("scaleMaximum").toDouble(&alsoOk);
Chris@670 1893 // if (ok && alsoOk && min != max) setDisplayExtents(min, max);
Chris@30 1894 }
Chris@30 1895
matthiasm@651 1896 void
matthiasm@656 1897 FlexiNoteLayer::setVerticalRangeToNoteRange(View *v)
matthiasm@651 1898 {
Chris@904 1899 double minf = std::numeric_limits<double>::max();
Chris@904 1900 double maxf = 0;
matthiasm@656 1901 bool hasNotes = 0;
matthiasm@651 1902 for (FlexiNoteModel::PointList::const_iterator i = m_model->getPoints().begin();
matthiasm@651 1903 i != m_model->getPoints().end(); ++i) {
Chris@714 1904 hasNotes = 1;
Chris@714 1905 FlexiNote note = *i;
Chris@714 1906 if (note.value < minf) minf = note.value;
Chris@714 1907 if (note.value > maxf) maxf = note.value;
matthiasm@651 1908 }
matthiasm@651 1909
matthiasm@656 1910 std::cerr << "min frequency:" << minf << ", max frequency: " << maxf << std::endl;
matthiasm@656 1911
matthiasm@656 1912 if (hasNotes) {
matthiasm@659 1913 v->getLayer(1)->setDisplayExtents(minf*0.66,maxf*1.5);
matthiasm@656 1914 // MM: this is a hack because we rely on
matthiasm@656 1915 // * this layer being automatically aligned to layer 1
matthiasm@656 1916 // * layer one is a log frequency layer.
matthiasm@651 1917 }
matthiasm@651 1918 }
Chris@30 1919
matthiasm@651 1920