comparison layer/FlexiNoteLayer.cpp @ 619:aa141d619142 tonioni

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