Mercurial > hg > svgui
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")); |