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