comparison layer/NoteLayer.cpp @ 1551:e79731086b0f

Fixes to NoteLayer, particularly to calculation of vertical scale when model unit is not Hz. To avoid inconsistency we now behave as if the unit is always Hz from the point of view of the external API and display, converting at the point where we obtain values from the events themselves. Also various fixes to editing.
author Chris Cannam
date Thu, 21 Nov 2019 14:02:57 +0000
parents e6362cf5ff1d
children 045063dcd2bc
comparison
equal deleted inserted replaced
1548:bd6af89982d7 1551:e79731086b0f
46 46
47 //#define DEBUG_NOTE_LAYER 1 47 //#define DEBUG_NOTE_LAYER 1
48 48
49 NoteLayer::NoteLayer() : 49 NoteLayer::NoteLayer() :
50 SingleColourLayer(), 50 SingleColourLayer(),
51 m_modelUsesHz(true),
51 m_editing(false), 52 m_editing(false),
52 m_dragPointX(0), 53 m_dragPointX(0),
53 m_dragPointY(0), 54 m_dragPointY(0),
54 m_dragStartX(0), 55 m_dragStartX(0),
55 m_dragStartY(0), 56 m_dragStartY(0),
84 if (m_model == modelId) return; 85 if (m_model == modelId) return;
85 m_model = modelId; 86 m_model = modelId;
86 87
87 if (newModel) { 88 if (newModel) {
88 connectSignals(m_model); 89 connectSignals(m_model);
90
91 QString unit = newModel->getScaleUnits();
92 m_modelUsesHz = (unit.toLower() == "hz");
89 } 93 }
90 94
91 m_scaleMinimum = 0; 95 m_scaleMinimum = 0;
92 m_scaleMaximum = 0; 96 m_scaleMaximum = 0;
93 97
129 } 133 }
130 134
131 QString 135 QString
132 NoteLayer::getScaleUnits() const 136 NoteLayer::getScaleUnits() const
133 { 137 {
134 auto model = ModelById::getAs<NoteModel>(m_model); 138 return "Hz";
135 if (model) return model->getScaleUnits();
136 else return "";
137 } 139 }
138 140
139 int 141 int
140 NoteLayer::getPropertyRangeAndValue(const PropertyName &name, 142 NoteLayer::getPropertyRangeAndValue(const PropertyName &name,
141 int *min, int *max, int *deflt) const 143 int *min, int *max, int *deflt) const
189 if (name == "Vertical Scale") { 191 if (name == "Vertical Scale") {
190 setVerticalScale(VerticalScale(value)); 192 setVerticalScale(VerticalScale(value));
191 } else if (name == "Scale Units") { 193 } else if (name == "Scale Units") {
192 auto model = ModelById::getAs<NoteModel>(m_model); 194 auto model = ModelById::getAs<NoteModel>(m_model);
193 if (model) { 195 if (model) {
194 model->setScaleUnits 196 QString unit = UnitDatabase::getInstance()->getUnitById(value);
195 (UnitDatabase::getInstance()->getUnitById(value)); 197 model->setScaleUnits(unit);
198 m_modelUsesHz = (unit.toLower() == "hz");
196 emit modelChanged(m_model); 199 emit modelChanged(m_model);
197 } 200 }
198 } else { 201 } else {
199 return SingleColourLayer::setProperty(name, value); 202 return SingleColourLayer::setProperty(name, value);
200 } 203 }
213 { 216 {
214 QPoint discard; 217 QPoint discard;
215 return !v->shouldIlluminateLocalFeatures(this, discard); 218 return !v->shouldIlluminateLocalFeatures(this, discard);
216 } 219 }
217 220
218 bool 221 double
219 NoteLayer::shouldConvertMIDIToHz() const 222 NoteLayer::valueOf(const Event &e) const
220 { 223 {
221 QString unit = getScaleUnits(); 224 return convertValueFromEventValue(e.getValue());
222 return (unit != "Hz"); 225 }
223 // if (unit == "" || 226
224 // unit.startsWith("MIDI") || 227 Event
225 // unit.startsWith("midi")) return true; 228 NoteLayer::eventWithValue(const Event &e, double value) const
226 // return false; 229 {
230 return e.withValue(convertValueToEventValue(value));
231 }
232
233 double
234 NoteLayer::convertValueFromEventValue(float eventValue) const
235 {
236 if (m_modelUsesHz) {
237 return eventValue;
238 } else {
239 double v = eventValue;
240 if (v < 0) v = 0;
241 if (v > 127) v = 127;
242 int p = int(round(v));
243 double c = 100.0 * (v - p);
244 return Pitch::getFrequencyForPitch(p, c);
245 }
246 }
247
248 float
249 NoteLayer::convertValueToEventValue(double value) const
250 {
251 if (m_modelUsesHz) {
252 return float(value);
253 } else {
254 float c = 0;
255 int p = Pitch::getPitchForFrequency(value, &c);
256 return float(p) + c / 100.f;
257 }
227 } 258 }
228 259
229 bool 260 bool
230 NoteLayer::getValueExtents(double &min, double &max, 261 NoteLayer::getValueExtents(double &min, double &max,
231 bool &logarithmic, QString &unit) const 262 bool &logarithmic, QString &unit) const
232 { 263 {
233 auto model = ModelById::getAs<NoteModel>(m_model); 264 auto model = ModelById::getAs<NoteModel>(m_model);
234 if (!model) return false; 265 if (!model) return false;
235 min = model->getValueMinimum(); 266
236 max = model->getValueMaximum(); 267 min = convertValueFromEventValue(model->getValueMinimum());
237 268 max = convertValueFromEventValue(model->getValueMaximum());
238 if (shouldConvertMIDIToHz()) { 269 min /= 1.06;
239 unit = "Hz"; 270 max *= 1.06;
240 min = Pitch::getFrequencyForPitch(int(lrint(min))); 271 unit = "Hz";
241 max = Pitch::getFrequencyForPitch(int(lrint(max + 1))); 272
242 } else unit = getScaleUnits(); 273 if (m_verticalScale != LinearScale) {
243
244 if (m_verticalScale == MIDIRangeScale ||
245 m_verticalScale == LogScale) {
246 logarithmic = true; 274 logarithmic = true;
247 } 275 }
248 276
249 return true; 277 return true;
250 } 278 }
260 max = Pitch::getFrequencyForPitch(127); 288 max = Pitch::getFrequencyForPitch(127);
261 return true; 289 return true;
262 } 290 }
263 291
264 if (m_scaleMinimum == m_scaleMaximum) { 292 if (m_scaleMinimum == m_scaleMaximum) {
265 min = model->getValueMinimum(); 293 QString unit;
266 max = model->getValueMaximum(); 294 bool log = false;
295 getValueExtents(min, max, log, unit);
267 } else { 296 } else {
268 min = m_scaleMinimum; 297 min = m_scaleMinimum;
269 max = m_scaleMaximum; 298 max = m_scaleMaximum;
270 } 299 }
271 300
272 if (shouldConvertMIDIToHz()) {
273 min = Pitch::getFrequencyForPitch(int(lrint(min)));
274 max = Pitch::getFrequencyForPitch(int(lrint(max + 1)));
275 }
276
277 #ifdef DEBUG_NOTE_LAYER 301 #ifdef DEBUG_NOTE_LAYER
278 cerr << "NoteLayer::getDisplayExtents: min = " << min << ", max = " << max << " (m_scaleMinimum = " << m_scaleMinimum << ", m_scaleMaximum = " << m_scaleMaximum << ")" << endl; 302 SVCERR << "NoteLayer::getDisplayExtents: min = " << min << ", max = " << max << " (m_scaleMinimum = " << m_scaleMinimum << ", m_scaleMaximum = " << m_scaleMaximum << ")" << endl;
279 #endif 303 #endif
280 304
281 return true; 305 return true;
282 } 306 }
283 307
296 320
297 m_scaleMinimum = min; 321 m_scaleMinimum = min;
298 m_scaleMaximum = max; 322 m_scaleMaximum = max;
299 323
300 #ifdef DEBUG_NOTE_LAYER 324 #ifdef DEBUG_NOTE_LAYER
301 cerr << "NoteLayer::setDisplayExtents: min = " << min << ", max = " << max << endl; 325 SVCERR << "NoteLayer::setDisplayExtents: min = " << min << ", max = " << max << endl;
302 #endif 326 #endif
303 327
304 emit layerParametersChanged(); 328 emit layerParametersChanged();
305 return true; 329 return true;
306 } 330 }
375 if (newmax > max) { 399 if (newmax > max) {
376 newmax = max; 400 newmax = max;
377 } 401 }
378 402
379 #ifdef DEBUG_NOTE_LAYER 403 #ifdef DEBUG_NOTE_LAYER
380 cerr << "NoteLayer::setVerticalZoomStep: " << step << ": " << newmin << " -> " << newmax << " (range " << newdist << ")" << endl; 404 SVCERR << "NoteLayer::setVerticalZoomStep: " << step << ": " << newmin << " -> " << newmax << " (range " << newdist << ")" << endl;
381 #endif 405 #endif
382 406
383 setDisplayExtents(newmin, newmax); 407 setDisplayExtents(newmin, newmax);
384 } 408 }
385 409
441 EventVector onPoints = model->getEventsCovering(frame); 465 EventVector onPoints = model->getEventsCovering(frame);
442 if (onPoints.empty()) return false; 466 if (onPoints.empty()) return false;
443 467
444 int nearestDistance = -1; 468 int nearestDistance = -1;
445 for (const auto &p: onPoints) { 469 for (const auto &p: onPoints) {
446 int distance = getYForValue(v, p.getValue()) - y; 470 int distance = getYForValue(v, valueOf(p)) - y;
447 if (distance < 0) distance = -distance; 471 if (distance < 0) distance = -distance;
448 if (nearestDistance == -1 || distance < nearestDistance) { 472 if (nearestDistance == -1 || distance < nearestDistance) {
449 nearestDistance = distance; 473 nearestDistance = distance;
450 point = p; 474 point = p;
451 } 475 }
475 Event note; 499 Event note;
476 EventVector::iterator i; 500 EventVector::iterator i;
477 501
478 for (i = points.begin(); i != points.end(); ++i) { 502 for (i = points.begin(); i != points.end(); ++i) {
479 503
480 int y = getYForValue(v, i->getValue()); 504 int y = getYForValue(v, valueOf(*i));
481 int h = 3; 505 int h = 3;
482 506
483 if (model->getValueQuantization() != 0.0) { 507 if (model->getValueQuantization() != 0.0) {
484 h = y - getYForValue 508 h = y - getYForValue
485 (v, i->getValue() + model->getValueQuantization()); 509 (v, convertValueFromEventValue(i->getValue() +
510 model->getValueQuantization()));
486 if (h < 3) h = 3; 511 if (h < 3) h = 3;
487 } 512 }
488 513
489 if (pos.y() >= y - h && pos.y() <= y) { 514 if (pos.y() >= y - h && pos.y() <= y) {
490 note = *i; 515 note = *i;
499 RealTime rd = RealTime::frame2RealTime(note.getDuration(), 524 RealTime rd = RealTime::frame2RealTime(note.getDuration(),
500 model->getSampleRate()); 525 model->getSampleRate());
501 526
502 QString pitchText; 527 QString pitchText;
503 528
504 float value = note.getValue(); 529 if (m_modelUsesHz) {
505 530
506 if (shouldConvertMIDIToHz()) { 531 float value = note.getValue();
507 532
508 int mnote = int(lrint(value));
509 int cents = int(lrint((value - float(mnote)) * 100));
510 double freq = Pitch::getFrequencyForPitch(mnote, cents);
511 pitchText = tr("%1 (%2, %3 Hz)")
512 .arg(Pitch::getPitchLabel(mnote, cents))
513 .arg(mnote)
514 .arg(freq);
515
516 } else if (getScaleUnits() == "Hz") {
517
518 pitchText = tr("%1 Hz (%2, %3)") 533 pitchText = tr("%1 Hz (%2, %3)")
519 .arg(value) 534 .arg(value)
520 .arg(Pitch::getPitchLabelForFrequency(value)) 535 .arg(Pitch::getPitchLabelForFrequency(value))
521 .arg(Pitch::getPitchForFrequency(value)); 536 .arg(Pitch::getPitchForFrequency(value));
522 537
523 } else { 538 } else {
524 pitchText = tr("%1 %2") 539
525 .arg(value).arg(getScaleUnits()); 540 float eventValue = note.getValue();
541 double value = convertValueFromEventValue(eventValue);
542
543 int mnote = int(lrint(eventValue));
544 int cents = int(lrint((eventValue - float(mnote)) * 100));
545
546 pitchText = tr("%1 (%2, %3 Hz)")
547 .arg(Pitch::getPitchLabel(mnote, cents))
548 .arg(eventValue)
549 .arg(value);
526 } 550 }
527 551
528 QString text; 552 QString text;
529 553
530 if (note.getLabel() == "") { 554 if (note.getLabel() == "") {
538 .arg(pitchText) 562 .arg(pitchText)
539 .arg(rd.toText(true).c_str()) 563 .arg(rd.toText(true).c_str())
540 .arg(note.getLabel()); 564 .arg(note.getLabel());
541 } 565 }
542 566
543 pos = QPoint(v->getXForFrame(note.getFrame()), getYForValue(v, value)); 567 pos = QPoint(v->getXForFrame(note.getFrame()),
568 getYForValue(v, valueOf(note)));
544 return text; 569 return text;
545 } 570 }
546 571
547 bool 572 bool
548 NoteLayer::snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame, 573 NoteLayer::snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame,
591 log = false; 616 log = false;
592 617
593 auto model = ModelById::getAs<NoteModel>(m_model); 618 auto model = ModelById::getAs<NoteModel>(m_model);
594 if (!model) return; 619 if (!model) return;
595 620
596 QString queryUnits;
597 if (shouldConvertMIDIToHz()) queryUnits = "Hz";
598 else queryUnits = getScaleUnits();
599
600 if (shouldAutoAlign()) { 621 if (shouldAutoAlign()) {
601 622
602 if (!v->getVisibleExtentsForUnit(queryUnits, min, max, log)) { 623 if (!v->getVisibleExtentsForUnit("Hz", min, max, log)) {
603 624
604 min = model->getValueMinimum(); 625 QString unit;
605 max = model->getValueMaximum(); 626 getValueExtents(min, max, log, unit);
606
607 if (shouldConvertMIDIToHz()) {
608 min = Pitch::getFrequencyForPitch(int(lrint(min)));
609 max = Pitch::getFrequencyForPitch(int(lrint(max + 1)));
610 }
611 627
612 #ifdef DEBUG_NOTE_LAYER 628 #ifdef DEBUG_NOTE_LAYER
613 cerr << "NoteLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << endl; 629 SVCERR << "NoteLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << endl;
614 #endif 630 #endif
615 631
616 } else if (log) { 632 } else if (log) {
617 633
618 LogRange::mapRange(min, max); 634 LogRange::mapRange(min, max);
619 635
620 #ifdef DEBUG_NOTE_LAYER 636 #ifdef DEBUG_NOTE_LAYER
621 cerr << "NoteLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << endl; 637 SVCERR << "NoteLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << endl;
622 #endif 638 #endif
623
624 } 639 }
625 640
626 } else { 641 } else {
627 642
628 getDisplayExtents(min, max); 643 getDisplayExtents(min, max);
629 644
630 if (m_verticalScale == MIDIRangeScale) { 645 if (m_verticalScale != LinearScale) {
631 min = Pitch::getFrequencyForPitch(0);
632 max = Pitch::getFrequencyForPitch(127);
633 } else if (shouldConvertMIDIToHz()) {
634 min = Pitch::getFrequencyForPitch(int(lrint(min)));
635 max = Pitch::getFrequencyForPitch(int(lrint(max + 1)));
636 }
637
638 if (m_verticalScale == LogScale || m_verticalScale == MIDIRangeScale) {
639 LogRange::mapRange(min, max); 646 LogRange::mapRange(min, max);
640 log = true; 647 log = true;
641 } 648 }
642 } 649 }
643 650
652 int h = v->getPaintHeight(); 659 int h = v->getPaintHeight();
653 660
654 getScaleExtents(v, min, max, logarithmic); 661 getScaleExtents(v, min, max, logarithmic);
655 662
656 #ifdef DEBUG_NOTE_LAYER 663 #ifdef DEBUG_NOTE_LAYER
657 cerr << "NoteLayer[" << this << "]::getYForValue(" << val << "): min = " << min << ", max = " << max << ", log = " << logarithmic << endl; 664 SVCERR << "NoteLayer[" << this << "]::getYForValue(" << val << "): min = " << min << ", max = " << max << ", log = " << logarithmic << endl;
658 #endif 665 #endif
659
660 if (shouldConvertMIDIToHz()) {
661 val = Pitch::getFrequencyForPitch(int(lrint(val)),
662 int(lrint((val - rint(val)) * 100)));
663 #ifdef DEBUG_NOTE_LAYER
664 cerr << "shouldConvertMIDIToHz true, val now = " << val << endl;
665 #endif
666 }
667 666
668 if (logarithmic) { 667 if (logarithmic) {
669 val = LogRange::map(val); 668 val = LogRange::map(val);
670 #ifdef DEBUG_NOTE_LAYER 669 #ifdef DEBUG_NOTE_LAYER
671 cerr << "logarithmic true, val now = " << val << endl; 670 SVCERR << "logarithmic true, val now = " << val << endl;
672 #endif 671 #endif
673 } 672 }
674 673
675 int y = int(h - ((val - min) * h) / (max - min)) - 1; 674 int y = int(h - ((val - min) * h) / (max - min)) - 1;
676 #ifdef DEBUG_NOTE_LAYER 675 #ifdef DEBUG_NOTE_LAYER
677 cerr << "y = " << y << endl; 676 SVCERR << "y = " << y << endl;
678 #endif 677 #endif
679 return y; 678 return y;
680 } 679 }
681 680
682 double 681 double
692 691
693 if (logarithmic) { 692 if (logarithmic) {
694 val = pow(10.0, val); 693 val = pow(10.0, val);
695 } 694 }
696 695
697 if (shouldConvertMIDIToHz()) {
698 val = Pitch::getPitchForFrequency(val);
699 }
700
701 return val; 696 return val;
702 } 697 }
703 698
704 bool 699 bool
705 NoteLayer::shouldAutoAlign() const 700 NoteLayer::shouldAutoAlign() const
732 brushColour.setAlpha(80); 727 brushColour.setAlpha(80);
733 728
734 // SVDEBUG << "NoteLayer::paint: resolution is " 729 // SVDEBUG << "NoteLayer::paint: resolution is "
735 // << model->getResolution() << " frames" << endl; 730 // << model->getResolution() << " frames" << endl;
736 731
737 double min = model->getValueMinimum(); 732 double min = convertValueFromEventValue(model->getValueMinimum());
738 double max = model->getValueMaximum(); 733 double max = convertValueFromEventValue(model->getValueMaximum());
739 if (max == min) max = min + 1.0; 734 if (max == min) max = min + 1.0;
740 735
741 QPoint localPos; 736 QPoint localPos;
742 Event illuminatePoint; 737 Event illuminatePoint;
743 bool shouldIlluminate = false; 738 bool shouldIlluminate = false;
744 739
745 if (v->shouldIlluminateLocalFeatures(this, localPos)) { 740 if (m_editing || m_editIsOpen) {
741 shouldIlluminate = true;
742 illuminatePoint = m_editingPoint;
743 } else if (v->shouldIlluminateLocalFeatures(this, localPos)) {
746 shouldIlluminate = getPointToDrag(v, localPos.x(), localPos.y(), 744 shouldIlluminate = getPointToDrag(v, localPos.x(), localPos.y(),
747 illuminatePoint); 745 illuminatePoint);
748 } else if (m_editIsOpen) {
749 shouldIlluminate = true;
750 illuminatePoint = m_editingPoint;
751 } 746 }
752 747
753 paint.save(); 748 paint.save();
754 paint.setRenderHint(QPainter::Antialiasing, false); 749 paint.setRenderHint(QPainter::Antialiasing, false);
755 750
757 i != points.end(); ++i) { 752 i != points.end(); ++i) {
758 753
759 const Event &p(*i); 754 const Event &p(*i);
760 755
761 int x = v->getXForFrame(p.getFrame()); 756 int x = v->getXForFrame(p.getFrame());
762 int y = getYForValue(v, p.getValue()); 757 int y = getYForValue(v, valueOf(p));
763 int w = v->getXForFrame(p.getFrame() + p.getDuration()) - x; 758 int w = v->getXForFrame(p.getFrame() + p.getDuration()) - x;
764 int h = 3; 759 int h = 3;
765 760
766 if (model->getValueQuantization() != 0.0) { 761 if (model->getValueQuantization() != 0.0) {
767 h = y - getYForValue(v, p.getValue() + model->getValueQuantization()); 762 h = y - getYForValue
763 (v, convertValueFromEventValue
764 (p.getValue() + model->getValueQuantization()));
768 if (h < 3) h = 3; 765 if (h < 3) h = 3;
769 } 766 }
770 767
771 if (w < 1) w = 1; 768 if (w < 1) w = 1;
772 paint.setPen(getBaseQColor()); 769 paint.setPen(getBaseQColor());
780 // Qt 5.13 deprecates QFontMetrics::width(), but its suggested 777 // Qt 5.13 deprecates QFontMetrics::width(), but its suggested
781 // replacement (horizontalAdvance) was only added in Qt 5.11 778 // replacement (horizontalAdvance) was only added in Qt 5.11
782 // which is too new for us 779 // which is too new for us
783 #pragma GCC diagnostic ignored "-Wdeprecated-declarations" 780 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
784 781
785 QString vlabel = QString("%1%2").arg(p.getValue()).arg(getScaleUnits()); 782 QString vlabel;
783 if (m_modelUsesHz) {
784 vlabel = QString("%1%2")
785 .arg(p.getValue())
786 .arg(model->getScaleUnits());
787 } else {
788 vlabel = QString("%1 %2")
789 .arg(p.getValue())
790 .arg(model->getScaleUnits());
791 }
792
786 PaintAssistant::drawVisibleText(v, paint, 793 PaintAssistant::drawVisibleText(v, paint,
787 x - paint.fontMetrics().width(vlabel) - 2, 794 x - paint.fontMetrics().width(vlabel) - 2,
788 y + paint.fontMetrics().height()/2 795 y + paint.fontMetrics().height()/2
789 - paint.fontMetrics().descent(), 796 - paint.fontMetrics().descent(),
790 vlabel, PaintAssistant::OutlinedText); 797 vlabel, PaintAssistant::OutlinedText);
812 819
813 if (shouldAutoAlign() && !valueExtentsMatchMine(v)) { 820 if (shouldAutoAlign() && !valueExtentsMatchMine(v)) {
814 return 0; 821 return 0;
815 } 822 }
816 823
817 if (m_verticalScale == LogScale || m_verticalScale == MIDIRangeScale) { 824 if (m_verticalScale != LinearScale) {
818 return LogNumericalScale().getWidth(v, paint) + 10; // for piano 825 return LogNumericalScale().getWidth(v, paint) + 10; // for piano
819 } else { 826 } else {
820 return LinearNumericalScale().getWidth(v, paint); 827 return LinearNumericalScale().getWidth(v, paint);
821 } 828 }
822 } 829 }
840 LogNumericalScale().paintVertical(v, this, paint, 0, min, max); 847 LogNumericalScale().paintVertical(v, this, paint, 0, min, max);
841 } else { 848 } else {
842 LinearNumericalScale().paintVertical(v, this, paint, 0, min, max); 849 LinearNumericalScale().paintVertical(v, this, paint, 0, min, max);
843 } 850 }
844 851
845 if (logarithmic && (getScaleUnits() == "Hz")) { 852 if (logarithmic) {
846 PianoScale().paintPianoVertical 853 PianoScale().paintPianoVertical
847 (v, paint, QRect(w - 10, 0, 10, h), 854 (v, paint, QRect(w - 10, 0, 10, h),
848 LogRange::unmap(min), 855 LogRange::unmap(min),
849 LogRange::unmap(max)); 856 LogRange::unmap(max));
850 paint.drawLine(w, 0, w, h); 857 paint.drawLine(w, 0, w, h);
871 sv_frame_t frame = v->getFrameForX(e->x()); 878 sv_frame_t frame = v->getFrameForX(e->x());
872 if (frame < 0) frame = 0; 879 if (frame < 0) frame = 0;
873 frame = frame / model->getResolution() * model->getResolution(); 880 frame = frame / model->getResolution() * model->getResolution();
874 881
875 double value = getValueForY(v, e->y()); 882 double value = getValueForY(v, e->y());
876 883 float eventValue = convertValueToEventValue(value);
877 m_editingPoint = Event(frame, float(value), 0, 0.8f, tr("New Point")); 884 eventValue = roundf(eventValue);
885
886 m_editingPoint = Event(frame, eventValue, 0, 0.8f, tr("New Point"));
878 m_originalPoint = m_editingPoint; 887 m_originalPoint = m_editingPoint;
879 888
880 if (m_editingCommand) finish(m_editingCommand); 889 if (m_editingCommand) finish(m_editingCommand);
881 m_editingCommand = new ChangeEventsCommand(m_model.untyped, tr("Draw Point")); 890 m_editingCommand = new ChangeEventsCommand(m_model.untyped, tr("Draw Point"));
882 m_editingCommand->add(m_editingPoint); 891 m_editingCommand->add(m_editingPoint);
895 sv_frame_t frame = v->getFrameForX(e->x()); 904 sv_frame_t frame = v->getFrameForX(e->x());
896 if (frame < 0) frame = 0; 905 if (frame < 0) frame = 0;
897 frame = frame / model->getResolution() * model->getResolution(); 906 frame = frame / model->getResolution() * model->getResolution();
898 907
899 double newValue = getValueForY(v, e->y()); 908 double newValue = getValueForY(v, e->y());
909 float newEventValue = convertValueToEventValue(newValue);
910 newEventValue = roundf(newEventValue);
900 911
901 sv_frame_t newFrame = m_editingPoint.getFrame(); 912 sv_frame_t newFrame = m_editingPoint.getFrame();
902 sv_frame_t newDuration = frame - newFrame; 913 sv_frame_t newDuration = frame - newFrame;
903 if (newDuration < 0) { 914 if (newDuration < 0) {
904 newFrame = frame; 915 newFrame = frame;
908 } 919 }
909 920
910 m_editingCommand->remove(m_editingPoint); 921 m_editingCommand->remove(m_editingPoint);
911 m_editingPoint = m_editingPoint 922 m_editingPoint = m_editingPoint
912 .withFrame(newFrame) 923 .withFrame(newFrame)
913 .withValue(float(newValue)) 924 .withDuration(newDuration)
914 .withDuration(newDuration); 925 .withValue(newEventValue);
915 m_editingCommand->add(m_editingPoint); 926 m_editingCommand->add(m_editingPoint);
916 } 927 }
917 928
918 void 929 void
919 NoteLayer::drawEnd(LayerGeometryProvider *, QMouseEvent *) 930 NoteLayer::drawEnd(LayerGeometryProvider *, QMouseEvent *)
979 990
980 if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return; 991 if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return;
981 m_originalPoint = m_editingPoint; 992 m_originalPoint = m_editingPoint;
982 993
983 m_dragPointX = v->getXForFrame(m_editingPoint.getFrame()); 994 m_dragPointX = v->getXForFrame(m_editingPoint.getFrame());
984 m_dragPointY = getYForValue(v, m_editingPoint.getValue()); 995 m_dragPointY = getYForValue(v, valueOf(m_editingPoint));
985 996
986 if (m_editingCommand) { 997 if (m_editingCommand) {
987 finish(m_editingCommand); 998 finish(m_editingCommand);
988 m_editingCommand = nullptr; 999 m_editingCommand = nullptr;
989 } 1000 }
1008 1019
1009 sv_frame_t frame = v->getFrameForX(newx); 1020 sv_frame_t frame = v->getFrameForX(newx);
1010 if (frame < 0) frame = 0; 1021 if (frame < 0) frame = 0;
1011 frame = frame / model->getResolution() * model->getResolution(); 1022 frame = frame / model->getResolution() * model->getResolution();
1012 1023
1013 double value = getValueForY(v, newy); 1024 double newValue = getValueForY(v, newy);
1025 float newEventValue = convertValueToEventValue(newValue);
1026 newEventValue = roundf(newEventValue);
1014 1027
1015 if (!m_editingCommand) { 1028 if (!m_editingCommand) {
1016 m_editingCommand = new ChangeEventsCommand 1029 m_editingCommand = new ChangeEventsCommand
1017 (m_model.untyped, tr("Drag Point")); 1030 (m_model.untyped, tr("Drag Point"));
1018 } 1031 }
1019 1032
1020 m_editingCommand->remove(m_editingPoint); 1033 m_editingCommand->remove(m_editingPoint);
1021 m_editingPoint = m_editingPoint 1034 m_editingPoint = m_editingPoint
1022 .withFrame(frame) 1035 .withFrame(frame)
1023 .withValue(float(value)); 1036 .withValue(newEventValue);
1024 m_editingCommand->add(m_editingPoint); 1037 m_editingCommand->add(m_editingPoint);
1025 } 1038 }
1026 1039
1027 void 1040 void
1028 NoteLayer::editEnd(LayerGeometryProvider *, QMouseEvent *) 1041 NoteLayer::editEnd(LayerGeometryProvider *, QMouseEvent *)
1268 } 1281 }
1269 1282
1270 void 1283 void
1271 NoteLayer::addNoteOn(sv_frame_t frame, int pitch, int velocity) 1284 NoteLayer::addNoteOn(sv_frame_t frame, int pitch, int velocity)
1272 { 1285 {
1273 m_pendingNoteOns.insert(Event(frame, float(pitch), 0, 1286 double value = Pitch::getFrequencyForPitch(pitch);
1287 float eventValue = convertValueToEventValue(value);
1288 m_pendingNoteOns.insert(Event(frame, eventValue, 0,
1274 float(velocity) / 127.f, QString())); 1289 float(velocity) / 127.f, QString()));
1275 } 1290 }
1276 1291
1277 void 1292 void
1278 NoteLayer::addNoteOff(sv_frame_t frame, int pitch) 1293 NoteLayer::addNoteOff(sv_frame_t frame, int pitch)
1281 1296
1282 for (NoteSet::iterator i = m_pendingNoteOns.begin(); 1297 for (NoteSet::iterator i = m_pendingNoteOns.begin();
1283 i != m_pendingNoteOns.end(); ++i) { 1298 i != m_pendingNoteOns.end(); ++i) {
1284 1299
1285 Event p = *i; 1300 Event p = *i;
1286 1301 double value = valueOf(p);
1287 if (lrintf(p.getValue()) == pitch) { 1302 int eventPitch = Pitch::getPitchForFrequency(value);
1303
1304 if (eventPitch == pitch) {
1288 m_pendingNoteOns.erase(i); 1305 m_pendingNoteOns.erase(i);
1289 Event note = p.withDuration(frame - p.getFrame()); 1306 Event note = p.withDuration(frame - p.getFrame());
1290 if (model) { 1307 if (model) {
1291 ChangeEventsCommand *c = new ChangeEventsCommand 1308 ChangeEventsCommand *c = new ChangeEventsCommand
1292 (m_model.untyped, tr("Record Note")); 1309 (m_model.untyped, tr("Record Note"));