annotate layer/FlexiNoteLayer.cpp @ 789:9fd1bdf214dd tonioni

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