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();