18 #include "data/model/Model.h" 19 #include "base/RealTime.h" 20 #include "base/Profiler.h" 21 #include "base/Pitch.h" 22 #include "base/LogRange.h" 23 #include "base/RangeMapper.h" 31 #include "data/model/NoteModel.h" 37 #include <QPainterPath> 38 #include <QMouseEvent> 39 #include <QTextStream> 40 #include <QMessageBox> 56 m_originalPoint(0, 0.0, 0, 1.f, tr(
"New Point")),
57 m_editingPoint(0, 0.0, 0, 1.f, tr(
"New Point")),
59 m_verticalScale(AutoAlignScale),
63 SVDEBUG <<
"constructed NoteLayer" << endl;
86 list.push_back(
"Vertical Scale");
87 list.push_back(
"Scale Units");
94 if (name ==
"Vertical Scale")
return tr(
"Vertical Scale");
95 if (name ==
"Scale Units")
return tr(
"Scale Units");
102 if (name ==
"Scale Units")
return UnitsProperty;
103 if (name ==
"Vertical Scale")
return ValueProperty;
110 if (name ==
"Vertical Scale" || name ==
"Scale Units") {
125 int *min,
int *max,
int *deflt)
const 129 if (name ==
"Vertical Scale") {
137 }
else if (name ==
"Scale Units") {
139 if (deflt) *deflt = 0;
141 val = UnitDatabase::getInstance()->getUnitId
157 if (name ==
"Vertical Scale") {
160 case 0:
return tr(
"Auto-Align");
161 case 1:
return tr(
"Linear");
162 case 2:
return tr(
"Log");
163 case 3:
return tr(
"MIDI Notes");
172 if (name ==
"Vertical Scale") {
174 }
else if (name ==
"Scale Units") {
177 (UnitDatabase::getInstance()->getUnitById(value));
204 return (unit !=
"Hz");
213 bool &logarithmic, QString &unit)
const 216 min =
m_model->getValueMinimum();
217 max =
m_model->getValueMaximum();
221 min = Pitch::getFrequencyForPitch(lrintf(min));
222 max = Pitch::getFrequencyForPitch(lrintf(max + 1));
237 min = Pitch::getFrequencyForPitch(0);
238 max = Pitch::getFrequencyForPitch(127);
243 min =
m_model->getValueMinimum();
244 max =
m_model->getValueMaximum();
251 min = Pitch::getFrequencyForPitch(lrintf(min));
252 max = Pitch::getFrequencyForPitch(lrintf(max + 1));
255 #ifdef DEBUG_NOTE_LAYER 256 cerr <<
"NoteLayer::getDisplayExtents: min = " << min <<
", max = " << max <<
" (m_scaleMinimum = " <<
m_scaleMinimum <<
", m_scaleMaximum = " <<
m_scaleMaximum <<
")" << endl;
278 #ifdef DEBUG_NOTE_LAYER 279 cerr <<
"NoteLayer::setDisplayExtents: min = " << min <<
", max = " << max << endl;
303 if (!mapper)
return 0;
308 int nr = mapper->getPositionForValue(dmax - dmin);
334 float newdist = mapper->getValueForPosition(100 - step);
336 float newmin, newmax;
342 newmax = (newdist + sqrtf(newdist*newdist + 4*dmin*dmax)) / 2;
343 newmin = newmax - newdist;
348 float dmid = (dmax + dmin) / 2;
349 newmin = dmid - newdist / 2;
350 newmax = dmid + newdist / 2;
354 newmax += (min - newmin);
361 #ifdef DEBUG_NOTE_LAYER 362 cerr <<
"NoteLayer::setVerticalZoomStep: " << step <<
": " << newmin <<
" -> " << newmax <<
" (range " << newdist <<
")" << endl;
380 if (min == max)
return 0;
383 mapper =
new LogRangeMapper(0, 100, min, max, unit);
385 mapper =
new LinearRangeMapper(0, 100, min, max, unit);
394 if (!
m_model)
return NoteModel::PointList();
398 NoteModel::PointList onPoints =
401 if (!onPoints.empty()) {
405 NoteModel::PointList prevPoints =
406 m_model->getPreviousPoints(frame);
407 NoteModel::PointList nextPoints =
410 NoteModel::PointList usePoints = prevPoints;
412 if (prevPoints.empty()) {
413 usePoints = nextPoints;
414 }
else if (
int(prevPoints.begin()->frame) < v->
getStartFrame() &&
416 usePoints = nextPoints;
417 }
else if (
int(nextPoints.begin()->frame) - frame <
418 frame -
int(prevPoints.begin()->frame)) {
419 usePoints = nextPoints;
422 if (!usePoints.empty()) {
425 if ((px > x && px - x > fuzz) ||
426 (px < x && x - px > fuzz + 1)) {
441 NoteModel::PointList onPoints =
m_model->getPoints(frame);
442 if (onPoints.empty())
return false;
446 int nearestDistance = -1;
448 for (NoteModel::PointList::const_iterator i = onPoints.begin();
449 i != onPoints.end(); ++i) {
452 if (distance < 0) distance = -distance;
453 if (nearestDistance == -1 || distance < nearestDistance) {
454 nearestDistance = distance;
471 if (points.empty()) {
473 return tr(
"In progress");
475 return tr(
"No local points");
480 NoteModel::PointList::iterator i;
482 for (i = points.begin(); i != points.end(); ++i) {
487 if (
m_model->getValueQuantization() != 0.0) {
492 if (pos.y() >= y - h && pos.y() <= y) {
498 if (i == points.end())
return tr(
"No local points");
500 RealTime rt = RealTime::frame2RealTime(note.frame,
502 RealTime rd = RealTime::frame2RealTime(note.duration,
509 int mnote = lrintf(note.value);
510 int cents = lrintf((note.value - mnote) * 100);
511 float freq = Pitch::getFrequencyForPitch(mnote, cents);
512 pitchText = tr(
"%1 (%2, %3 Hz)")
513 .arg(Pitch::getPitchLabel(mnote, cents))
519 pitchText = tr(
"%1 Hz (%2, %3)")
521 .arg(Pitch::getPitchLabelForFrequency(note.value))
522 .arg(Pitch::getPitchForFrequency(note.value));
525 pitchText = tr(
"%1 %2")
531 if (note.label ==
"") {
532 text = QString(tr(
"Time:\t%1\nPitch:\t%2\nDuration:\t%3\nNo label"))
533 .arg(rt.toText(
true).c_str())
535 .arg(rd.toText(
true).c_str());
537 text = QString(tr(
"Time:\t%1\nPitch:\t%2\nDuration:\t%3\nLabel:\t%4"))
538 .arg(rt.toText(
true).c_str())
540 .arg(rd.toText(
true).c_str())
558 resolution =
m_model->getResolution();
559 NoteModel::PointList points;
564 if (points.empty())
return false;
565 frame = points.begin()->frame;
569 points =
m_model->getPoints(frame, frame);
573 for (NoteModel::PointList::const_iterator i = points.begin();
574 i != points.end(); ++i) {
578 if (i->frame > frame) {
586 if (i->frame <= frame) {
595 NoteModel::PointList::const_iterator j = i;
598 if (j == points.end()) {
604 }
else if (j->frame >= frame) {
606 if (j->frame - frame < frame - i->frame) {
636 min =
m_model->getValueMinimum();
637 max =
m_model->getValueMaximum();
640 min = Pitch::getFrequencyForPitch(lrintf(min));
641 max = Pitch::getFrequencyForPitch(lrintf(max + 1));
644 #ifdef DEBUG_NOTE_LAYER 645 cerr <<
"NoteLayer[" <<
this <<
"]::getScaleExtents: min = " << min <<
", max = " << max <<
", log = " << log << endl;
650 LogRange::mapRange(min, max);
652 #ifdef DEBUG_NOTE_LAYER 653 cerr <<
"NoteLayer[" <<
this <<
"]::getScaleExtents: min = " << min <<
", max = " << max <<
", log = " << log << endl;
663 min = Pitch::getFrequencyForPitch(0);
664 max = Pitch::getFrequencyForPitch(127);
666 min = Pitch::getFrequencyForPitch(lrintf(min));
667 max = Pitch::getFrequencyForPitch(lrintf(max + 1));
671 LogRange::mapRange(min, max);
676 if (max == min) max = min + 1.0;
682 float min = 0.0, max = 0.0;
683 bool logarithmic =
false;
688 #ifdef DEBUG_NOTE_LAYER 689 cerr <<
"NoteLayer[" <<
this <<
"]::getYForValue(" << val <<
"): min = " << min <<
", max = " << max <<
", log = " << logarithmic << endl;
693 val = Pitch::getFrequencyForPitch(lrintf(val),
694 lrintf((val - lrintf(val)) * 100));
695 #ifdef DEBUG_NOTE_LAYER 696 cerr <<
"shouldConvertMIDIToHz true, val now = " << val << endl;
701 val = LogRange::map(val);
702 #ifdef DEBUG_NOTE_LAYER 703 cerr <<
"logarithmic true, val now = " << val << endl;
707 int y = int(h - ((val - min) * h) / (max - min)) - 1;
708 #ifdef DEBUG_NOTE_LAYER 709 cerr <<
"y = " << y << endl;
717 float min = 0.0, max = 0.0;
718 bool logarithmic =
false;
723 float val = min + (float(h - y) * float(max - min)) / h;
726 val = powf(10.f, val);
730 val = Pitch::getPitchForFrequency(val);
748 int sampleRate =
m_model->getSampleRate();
749 if (!sampleRate)
return;
753 int x0 = rect.left(), x1 = rect.right();
757 NoteModel::PointList points(
m_model->getPoints(frame0, frame1));
758 if (points.empty())
return;
763 brushColour.setAlpha(80);
768 float min =
m_model->getValueMinimum();
769 float max =
m_model->getValueMaximum();
770 if (max == min) max = min + 1.0;
773 NoteModel::Point illuminatePoint(0);
774 bool shouldIlluminate =
false;
782 paint.setRenderHint(QPainter::Antialiasing,
false);
784 for (NoteModel::PointList::const_iterator i = points.begin();
785 i != points.end(); ++i) {
787 const NoteModel::Point &p(*i);
794 if (
m_model->getValueQuantization() != 0.0) {
801 paint.setBrush(brushColour);
803 if (shouldIlluminate &&
805 !NoteModel::Point::Comparator()(illuminatePoint, p) &&
806 !NoteModel::Point::Comparator()(p, illuminatePoint)) {
811 QString vlabel = QString(
"%1%2").arg(p.value).arg(
getScaleUnits());
813 x -
paint.fontMetrics().width(vlabel) - 2,
814 y +
paint.fontMetrics().height()/2
815 -
paint.fontMetrics().descent(),
818 QString hlabel = RealTime::frame2RealTime
819 (p.frame,
m_model->getSampleRate()).toText(
true).c_str();
822 y - h/2 -
paint.fontMetrics().descent() - 2,
826 paint.drawRect(x, y - h/2, w, h);
868 (v,
paint, QRect(w - 10, 0, 10, h),
869 LogRange::unmap(min),
870 LogRange::unmap(max));
871 paint.drawLine(w, 0, w, h);
877 5 +
paint.fontMetrics().ascent(),
892 if (frame < 0) frame = 0;
893 frame = frame /
m_model->getResolution() *
m_model->getResolution();
897 m_editingPoint = NoteModel::Point(frame, value, 0, 0.8, tr(
"New Point"));
916 if (frame < 0) frame = 0;
917 frame = frame /
m_model->getResolution() *
m_model->getResolution();
922 int newDuration = frame - newFrame;
923 if (newDuration < 0) {
925 newDuration = -newDuration;
926 }
else if (newDuration == 0) {
974 NoteModel::Point p(0);
1023 if (frame < 0) frame = 0;
1024 frame = frame /
m_model->getResolution() *
m_model->getResolution();
1051 newName = tr(
"Edit Point");
1053 newName = tr(
"Relocate Point");
1056 newName = tr(
"Change Point Value");
1072 NoteModel::Point note(0);
1090 if (dialog->exec() == QDialog::Accepted) {
1092 NoteModel::Point newNote = note;
1094 newNote.value = dialog->
getValue();
1096 newNote.label = dialog->
getText();
1098 NoteModel::EditCommand *command =
new NoteModel::EditCommand
1100 command->deletePoint(note);
1101 command->addPoint(newNote);
1114 NoteModel::EditCommand *command =
1115 new NoteModel::EditCommand(
m_model, tr(
"Drag Selection"));
1117 NoteModel::PointList points =
1118 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
1120 for (NoteModel::PointList::iterator i = points.begin();
1121 i != points.end(); ++i) {
1123 if (s.contains(i->frame)) {
1124 NoteModel::Point newPoint(*i);
1125 newPoint.frame = i->frame + newStartFrame - s.getStartFrame();
1126 command->deletePoint(*i);
1127 command->addPoint(newPoint);
1139 NoteModel::EditCommand *command =
1140 new NoteModel::EditCommand(
m_model, tr(
"Resize Selection"));
1142 NoteModel::PointList points =
1143 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
1146 double(newSize.getEndFrame() - newSize.getStartFrame()) /
1147 double(s.getEndFrame() - s.getStartFrame());
1149 for (NoteModel::PointList::iterator i = points.begin();
1150 i != points.end(); ++i) {
1152 if (s.contains(i->frame)) {
1154 double targetStart = i->frame;
1155 targetStart = newSize.getStartFrame() +
1156 double(targetStart - s.getStartFrame()) * ratio;
1158 double targetEnd = i->frame + i->duration;
1159 targetEnd = newSize.getStartFrame() +
1160 double(targetEnd - s.getStartFrame()) * ratio;
1162 NoteModel::Point newPoint(*i);
1163 newPoint.frame = lrint(targetStart);
1164 newPoint.duration = lrint(targetEnd - targetStart);
1165 command->deletePoint(*i);
1166 command->addPoint(newPoint);
1178 NoteModel::EditCommand *command =
1179 new NoteModel::EditCommand(
m_model, tr(
"Delete Selected Points"));
1181 NoteModel::PointList points =
1182 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
1184 for (NoteModel::PointList::iterator i = points.begin();
1185 i != points.end(); ++i) {
1187 if (s.contains(i->frame)) {
1188 command->deletePoint(*i);
1200 NoteModel::PointList points =
1201 m_model->getPoints(s.getStartFrame(), s.getEndFrame());
1203 for (NoteModel::PointList::iterator i = points.begin();
1204 i != points.end(); ++i) {
1205 if (s.contains(i->frame)) {
1206 Clipboard::Point point(i->frame, i->value, i->duration, i->level, i->label);
1218 const Clipboard::PointList &points = from.getPoints();
1220 bool realign =
false;
1224 QMessageBox::StandardButton button =
1225 QMessageBox::question(v, tr(
"Re-align pasted items?"),
1226 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?"),
1227 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
1230 if (button == QMessageBox::Cancel) {
1234 if (button == QMessageBox::Yes) {
1239 NoteModel::EditCommand *command =
1240 new NoteModel::EditCommand(
m_model, tr(
"Paste"));
1242 for (Clipboard::PointList::const_iterator i = points.begin();
1243 i != points.end(); ++i) {
1245 if (!i->haveFrame())
continue;
1250 frame = i->getFrame();
1254 if (i->haveReferenceFrame()) {
1255 frame = i->getReferenceFrame();
1258 frame = i->getFrame();
1262 NoteModel::Point newPoint(frame);
1264 if (i->haveLabel()) newPoint.label = i->getLabel();
1265 if (i->haveValue()) newPoint.value = i->getValue();
1266 else newPoint.value = (
m_model->getValueMinimum() +
1267 m_model->getValueMaximum()) / 2;
1268 if (i->haveLevel()) newPoint.level = i->getLevel();
1269 if (i->haveDuration()) newPoint.duration = i->getDuration();
1271 int nextFrame = frame;
1272 Clipboard::PointList::const_iterator j = i;
1273 for (; j != points.end(); ++j) {
1274 if (!j->haveFrame())
continue;
1277 if (j != points.end()) {
1278 nextFrame = j->getFrame();
1280 if (nextFrame == frame) {
1281 newPoint.duration =
m_model->getResolution();
1283 newPoint.duration = nextFrame - frame;
1287 command->addPoint(newPoint);
1297 m_pendingNoteOns.insert(Note(frame, pitch, 0,
float(velocity) / 127.0,
""));
1305 if (lrintf((*i).value) == pitch) {
1308 note.duration = frame - note.frame;
1310 NoteModel::AddPointCommand *c =
new NoteModel::AddPointCommand
1311 (
m_model, note, tr(
"Record Note"));
1331 (QString(darkbg ?
"White" :
"Black"));
1336 QString indent, QString extraAttributes)
const 1339 QString(
" verticalScale=\"%1\" scaleMinimum=\"%2\" scaleMaximum=\"%3\" ")
1352 attributes.value(
"verticalScale").toInt(&ok);
1355 float min = attributes.value(
"scaleMinimum").toFloat(&ok);
1356 float max = attributes.value(
"scaleMaximum").toFloat(&alsoOk);
void paintPianoVertical(View *v, QPainter &paint, QRect rect, float minf, float maxf)
virtual void editDrag(View *v, QMouseEvent *)
int getFrameForX(int x) const
Return the closest frame to the given pixel x-coordinate.
void finish(NoteModel::EditCommand *command)
virtual void copy(View *v, Selection s, Clipboard &to)
void getScaleExtents(View *, float &min, float &max, bool &log) const
void setFrameTime(int frame)
virtual QString getPropertyGroupName(const PropertyName &) const
virtual void eraseDrag(View *v, QMouseEvent *)
bool shouldAutoAlign() const
virtual bool snapToFeatureFrame(View *, int &, int &resolution, SnapType) const
Adjust the given frame to snap to the nearest feature, if possible.
void connectSignals(const Model *)
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...
virtual bool editOpen(View *v, QMouseEvent *)
Open an editor on the item under the mouse (e.g.
virtual QColor getForeground() const
virtual bool getValueExtents(QString unit, float &min, float &max, bool &log) const
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...
virtual void moveSelection(Selection s, int newStartFrame)
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.
NoteModel::Point m_editingPoint
void addCommand(Command *command)
Add a command to the command history.
void addNoteOn(int frame, int pitch, int velocity)
Add a note-on.
virtual PropertyList getProperties() const
bool getPointToDrag(View *v, int x, int y, NoteModel::Point &) const
virtual void setProperty(const PropertyName &, int value)
virtual void setVerticalZoomStep(int)
!! lots of duplication with TimeValueLayer
NoteModel::EditCommand * m_editingCommand
virtual bool shouldIlluminateLocalFeatures(const Layer *, QPoint &) const
NoteModel::Point m_originalPoint
virtual void drawEnd(View *v, QMouseEvent *)
void paintVertical(View *v, const VerticalScaleLayer *layer, QPainter &paint, int x0, float minlog, float maxlog)
virtual int getPropertyRangeAndValue(const PropertyName &, int *min, int *max, int *deflt) const
VerticalScale m_verticalScale
void addNoteOff(int frame, int pitch)
Add a note-off.
virtual RangeMapper * getNewVerticalZoomRangeMapper() const
Create and return a range mapper for vertical zoom step values.
virtual void paintVerticalScale(View *v, bool, QPainter &paint, QRect rect) const
virtual QColor getBaseQColor() const
virtual void deleteSelection(Selection s)
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 resizeSelection(Selection s, Selection newSize)
void setText(QString text)
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 ...
virtual QString getFeatureDescription(View *v, QPoint &) const
virtual void editStart(View *v, QMouseEvent *)
void setModel(NoteModel *model)
int getColourIndex(QString name) const
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.
int getWidth(View *v, QPainter &paint)
virtual void editEnd(View *v, QMouseEvent *)
virtual int getCurrentVerticalZoomStep() const
Get the current vertical zoom step.
virtual bool setDisplayExtents(float min, float max)
Set the displayed minimum and maximum values for the y axis to the given range, if supported.
virtual QString getPropertyLabel(const PropertyName &) const
virtual PropertyType getPropertyType(const PropertyName &) const
int getStartFrame() const
Retrieve the first visible sample frame on the widget.
void paintVertical(View *v, const VerticalScaleLayer *layer, QPainter &paint, int x0, float minf, float maxf)
void layerParametersChanged()
virtual QString getPropertyLabel(const PropertyName &) const
virtual QString getPropertyGroupName(const PropertyName &) const
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...
virtual void eraseStart(View *v, QMouseEvent *)
int getEndFrame() const
Retrieve the last visible sample frame on the widget.
virtual int getPropertyRangeAndValue(const PropertyName &, int *min, int *max, int *deflt) const
virtual QString getPropertyValueLabel(const PropertyName &, int value) const
static CommandHistory * getInstance()
void abandonNoteOns()
Abandon all pending note-on events.
bool clipboardHasDifferentAlignment(View *v, const Clipboard &clip) const
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 bool isLayerScrollable(const View *v) const
This should return true if the layer can safely be scrolled automatically by a given view (simply cop...
virtual void drawStart(View *v, QMouseEvent *)
virtual void drawDrag(View *v, QMouseEvent *)
virtual int alignFromReference(View *v, int frame) const
virtual int alignToReference(View *v, int frame) const
View is the base class of widgets that display one or more overlaid views of data against a horizonta...
virtual QString getScaleUnits() const
virtual int getVerticalScaleWidth(View *v, bool, QPainter &) const
NoteModel::PointList getLocalPoints(View *v, int) const
virtual float getValueForY(View *v, int y) const
virtual QString getPropertyValueLabel(const PropertyName &, int value) const
virtual void drawVisibleText(QPainter &p, int x, int y, QString text, TextStyle style) const
bool shouldConvertMIDIToHz() const
int getFrameDuration() const
void setVerticalScale(VerticalScale scale)
virtual PropertyList getProperties() const
virtual int getDefaultColourHint(bool dark, bool &impose)
virtual int getVerticalZoomSteps(int &defaultStep) const
Get the number of vertical zoom steps available for this layer.
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,...
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).
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.
static ColourDatabase * getInstance()
virtual void setProperty(const PropertyName &, int value)
virtual void eraseEnd(View *v, QMouseEvent *)
void setValue(float value)
void setFrameDuration(int frame)
virtual int getYForValue(View *v, float value) const
VerticalScaleLayer methods.