annotate layer/FlexiNoteLayer.cpp @ 1204:d421df27e184 3.0-integration

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