comparison layer/FlexiNoteLayer.cpp @ 771:a964151832a7

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