Mercurial > hg > svgui
comparison layer/NoteLayer.cpp @ 101:0f36cdf407a6 sv1-v0.9rc1
* Make vertical scale alignment modes work in note layer as well as time-value
layer, and several significant fixes to it
* Make it possible to draw notes properly on the note layer
* Show units (and frequencies etc in note layer's case) in the time-value and
note layer description boxes
* Minor fix to item edit dialog layout
* Some minor menu rearrangement
* Comment out a lot of debug output
* Add SV website and reference URLs to Help menu, and add code to (attempt to)
open them in the user's preferred browser
author | Chris Cannam |
---|---|
date | Fri, 12 May 2006 14:40:43 +0000 |
parents | 0db5e7492ce8 |
children | 571805759a66 |
comparison
equal
deleted
inserted
replaced
100:0db5e7492ce8 | 101:0f36cdf407a6 |
---|---|
40 m_editing(false), | 40 m_editing(false), |
41 m_originalPoint(0, 0.0, 0, tr("New Point")), | 41 m_originalPoint(0, 0.0, 0, tr("New Point")), |
42 m_editingPoint(0, 0.0, 0, tr("New Point")), | 42 m_editingPoint(0, 0.0, 0, tr("New Point")), |
43 m_editingCommand(0), | 43 m_editingCommand(0), |
44 m_colour(Qt::black), | 44 m_colour(Qt::black), |
45 m_verticalScale(MinMaxRangeScale) | 45 m_verticalScale(AutoAlignScale) |
46 { | 46 { |
47 | 47 |
48 } | 48 } |
49 | 49 |
50 void | 50 void |
58 this, SIGNAL(modelChanged(size_t, size_t))); | 58 this, SIGNAL(modelChanged(size_t, size_t))); |
59 | 59 |
60 connect(m_model, SIGNAL(completionChanged()), | 60 connect(m_model, SIGNAL(completionChanged()), |
61 this, SIGNAL(modelCompletionChanged())); | 61 this, SIGNAL(modelCompletionChanged())); |
62 | 62 |
63 std::cerr << "NoteLayer::setModel(" << model << ")" << std::endl; | 63 // std::cerr << "NoteLayer::setModel(" << model << ")" << std::endl; |
64 | 64 |
65 emit modelReplaced(); | 65 emit modelReplaced(); |
66 } | 66 } |
67 | 67 |
68 Layer::PropertyList | 68 Layer::PropertyList |
112 else if (m_colour == QColor(255, 150, 50)) deft = 5; | 112 else if (m_colour == QColor(255, 150, 50)) deft = 5; |
113 | 113 |
114 } else if (name == "Vertical Scale") { | 114 } else if (name == "Vertical Scale") { |
115 | 115 |
116 if (min) *min = 0; | 116 if (min) *min = 0; |
117 if (max) *max = 2; | 117 if (max) *max = 3; |
118 | 118 |
119 deft = int(m_verticalScale); | 119 deft = int(m_verticalScale); |
120 | 120 |
121 } else if (name == "Scale Units") { | 121 } else if (name == "Scale Units") { |
122 | 122 |
148 case 5: return tr("Orange"); | 148 case 5: return tr("Orange"); |
149 } | 149 } |
150 } else if (name == "Vertical Scale") { | 150 } else if (name == "Vertical Scale") { |
151 switch (value) { | 151 switch (value) { |
152 default: | 152 default: |
153 case 0: return tr("Note Range In Use"); | 153 case 0: return tr("Auto-Align"); |
154 case 1: return tr("MIDI Note Range"); | 154 case 1: return tr("Linear Scale"); |
155 case 2: return tr("Frequency"); | 155 case 2: return tr("Log Scale"); |
156 case 3: return tr("MIDI Note Range"); | |
156 } | 157 } |
157 } | 158 } |
158 return tr("<unknown>"); | 159 return tr("<unknown>"); |
159 } | 160 } |
160 | 161 |
204 QPoint discard; | 205 QPoint discard; |
205 return !v->shouldIlluminateLocalFeatures(this, discard); | 206 return !v->shouldIlluminateLocalFeatures(this, discard); |
206 } | 207 } |
207 | 208 |
208 bool | 209 bool |
209 NoteLayer::getValueExtents(float &min, float &max, QString &unit) const | 210 NoteLayer::shouldConvertMIDIToHz() const |
211 { | |
212 QString unit = m_model->getScaleUnits(); | |
213 return (unit != "Hz"); | |
214 // if (unit == "" || | |
215 // unit.startsWith("MIDI") || | |
216 // unit.startsWith("midi")) return true; | |
217 // return false; | |
218 } | |
219 | |
220 bool | |
221 NoteLayer::getValueExtents(float &min, float &max, | |
222 bool &logarithmic, QString &unit) const | |
210 { | 223 { |
211 if (!m_model) return false; | 224 if (!m_model) return false; |
212 min = m_model->getValueMinimum(); | 225 min = m_model->getValueMinimum(); |
213 max = m_model->getValueMaximum(); | 226 max = m_model->getValueMaximum(); |
214 unit = m_model->getScaleUnits(); | 227 |
228 if (shouldConvertMIDIToHz()) unit = "Hz"; | |
229 else unit = m_model->getScaleUnits(); | |
230 | |
231 if (m_verticalScale == MIDIRangeScale || | |
232 m_verticalScale == LogScale) logarithmic = true; | |
233 | |
234 return true; | |
235 } | |
236 | |
237 bool | |
238 NoteLayer::getDisplayExtents(float &min, float &max) const | |
239 { | |
240 if (!m_model || m_verticalScale == AutoAlignScale) return false; | |
241 | |
242 if (m_verticalScale == MIDIRangeScale) { | |
243 min = Pitch::getFrequencyForPitch(0); | |
244 max = Pitch::getFrequencyForPitch(127); | |
245 return true; | |
246 } | |
247 | |
248 min = m_model->getValueMinimum(); | |
249 max = m_model->getValueMaximum(); | |
250 | |
251 if (shouldConvertMIDIToHz()) { | |
252 min = Pitch::getFrequencyForPitch(lrintf(min)); | |
253 max = Pitch::getFrequencyForPitch(lrintf(max + 1)); | |
254 } | |
255 | |
215 return true; | 256 return true; |
216 } | 257 } |
217 | 258 |
218 NoteModel::PointList | 259 NoteModel::PointList |
219 NoteLayer::getLocalPoints(View *v, int x) const | 260 NoteLayer::getLocalPoints(View *v, int x) const |
299 RealTime rt = RealTime::frame2RealTime(note.frame, | 340 RealTime rt = RealTime::frame2RealTime(note.frame, |
300 m_model->getSampleRate()); | 341 m_model->getSampleRate()); |
301 RealTime rd = RealTime::frame2RealTime(note.duration, | 342 RealTime rd = RealTime::frame2RealTime(note.duration, |
302 m_model->getSampleRate()); | 343 m_model->getSampleRate()); |
303 | 344 |
345 QString pitchText; | |
346 | |
347 if (shouldConvertMIDIToHz()) { | |
348 | |
349 int mnote = lrintf(note.value); | |
350 int cents = lrintf((note.value - mnote) * 100); | |
351 float freq = Pitch::getFrequencyForPitch(mnote, cents); | |
352 pitchText = QString("%1 (%2 Hz)") | |
353 .arg(Pitch::getPitchLabel(mnote, cents)).arg(freq); | |
354 | |
355 } else if (m_model->getScaleUnits() == "Hz") { | |
356 | |
357 pitchText = QString("%1 Hz (%2)") | |
358 .arg(note.value) | |
359 .arg(Pitch::getPitchLabelForFrequency(note.value)); | |
360 | |
361 } else { | |
362 pitchText = QString("%1 %2") | |
363 .arg(note.value).arg(m_model->getScaleUnits()); | |
364 } | |
365 | |
304 QString text; | 366 QString text; |
305 | 367 |
306 if (note.label == "") { | 368 if (note.label == "") { |
307 text = QString(tr("Time:\t%1\nPitch:\t%2\nDuration:\t%3\nNo label")) | 369 text = QString(tr("Time:\t%1\nPitch:\t%2\nDuration:\t%3\nNo label")) |
308 .arg(rt.toText(true).c_str()) | 370 .arg(rt.toText(true).c_str()) |
309 .arg(note.value) | 371 .arg(pitchText) |
310 .arg(rd.toText(true).c_str()); | 372 .arg(rd.toText(true).c_str()); |
311 } else { | 373 } else { |
312 text = QString(tr("Time:\t%1\nPitch:\t%2\nDuration:\t%3\nLabel:\t%4")) | 374 text = QString(tr("Time:\t%1\nPitch:\t%2\nDuration:\t%3\nLabel:\t%4")) |
313 .arg(rt.toText(true).c_str()) | 375 .arg(rt.toText(true).c_str()) |
314 .arg(note.value) | 376 .arg(pitchText) |
315 .arg(rd.toText(true).c_str()) | 377 .arg(rd.toText(true).c_str()) |
316 .arg(note.label); | 378 .arg(note.label); |
317 } | 379 } |
318 | 380 |
319 pos = QPoint(v->getXForFrame(note.frame), | 381 pos = QPoint(v->getXForFrame(note.frame), |
391 | 453 |
392 frame = snapped; | 454 frame = snapped; |
393 return found; | 455 return found; |
394 } | 456 } |
395 | 457 |
458 void | |
459 NoteLayer::getScaleExtents(View *v, float &min, float &max, bool &log) const | |
460 { | |
461 min = 0.0; | |
462 max = 0.0; | |
463 log = false; | |
464 | |
465 QString queryUnits; | |
466 if (shouldConvertMIDIToHz()) queryUnits = "Hz"; | |
467 else queryUnits = m_model->getScaleUnits(); | |
468 | |
469 if (m_verticalScale == AutoAlignScale) { | |
470 | |
471 if (!v->getValueExtents(queryUnits, min, max, log)) { | |
472 | |
473 min = m_model->getValueMinimum(); | |
474 max = m_model->getValueMaximum(); | |
475 | |
476 if (shouldConvertMIDIToHz()) { | |
477 min = Pitch::getFrequencyForPitch(lrintf(min)); | |
478 max = Pitch::getFrequencyForPitch(lrintf(max + 1)); | |
479 } | |
480 | |
481 } else if (log) { | |
482 | |
483 // std::cerr << "NoteLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << std::endl; | |
484 | |
485 min = (min < 0.0) ? -log10(-min) : (min == 0.0) ? 0.0 : log10(min); | |
486 max = (max < 0.0) ? -log10(-max) : (max == 0.0) ? 0.0 : log10(max); | |
487 } | |
488 | |
489 } else { | |
490 | |
491 min = m_model->getValueMinimum(); | |
492 max = m_model->getValueMaximum(); | |
493 | |
494 if (m_verticalScale == MIDIRangeScale) { | |
495 min = Pitch::getFrequencyForPitch(0); | |
496 max = Pitch::getFrequencyForPitch(127); | |
497 } else if (shouldConvertMIDIToHz()) { | |
498 min = Pitch::getFrequencyForPitch(lrintf(min)); | |
499 max = Pitch::getFrequencyForPitch(lrintf(max + 1)); | |
500 } | |
501 | |
502 if (m_verticalScale == LogScale || m_verticalScale == MIDIRangeScale) { | |
503 min = (min < 0.0) ? -log10(-min) : (min == 0.0) ? 0.0 : log10(min); | |
504 max = (max < 0.0) ? -log10(-max) : (max == 0.0) ? 0.0 : log10(max); | |
505 log = true; | |
506 } | |
507 } | |
508 | |
509 if (max == min) max = min + 1.0; | |
510 } | |
511 | |
396 int | 512 int |
397 NoteLayer::getYForValue(View *v, float value) const | 513 NoteLayer::getYForValue(View *v, float val) const |
398 { | 514 { |
399 float min, max, h = v->height(); | 515 float min = 0.0, max = 0.0; |
400 | 516 bool logarithmic = false; |
401 switch (m_verticalScale) { | 517 int h = v->height(); |
402 | 518 |
403 case MIDIRangeScale: | 519 getScaleExtents(v, min, max, logarithmic); |
404 min = 0.0; | 520 |
405 max = 127.0; | 521 // std::cerr << "NoteLayer[" << this << "]::getYForValue(" << val << "): min = " << min << ", max = " << max << ", log = " << logarithmic << std::endl; |
406 break; | 522 |
407 | 523 if (shouldConvertMIDIToHz()) { |
408 case MinMaxRangeScale: | 524 val = Pitch::getFrequencyForPitch(lrintf(val), |
409 min = m_model->getValueMinimum(); | 525 lrintf((val - lrintf(val)) * 100)); |
410 max = m_model->getValueMaximum(); | 526 // std::cerr << "shouldConvertMIDIToHz true, val now = " << val << std::endl; |
411 break; | 527 } |
412 | 528 |
413 case FrequencyScale: | 529 if (logarithmic) { |
414 | 530 val = (val < 0.0) ? -log10(-val) : (val == 0.0) ? 0.0 : log10(val); |
415 value = Pitch::getFrequencyForPitch(lrintf(value), | 531 // std::cerr << "logarithmic true, val now = " << val << std::endl; |
416 value - lrintf(value)); | 532 } |
417 | 533 |
418 // If we have a spectrogram layer on the same view as us, align | 534 int y = int(h - ((val - min) * h) / (max - min)) - 1; |
419 // ourselves with it... | 535 // std::cerr << "y = " << y << std::endl; |
420 for (int i = 0; i < v->getLayerCount(); ++i) { | 536 return y; |
421 SpectrogramLayer *spectrogram = dynamic_cast<SpectrogramLayer *> | |
422 (v->getLayer(i)); | |
423 if (spectrogram) { | |
424 return spectrogram->getYForFrequency(v, value); | |
425 } | |
426 } | |
427 | |
428 // ...otherwise just interpolate | |
429 std::cerr << "FrequencyScale: value in = " << value << std::endl; | |
430 min = m_model->getValueMinimum(); | |
431 min = Pitch::getFrequencyForPitch(lrintf(min), min - lrintf(min)); | |
432 max = m_model->getValueMaximum(); | |
433 max = Pitch::getFrequencyForPitch(lrintf(max), max - lrintf(max)); | |
434 std::cerr << "FrequencyScale: min = " << min << ", max = " << max << ", value = " << value << std::endl; | |
435 break; | |
436 } | |
437 | |
438 if (max < min) max = min; | |
439 max = max + 1.0; | |
440 | |
441 return int(h - ((value - min) * h) / (max - min)) - 1; | |
442 } | 537 } |
443 | 538 |
444 float | 539 float |
445 NoteLayer::getValueForY(View *v, int y) const | 540 NoteLayer::getValueForY(View *v, int y) const |
446 { | 541 { |
447 //!!! | 542 float min = 0.0, max = 0.0; |
448 | 543 bool logarithmic = false; |
449 float min = m_model->getValueMinimum(); | |
450 float max = m_model->getValueMaximum(); | |
451 if (max == min) max = min + 1.0; | |
452 | |
453 int h = v->height(); | 544 int h = v->height(); |
454 | 545 |
455 return min + (float(h - y) * float(max - min)) / h; | 546 getScaleExtents(v, min, max, logarithmic); |
547 | |
548 float val = min + (float(h - y) * float(max - min)) / h; | |
549 | |
550 if (logarithmic) { | |
551 val = pow(10, val); | |
552 } | |
553 | |
554 if (shouldConvertMIDIToHz()) { | |
555 val = Pitch::getPitchForFrequency(val); | |
556 } | |
557 | |
558 return val; | |
456 } | 559 } |
457 | 560 |
458 void | 561 void |
459 NoteLayer::paint(View *v, QPainter &paint, QRect rect) const | 562 NoteLayer::paint(View *v, QPainter &paint, QRect rect) const |
460 { | 563 { |
536 } | 639 } |
537 | 640 |
538 void | 641 void |
539 NoteLayer::drawStart(View *v, QMouseEvent *e) | 642 NoteLayer::drawStart(View *v, QMouseEvent *e) |
540 { | 643 { |
541 std::cerr << "NoteLayer::drawStart(" << e->x() << "," << e->y() << ")" << std::endl; | 644 // std::cerr << "NoteLayer::drawStart(" << e->x() << "," << e->y() << ")" << std::endl; |
542 | 645 |
543 if (!m_model) return; | 646 if (!m_model) return; |
544 | 647 |
545 long frame = v->getFrameForX(e->x()); | 648 long frame = v->getFrameForX(e->x()); |
546 if (frame < 0) frame = 0; | 649 if (frame < 0) frame = 0; |
560 } | 663 } |
561 | 664 |
562 void | 665 void |
563 NoteLayer::drawDrag(View *v, QMouseEvent *e) | 666 NoteLayer::drawDrag(View *v, QMouseEvent *e) |
564 { | 667 { |
565 std::cerr << "NoteLayer::drawDrag(" << e->x() << "," << e->y() << ")" << std::endl; | 668 // std::cerr << "NoteLayer::drawDrag(" << e->x() << "," << e->y() << ")" << std::endl; |
566 | 669 |
567 if (!m_model || !m_editing) return; | 670 if (!m_model || !m_editing) return; |
568 | 671 |
569 long frame = v->getFrameForX(e->x()); | 672 long frame = v->getFrameForX(e->x()); |
570 if (frame < 0) frame = 0; | 673 if (frame < 0) frame = 0; |
571 frame = frame / m_model->getResolution() * m_model->getResolution(); | 674 frame = frame / m_model->getResolution() * m_model->getResolution(); |
572 | 675 |
676 float newValue = getValueForY(v, e->y()); | |
677 | |
678 long newFrame = m_editingPoint.frame; | |
679 long newDuration = frame - newFrame; | |
680 if (newDuration < 0) { | |
681 newFrame = frame; | |
682 newDuration = -newDuration; | |
683 } else if (newDuration == 0) { | |
684 newDuration = 1; | |
685 } | |
686 | |
687 m_editingCommand->deletePoint(m_editingPoint); | |
688 m_editingPoint.frame = newFrame; | |
689 m_editingPoint.value = newValue; | |
690 m_editingPoint.duration = newDuration; | |
691 m_editingCommand->addPoint(m_editingPoint); | |
692 } | |
693 | |
694 void | |
695 NoteLayer::drawEnd(View *v, QMouseEvent *e) | |
696 { | |
697 // std::cerr << "NoteLayer::drawEnd(" << e->x() << "," << e->y() << ")" << std::endl; | |
698 if (!m_model || !m_editing) return; | |
699 m_editingCommand->finish(); | |
700 m_editingCommand = 0; | |
701 m_editing = false; | |
702 } | |
703 | |
704 void | |
705 NoteLayer::editStart(View *v, QMouseEvent *e) | |
706 { | |
707 // std::cerr << "NoteLayer::editStart(" << e->x() << "," << e->y() << ")" << std::endl; | |
708 | |
709 if (!m_model) return; | |
710 | |
711 NoteModel::PointList points = getLocalPoints(v, e->x()); | |
712 if (points.empty()) return; | |
713 | |
714 m_editingPoint = *points.begin(); | |
715 m_originalPoint = m_editingPoint; | |
716 | |
717 if (m_editingCommand) { | |
718 m_editingCommand->finish(); | |
719 m_editingCommand = 0; | |
720 } | |
721 | |
722 m_editing = true; | |
723 } | |
724 | |
725 void | |
726 NoteLayer::editDrag(View *v, QMouseEvent *e) | |
727 { | |
728 // std::cerr << "NoteLayer::editDrag(" << e->x() << "," << e->y() << ")" << std::endl; | |
729 | |
730 if (!m_model || !m_editing) return; | |
731 | |
732 long frame = v->getFrameForX(e->x()); | |
733 if (frame < 0) frame = 0; | |
734 frame = frame / m_model->getResolution() * m_model->getResolution(); | |
735 | |
573 float value = getValueForY(v, e->y()); | 736 float value = getValueForY(v, e->y()); |
737 | |
738 if (!m_editingCommand) { | |
739 m_editingCommand = new NoteModel::EditCommand(m_model, | |
740 tr("Drag Point")); | |
741 } | |
574 | 742 |
575 m_editingCommand->deletePoint(m_editingPoint); | 743 m_editingCommand->deletePoint(m_editingPoint); |
576 m_editingPoint.frame = frame; | 744 m_editingPoint.frame = frame; |
577 m_editingPoint.value = value; | 745 m_editingPoint.value = value; |
578 m_editingCommand->addPoint(m_editingPoint); | 746 m_editingCommand->addPoint(m_editingPoint); |
579 } | 747 } |
580 | 748 |
581 void | 749 void |
582 NoteLayer::drawEnd(View *v, QMouseEvent *e) | |
583 { | |
584 std::cerr << "NoteLayer::drawEnd(" << e->x() << "," << e->y() << ")" << std::endl; | |
585 if (!m_model || !m_editing) return; | |
586 m_editingCommand->finish(); | |
587 m_editingCommand = 0; | |
588 m_editing = false; | |
589 } | |
590 | |
591 void | |
592 NoteLayer::editStart(View *v, QMouseEvent *e) | |
593 { | |
594 std::cerr << "NoteLayer::editStart(" << e->x() << "," << e->y() << ")" << std::endl; | |
595 | |
596 if (!m_model) return; | |
597 | |
598 NoteModel::PointList points = getLocalPoints(v, e->x()); | |
599 if (points.empty()) return; | |
600 | |
601 m_editingPoint = *points.begin(); | |
602 m_originalPoint = m_editingPoint; | |
603 | |
604 if (m_editingCommand) { | |
605 m_editingCommand->finish(); | |
606 m_editingCommand = 0; | |
607 } | |
608 | |
609 m_editing = true; | |
610 } | |
611 | |
612 void | |
613 NoteLayer::editDrag(View *v, QMouseEvent *e) | |
614 { | |
615 std::cerr << "NoteLayer::editDrag(" << e->x() << "," << e->y() << ")" << std::endl; | |
616 | |
617 if (!m_model || !m_editing) return; | |
618 | |
619 long frame = v->getFrameForX(e->x()); | |
620 if (frame < 0) frame = 0; | |
621 frame = frame / m_model->getResolution() * m_model->getResolution(); | |
622 | |
623 float value = getValueForY(v, e->y()); | |
624 | |
625 if (!m_editingCommand) { | |
626 m_editingCommand = new NoteModel::EditCommand(m_model, | |
627 tr("Drag Point")); | |
628 } | |
629 | |
630 m_editingCommand->deletePoint(m_editingPoint); | |
631 m_editingPoint.frame = frame; | |
632 m_editingPoint.value = value; | |
633 m_editingCommand->addPoint(m_editingPoint); | |
634 } | |
635 | |
636 void | |
637 NoteLayer::editEnd(View *v, QMouseEvent *e) | 750 NoteLayer::editEnd(View *v, QMouseEvent *e) |
638 { | 751 { |
639 std::cerr << "NoteLayer::editEnd(" << e->x() << "," << e->y() << ")" << std::endl; | 752 // std::cerr << "NoteLayer::editEnd(" << e->x() << "," << e->y() << ")" << std::endl; |
640 if (!m_model || !m_editing) return; | 753 if (!m_model || !m_editing) return; |
641 | 754 |
642 if (m_editingCommand) { | 755 if (m_editingCommand) { |
643 | 756 |
644 QString newName = m_editingCommand->getName(); | 757 QString newName = m_editingCommand->getName(); |