annotate layer/FlexiNoteLayer.cpp @ 1374:631897ba9fca zoom

Merge from default branch
author Chris Cannam
date Tue, 06 Nov 2018 08:59:03 +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