annotate layer/FlexiNoteLayer.cpp @ 1077:5144d7185fb5 spectrogram-minor-refactor

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