annotate layer/NoteLayer.cpp @ 561:aced8ec09bc8

* Complete the overhaul of CSV file import; now you can pick the purpose for each column in the file, and SV should do the rest. The most significant practical improvement here is that we can now handle files in which time and duration do not necessarily appear in known columns.
author Chris Cannam
date Mon, 19 Jul 2010 17:08:56 +0000
parents c2ba2796cbee
children f4960f8ce798
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
Chris@30 16 #include "NoteLayer.h"
Chris@30 17
Chris@128 18 #include "data/model/Model.h"
Chris@30 19 #include "base/RealTime.h"
Chris@30 20 #include "base/Profiler.h"
Chris@30 21 #include "base/Pitch.h"
Chris@197 22 #include "base/LogRange.h"
Chris@439 23 #include "base/RangeMapper.h"
Chris@376 24 #include "ColourDatabase.h"
Chris@128 25 #include "view/View.h"
Chris@30 26
Chris@128 27 #include "data/model/NoteModel.h"
Chris@30 28
Chris@70 29 #include "widgets/ItemEditDialog.h"
Chris@70 30
Chris@30 31 #include <QPainter>
Chris@30 32 #include <QPainterPath>
Chris@30 33 #include <QMouseEvent>
Chris@316 34 #include <QTextStream>
Chris@360 35 #include <QMessageBox>
Chris@30 36
Chris@30 37 #include <iostream>
Chris@30 38 #include <cmath>
Chris@551 39 #include <utility>
Chris@30 40
Chris@44 41 NoteLayer::NoteLayer() :
Chris@287 42 SingleColourLayer(),
Chris@30 43 m_model(0),
Chris@30 44 m_editing(false),
Chris@335 45 m_originalPoint(0, 0.0, 0, 1.f, tr("New Point")),
Chris@335 46 m_editingPoint(0, 0.0, 0, 1.f, tr("New Point")),
Chris@30 47 m_editingCommand(0),
Chris@439 48 m_verticalScale(AutoAlignScale),
Chris@439 49 m_scaleMinimum(0),
Chris@439 50 m_scaleMaximum(0)
Chris@30 51 {
Chris@44 52
Chris@30 53 }
Chris@30 54
Chris@30 55 void
Chris@30 56 NoteLayer::setModel(NoteModel *model)
Chris@30 57 {
Chris@30 58 if (m_model == model) return;
Chris@30 59 m_model = model;
Chris@30 60
Chris@320 61 connectSignals(m_model);
Chris@30 62
Chris@101 63 // std::cerr << "NoteLayer::setModel(" << model << ")" << std::endl;
Chris@30 64
Chris@439 65 m_scaleMinimum = 0;
Chris@439 66 m_scaleMaximum = 0;
Chris@439 67
Chris@30 68 emit modelReplaced();
Chris@30 69 }
Chris@30 70
Chris@30 71 Layer::PropertyList
Chris@30 72 NoteLayer::getProperties() const
Chris@30 73 {
Chris@287 74 PropertyList list = SingleColourLayer::getProperties();
Chris@87 75 list.push_back("Vertical Scale");
Chris@100 76 list.push_back("Scale Units");
Chris@30 77 return list;
Chris@30 78 }
Chris@30 79
Chris@87 80 QString
Chris@87 81 NoteLayer::getPropertyLabel(const PropertyName &name) const
Chris@87 82 {
Chris@87 83 if (name == "Vertical Scale") return tr("Vertical Scale");
Chris@116 84 if (name == "Scale Units") return tr("Scale Units");
Chris@287 85 return SingleColourLayer::getPropertyLabel(name);
Chris@87 86 }
Chris@87 87
Chris@30 88 Layer::PropertyType
Chris@100 89 NoteLayer::getPropertyType(const PropertyName &name) const
Chris@30 90 {
Chris@100 91 if (name == "Scale Units") return UnitsProperty;
Chris@287 92 if (name == "Vertical Scale") return ValueProperty;
Chris@287 93 return SingleColourLayer::getPropertyType(name);
Chris@30 94 }
Chris@30 95
Chris@198 96 QString
Chris@198 97 NoteLayer::getPropertyGroupName(const PropertyName &name) const
Chris@198 98 {
Chris@198 99 if (name == "Vertical Scale" || name == "Scale Units") {
Chris@198 100 return tr("Scale");
Chris@198 101 }
Chris@287 102 return SingleColourLayer::getPropertyGroupName(name);
Chris@198 103 }
Chris@198 104
Chris@30 105 int
Chris@30 106 NoteLayer::getPropertyRangeAndValue(const PropertyName &name,
Chris@216 107 int *min, int *max, int *deflt) const
Chris@30 108 {
Chris@216 109 int val = 0;
Chris@30 110
Chris@287 111 if (name == "Vertical Scale") {
Chris@30 112
Chris@30 113 if (min) *min = 0;
Chris@101 114 if (max) *max = 3;
Chris@216 115 if (deflt) *deflt = int(AutoAlignScale);
Chris@30 116
Chris@216 117 val = int(m_verticalScale);
Chris@30 118
Chris@100 119 } else if (name == "Scale Units") {
Chris@100 120
Chris@216 121 if (deflt) *deflt = 0;
Chris@100 122 if (m_model) {
Chris@216 123 val = UnitDatabase::getInstance()->getUnitId
Chris@100 124 (m_model->getScaleUnits());
Chris@100 125 }
Chris@100 126
Chris@30 127 } else {
Chris@216 128
Chris@287 129 val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt);
Chris@30 130 }
Chris@30 131
Chris@216 132 return val;
Chris@30 133 }
Chris@30 134
Chris@30 135 QString
Chris@30 136 NoteLayer::getPropertyValueLabel(const PropertyName &name,
Chris@287 137 int value) const
Chris@30 138 {
Chris@287 139 if (name == "Vertical Scale") {
Chris@30 140 switch (value) {
Chris@30 141 default:
Chris@101 142 case 0: return tr("Auto-Align");
Chris@198 143 case 1: return tr("Linear");
Chris@198 144 case 2: return tr("Log");
Chris@198 145 case 3: return tr("MIDI Notes");
Chris@30 146 }
Chris@30 147 }
Chris@287 148 return SingleColourLayer::getPropertyValueLabel(name, value);
Chris@30 149 }
Chris@30 150
Chris@30 151 void
Chris@30 152 NoteLayer::setProperty(const PropertyName &name, int value)
Chris@30 153 {
Chris@287 154 if (name == "Vertical Scale") {
Chris@30 155 setVerticalScale(VerticalScale(value));
Chris@100 156 } else if (name == "Scale Units") {
Chris@100 157 if (m_model) {
Chris@100 158 m_model->setScaleUnits
Chris@100 159 (UnitDatabase::getInstance()->getUnitById(value));
Chris@100 160 emit modelChanged();
Chris@100 161 }
Chris@287 162 } else {
Chris@287 163 return SingleColourLayer::setProperty(name, value);
Chris@30 164 }
Chris@30 165 }
Chris@30 166
Chris@30 167 void
Chris@30 168 NoteLayer::setVerticalScale(VerticalScale scale)
Chris@30 169 {
Chris@30 170 if (m_verticalScale == scale) return;
Chris@30 171 m_verticalScale = scale;
Chris@30 172 emit layerParametersChanged();
Chris@30 173 }
Chris@30 174
Chris@30 175 bool
Chris@44 176 NoteLayer::isLayerScrollable(const View *v) const
Chris@30 177 {
Chris@30 178 QPoint discard;
Chris@44 179 return !v->shouldIlluminateLocalFeatures(this, discard);
Chris@30 180 }
Chris@30 181
Chris@79 182 bool
Chris@101 183 NoteLayer::shouldConvertMIDIToHz() const
Chris@101 184 {
Chris@101 185 QString unit = m_model->getScaleUnits();
Chris@101 186 return (unit != "Hz");
Chris@101 187 // if (unit == "" ||
Chris@101 188 // unit.startsWith("MIDI") ||
Chris@101 189 // unit.startsWith("midi")) return true;
Chris@101 190 // return false;
Chris@101 191 }
Chris@101 192
Chris@101 193 bool
Chris@101 194 NoteLayer::getValueExtents(float &min, float &max,
Chris@101 195 bool &logarithmic, QString &unit) const
Chris@79 196 {
Chris@79 197 if (!m_model) return false;
Chris@79 198 min = m_model->getValueMinimum();
Chris@79 199 max = m_model->getValueMaximum();
Chris@101 200
Chris@105 201 if (shouldConvertMIDIToHz()) {
Chris@105 202 unit = "Hz";
Chris@105 203 min = Pitch::getFrequencyForPitch(lrintf(min));
Chris@105 204 max = Pitch::getFrequencyForPitch(lrintf(max + 1));
Chris@105 205 } else unit = m_model->getScaleUnits();
Chris@101 206
Chris@101 207 if (m_verticalScale == MIDIRangeScale ||
Chris@101 208 m_verticalScale == LogScale) logarithmic = true;
Chris@101 209
Chris@101 210 return true;
Chris@101 211 }
Chris@101 212
Chris@101 213 bool
Chris@101 214 NoteLayer::getDisplayExtents(float &min, float &max) const
Chris@101 215 {
Chris@439 216 if (!m_model || shouldAutoAlign()) return false;
Chris@101 217
Chris@101 218 if (m_verticalScale == MIDIRangeScale) {
Chris@101 219 min = Pitch::getFrequencyForPitch(0);
Chris@101 220 max = Pitch::getFrequencyForPitch(127);
Chris@101 221 return true;
Chris@101 222 }
Chris@101 223
Chris@439 224 if (m_scaleMinimum == m_scaleMaximum) {
Chris@455 225 min = m_model->getValueMinimum();
Chris@455 226 max = m_model->getValueMaximum();
Chris@455 227 } else {
Chris@455 228 min = m_scaleMinimum;
Chris@455 229 max = m_scaleMaximum;
Chris@439 230 }
Chris@439 231
Chris@101 232 if (shouldConvertMIDIToHz()) {
Chris@101 233 min = Pitch::getFrequencyForPitch(lrintf(min));
Chris@101 234 max = Pitch::getFrequencyForPitch(lrintf(max + 1));
Chris@101 235 }
Chris@101 236
Chris@79 237 return true;
Chris@79 238 }
Chris@79 239
Chris@439 240 bool
Chris@439 241 NoteLayer::setDisplayExtents(float min, float max)
Chris@439 242 {
Chris@439 243 if (!m_model) return false;
Chris@439 244
Chris@439 245 if (min == max) {
Chris@439 246 if (min == 0.f) {
Chris@439 247 max = 1.f;
Chris@439 248 } else {
Chris@439 249 max = min * 1.0001;
Chris@439 250 }
Chris@439 251 }
Chris@439 252
Chris@439 253 m_scaleMinimum = min;
Chris@439 254 m_scaleMaximum = max;
Chris@439 255
Chris@439 256 // std::cerr << "NoteLayer::setDisplayExtents: min = " << min << ", max = " << max << std::endl;
Chris@439 257
Chris@439 258 emit layerParametersChanged();
Chris@439 259 return true;
Chris@439 260 }
Chris@439 261
Chris@439 262 int
Chris@439 263 NoteLayer::getVerticalZoomSteps(int &defaultStep) const
Chris@439 264 {
Chris@439 265 if (shouldAutoAlign()) return 0;
Chris@439 266 if (!m_model) return 0;
Chris@439 267
Chris@439 268 defaultStep = 0;
Chris@439 269 return 100;
Chris@439 270 }
Chris@439 271
Chris@439 272 int
Chris@439 273 NoteLayer::getCurrentVerticalZoomStep() const
Chris@439 274 {
Chris@439 275 if (shouldAutoAlign()) return 0;
Chris@439 276 if (!m_model) return 0;
Chris@439 277
Chris@439 278 RangeMapper *mapper = getNewVerticalZoomRangeMapper();
Chris@439 279 if (!mapper) return 0;
Chris@439 280
Chris@439 281 float dmin, dmax;
Chris@439 282 getDisplayExtents(dmin, dmax);
Chris@439 283
Chris@439 284 int nr = mapper->getPositionForValue(dmax - dmin);
Chris@439 285
Chris@439 286 delete mapper;
Chris@439 287
Chris@439 288 return 100 - nr;
Chris@439 289 }
Chris@439 290
Chris@439 291 //!!! lots of duplication with TimeValueLayer
Chris@439 292
Chris@439 293 void
Chris@439 294 NoteLayer::setVerticalZoomStep(int step)
Chris@439 295 {
Chris@439 296 if (shouldAutoAlign()) return;
Chris@439 297 if (!m_model) return;
Chris@439 298
Chris@439 299 RangeMapper *mapper = getNewVerticalZoomRangeMapper();
Chris@439 300 if (!mapper) return;
Chris@439 301
Chris@439 302 float min, max;
Chris@439 303 bool logarithmic;
Chris@439 304 QString unit;
Chris@439 305 getValueExtents(min, max, logarithmic, unit);
Chris@439 306
Chris@439 307 float dmin, dmax;
Chris@439 308 getDisplayExtents(dmin, dmax);
Chris@439 309
Chris@439 310 float newdist = mapper->getValueForPosition(100 - step);
Chris@439 311
Chris@439 312 float newmin, newmax;
Chris@439 313
Chris@439 314 if (logarithmic) {
Chris@439 315
Chris@439 316 // see SpectrogramLayer::setVerticalZoomStep
Chris@439 317
Chris@439 318 newmax = (newdist + sqrtf(newdist*newdist + 4*dmin*dmax)) / 2;
Chris@439 319 newmin = newmax - newdist;
Chris@439 320
Chris@439 321 // std::cerr << "newmin = " << newmin << ", newmax = " << newmax << std::endl;
Chris@439 322
Chris@439 323 } else {
Chris@439 324 float dmid = (dmax + dmin) / 2;
Chris@439 325 newmin = dmid - newdist / 2;
Chris@439 326 newmax = dmid + newdist / 2;
Chris@439 327 }
Chris@439 328
Chris@439 329 if (newmin < min) {
Chris@439 330 newmax += (min - newmin);
Chris@439 331 newmin = min;
Chris@439 332 }
Chris@439 333 if (newmax > max) {
Chris@439 334 newmax = max;
Chris@439 335 }
Chris@439 336
Chris@439 337 std::cerr << "NoteLayer::setVerticalZoomStep: " << step << ": " << newmin << " -> " << newmax << " (range " << newdist << ")" << std::endl;
Chris@439 338
Chris@439 339 setDisplayExtents(newmin, newmax);
Chris@439 340 }
Chris@439 341
Chris@439 342 RangeMapper *
Chris@439 343 NoteLayer::getNewVerticalZoomRangeMapper() const
Chris@439 344 {
Chris@439 345 if (!m_model) return 0;
Chris@439 346
Chris@439 347 RangeMapper *mapper;
Chris@439 348
Chris@439 349 float min, max;
Chris@439 350 bool logarithmic;
Chris@439 351 QString unit;
Chris@439 352 getValueExtents(min, max, logarithmic, unit);
Chris@439 353
Chris@439 354 if (min == max) return 0;
Chris@439 355
Chris@439 356 if (logarithmic) {
Chris@439 357 mapper = new LogRangeMapper(0, 100, min, max, unit);
Chris@439 358 } else {
Chris@439 359 mapper = new LinearRangeMapper(0, 100, min, max, unit);
Chris@439 360 }
Chris@439 361
Chris@439 362 return mapper;
Chris@439 363 }
Chris@439 364
Chris@30 365 NoteModel::PointList
Chris@44 366 NoteLayer::getLocalPoints(View *v, int x) const
Chris@30 367 {
Chris@30 368 if (!m_model) return NoteModel::PointList();
Chris@30 369
Chris@44 370 long frame = v->getFrameForX(x);
Chris@30 371
Chris@30 372 NoteModel::PointList onPoints =
Chris@30 373 m_model->getPoints(frame);
Chris@30 374
Chris@30 375 if (!onPoints.empty()) {
Chris@30 376 return onPoints;
Chris@30 377 }
Chris@30 378
Chris@30 379 NoteModel::PointList prevPoints =
Chris@30 380 m_model->getPreviousPoints(frame);
Chris@30 381 NoteModel::PointList nextPoints =
Chris@30 382 m_model->getNextPoints(frame);
Chris@30 383
Chris@30 384 NoteModel::PointList usePoints = prevPoints;
Chris@30 385
Chris@30 386 if (prevPoints.empty()) {
Chris@30 387 usePoints = nextPoints;
Chris@248 388 } else if (long(prevPoints.begin()->frame) < v->getStartFrame() &&
Chris@44 389 !(nextPoints.begin()->frame > v->getEndFrame())) {
Chris@30 390 usePoints = nextPoints;
Chris@248 391 } else if (long(nextPoints.begin()->frame) - frame <
Chris@248 392 frame - long(prevPoints.begin()->frame)) {
Chris@30 393 usePoints = nextPoints;
Chris@30 394 }
Chris@30 395
Chris@30 396 if (!usePoints.empty()) {
Chris@30 397 int fuzz = 2;
Chris@44 398 int px = v->getXForFrame(usePoints.begin()->frame);
Chris@30 399 if ((px > x && px - x > fuzz) ||
Chris@30 400 (px < x && x - px > fuzz + 1)) {
Chris@30 401 usePoints.clear();
Chris@30 402 }
Chris@30 403 }
Chris@30 404
Chris@30 405 return usePoints;
Chris@30 406 }
Chris@30 407
Chris@550 408 bool
Chris@550 409 NoteLayer::getPointToDrag(View *v, int x, int y, NoteModel::Point &p) const
Chris@550 410 {
Chris@550 411 if (!m_model) return false;
Chris@550 412
Chris@550 413 long frame = v->getFrameForX(x);
Chris@550 414
Chris@550 415 NoteModel::PointList onPoints = m_model->getPoints(frame);
Chris@550 416 if (onPoints.empty()) return false;
Chris@550 417
Chris@551 418 // std::cerr << "frame " << frame << ": " << onPoints.size() << " candidate points" << std::endl;
Chris@550 419
Chris@550 420 int nearestDistance = -1;
Chris@550 421
Chris@550 422 for (NoteModel::PointList::const_iterator i = onPoints.begin();
Chris@550 423 i != onPoints.end(); ++i) {
Chris@550 424
Chris@550 425 int distance = getYForValue(v, (*i).value) - y;
Chris@550 426 if (distance < 0) distance = -distance;
Chris@550 427 if (nearestDistance == -1 || distance < nearestDistance) {
Chris@550 428 nearestDistance = distance;
Chris@550 429 p = *i;
Chris@550 430 }
Chris@550 431 }
Chris@550 432
Chris@550 433 return true;
Chris@550 434 }
Chris@550 435
Chris@30 436 QString
Chris@44 437 NoteLayer::getFeatureDescription(View *v, QPoint &pos) const
Chris@30 438 {
Chris@30 439 int x = pos.x();
Chris@30 440
Chris@30 441 if (!m_model || !m_model->getSampleRate()) return "";
Chris@30 442
Chris@44 443 NoteModel::PointList points = getLocalPoints(v, x);
Chris@30 444
Chris@30 445 if (points.empty()) {
Chris@30 446 if (!m_model->isReady()) {
Chris@30 447 return tr("In progress");
Chris@30 448 } else {
Chris@30 449 return tr("No local points");
Chris@30 450 }
Chris@30 451 }
Chris@30 452
Chris@30 453 Note note(0);
Chris@30 454 NoteModel::PointList::iterator i;
Chris@30 455
Chris@30 456 for (i = points.begin(); i != points.end(); ++i) {
Chris@30 457
Chris@44 458 int y = getYForValue(v, i->value);
Chris@30 459 int h = 3;
Chris@30 460
Chris@30 461 if (m_model->getValueQuantization() != 0.0) {
Chris@44 462 h = y - getYForValue(v, i->value + m_model->getValueQuantization());
Chris@30 463 if (h < 3) h = 3;
Chris@30 464 }
Chris@30 465
Chris@30 466 if (pos.y() >= y - h && pos.y() <= y) {
Chris@30 467 note = *i;
Chris@30 468 break;
Chris@30 469 }
Chris@30 470 }
Chris@30 471
Chris@30 472 if (i == points.end()) return tr("No local points");
Chris@30 473
Chris@30 474 RealTime rt = RealTime::frame2RealTime(note.frame,
Chris@30 475 m_model->getSampleRate());
Chris@30 476 RealTime rd = RealTime::frame2RealTime(note.duration,
Chris@30 477 m_model->getSampleRate());
Chris@30 478
Chris@101 479 QString pitchText;
Chris@101 480
Chris@101 481 if (shouldConvertMIDIToHz()) {
Chris@101 482
Chris@101 483 int mnote = lrintf(note.value);
Chris@101 484 int cents = lrintf((note.value - mnote) * 100);
Chris@101 485 float freq = Pitch::getFrequencyForPitch(mnote, cents);
Chris@544 486 pitchText = tr("%1 (%2, %3 Hz)")
Chris@544 487 .arg(Pitch::getPitchLabel(mnote, cents))
Chris@544 488 .arg(mnote)
Chris@544 489 .arg(freq);
Chris@101 490
Chris@101 491 } else if (m_model->getScaleUnits() == "Hz") {
Chris@101 492
Chris@544 493 pitchText = tr("%1 Hz (%2, %3)")
Chris@101 494 .arg(note.value)
Chris@544 495 .arg(Pitch::getPitchLabelForFrequency(note.value))
Chris@544 496 .arg(Pitch::getPitchForFrequency(note.value));
Chris@101 497
Chris@101 498 } else {
Chris@234 499 pitchText = tr("%1 %2")
Chris@101 500 .arg(note.value).arg(m_model->getScaleUnits());
Chris@101 501 }
Chris@101 502
Chris@30 503 QString text;
Chris@30 504
Chris@30 505 if (note.label == "") {
Chris@30 506 text = QString(tr("Time:\t%1\nPitch:\t%2\nDuration:\t%3\nNo label"))
Chris@30 507 .arg(rt.toText(true).c_str())
Chris@101 508 .arg(pitchText)
Chris@30 509 .arg(rd.toText(true).c_str());
Chris@30 510 } else {
Chris@30 511 text = QString(tr("Time:\t%1\nPitch:\t%2\nDuration:\t%3\nLabel:\t%4"))
Chris@30 512 .arg(rt.toText(true).c_str())
Chris@101 513 .arg(pitchText)
Chris@30 514 .arg(rd.toText(true).c_str())
Chris@30 515 .arg(note.label);
Chris@30 516 }
Chris@30 517
Chris@44 518 pos = QPoint(v->getXForFrame(note.frame),
Chris@44 519 getYForValue(v, note.value));
Chris@30 520 return text;
Chris@30 521 }
Chris@30 522
Chris@30 523 bool
Chris@44 524 NoteLayer::snapToFeatureFrame(View *v, int &frame,
Chris@44 525 size_t &resolution,
Chris@44 526 SnapType snap) const
Chris@30 527 {
Chris@30 528 if (!m_model) {
Chris@44 529 return Layer::snapToFeatureFrame(v, frame, resolution, snap);
Chris@30 530 }
Chris@30 531
Chris@30 532 resolution = m_model->getResolution();
Chris@30 533 NoteModel::PointList points;
Chris@30 534
Chris@30 535 if (snap == SnapNeighbouring) {
Chris@30 536
Chris@44 537 points = getLocalPoints(v, v->getXForFrame(frame));
Chris@30 538 if (points.empty()) return false;
Chris@30 539 frame = points.begin()->frame;
Chris@30 540 return true;
Chris@30 541 }
Chris@30 542
Chris@30 543 points = m_model->getPoints(frame, frame);
Chris@30 544 int snapped = frame;
Chris@30 545 bool found = false;
Chris@30 546
Chris@30 547 for (NoteModel::PointList::const_iterator i = points.begin();
Chris@30 548 i != points.end(); ++i) {
Chris@30 549
Chris@30 550 if (snap == SnapRight) {
Chris@30 551
Chris@30 552 if (i->frame > frame) {
Chris@30 553 snapped = i->frame;
Chris@30 554 found = true;
Chris@30 555 break;
Chris@30 556 }
Chris@30 557
Chris@30 558 } else if (snap == SnapLeft) {
Chris@30 559
Chris@30 560 if (i->frame <= frame) {
Chris@30 561 snapped = i->frame;
Chris@30 562 found = true; // don't break, as the next may be better
Chris@30 563 } else {
Chris@30 564 break;
Chris@30 565 }
Chris@30 566
Chris@30 567 } else { // nearest
Chris@30 568
Chris@30 569 NoteModel::PointList::const_iterator j = i;
Chris@30 570 ++j;
Chris@30 571
Chris@30 572 if (j == points.end()) {
Chris@30 573
Chris@30 574 snapped = i->frame;
Chris@30 575 found = true;
Chris@30 576 break;
Chris@30 577
Chris@30 578 } else if (j->frame >= frame) {
Chris@30 579
Chris@30 580 if (j->frame - frame < frame - i->frame) {
Chris@30 581 snapped = j->frame;
Chris@30 582 } else {
Chris@30 583 snapped = i->frame;
Chris@30 584 }
Chris@30 585 found = true;
Chris@30 586 break;
Chris@30 587 }
Chris@30 588 }
Chris@30 589 }
Chris@30 590
Chris@30 591 frame = snapped;
Chris@30 592 return found;
Chris@30 593 }
Chris@30 594
Chris@101 595 void
Chris@101 596 NoteLayer::getScaleExtents(View *v, float &min, float &max, bool &log) const
Chris@30 597 {
Chris@101 598 min = 0.0;
Chris@101 599 max = 0.0;
Chris@101 600 log = false;
Chris@42 601
Chris@101 602 QString queryUnits;
Chris@101 603 if (shouldConvertMIDIToHz()) queryUnits = "Hz";
Chris@101 604 else queryUnits = m_model->getScaleUnits();
Chris@30 605
Chris@439 606 if (shouldAutoAlign()) {
Chris@30 607
Chris@101 608 if (!v->getValueExtents(queryUnits, min, max, log)) {
Chris@30 609
Chris@101 610 min = m_model->getValueMinimum();
Chris@101 611 max = m_model->getValueMaximum();
Chris@42 612
Chris@101 613 if (shouldConvertMIDIToHz()) {
Chris@101 614 min = Pitch::getFrequencyForPitch(lrintf(min));
Chris@101 615 max = Pitch::getFrequencyForPitch(lrintf(max + 1));
Chris@101 616 }
Chris@42 617
Chris@105 618 std::cerr << "NoteLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << std::endl;
Chris@105 619
Chris@101 620 } else if (log) {
Chris@101 621
Chris@197 622 LogRange::mapRange(min, max);
Chris@105 623
Chris@105 624 std::cerr << "NoteLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << std::endl;
Chris@105 625
Chris@101 626 }
Chris@101 627
Chris@101 628 } else {
Chris@101 629
Chris@439 630 getDisplayExtents(min, max);
Chris@101 631
Chris@101 632 if (m_verticalScale == MIDIRangeScale) {
Chris@101 633 min = Pitch::getFrequencyForPitch(0);
Chris@101 634 max = Pitch::getFrequencyForPitch(127);
Chris@101 635 } else if (shouldConvertMIDIToHz()) {
Chris@101 636 min = Pitch::getFrequencyForPitch(lrintf(min));
Chris@101 637 max = Pitch::getFrequencyForPitch(lrintf(max + 1));
Chris@101 638 }
Chris@101 639
Chris@101 640 if (m_verticalScale == LogScale || m_verticalScale == MIDIRangeScale) {
Chris@197 641 LogRange::mapRange(min, max);
Chris@101 642 log = true;
Chris@101 643 }
Chris@30 644 }
Chris@30 645
Chris@101 646 if (max == min) max = min + 1.0;
Chris@101 647 }
Chris@30 648
Chris@101 649 int
Chris@101 650 NoteLayer::getYForValue(View *v, float val) const
Chris@101 651 {
Chris@101 652 float min = 0.0, max = 0.0;
Chris@101 653 bool logarithmic = false;
Chris@101 654 int h = v->height();
Chris@101 655
Chris@101 656 getScaleExtents(v, min, max, logarithmic);
Chris@101 657
Chris@101 658 // std::cerr << "NoteLayer[" << this << "]::getYForValue(" << val << "): min = " << min << ", max = " << max << ", log = " << logarithmic << std::endl;
Chris@101 659
Chris@101 660 if (shouldConvertMIDIToHz()) {
Chris@101 661 val = Pitch::getFrequencyForPitch(lrintf(val),
Chris@101 662 lrintf((val - lrintf(val)) * 100));
Chris@101 663 // std::cerr << "shouldConvertMIDIToHz true, val now = " << val << std::endl;
Chris@101 664 }
Chris@101 665
Chris@101 666 if (logarithmic) {
Chris@197 667 val = LogRange::map(val);
Chris@101 668 // std::cerr << "logarithmic true, val now = " << val << std::endl;
Chris@101 669 }
Chris@101 670
Chris@101 671 int y = int(h - ((val - min) * h) / (max - min)) - 1;
Chris@101 672 // std::cerr << "y = " << y << std::endl;
Chris@101 673 return y;
Chris@30 674 }
Chris@30 675
Chris@30 676 float
Chris@44 677 NoteLayer::getValueForY(View *v, int y) const
Chris@30 678 {
Chris@101 679 float min = 0.0, max = 0.0;
Chris@101 680 bool logarithmic = false;
Chris@44 681 int h = v->height();
Chris@30 682
Chris@101 683 getScaleExtents(v, min, max, logarithmic);
Chris@101 684
Chris@101 685 float val = min + (float(h - y) * float(max - min)) / h;
Chris@101 686
Chris@101 687 if (logarithmic) {
Chris@197 688 val = powf(10.f, val);
Chris@101 689 }
Chris@101 690
Chris@101 691 if (shouldConvertMIDIToHz()) {
Chris@101 692 val = Pitch::getPitchForFrequency(val);
Chris@101 693 }
Chris@101 694
Chris@101 695 return val;
Chris@30 696 }
Chris@30 697
Chris@439 698 bool
Chris@439 699 NoteLayer::shouldAutoAlign() const
Chris@439 700 {
Chris@439 701 if (!m_model) return false;
Chris@439 702 return (m_verticalScale == AutoAlignScale);
Chris@439 703 }
Chris@439 704
Chris@30 705 void
Chris@44 706 NoteLayer::paint(View *v, QPainter &paint, QRect rect) const
Chris@30 707 {
Chris@30 708 if (!m_model || !m_model->isOK()) return;
Chris@30 709
Chris@30 710 int sampleRate = m_model->getSampleRate();
Chris@30 711 if (!sampleRate) return;
Chris@30 712
Chris@30 713 // Profiler profiler("NoteLayer::paint", true);
Chris@30 714
Chris@30 715 int x0 = rect.left(), x1 = rect.right();
Chris@44 716 long frame0 = v->getFrameForX(x0);
Chris@44 717 long frame1 = v->getFrameForX(x1);
Chris@30 718
Chris@30 719 NoteModel::PointList points(m_model->getPoints(frame0, frame1));
Chris@30 720 if (points.empty()) return;
Chris@30 721
Chris@287 722 paint.setPen(getBaseQColor());
Chris@30 723
Chris@287 724 QColor brushColour(getBaseQColor());
Chris@30 725 brushColour.setAlpha(80);
Chris@30 726
Chris@30 727 // std::cerr << "NoteLayer::paint: resolution is "
Chris@30 728 // << m_model->getResolution() << " frames" << std::endl;
Chris@30 729
Chris@30 730 float min = m_model->getValueMinimum();
Chris@30 731 float max = m_model->getValueMaximum();
Chris@30 732 if (max == min) max = min + 1.0;
Chris@30 733
Chris@30 734 QPoint localPos;
Chris@551 735 NoteModel::Point illuminatePoint(0);
Chris@551 736 bool shouldIlluminate = false;
Chris@30 737
Chris@44 738 if (v->shouldIlluminateLocalFeatures(this, localPos)) {
Chris@551 739 shouldIlluminate = getPointToDrag(v, localPos.x(), localPos.y(),
Chris@551 740 illuminatePoint);
Chris@30 741 }
Chris@30 742
Chris@30 743 paint.save();
Chris@30 744 paint.setRenderHint(QPainter::Antialiasing, false);
Chris@30 745
Chris@30 746 for (NoteModel::PointList::const_iterator i = points.begin();
Chris@30 747 i != points.end(); ++i) {
Chris@30 748
Chris@30 749 const NoteModel::Point &p(*i);
Chris@30 750
Chris@44 751 int x = v->getXForFrame(p.frame);
Chris@44 752 int y = getYForValue(v, p.value);
Chris@44 753 int w = v->getXForFrame(p.frame + p.duration) - x;
Chris@30 754 int h = 3;
Chris@30 755
Chris@30 756 if (m_model->getValueQuantization() != 0.0) {
Chris@44 757 h = y - getYForValue(v, p.value + m_model->getValueQuantization());
Chris@30 758 if (h < 3) h = 3;
Chris@30 759 }
Chris@30 760
Chris@30 761 if (w < 1) w = 1;
Chris@287 762 paint.setPen(getBaseQColor());
Chris@30 763 paint.setBrush(brushColour);
Chris@30 764
Chris@551 765 if (shouldIlluminate &&
Chris@551 766 // "illuminatePoint == p"
Chris@551 767 !NoteModel::Point::Comparator()(illuminatePoint, p) &&
Chris@551 768 !NoteModel::Point::Comparator()(p, illuminatePoint)) {
Chris@551 769
Chris@551 770 paint.setPen(v->getForeground());
Chris@551 771 paint.setBrush(v->getForeground());
Chris@551 772
Chris@551 773 QString vlabel = QString("%1%2").arg(p.value).arg(m_model->getScaleUnits());
Chris@551 774 v->drawVisibleText(paint,
Chris@551 775 x - paint.fontMetrics().width(vlabel) - 2,
Chris@551 776 y + paint.fontMetrics().height()/2
Chris@551 777 - paint.fontMetrics().descent(),
Chris@551 778 vlabel, View::OutlinedText);
Chris@551 779
Chris@551 780 QString hlabel = RealTime::frame2RealTime
Chris@551 781 (p.frame, m_model->getSampleRate()).toText(true).c_str();
Chris@551 782 v->drawVisibleText(paint,
Chris@551 783 x,
Chris@551 784 y - h/2 - paint.fontMetrics().descent() - 2,
Chris@551 785 hlabel, View::OutlinedText);
Chris@30 786 }
Chris@30 787
Chris@124 788 paint.drawRect(x, y - h/2, w, h);
Chris@30 789 }
Chris@30 790
Chris@30 791 paint.restore();
Chris@30 792 }
Chris@30 793
Chris@30 794 void
Chris@44 795 NoteLayer::drawStart(View *v, QMouseEvent *e)
Chris@30 796 {
Chris@101 797 // std::cerr << "NoteLayer::drawStart(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@30 798
Chris@30 799 if (!m_model) return;
Chris@30 800
Chris@44 801 long frame = v->getFrameForX(e->x());
Chris@30 802 if (frame < 0) frame = 0;
Chris@30 803 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@30 804
Chris@44 805 float value = getValueForY(v, e->y());
Chris@30 806
Chris@335 807 m_editingPoint = NoteModel::Point(frame, value, 0, 0.8, tr("New Point"));
Chris@30 808 m_originalPoint = m_editingPoint;
Chris@30 809
Chris@376 810 if (m_editingCommand) finish(m_editingCommand);
Chris@30 811 m_editingCommand = new NoteModel::EditCommand(m_model,
Chris@30 812 tr("Draw Point"));
Chris@30 813 m_editingCommand->addPoint(m_editingPoint);
Chris@30 814
Chris@30 815 m_editing = true;
Chris@30 816 }
Chris@30 817
Chris@30 818 void
Chris@44 819 NoteLayer::drawDrag(View *v, QMouseEvent *e)
Chris@30 820 {
Chris@101 821 // std::cerr << "NoteLayer::drawDrag(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@30 822
Chris@30 823 if (!m_model || !m_editing) return;
Chris@30 824
Chris@44 825 long frame = v->getFrameForX(e->x());
Chris@30 826 if (frame < 0) frame = 0;
Chris@30 827 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@30 828
Chris@101 829 float newValue = getValueForY(v, e->y());
Chris@101 830
Chris@101 831 long newFrame = m_editingPoint.frame;
Chris@101 832 long newDuration = frame - newFrame;
Chris@101 833 if (newDuration < 0) {
Chris@101 834 newFrame = frame;
Chris@101 835 newDuration = -newDuration;
Chris@101 836 } else if (newDuration == 0) {
Chris@101 837 newDuration = 1;
Chris@101 838 }
Chris@30 839
Chris@30 840 m_editingCommand->deletePoint(m_editingPoint);
Chris@101 841 m_editingPoint.frame = newFrame;
Chris@101 842 m_editingPoint.value = newValue;
Chris@101 843 m_editingPoint.duration = newDuration;
Chris@30 844 m_editingCommand->addPoint(m_editingPoint);
Chris@30 845 }
Chris@30 846
Chris@30 847 void
Chris@248 848 NoteLayer::drawEnd(View *, QMouseEvent *)
Chris@30 849 {
Chris@101 850 // std::cerr << "NoteLayer::drawEnd(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@30 851 if (!m_model || !m_editing) return;
Chris@376 852 finish(m_editingCommand);
Chris@30 853 m_editingCommand = 0;
Chris@30 854 m_editing = false;
Chris@30 855 }
Chris@30 856
Chris@30 857 void
Chris@335 858 NoteLayer::eraseStart(View *v, QMouseEvent *e)
Chris@335 859 {
Chris@335 860 if (!m_model) return;
Chris@335 861
Chris@550 862 if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return;
Chris@335 863
Chris@335 864 if (m_editingCommand) {
Chris@376 865 finish(m_editingCommand);
Chris@335 866 m_editingCommand = 0;
Chris@335 867 }
Chris@335 868
Chris@335 869 m_editing = true;
Chris@335 870 }
Chris@335 871
Chris@335 872 void
Chris@335 873 NoteLayer::eraseDrag(View *v, QMouseEvent *e)
Chris@335 874 {
Chris@335 875 }
Chris@335 876
Chris@335 877 void
Chris@335 878 NoteLayer::eraseEnd(View *v, QMouseEvent *e)
Chris@335 879 {
Chris@335 880 if (!m_model || !m_editing) return;
Chris@335 881
Chris@335 882 m_editing = false;
Chris@335 883
Chris@550 884 NoteModel::Point p(0);
Chris@550 885 if (!getPointToDrag(v, e->x(), e->y(), p)) return;
Chris@550 886 if (p.frame != m_editingPoint.frame || p.value != m_editingPoint.value) return;
Chris@550 887
Chris@550 888 m_editingCommand = new NoteModel::EditCommand(m_model, tr("Erase Point"));
Chris@335 889
Chris@335 890 m_editingCommand->deletePoint(m_editingPoint);
Chris@335 891
Chris@376 892 finish(m_editingCommand);
Chris@335 893 m_editingCommand = 0;
Chris@335 894 m_editing = false;
Chris@335 895 }
Chris@335 896
Chris@335 897 void
Chris@44 898 NoteLayer::editStart(View *v, QMouseEvent *e)
Chris@30 899 {
Chris@101 900 // std::cerr << "NoteLayer::editStart(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@30 901
Chris@30 902 if (!m_model) return;
Chris@30 903
Chris@550 904 if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return;
Chris@30 905 m_originalPoint = m_editingPoint;
Chris@30 906
Chris@551 907 m_dragPointX = v->getXForFrame(m_editingPoint.frame);
Chris@551 908 m_dragPointY = getYForValue(v, m_editingPoint.value);
Chris@551 909
Chris@30 910 if (m_editingCommand) {
Chris@376 911 finish(m_editingCommand);
Chris@30 912 m_editingCommand = 0;
Chris@30 913 }
Chris@30 914
Chris@30 915 m_editing = true;
Chris@551 916 m_dragStartX = e->x();
Chris@551 917 m_dragStartY = e->y();
Chris@30 918 }
Chris@30 919
Chris@30 920 void
Chris@44 921 NoteLayer::editDrag(View *v, QMouseEvent *e)
Chris@30 922 {
Chris@101 923 // std::cerr << "NoteLayer::editDrag(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@30 924
Chris@30 925 if (!m_model || !m_editing) return;
Chris@30 926
Chris@551 927 int xdist = e->x() - m_dragStartX;
Chris@551 928 int ydist = e->y() - m_dragStartY;
Chris@551 929 int newx = m_dragPointX + xdist;
Chris@551 930 int newy = m_dragPointY + ydist;
Chris@551 931
Chris@551 932 long frame = v->getFrameForX(newx);
Chris@30 933 if (frame < 0) frame = 0;
Chris@30 934 frame = frame / m_model->getResolution() * m_model->getResolution();
Chris@30 935
Chris@551 936 float value = getValueForY(v, newy);
Chris@30 937
Chris@30 938 if (!m_editingCommand) {
Chris@30 939 m_editingCommand = new NoteModel::EditCommand(m_model,
Chris@30 940 tr("Drag Point"));
Chris@30 941 }
Chris@30 942
Chris@30 943 m_editingCommand->deletePoint(m_editingPoint);
Chris@30 944 m_editingPoint.frame = frame;
Chris@30 945 m_editingPoint.value = value;
Chris@30 946 m_editingCommand->addPoint(m_editingPoint);
Chris@30 947 }
Chris@30 948
Chris@30 949 void
Chris@248 950 NoteLayer::editEnd(View *, QMouseEvent *)
Chris@30 951 {
Chris@101 952 // std::cerr << "NoteLayer::editEnd(" << e->x() << "," << e->y() << ")" << std::endl;
Chris@30 953 if (!m_model || !m_editing) return;
Chris@30 954
Chris@30 955 if (m_editingCommand) {
Chris@30 956
Chris@30 957 QString newName = m_editingCommand->getName();
Chris@30 958
Chris@30 959 if (m_editingPoint.frame != m_originalPoint.frame) {
Chris@30 960 if (m_editingPoint.value != m_originalPoint.value) {
Chris@30 961 newName = tr("Edit Point");
Chris@30 962 } else {
Chris@30 963 newName = tr("Relocate Point");
Chris@30 964 }
Chris@30 965 } else {
Chris@30 966 newName = tr("Change Point Value");
Chris@30 967 }
Chris@30 968
Chris@30 969 m_editingCommand->setName(newName);
Chris@376 970 finish(m_editingCommand);
Chris@30 971 }
Chris@30 972
Chris@30 973 m_editingCommand = 0;
Chris@30 974 m_editing = false;
Chris@30 975 }
Chris@30 976
Chris@255 977 bool
Chris@70 978 NoteLayer::editOpen(View *v, QMouseEvent *e)
Chris@70 979 {
Chris@255 980 if (!m_model) return false;
Chris@70 981
Chris@550 982 NoteModel::Point note(0);
Chris@550 983 if (!getPointToDrag(v, e->x(), e->y(), note)) return false;
Chris@550 984
Chris@550 985 // NoteModel::Point note = *points.begin();
Chris@70 986
Chris@70 987 ItemEditDialog *dialog = new ItemEditDialog
Chris@70 988 (m_model->getSampleRate(),
Chris@70 989 ItemEditDialog::ShowTime |
Chris@70 990 ItemEditDialog::ShowDuration |
Chris@70 991 ItemEditDialog::ShowValue |
Chris@100 992 ItemEditDialog::ShowText,
Chris@100 993 m_model->getScaleUnits());
Chris@70 994
Chris@70 995 dialog->setFrameTime(note.frame);
Chris@70 996 dialog->setValue(note.value);
Chris@70 997 dialog->setFrameDuration(note.duration);
Chris@70 998 dialog->setText(note.label);
Chris@70 999
Chris@70 1000 if (dialog->exec() == QDialog::Accepted) {
Chris@70 1001
Chris@70 1002 NoteModel::Point newNote = note;
Chris@70 1003 newNote.frame = dialog->getFrameTime();
Chris@70 1004 newNote.value = dialog->getValue();
Chris@70 1005 newNote.duration = dialog->getFrameDuration();
Chris@70 1006 newNote.label = dialog->getText();
Chris@70 1007
Chris@70 1008 NoteModel::EditCommand *command = new NoteModel::EditCommand
Chris@70 1009 (m_model, tr("Edit Point"));
Chris@70 1010 command->deletePoint(note);
Chris@70 1011 command->addPoint(newNote);
Chris@376 1012 finish(command);
Chris@70 1013 }
Chris@70 1014
Chris@70 1015 delete dialog;
Chris@255 1016 return true;
Chris@70 1017 }
Chris@70 1018
Chris@70 1019 void
Chris@43 1020 NoteLayer::moveSelection(Selection s, size_t newStartFrame)
Chris@43 1021 {
Chris@99 1022 if (!m_model) return;
Chris@99 1023
Chris@43 1024 NoteModel::EditCommand *command =
Chris@43 1025 new NoteModel::EditCommand(m_model, tr("Drag Selection"));
Chris@43 1026
Chris@43 1027 NoteModel::PointList points =
Chris@43 1028 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@43 1029
Chris@43 1030 for (NoteModel::PointList::iterator i = points.begin();
Chris@43 1031 i != points.end(); ++i) {
Chris@43 1032
Chris@43 1033 if (s.contains(i->frame)) {
Chris@43 1034 NoteModel::Point newPoint(*i);
Chris@43 1035 newPoint.frame = i->frame + newStartFrame - s.getStartFrame();
Chris@43 1036 command->deletePoint(*i);
Chris@43 1037 command->addPoint(newPoint);
Chris@43 1038 }
Chris@43 1039 }
Chris@43 1040
Chris@376 1041 finish(command);
Chris@43 1042 }
Chris@43 1043
Chris@43 1044 void
Chris@43 1045 NoteLayer::resizeSelection(Selection s, Selection newSize)
Chris@43 1046 {
Chris@99 1047 if (!m_model) return;
Chris@99 1048
Chris@43 1049 NoteModel::EditCommand *command =
Chris@43 1050 new NoteModel::EditCommand(m_model, tr("Resize Selection"));
Chris@43 1051
Chris@43 1052 NoteModel::PointList points =
Chris@43 1053 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@43 1054
Chris@43 1055 double ratio =
Chris@43 1056 double(newSize.getEndFrame() - newSize.getStartFrame()) /
Chris@43 1057 double(s.getEndFrame() - s.getStartFrame());
Chris@43 1058
Chris@43 1059 for (NoteModel::PointList::iterator i = points.begin();
Chris@43 1060 i != points.end(); ++i) {
Chris@43 1061
Chris@43 1062 if (s.contains(i->frame)) {
Chris@43 1063
Chris@43 1064 double targetStart = i->frame;
Chris@43 1065 targetStart = newSize.getStartFrame() +
Chris@43 1066 double(targetStart - s.getStartFrame()) * ratio;
Chris@43 1067
Chris@43 1068 double targetEnd = i->frame + i->duration;
Chris@43 1069 targetEnd = newSize.getStartFrame() +
Chris@43 1070 double(targetEnd - s.getStartFrame()) * ratio;
Chris@43 1071
Chris@43 1072 NoteModel::Point newPoint(*i);
Chris@43 1073 newPoint.frame = lrint(targetStart);
Chris@43 1074 newPoint.duration = lrint(targetEnd - targetStart);
Chris@43 1075 command->deletePoint(*i);
Chris@43 1076 command->addPoint(newPoint);
Chris@43 1077 }
Chris@43 1078 }
Chris@43 1079
Chris@376 1080 finish(command);
Chris@43 1081 }
Chris@43 1082
Chris@76 1083 void
Chris@76 1084 NoteLayer::deleteSelection(Selection s)
Chris@76 1085 {
Chris@99 1086 if (!m_model) return;
Chris@99 1087
Chris@76 1088 NoteModel::EditCommand *command =
Chris@76 1089 new NoteModel::EditCommand(m_model, tr("Delete Selected Points"));
Chris@76 1090
Chris@76 1091 NoteModel::PointList points =
Chris@76 1092 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@76 1093
Chris@76 1094 for (NoteModel::PointList::iterator i = points.begin();
Chris@76 1095 i != points.end(); ++i) {
Chris@76 1096
Chris@76 1097 if (s.contains(i->frame)) {
Chris@76 1098 command->deletePoint(*i);
Chris@76 1099 }
Chris@76 1100 }
Chris@76 1101
Chris@376 1102 finish(command);
Chris@76 1103 }
Chris@76 1104
Chris@76 1105 void
Chris@359 1106 NoteLayer::copy(View *v, Selection s, Clipboard &to)
Chris@76 1107 {
Chris@99 1108 if (!m_model) return;
Chris@99 1109
Chris@76 1110 NoteModel::PointList points =
Chris@76 1111 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
Chris@76 1112
Chris@76 1113 for (NoteModel::PointList::iterator i = points.begin();
Chris@76 1114 i != points.end(); ++i) {
Chris@76 1115 if (s.contains(i->frame)) {
Chris@335 1116 Clipboard::Point point(i->frame, i->value, i->duration, i->level, i->label);
Chris@360 1117 point.setReferenceFrame(alignToReference(v, i->frame));
Chris@76 1118 to.addPoint(point);
Chris@76 1119 }
Chris@76 1120 }
Chris@76 1121 }
Chris@76 1122
Chris@125 1123 bool
Chris@359 1124 NoteLayer::paste(View *v, const Clipboard &from, int frameOffset, bool /* interactive */)
Chris@76 1125 {
Chris@125 1126 if (!m_model) return false;
Chris@99 1127
Chris@76 1128 const Clipboard::PointList &points = from.getPoints();
Chris@76 1129
Chris@360 1130 bool realign = false;
Chris@360 1131
Chris@360 1132 if (clipboardHasDifferentAlignment(v, from)) {
Chris@360 1133
Chris@360 1134 QMessageBox::StandardButton button =
Chris@360 1135 QMessageBox::question(v, tr("Re-align pasted items?"),
Chris@360 1136 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 1137 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
Chris@360 1138 QMessageBox::Yes);
Chris@360 1139
Chris@360 1140 if (button == QMessageBox::Cancel) {
Chris@360 1141 return false;
Chris@360 1142 }
Chris@360 1143
Chris@360 1144 if (button == QMessageBox::Yes) {
Chris@360 1145 realign = true;
Chris@360 1146 }
Chris@360 1147 }
Chris@360 1148
Chris@76 1149 NoteModel::EditCommand *command =
Chris@76 1150 new NoteModel::EditCommand(m_model, tr("Paste"));
Chris@76 1151
Chris@76 1152 for (Clipboard::PointList::const_iterator i = points.begin();
Chris@76 1153 i != points.end(); ++i) {
Chris@76 1154
Chris@76 1155 if (!i->haveFrame()) continue;
Chris@76 1156 size_t frame = 0;
Chris@360 1157
Chris@360 1158 if (!realign) {
Chris@360 1159
Chris@360 1160 frame = i->getFrame();
Chris@360 1161
Chris@360 1162 } else {
Chris@360 1163
Chris@360 1164 if (i->haveReferenceFrame()) {
Chris@360 1165 frame = i->getReferenceFrame();
Chris@360 1166 frame = alignFromReference(v, frame);
Chris@360 1167 } else {
Chris@360 1168 frame = i->getFrame();
Chris@360 1169 }
Chris@76 1170 }
Chris@360 1171
Chris@76 1172 NoteModel::Point newPoint(frame);
Chris@76 1173
Chris@76 1174 if (i->haveLabel()) newPoint.label = i->getLabel();
Chris@76 1175 if (i->haveValue()) newPoint.value = i->getValue();
Chris@76 1176 else newPoint.value = (m_model->getValueMinimum() +
Chris@76 1177 m_model->getValueMaximum()) / 2;
Chris@335 1178 if (i->haveLevel()) newPoint.level = i->getLevel();
Chris@76 1179 if (i->haveDuration()) newPoint.duration = i->getDuration();
Chris@125 1180 else {
Chris@125 1181 size_t nextFrame = frame;
Chris@125 1182 Clipboard::PointList::const_iterator j = i;
Chris@125 1183 for (; j != points.end(); ++j) {
Chris@125 1184 if (!j->haveFrame()) continue;
Chris@125 1185 if (j != i) break;
Chris@125 1186 }
Chris@125 1187 if (j != points.end()) {
Chris@125 1188 nextFrame = j->getFrame();
Chris@125 1189 }
Chris@125 1190 if (nextFrame == frame) {
Chris@125 1191 newPoint.duration = m_model->getResolution();
Chris@125 1192 } else {
Chris@125 1193 newPoint.duration = nextFrame - frame;
Chris@125 1194 }
Chris@125 1195 }
Chris@76 1196
Chris@76 1197 command->addPoint(newPoint);
Chris@76 1198 }
Chris@76 1199
Chris@376 1200 finish(command);
Chris@125 1201 return true;
Chris@76 1202 }
Chris@76 1203
Chris@507 1204 void
Chris@507 1205 NoteLayer::addNoteOn(long frame, int pitch, int velocity)
Chris@507 1206 {
Chris@507 1207 m_pendingNoteOns.insert(Note(frame, pitch, 0, float(velocity) / 127.0, ""));
Chris@507 1208 }
Chris@507 1209
Chris@507 1210 void
Chris@507 1211 NoteLayer::addNoteOff(long frame, int pitch)
Chris@507 1212 {
Chris@507 1213 for (NoteSet::iterator i = m_pendingNoteOns.begin();
Chris@507 1214 i != m_pendingNoteOns.end(); ++i) {
Chris@507 1215 if (lrintf((*i).value) == pitch) {
Chris@507 1216 Note note(*i);
Chris@507 1217 m_pendingNoteOns.erase(i);
Chris@507 1218 note.duration = frame - note.frame;
Chris@507 1219 if (m_model) {
Chris@507 1220 NoteModel::AddPointCommand *c = new NoteModel::AddPointCommand
Chris@507 1221 (m_model, note, tr("Record Note"));
Chris@507 1222 // execute and bundle:
Chris@507 1223 CommandHistory::getInstance()->addCommand(c, true, true);
Chris@507 1224 }
Chris@507 1225 break;
Chris@507 1226 }
Chris@507 1227 }
Chris@507 1228 }
Chris@507 1229
Chris@507 1230 void
Chris@507 1231 NoteLayer::abandonNoteOns()
Chris@507 1232 {
Chris@507 1233 m_pendingNoteOns.clear();
Chris@507 1234 }
Chris@507 1235
Chris@287 1236 int
Chris@287 1237 NoteLayer::getDefaultColourHint(bool darkbg, bool &impose)
Chris@287 1238 {
Chris@287 1239 impose = false;
Chris@287 1240 return ColourDatabase::getInstance()->getColourIndex
Chris@287 1241 (QString(darkbg ? "White" : "Black"));
Chris@287 1242 }
Chris@287 1243
Chris@316 1244 void
Chris@316 1245 NoteLayer::toXml(QTextStream &stream,
Chris@316 1246 QString indent, QString extraAttributes) const
Chris@30 1247 {
Chris@316 1248 SingleColourLayer::toXml(stream, indent, extraAttributes +
Chris@445 1249 QString(" verticalScale=\"%1\" scaleMinimum=\"%2\" scaleMaximum=\"%3\" ")
Chris@445 1250 .arg(m_verticalScale)
Chris@445 1251 .arg(m_scaleMinimum)
Chris@445 1252 .arg(m_scaleMaximum));
Chris@30 1253 }
Chris@30 1254
Chris@30 1255 void
Chris@30 1256 NoteLayer::setProperties(const QXmlAttributes &attributes)
Chris@30 1257 {
Chris@287 1258 SingleColourLayer::setProperties(attributes);
Chris@30 1259
Chris@445 1260 bool ok, alsoOk;
Chris@30 1261 VerticalScale scale = (VerticalScale)
Chris@30 1262 attributes.value("verticalScale").toInt(&ok);
Chris@30 1263 if (ok) setVerticalScale(scale);
Chris@445 1264
Chris@445 1265 float min = attributes.value("scaleMinimum").toFloat(&ok);
Chris@445 1266 float max = attributes.value("scaleMaximum").toFloat(&alsoOk);
Chris@445 1267 if (ok && alsoOk) setDisplayExtents(min, max);
Chris@30 1268 }
Chris@30 1269
Chris@30 1270