svgui
1.9
|
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ 00002 00003 /* 00004 Sonic Visualiser 00005 An audio file viewer and annotation editor. 00006 Centre for Digital Music, Queen Mary, University of London. 00007 This file copyright 2006 Chris Cannam. 00008 00009 This program is free software; you can redistribute it and/or 00010 modify it under the terms of the GNU General Public License as 00011 published by the Free Software Foundation; either version 2 of the 00012 License, or (at your option) any later version. See the file 00013 COPYING included with this distribution for more information. 00014 */ 00015 00016 #include "FlexiNoteLayer.h" 00017 00018 #include "data/model/Model.h" 00019 #include "data/model/SparseTimeValueModel.h" 00020 #include "base/RealTime.h" 00021 #include "base/Profiler.h" 00022 #include "base/Pitch.h" 00023 #include "base/LogRange.h" 00024 #include "base/RangeMapper.h" 00025 #include "ColourDatabase.h" 00026 #include "view/View.h" 00027 00028 #include "PianoScale.h" 00029 #include "LinearNumericalScale.h" 00030 #include "LogNumericalScale.h" 00031 00032 #include "data/model/FlexiNoteModel.h" 00033 00034 #include "widgets/ItemEditDialog.h" 00035 #include "widgets/TextAbbrev.h" 00036 00037 #include <QPainter> 00038 #include <QPainterPath> 00039 #include <QMouseEvent> 00040 #include <QTextStream> 00041 #include <QMessageBox> 00042 00043 #include <iostream> 00044 #include <cmath> 00045 #include <utility> 00046 #include <limits> // GF: included to compile std::numerical_limits on linux 00047 #include <vector> 00048 00049 00050 FlexiNoteLayer::FlexiNoteLayer() : 00051 SingleColourLayer(), 00052 00053 // m_model(0), 00054 // m_editing(false), 00055 // m_originalPoint(0, 0.0, 0, 1.f, tr("New Point")), 00056 // m_editingPoint(0, 0.0, 0, 1.f, tr("New Point")), 00057 // m_editingCommand(0), 00058 // m_verticalScale(AutoAlignScale), 00059 // m_scaleMinimum(0), 00060 // m_scaleMaximum(0) 00061 00062 m_model(0), 00063 m_editing(false), 00064 m_intelligentActions(true), 00065 m_dragPointX(0), 00066 m_dragPointY(0), 00067 m_dragStartX(0), 00068 m_dragStartY(0), 00069 m_originalPoint(0, 0.0, 0, 1.f, tr("New Point")), 00070 m_editingPoint(0, 0.0, 0, 1.f, tr("New Point")), 00071 m_greatestLeftNeighbourFrame(0), 00072 m_smallestRightNeighbourFrame(0), 00073 m_editingCommand(0), 00074 m_verticalScale(AutoAlignScale), 00075 m_editMode(DragNote), 00076 m_scaleMinimum(34), 00077 m_scaleMaximum(77) 00078 { 00079 } 00080 00081 void 00082 FlexiNoteLayer::setModel(FlexiNoteModel *model) 00083 { 00084 if (m_model == model) return; 00085 m_model = model; 00086 00087 connectSignals(m_model); 00088 00089 // m_scaleMinimum = 0; 00090 // m_scaleMaximum = 0; 00091 00092 emit modelReplaced(); 00093 } 00094 00095 Layer::PropertyList 00096 FlexiNoteLayer::getProperties() const 00097 { 00098 PropertyList list = SingleColourLayer::getProperties(); 00099 list.push_back("Vertical Scale"); 00100 list.push_back("Scale Units"); 00101 return list; 00102 } 00103 00104 QString 00105 FlexiNoteLayer::getPropertyLabel(const PropertyName &name) const 00106 { 00107 if (name == "Vertical Scale") return tr("Vertical Scale"); 00108 if (name == "Scale Units") return tr("Scale Units"); 00109 return SingleColourLayer::getPropertyLabel(name); 00110 } 00111 00112 Layer::PropertyType 00113 FlexiNoteLayer::getPropertyType(const PropertyName &name) const 00114 { 00115 if (name == "Scale Units") return UnitsProperty; 00116 if (name == "Vertical Scale") return ValueProperty; 00117 return SingleColourLayer::getPropertyType(name); 00118 } 00119 00120 QString 00121 FlexiNoteLayer::getPropertyGroupName(const PropertyName &name) const 00122 { 00123 if (name == "Vertical Scale" || name == "Scale Units") { 00124 return tr("Scale"); 00125 } 00126 return SingleColourLayer::getPropertyGroupName(name); 00127 } 00128 00129 QString 00130 FlexiNoteLayer::getScaleUnits() const 00131 { 00132 if (m_model) return m_model->getScaleUnits(); 00133 else return ""; 00134 } 00135 00136 int 00137 FlexiNoteLayer::getPropertyRangeAndValue(const PropertyName &name, 00138 int *min, int *max, int *deflt) const 00139 { 00140 int val = 0; 00141 00142 if (name == "Vertical Scale") { 00143 00144 if (min) *min = 0; 00145 if (max) *max = 3; 00146 if (deflt) *deflt = int(AutoAlignScale); 00147 00148 val = int(m_verticalScale); 00149 00150 } else if (name == "Scale Units") { 00151 00152 if (deflt) *deflt = 0; 00153 if (m_model) { 00154 val = UnitDatabase::getInstance()->getUnitId 00155 (getScaleUnits()); 00156 } 00157 00158 } else { 00159 00160 val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt); 00161 } 00162 00163 return val; 00164 } 00165 00166 QString 00167 FlexiNoteLayer::getPropertyValueLabel(const PropertyName &name, 00168 int value) const 00169 { 00170 if (name == "Vertical Scale") { 00171 switch (value) { 00172 default: 00173 case 0: return tr("Auto-Align"); 00174 case 1: return tr("Linear"); 00175 case 2: return tr("Log"); 00176 case 3: return tr("MIDI Notes"); 00177 } 00178 } 00179 return SingleColourLayer::getPropertyValueLabel(name, value); 00180 } 00181 00182 void 00183 FlexiNoteLayer::setProperty(const PropertyName &name, int value) 00184 { 00185 if (name == "Vertical Scale") { 00186 setVerticalScale(VerticalScale(value)); 00187 } else if (name == "Scale Units") { 00188 if (m_model) { 00189 m_model->setScaleUnits 00190 (UnitDatabase::getInstance()->getUnitById(value)); 00191 emit modelChanged(); 00192 } 00193 } else { 00194 return SingleColourLayer::setProperty(name, value); 00195 } 00196 } 00197 00198 void 00199 FlexiNoteLayer::setVerticalScale(VerticalScale scale) 00200 { 00201 if (m_verticalScale == scale) return; 00202 m_verticalScale = scale; 00203 emit layerParametersChanged(); 00204 } 00205 00206 bool 00207 FlexiNoteLayer::isLayerScrollable(const View *v) const 00208 { 00209 QPoint discard; 00210 return !v->shouldIlluminateLocalFeatures(this, discard); 00211 } 00212 00213 bool 00214 FlexiNoteLayer::shouldConvertMIDIToHz() const 00215 { 00216 QString unit = getScaleUnits(); 00217 return (unit != "Hz"); 00218 // if (unit == "" || 00219 // unit.startsWith("MIDI") || 00220 // unit.startsWith("midi")) return true; 00221 // return false; 00222 } 00223 00224 bool 00225 FlexiNoteLayer::getValueExtents(float &min, float &max, 00226 bool &logarithmic, QString &unit) const 00227 { 00228 if (!m_model) return false; 00229 min = m_model->getValueMinimum(); 00230 max = m_model->getValueMaximum(); 00231 00232 if (shouldConvertMIDIToHz()) { 00233 unit = "Hz"; 00234 min = Pitch::getFrequencyForPitch(lrintf(min)); 00235 max = Pitch::getFrequencyForPitch(lrintf(max + 1)); 00236 } else unit = getScaleUnits(); 00237 00238 if (m_verticalScale == MIDIRangeScale || 00239 m_verticalScale == LogScale) logarithmic = true; 00240 00241 return true; 00242 } 00243 00244 bool 00245 FlexiNoteLayer::getDisplayExtents(float &min, float &max) const 00246 { 00247 if (!m_model || shouldAutoAlign()) { 00248 // std::cerr << "No model or shouldAutoAlign()" << std::endl; 00249 return false; 00250 } 00251 00252 if (m_verticalScale == MIDIRangeScale) { 00253 min = Pitch::getFrequencyForPitch(0); 00254 max = Pitch::getFrequencyForPitch(127); 00255 return true; 00256 } 00257 00258 if (m_scaleMinimum == m_scaleMaximum) { 00259 min = m_model->getValueMinimum(); 00260 max = m_model->getValueMaximum(); 00261 } else { 00262 min = m_scaleMinimum; 00263 max = m_scaleMaximum; 00264 } 00265 00266 if (shouldConvertMIDIToHz()) { 00267 min = Pitch::getFrequencyForPitch(lrintf(min)); 00268 max = Pitch::getFrequencyForPitch(lrintf(max + 1)); 00269 } 00270 00271 #ifdef DEBUG_NOTE_LAYER 00272 cerr << "NoteLayer::getDisplayExtents: min = " << min << ", max = " << max << " (m_scaleMinimum = " << m_scaleMinimum << ", m_scaleMaximum = " << m_scaleMaximum << ")" << endl; 00273 #endif 00274 00275 return true; 00276 } 00277 00278 bool 00279 FlexiNoteLayer::setDisplayExtents(float min, float max) 00280 { 00281 if (!m_model) return false; 00282 00283 if (min == max) { 00284 if (min == 0.f) { 00285 max = 1.f; 00286 } else { 00287 max = min * 1.0001; 00288 } 00289 } 00290 00291 m_scaleMinimum = min; 00292 m_scaleMaximum = max; 00293 00294 #ifdef DEBUG_NOTE_LAYER 00295 cerr << "FlexiNoteLayer::setDisplayExtents: min = " << min << ", max = " << max << endl; 00296 #endif 00297 00298 emit layerParametersChanged(); 00299 return true; 00300 } 00301 00302 int 00303 FlexiNoteLayer::getVerticalZoomSteps(int &defaultStep) const 00304 { 00305 if (shouldAutoAlign()) return 0; 00306 if (!m_model) return 0; 00307 00308 defaultStep = 0; 00309 return 100; 00310 } 00311 00312 int 00313 FlexiNoteLayer::getCurrentVerticalZoomStep() const 00314 { 00315 if (shouldAutoAlign()) return 0; 00316 if (!m_model) return 0; 00317 00318 RangeMapper *mapper = getNewVerticalZoomRangeMapper(); 00319 if (!mapper) return 0; 00320 00321 float dmin, dmax; 00322 getDisplayExtents(dmin, dmax); 00323 00324 int nr = mapper->getPositionForValue(dmax - dmin); 00325 00326 delete mapper; 00327 00328 return 100 - nr; 00329 } 00330 00332 00333 void 00334 FlexiNoteLayer::setVerticalZoomStep(int step) 00335 { 00336 if (shouldAutoAlign()) return; 00337 if (!m_model) return; 00338 00339 RangeMapper *mapper = getNewVerticalZoomRangeMapper(); 00340 if (!mapper) return; 00341 00342 float min, max; 00343 bool logarithmic; 00344 QString unit; 00345 getValueExtents(min, max, logarithmic, unit); 00346 00347 float dmin, dmax; 00348 getDisplayExtents(dmin, dmax); 00349 00350 float newdist = mapper->getValueForPosition(100 - step); 00351 00352 float newmin, newmax; 00353 00354 if (logarithmic) { 00355 00356 // see SpectrogramLayer::setVerticalZoomStep 00357 00358 newmax = (newdist + sqrtf(newdist*newdist + 4*dmin*dmax)) / 2; 00359 newmin = newmax - newdist; 00360 00361 // cerr << "newmin = " << newmin << ", newmax = " << newmax << endl; 00362 00363 } else { 00364 float dmid = (dmax + dmin) / 2; 00365 newmin = dmid - newdist / 2; 00366 newmax = dmid + newdist / 2; 00367 } 00368 00369 if (newmin < min) { 00370 newmax += (min - newmin); 00371 newmin = min; 00372 } 00373 if (newmax > max) { 00374 newmax = max; 00375 } 00376 00377 #ifdef DEBUG_NOTE_LAYER 00378 cerr << "FlexiNoteLayer::setVerticalZoomStep: " << step << ": " << newmin << " -> " << newmax << " (range " << newdist << ")" << endl; 00379 #endif 00380 00381 setDisplayExtents(newmin, newmax); 00382 } 00383 00384 RangeMapper * 00385 FlexiNoteLayer::getNewVerticalZoomRangeMapper() const 00386 { 00387 if (!m_model) return 0; 00388 00389 RangeMapper *mapper; 00390 00391 float min, max; 00392 bool logarithmic; 00393 QString unit; 00394 getValueExtents(min, max, logarithmic, unit); 00395 00396 if (min == max) return 0; 00397 00398 if (logarithmic) { 00399 mapper = new LogRangeMapper(0, 100, min, max, unit); 00400 } else { 00401 mapper = new LinearRangeMapper(0, 100, min, max, unit); 00402 } 00403 00404 return mapper; 00405 } 00406 00407 FlexiNoteModel::PointList 00408 FlexiNoteLayer::getLocalPoints(View *v, int x) const 00409 { 00410 if (!m_model) return FlexiNoteModel::PointList(); 00411 00412 int frame = v->getFrameForX(x); 00413 00414 FlexiNoteModel::PointList onPoints = 00415 m_model->getPoints(frame); 00416 00417 if (!onPoints.empty()) { 00418 return onPoints; 00419 } 00420 00421 FlexiNoteModel::PointList prevPoints = 00422 m_model->getPreviousPoints(frame); 00423 FlexiNoteModel::PointList nextPoints = 00424 m_model->getNextPoints(frame); 00425 00426 FlexiNoteModel::PointList usePoints = prevPoints; 00427 00428 if (prevPoints.empty()) { 00429 usePoints = nextPoints; 00430 } else if (prevPoints.begin()->frame < v->getStartFrame() && 00431 !(nextPoints.begin()->frame > v->getEndFrame())) { 00432 usePoints = nextPoints; 00433 } else if (nextPoints.begin()->frame - frame < 00434 frame - prevPoints.begin()->frame) { 00435 usePoints = nextPoints; 00436 } 00437 00438 if (!usePoints.empty()) { 00439 int fuzz = 2; 00440 int px = v->getXForFrame(usePoints.begin()->frame); 00441 if ((px > x && px - x > fuzz) || 00442 (px < x && x - px > fuzz + 1)) { 00443 usePoints.clear(); 00444 } 00445 } 00446 00447 return usePoints; 00448 } 00449 00450 bool 00451 FlexiNoteLayer::getPointToDrag(View *v, int x, int y, FlexiNoteModel::Point &p) const 00452 { 00453 if (!m_model) return false; 00454 00455 int frame = v->getFrameForX(x); 00456 00457 FlexiNoteModel::PointList onPoints = m_model->getPoints(frame); 00458 if (onPoints.empty()) return false; 00459 00460 // cerr << "frame " << frame << ": " << onPoints.size() << " candidate points" << endl; 00461 00462 int nearestDistance = -1; 00463 00464 for (FlexiNoteModel::PointList::const_iterator i = onPoints.begin(); 00465 i != onPoints.end(); ++i) { 00466 00467 int distance = getYForValue(v, (*i).value) - y; 00468 if (distance < 0) distance = -distance; 00469 if (nearestDistance == -1 || distance < nearestDistance) { 00470 nearestDistance = distance; 00471 p = *i; 00472 } 00473 } 00474 00475 return true; 00476 } 00477 00478 bool 00479 FlexiNoteLayer::getNoteToEdit(View *v, int x, int y, FlexiNoteModel::Point &p) const 00480 { 00481 // GF: find the note that is closest to the cursor 00482 if (!m_model) return false; 00483 00484 int frame = v->getFrameForX(x); 00485 00486 FlexiNoteModel::PointList onPoints = m_model->getPoints(frame); 00487 if (onPoints.empty()) return false; 00488 00489 // std::cerr << "frame " << frame << ": " << onPoints.size() << " candidate points" << std::endl; 00490 00491 int nearestDistance = -1; 00492 00493 for (FlexiNoteModel::PointList::const_iterator i = onPoints.begin(); 00494 i != onPoints.end(); ++i) { 00495 00496 int distance = getYForValue(v, (*i).value) - y; 00497 if (distance < 0) distance = -distance; 00498 if (nearestDistance == -1 || distance < nearestDistance) { 00499 nearestDistance = distance; 00500 p = *i; 00501 } 00502 } 00503 00504 return true; 00505 } 00506 00507 QString 00508 FlexiNoteLayer::getFeatureDescription(View *v, QPoint &pos) const 00509 { 00510 int x = pos.x(); 00511 00512 if (!m_model || !m_model->getSampleRate()) return ""; 00513 00514 FlexiNoteModel::PointList points = getLocalPoints(v, x); 00515 00516 if (points.empty()) { 00517 if (!m_model->isReady()) { 00518 return tr("In progress"); 00519 } else { 00520 return tr("No local points"); 00521 } 00522 } 00523 00524 FlexiNote note(0); 00525 FlexiNoteModel::PointList::iterator i; 00526 00527 for (i = points.begin(); i != points.end(); ++i) { 00528 00529 int y = getYForValue(v, i->value); 00530 int h = NOTE_HEIGHT; // GF: larger notes 00531 00532 if (m_model->getValueQuantization() != 0.0) { 00533 h = y - getYForValue(v, i->value + m_model->getValueQuantization()); 00534 if (h < NOTE_HEIGHT) h = NOTE_HEIGHT; 00535 } 00536 00537 // GF: this is not quite correct 00538 if (pos.y() >= y - 4 && pos.y() <= y + h) { 00539 note = *i; 00540 break; 00541 } 00542 } 00543 00544 if (i == points.end()) return tr("No local points"); 00545 00546 RealTime rt = RealTime::frame2RealTime(note.frame, 00547 m_model->getSampleRate()); 00548 RealTime rd = RealTime::frame2RealTime(note.duration, 00549 m_model->getSampleRate()); 00550 00551 QString pitchText; 00552 00553 if (shouldConvertMIDIToHz()) { 00554 00555 int mnote = lrintf(note.value); 00556 int cents = lrintf((note.value - mnote) * 100); 00557 float freq = Pitch::getFrequencyForPitch(mnote, cents); 00558 pitchText = tr("%1 (%2, %3 Hz)") 00559 .arg(Pitch::getPitchLabel(mnote, cents)) 00560 .arg(mnote) 00561 .arg(freq); 00562 00563 } else if (getScaleUnits() == "Hz") { 00564 00565 pitchText = tr("%1 Hz (%2, %3)") 00566 .arg(note.value) 00567 .arg(Pitch::getPitchLabelForFrequency(note.value)) 00568 .arg(Pitch::getPitchForFrequency(note.value)); 00569 00570 } else { 00571 pitchText = tr("%1 %2") 00572 .arg(note.value).arg(getScaleUnits()); 00573 } 00574 00575 QString text; 00576 00577 if (note.label == "") { 00578 text = QString(tr("Time:\t%1\nPitch:\t%2\nDuration:\t%3\nNo label")) 00579 .arg(rt.toText(true).c_str()) 00580 .arg(pitchText) 00581 .arg(rd.toText(true).c_str()); 00582 } else { 00583 text = QString(tr("Time:\t%1\nPitch:\t%2\nDuration:\t%3\nLabel:\t%4")) 00584 .arg(rt.toText(true).c_str()) 00585 .arg(pitchText) 00586 .arg(rd.toText(true).c_str()) 00587 .arg(note.label); 00588 } 00589 00590 pos = QPoint(v->getXForFrame(note.frame), 00591 getYForValue(v, note.value)); 00592 return text; 00593 } 00594 00595 bool 00596 FlexiNoteLayer::snapToFeatureFrame(View *v, int &frame, 00597 int &resolution, 00598 SnapType snap) const 00599 { 00600 if (!m_model) { 00601 return Layer::snapToFeatureFrame(v, frame, resolution, snap); 00602 } 00603 00604 resolution = m_model->getResolution(); 00605 FlexiNoteModel::PointList points; 00606 00607 if (snap == SnapNeighbouring) { 00608 00609 points = getLocalPoints(v, v->getXForFrame(frame)); 00610 if (points.empty()) return false; 00611 frame = points.begin()->frame; 00612 return true; 00613 } 00614 00615 points = m_model->getPoints(frame, frame); 00616 int snapped = frame; 00617 bool found = false; 00618 00619 for (FlexiNoteModel::PointList::const_iterator i = points.begin(); 00620 i != points.end(); ++i) { 00621 00622 cerr << "FlexiNoteModel: point at " << i->frame << endl; 00623 00624 if (snap == SnapRight) { 00625 00626 if (i->frame > frame) { 00627 snapped = i->frame; 00628 found = true; 00629 break; 00630 } else if (i->frame + i->duration >= frame) { 00631 snapped = i->frame + i->duration; 00632 found = true; 00633 break; 00634 } 00635 00636 } else if (snap == SnapLeft) { 00637 00638 if (i->frame <= frame) { 00639 snapped = i->frame; 00640 found = true; // don't break, as the next may be better 00641 } else { 00642 break; 00643 } 00644 00645 } else { // nearest 00646 00647 FlexiNoteModel::PointList::const_iterator j = i; 00648 ++j; 00649 00650 if (j == points.end()) { 00651 00652 snapped = i->frame; 00653 found = true; 00654 break; 00655 00656 } else if (j->frame >= frame) { 00657 00658 if (j->frame - frame < frame - i->frame) { 00659 snapped = j->frame; 00660 } else { 00661 snapped = i->frame; 00662 } 00663 found = true; 00664 break; 00665 } 00666 } 00667 } 00668 00669 cerr << "snapToFeatureFrame: frame " << frame << " -> snapped " << snapped << ", found = " << found << endl; 00670 00671 frame = snapped; 00672 return found; 00673 } 00674 00675 void 00676 FlexiNoteLayer::getScaleExtents(View *v, float &min, float &max, bool &log) const 00677 { 00678 min = 0.0; 00679 max = 0.0; 00680 log = false; 00681 00682 QString queryUnits; 00683 if (shouldConvertMIDIToHz()) queryUnits = "Hz"; 00684 else queryUnits = getScaleUnits(); 00685 00686 if (shouldAutoAlign()) { 00687 00688 if (!v->getValueExtents(queryUnits, min, max, log)) { 00689 00690 min = m_model->getValueMinimum(); 00691 max = m_model->getValueMaximum(); 00692 00693 if (shouldConvertMIDIToHz()) { 00694 min = Pitch::getFrequencyForPitch(lrintf(min)); 00695 max = Pitch::getFrequencyForPitch(lrintf(max + 1)); 00696 } 00697 00698 #ifdef DEBUG_NOTE_LAYER 00699 cerr << "FlexiNoteLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << endl; 00700 #endif 00701 00702 } else if (log) { 00703 00704 LogRange::mapRange(min, max); 00705 00706 #ifdef DEBUG_NOTE_LAYER 00707 cerr << "FlexiNoteLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << endl; 00708 #endif 00709 } 00710 00711 } else { 00712 00713 getDisplayExtents(min, max); 00714 00715 if (m_verticalScale == MIDIRangeScale) { 00716 min = Pitch::getFrequencyForPitch(0); 00717 max = Pitch::getFrequencyForPitch(70); 00718 } else if (shouldConvertMIDIToHz()) { 00719 min = Pitch::getFrequencyForPitch(lrintf(min)); 00720 max = Pitch::getFrequencyForPitch(lrintf(max + 1)); 00721 } 00722 00723 if (m_verticalScale == LogScale || m_verticalScale == MIDIRangeScale) { 00724 LogRange::mapRange(min, max); 00725 log = true; 00726 } 00727 } 00728 00729 if (max == min) max = min + 1.0; 00730 } 00731 00732 int 00733 FlexiNoteLayer::getYForValue(View *v, float val) const 00734 { 00735 float min = 0.0, max = 0.0; 00736 bool logarithmic = false; 00737 int h = v->height(); 00738 00739 getScaleExtents(v, min, max, logarithmic); 00740 00741 #ifdef DEBUG_NOTE_LAYER 00742 cerr << "FlexiNoteLayer[" << this << "]::getYForValue(" << val << "): min = " << min << ", max = " << max << ", log = " << logarithmic << endl; 00743 #endif 00744 00745 if (shouldConvertMIDIToHz()) { 00746 val = Pitch::getFrequencyForPitch(lrintf(val), 00747 lrintf((val - lrintf(val)) * 100)); 00748 #ifdef DEBUG_NOTE_LAYER 00749 cerr << "shouldConvertMIDIToHz true, val now = " << val << endl; 00750 #endif 00751 } 00752 00753 if (logarithmic) { 00754 val = LogRange::map(val); 00755 #ifdef DEBUG_NOTE_LAYER 00756 cerr << "logarithmic true, val now = " << val << endl; 00757 #endif 00758 } 00759 00760 int y = int(h - ((val - min) * h) / (max - min)) - 1; 00761 #ifdef DEBUG_NOTE_LAYER 00762 cerr << "y = " << y << endl; 00763 #endif 00764 return y; 00765 } 00766 00767 float 00768 FlexiNoteLayer::getValueForY(View *v, int y) const 00769 { 00770 float min = 0.0, max = 0.0; 00771 bool logarithmic = false; 00772 int h = v->height(); 00773 00774 getScaleExtents(v, min, max, logarithmic); 00775 00776 float val = min + (float(h - y) * float(max - min)) / h; 00777 00778 if (logarithmic) { 00779 val = powf(10.f, val); 00780 } 00781 00782 if (shouldConvertMIDIToHz()) { 00783 val = Pitch::getPitchForFrequency(val); 00784 } 00785 00786 return val; 00787 } 00788 00789 bool 00790 FlexiNoteLayer::shouldAutoAlign() const 00791 { 00792 if (!m_model) return false; 00793 return (m_verticalScale == AutoAlignScale); 00794 } 00795 00796 void 00797 FlexiNoteLayer::paint(View *v, QPainter &paint, QRect rect) const 00798 { 00799 if (!m_model || !m_model->isOK()) return; 00800 00801 int sampleRate = m_model->getSampleRate(); 00802 if (!sampleRate) return; 00803 00804 // Profiler profiler("FlexiNoteLayer::paint", true); 00805 00806 int x1 = rect.right(); 00807 int frame1 = v->getFrameForX(x1); 00808 00809 FlexiNoteModel::PointList points(m_model->getPoints(0, frame1)); 00810 if (points.empty()) return; 00811 00812 paint.setPen(getBaseQColor()); 00813 00814 QColor brushColour(getBaseQColor()); 00815 brushColour.setAlpha(80); 00816 00817 // SVDEBUG << "FlexiNoteLayer::paint: resolution is " 00818 // << m_model->getResolution() << " frames" << endl; 00819 00820 float min = m_model->getValueMinimum(); 00821 float max = m_model->getValueMaximum(); 00822 if (max == min) max = min + 1.0; 00823 00824 QPoint localPos; 00825 FlexiNoteModel::Point illuminatePoint(0); 00826 bool shouldIlluminate = false; 00827 00828 if (v->shouldIlluminateLocalFeatures(this, localPos)) { 00829 shouldIlluminate = getPointToDrag(v, localPos.x(), localPos.y(), 00830 illuminatePoint); 00831 } 00832 00833 paint.save(); 00834 paint.setRenderHint(QPainter::Antialiasing, false); 00835 00836 int noteNumber = 0; 00837 00838 for (FlexiNoteModel::PointList::const_iterator i = points.begin(); 00839 i != points.end(); ++i) { 00840 00841 ++noteNumber; 00842 const FlexiNoteModel::Point &p(*i); 00843 00844 int x = v->getXForFrame(p.frame); 00845 int y = getYForValue(v, p.value); 00846 int w = v->getXForFrame(p.frame + p.duration) - x; 00847 int h = NOTE_HEIGHT; //GF: larger notes 00848 00849 if (m_model->getValueQuantization() != 0.0) { 00850 h = y - getYForValue(v, p.value + m_model->getValueQuantization()); 00851 if (h < NOTE_HEIGHT) h = NOTE_HEIGHT; //GF: larger notes 00852 } 00853 00854 if (w < 1) w = 1; 00855 paint.setPen(getBaseQColor()); 00856 paint.setBrush(brushColour); 00857 00858 if (shouldIlluminate && 00859 // "illuminatePoint == p" 00860 !FlexiNoteModel::Point::Comparator()(illuminatePoint, p) && 00861 !FlexiNoteModel::Point::Comparator()(p, illuminatePoint)) { 00862 00863 paint.drawLine(x, -1, x, v->height() + 1); 00864 paint.drawLine(x+w, -1, x+w, v->height() + 1); 00865 00866 paint.setPen(v->getForeground()); 00867 // paint.setBrush(v->getForeground()); 00868 00869 QString vlabel = QString("freq: %1%2").arg(p.value).arg(m_model->getScaleUnits()); 00870 // v->drawVisibleText(paint, 00871 // x - paint.fontMetrics().width(vlabel) - 2, 00872 // y + paint.fontMetrics().height()/2 00873 // - paint.fontMetrics().descent(), 00874 // vlabel, View::OutlinedText); 00875 v->drawVisibleText(paint, 00876 x, 00877 y - h/2 - 2 - paint.fontMetrics().height() 00878 - paint.fontMetrics().descent(), 00879 vlabel, View::OutlinedText); 00880 00881 QString hlabel = "dur: " + QString(RealTime::frame2RealTime 00882 (p.duration, m_model->getSampleRate()).toText(true).c_str()); 00883 v->drawVisibleText(paint, 00884 x, 00885 y - h/2 - paint.fontMetrics().descent() - 2, 00886 hlabel, View::OutlinedText); 00887 00888 QString llabel = QString("%1").arg(p.label); 00889 v->drawVisibleText(paint, 00890 x, 00891 y + h + 2 + paint.fontMetrics().descent(), 00892 llabel, View::OutlinedText); 00893 QString nlabel = QString("%1").arg(noteNumber); 00894 v->drawVisibleText(paint, 00895 x + paint.fontMetrics().averageCharWidth() / 2, 00896 y + h/2 - paint.fontMetrics().descent(), 00897 nlabel, View::OutlinedText); 00898 } 00899 00900 paint.drawRect(x, y - h/2, w, h); 00901 } 00902 00903 paint.restore(); 00904 } 00905 00906 int 00907 FlexiNoteLayer::getVerticalScaleWidth(View *v, bool, QPainter &paint) const 00908 { 00909 if (!m_model || shouldAutoAlign()) { 00910 return 0; 00911 } else { 00912 if (m_verticalScale == LogScale || m_verticalScale == MIDIRangeScale) { 00913 return LogNumericalScale().getWidth(v, paint) + 10; // for piano 00914 } else { 00915 return LinearNumericalScale().getWidth(v, paint); 00916 } 00917 } 00918 } 00919 00920 void 00921 FlexiNoteLayer::paintVerticalScale(View *v, bool, QPainter &paint, QRect) const 00922 { 00923 if (!m_model || m_model->getPoints().empty()) return; 00924 00925 QString unit; 00926 float min, max; 00927 bool logarithmic; 00928 00929 int w = getVerticalScaleWidth(v, false, paint); 00930 int h = v->height(); 00931 00932 getScaleExtents(v, min, max, logarithmic); 00933 00934 if (logarithmic) { 00935 LogNumericalScale().paintVertical(v, this, paint, 0, min, max); 00936 } else { 00937 LinearNumericalScale().paintVertical(v, this, paint, 0, min, max); 00938 } 00939 00940 if (logarithmic && (getScaleUnits() == "Hz")) { 00941 PianoScale().paintPianoVertical 00942 (v, paint, QRect(w - 10, 0, 10, h), 00943 LogRange::unmap(min), 00944 LogRange::unmap(max)); 00945 paint.drawLine(w, 0, w, h); 00946 } 00947 00948 if (getScaleUnits() != "") { 00949 int mw = w - 5; 00950 paint.drawText(5, 00951 5 + paint.fontMetrics().ascent(), 00952 TextAbbrev::abbreviate(getScaleUnits(), 00953 paint.fontMetrics(), 00954 mw)); 00955 } 00956 } 00957 00958 void 00959 FlexiNoteLayer::drawStart(View *v, QMouseEvent *e) 00960 { 00961 // SVDEBUG << "FlexiNoteLayer::drawStart(" << e->x() << "," << e->y() << ")" << endl; 00962 00963 if (!m_model) return; 00964 00965 int frame = v->getFrameForX(e->x()); 00966 if (frame < 0) frame = 0; 00967 frame = frame / m_model->getResolution() * m_model->getResolution(); 00968 00969 float value = getValueForY(v, e->y()); 00970 00971 m_editingPoint = FlexiNoteModel::Point(frame, value, 0, 0.8, tr("New Point")); 00972 m_originalPoint = m_editingPoint; 00973 00974 if (m_editingCommand) finish(m_editingCommand); 00975 m_editingCommand = new FlexiNoteModel::EditCommand(m_model, 00976 tr("Draw Point")); 00977 m_editingCommand->addPoint(m_editingPoint); 00978 00979 m_editing = true; 00980 } 00981 00982 void 00983 FlexiNoteLayer::drawDrag(View *v, QMouseEvent *e) 00984 { 00985 // SVDEBUG << "FlexiNoteLayer::drawDrag(" << e->x() << "," << e->y() << ")" << endl; 00986 00987 if (!m_model || !m_editing) return; 00988 00989 int frame = v->getFrameForX(e->x()); 00990 if (frame < 0) frame = 0; 00991 frame = frame / m_model->getResolution() * m_model->getResolution(); 00992 00993 float newValue = getValueForY(v, e->y()); 00994 00995 int newFrame = m_editingPoint.frame; 00996 int newDuration = frame - newFrame; 00997 if (newDuration < 0) { 00998 newFrame = frame; 00999 newDuration = -newDuration; 01000 } else if (newDuration == 0) { 01001 newDuration = 1; 01002 } 01003 01004 m_editingCommand->deletePoint(m_editingPoint); 01005 m_editingPoint.frame = newFrame; 01006 m_editingPoint.value = newValue; 01007 m_editingPoint.duration = newDuration; 01008 m_editingCommand->addPoint(m_editingPoint); 01009 } 01010 01011 void 01012 FlexiNoteLayer::drawEnd(View *, QMouseEvent *) 01013 { 01014 // SVDEBUG << "FlexiNoteLayer::drawEnd(" << e->x() << "," << e->y() << ")" << endl; 01015 if (!m_model || !m_editing) return; 01016 finish(m_editingCommand); 01017 m_editingCommand = 0; 01018 m_editing = false; 01019 } 01020 01021 void 01022 FlexiNoteLayer::eraseStart(View *v, QMouseEvent *e) 01023 { 01024 if (!m_model) return; 01025 01026 if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return; 01027 01028 if (m_editingCommand) { 01029 finish(m_editingCommand); 01030 m_editingCommand = 0; 01031 } 01032 01033 m_editing = true; 01034 } 01035 01036 void 01037 FlexiNoteLayer::eraseDrag(View *, QMouseEvent *) 01038 { 01039 } 01040 01041 void 01042 FlexiNoteLayer::eraseEnd(View *v, QMouseEvent *e) 01043 { 01044 if (!m_model || !m_editing) return; 01045 01046 m_editing = false; 01047 01048 FlexiNoteModel::Point p(0); 01049 if (!getPointToDrag(v, e->x(), e->y(), p)) return; 01050 if (p.frame != m_editingPoint.frame || p.value != m_editingPoint.value) return; 01051 01052 m_editingCommand = new FlexiNoteModel::EditCommand(m_model, tr("Erase Point")); 01053 01054 m_editingCommand->deletePoint(m_editingPoint); 01055 01056 finish(m_editingCommand); 01057 m_editingCommand = 0; 01058 m_editing = false; 01059 } 01060 01061 void 01062 FlexiNoteLayer::editStart(View *v, QMouseEvent *e) 01063 { 01064 // SVDEBUG << "FlexiNoteLayer::editStart(" << e->x() << "," << e->y() << ")" << endl; 01065 std::cerr << "FlexiNoteLayer::editStart(" << e->x() << "," << e->y() << ")" << std::endl; 01066 01067 if (!m_model) return; 01068 01069 if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return; 01070 m_originalPoint = FlexiNote(m_editingPoint); 01071 01072 if (m_editMode == RightBoundary) { 01073 m_dragPointX = v->getXForFrame(m_editingPoint.frame + m_editingPoint.duration); 01074 } else { 01075 m_dragPointX = v->getXForFrame(m_editingPoint.frame); 01076 } 01077 m_dragPointY = getYForValue(v, m_editingPoint.value); 01078 01079 if (m_editingCommand) { 01080 finish(m_editingCommand); 01081 m_editingCommand = 0; 01082 } 01083 01084 m_editing = true; 01085 m_dragStartX = e->x(); 01086 m_dragStartY = e->y(); 01087 01088 int onset = m_originalPoint.frame; 01089 int offset = m_originalPoint.frame + m_originalPoint.duration - 1; 01090 01091 m_greatestLeftNeighbourFrame = -1; 01092 m_smallestRightNeighbourFrame = std::numeric_limits<int>::max(); 01093 01094 for (FlexiNoteModel::PointList::const_iterator i = m_model->getPoints().begin(); 01095 i != m_model->getPoints().end(); ++i) { 01096 FlexiNote currentNote = *i; 01097 01098 // left boundary 01099 if (currentNote.frame + currentNote.duration - 1 < onset) { 01100 m_greatestLeftNeighbourFrame = currentNote.frame + currentNote.duration - 1; 01101 } 01102 01103 // right boundary 01104 if (currentNote.frame > offset) { 01105 m_smallestRightNeighbourFrame = currentNote.frame; 01106 break; 01107 } 01108 } 01109 std::cerr << "editStart: mode is " << m_editMode << ", note frame: " << onset << ", left boundary: " << m_greatestLeftNeighbourFrame << ", right boundary: " << m_smallestRightNeighbourFrame << std::endl; 01110 } 01111 01112 void 01113 FlexiNoteLayer::editDrag(View *v, QMouseEvent *e) 01114 { 01115 // SVDEBUG << "FlexiNoteLayer::editDrag(" << e->x() << "," << e->y() << ")" << endl; 01116 std::cerr << "FlexiNoteLayer::editDrag(" << e->x() << "," << e->y() << ")" << std::endl; 01117 01118 if (!m_model || !m_editing) return; 01119 01120 int xdist = e->x() - m_dragStartX; 01121 int ydist = e->y() - m_dragStartY; 01122 int newx = m_dragPointX + xdist; 01123 int newy = m_dragPointY + ydist; 01124 01125 int dragFrame = v->getFrameForX(newx); 01126 if (dragFrame < 0) dragFrame = 0; 01127 dragFrame = dragFrame / m_model->getResolution() * m_model->getResolution(); 01128 01129 float value = getValueForY(v, newy); 01130 01131 if (!m_editingCommand) { 01132 m_editingCommand = new FlexiNoteModel::EditCommand(m_model, 01133 tr("Drag Point")); 01134 } 01135 01136 m_editingCommand->deletePoint(m_editingPoint); 01137 01138 std::cerr << "edit mode: " << m_editMode << std::endl; 01139 01140 switch (m_editMode) { 01141 case LeftBoundary : { 01142 // left 01143 if (m_intelligentActions && dragFrame <= m_greatestLeftNeighbourFrame) dragFrame = m_greatestLeftNeighbourFrame + 1; 01144 // right 01145 if (m_intelligentActions && dragFrame >= m_originalPoint.frame + m_originalPoint.duration) { 01146 dragFrame = m_originalPoint.frame + m_originalPoint.duration - 1; 01147 } 01148 m_editingPoint.frame = dragFrame; 01149 m_editingPoint.duration = m_originalPoint.frame - dragFrame + m_originalPoint.duration; 01150 break; 01151 } 01152 case RightBoundary : { 01153 // left 01154 if (m_intelligentActions && dragFrame <= m_greatestLeftNeighbourFrame) dragFrame = m_greatestLeftNeighbourFrame + 1; 01155 if (m_intelligentActions && dragFrame >= m_smallestRightNeighbourFrame) dragFrame = m_smallestRightNeighbourFrame - 1; 01156 m_editingPoint.duration = dragFrame - m_originalPoint.frame + 1; 01157 break; 01158 } 01159 case DragNote : { 01160 // left 01161 if (m_intelligentActions && dragFrame <= m_greatestLeftNeighbourFrame) dragFrame = m_greatestLeftNeighbourFrame + 1; 01162 // right 01163 if (m_intelligentActions && dragFrame + m_originalPoint.duration >= m_smallestRightNeighbourFrame) { 01164 dragFrame = m_smallestRightNeighbourFrame - m_originalPoint.duration; 01165 } 01166 m_editingPoint.frame = dragFrame; 01167 m_editingPoint.value = value; 01168 break; 01169 } 01170 case SplitNote: // nothing 01171 break; 01172 } 01173 updateNoteValue(v, m_editingPoint); 01174 m_editingCommand->addPoint(m_editingPoint); 01175 std::cerr << "added new point(" << m_editingPoint.frame << "," << m_editingPoint.duration << ")" << std::endl; 01176 01177 } 01178 01179 void 01180 FlexiNoteLayer::editEnd(View *, QMouseEvent *e) 01181 { 01182 // SVDEBUG << "FlexiNoteLayer::editEnd(" << e->x() << "," << e->y() << ")" << endl; 01183 std::cerr << "FlexiNoteLayer::editEnd(" << e->x() << "," << e->y() << ")" << std::endl; 01184 01185 if (!m_model || !m_editing) return; 01186 01187 if (m_editingCommand) { 01188 01189 QString newName = m_editingCommand->getName(); 01190 01191 if (m_editingPoint.frame != m_originalPoint.frame) { 01192 if (m_editingPoint.value != m_originalPoint.value) { 01193 newName = tr("Edit Point"); 01194 } else { 01195 newName = tr("Relocate Point"); 01196 } 01197 } else { 01198 newName = tr("Change Point Value"); 01199 } 01200 01201 m_editingCommand->setName(newName); 01202 finish(m_editingCommand); 01203 } 01204 01205 m_editingCommand = 0; 01206 m_editing = false; 01207 } 01208 01209 void 01210 FlexiNoteLayer::splitStart(View *v, QMouseEvent *e) 01211 { 01212 // GF: note splitting starts (!! remove printing soon) 01213 std::cerr << "splitStart" << std::endl; 01214 if (!m_model) return; 01215 01216 if (!getPointToDrag(v, e->x(), e->y(), m_editingPoint)) return; 01217 // m_originalPoint = m_editingPoint; 01218 // 01219 // m_dragPointX = v->getXForFrame(m_editingPoint.frame); 01220 // m_dragPointY = getYForValue(v, m_editingPoint.value); 01221 01222 if (m_editingCommand) { 01223 finish(m_editingCommand); 01224 m_editingCommand = 0; 01225 } 01226 01227 m_editing = true; 01228 m_dragStartX = e->x(); 01229 m_dragStartY = e->y(); 01230 01231 } 01232 01233 void 01234 FlexiNoteLayer::splitEnd(View *v, QMouseEvent *e) 01235 { 01236 // GF: note splitting ends. (!! remove printing soon) 01237 std::cerr << "splitEnd" << std::endl; 01238 if (!m_model || !m_editing || m_editMode != SplitNote) return; 01239 01240 int xdist = e->x() - m_dragStartX; 01241 int ydist = e->y() - m_dragStartY; 01242 if (xdist != 0 || ydist != 0) { 01243 std::cerr << "mouse moved" << std::endl; 01244 return; 01245 } 01246 01247 int frame = v->getFrameForX(e->x()); 01248 01249 splitNotesAt(v, frame, e); 01250 } 01251 01252 void 01253 FlexiNoteLayer::splitNotesAt(View *v, int frame) 01254 { 01255 splitNotesAt(v, frame, 0); 01256 } 01257 01258 void 01259 FlexiNoteLayer::splitNotesAt(View *v, int frame, QMouseEvent *e) 01260 { 01261 FlexiNoteModel::PointList onPoints = m_model->getPoints(frame); 01262 if (onPoints.empty()) return; 01263 01264 FlexiNote note(*onPoints.begin()); 01265 01266 FlexiNoteModel::EditCommand *command = new FlexiNoteModel::EditCommand 01267 (m_model, tr("Edit Point")); 01268 command->deletePoint(note); 01269 01270 if (!e || !(e->modifiers() & Qt::ShiftModifier)) { 01271 01272 int gap = 0; // MM: I prefer a gap of 0, but we can decide later 01273 01274 FlexiNote newNote1(note.frame, note.value, 01275 frame - note.frame - gap, 01276 note.level, note.label); 01277 01278 FlexiNote newNote2(frame, note.value, 01279 note.duration - newNote1.duration, 01280 note.level, note.label); 01281 01282 if (m_intelligentActions) { 01283 if (updateNoteValue(v, newNote1)) { 01284 command->addPoint(newNote1); 01285 } 01286 if (updateNoteValue(v, newNote2)) { 01287 command->addPoint(newNote2); 01288 } 01289 } else { 01290 command->addPoint(newNote1); 01291 command->addPoint(newNote2); 01292 } 01293 } 01294 01295 finish(command); 01296 } 01297 01298 void 01299 FlexiNoteLayer::addNote(View *v, QMouseEvent *e) 01300 { 01301 std::cerr << "addNote" << std::endl; 01302 if (!m_model) return; 01303 01304 int duration = 10000; 01305 01306 int frame = v->getFrameForX(e->x()); 01307 float value = getValueForY(v, e->y()); 01308 01309 FlexiNoteModel::PointList noteList = m_model->getPoints(); 01310 01311 if (m_intelligentActions) { 01312 int smallestRightNeighbourFrame = 0; 01313 for (FlexiNoteModel::PointList::const_iterator i = noteList.begin(); 01314 i != noteList.end(); ++i) { 01315 FlexiNote currentNote = *i; 01316 if (currentNote.frame > frame) { 01317 smallestRightNeighbourFrame = currentNote.frame; 01318 break; 01319 } 01320 } 01321 if (smallestRightNeighbourFrame > 0) { 01322 duration = std::min(smallestRightNeighbourFrame - frame + 1, duration); 01323 duration = (duration > 0) ? duration : 0; 01324 } 01325 } 01326 01327 if (!m_intelligentActions || 01328 (m_model->getPoints(frame).empty() && duration > 0)) { 01329 FlexiNote newNote(frame, value, duration, 100, "new note"); 01330 FlexiNoteModel::EditCommand *command = new FlexiNoteModel::EditCommand 01331 (m_model, tr("Add Point")); 01332 command->addPoint(newNote); 01333 finish(command); 01334 } 01335 } 01336 01337 SparseTimeValueModel * 01338 FlexiNoteLayer::getAssociatedPitchModel(View *v) const 01339 { 01340 // Better than we used to do, but still not very satisfactory 01341 01342 cerr << "FlexiNoteLayer::getAssociatedPitchModel()" << endl; 01343 01344 for (int i = 0; i < v->getLayerCount(); ++i) { 01345 Layer *layer = v->getLayer(i); 01346 if (layer && 01347 layer->getLayerPresentationName() != "candidate") { 01348 cerr << "FlexiNoteLayer::getAssociatedPitchModel: looks like our layer is " << layer << endl; 01349 SparseTimeValueModel *model = qobject_cast<SparseTimeValueModel *> 01350 (layer->getModel()); 01351 cerr << "FlexiNoteLayer::getAssociatedPitchModel: and its model is " << model << endl; 01352 if (model && model->getScaleUnits() == "Hz") { 01353 cerr << "FlexiNoteLayer::getAssociatedPitchModel: it's good, returning " << model << endl; 01354 return model; 01355 } 01356 } 01357 } 01358 return 0; 01359 } 01360 01361 void 01362 FlexiNoteLayer::snapSelectedNotesToPitchTrack(View *v, Selection s) 01363 { 01364 if (!m_model) return; 01365 01366 FlexiNoteModel::PointList points = 01367 m_model->getPoints(s.getStartFrame(), s.getEndFrame()); 01368 01369 FlexiNoteModel::EditCommand *command = new FlexiNoteModel::EditCommand 01370 (m_model, tr("Snap Notes")); 01371 01372 cerr << "snapSelectedNotesToPitchTrack: selection is from " << s.getStartFrame() << " to " << s.getEndFrame() << endl; 01373 01374 for (FlexiNoteModel::PointList::iterator i = points.begin(); 01375 i != points.end(); ++i) { 01376 01377 FlexiNote note(*i); 01378 01379 cerr << "snapSelectedNotesToPitchTrack: looking at note from " << note.frame << " to " << note.frame + note.duration << endl; 01380 01381 if (!s.contains(note.frame) && 01382 !s.contains(note.frame + note.duration - 1)) { 01383 continue; 01384 } 01385 01386 cerr << "snapSelectedNotesToPitchTrack: making new note" << endl; 01387 FlexiNote newNote(note); 01388 01389 command->deletePoint(note); 01390 01391 if (updateNoteValue(v, newNote)) { 01392 command->addPoint(newNote); 01393 } 01394 } 01395 01396 finish(command); 01397 } 01398 01399 void 01400 FlexiNoteLayer::mergeNotes(View *v, Selection s, bool inclusive) 01401 { 01402 FlexiNoteModel::PointList points = 01403 m_model->getPoints(s.getStartFrame(), s.getEndFrame()); 01404 01405 FlexiNoteModel::PointList::iterator i = points.begin(); 01406 if (inclusive) { 01407 while (i != points.end() && i->frame + i->duration < s.getStartFrame()) { 01408 ++i; 01409 } 01410 } else { 01411 while (i != points.end() && i->frame < s.getStartFrame()) { 01412 ++i; 01413 } 01414 } 01415 01416 if (i == points.end()) return; 01417 01418 FlexiNoteModel::EditCommand *command = 01419 new FlexiNoteModel::EditCommand(m_model, tr("Merge Notes")); 01420 01421 FlexiNote newNote(*i); 01422 01423 while (i != points.end()) { 01424 01425 if (inclusive) { 01426 if (i->frame >= s.getEndFrame()) break; 01427 } else { 01428 if (i->frame + i->duration > s.getEndFrame()) break; 01429 } 01430 01431 newNote.duration = i->frame + i->duration - newNote.frame; 01432 command->deletePoint(*i); 01433 01434 ++i; 01435 } 01436 01437 updateNoteValue(v, newNote); 01438 command->addPoint(newNote); 01439 finish(command); 01440 } 01441 01442 bool 01443 FlexiNoteLayer::updateNoteValue(View *v, FlexiNoteModel::Point ¬e) const 01444 { 01445 SparseTimeValueModel *model = getAssociatedPitchModel(v); 01446 if (!model) return false; 01447 01448 std::cerr << model->getTypeName() << std::endl; 01449 01450 SparseModel<TimeValuePoint>::PointList dataPoints = 01451 model->getPoints(note.frame, note.frame + note.duration); 01452 01453 std::cerr << "frame " << note.frame << ": " << dataPoints.size() << " candidate points" << std::endl; 01454 01455 if (dataPoints.empty()) return false; 01456 01457 std::vector<float> pitchValues; 01458 01459 for (SparseModel<TimeValuePoint>::PointList::const_iterator i = 01460 dataPoints.begin(); i != dataPoints.end(); ++i) { 01461 if (i->frame >= note.frame && 01462 i->frame < note.frame + note.duration) { 01463 pitchValues.push_back(i->value); 01464 } 01465 } 01466 01467 if (pitchValues.empty()) return false; 01468 01469 sort(pitchValues.begin(), pitchValues.end()); 01470 int size = pitchValues.size(); 01471 double median; 01472 01473 if (size % 2 == 0) { 01474 median = (pitchValues[size/2 - 1] + pitchValues[size/2]) / 2; 01475 } else { 01476 median = pitchValues[size/2]; 01477 } 01478 01479 note.value = median; 01480 01481 return true; 01482 } 01483 01484 void 01485 FlexiNoteLayer::mouseMoveEvent(View *v, QMouseEvent *e) 01486 { 01487 // GF: context sensitive cursors 01488 // v->setCursor(Qt::ArrowCursor); 01489 FlexiNoteModel::Point note(0); 01490 if (!getNoteToEdit(v, e->x(), e->y(), note)) { 01491 // v->setCursor(Qt::UpArrowCursor); 01492 return; 01493 } 01494 01495 bool closeToLeft = false, closeToRight = false, closeToTop = false, closeToBottom = false; 01496 getRelativeMousePosition(v, note, e->x(), e->y(), closeToLeft, closeToRight, closeToTop, closeToBottom); 01497 // if (!closeToLeft) return; 01498 // if (closeToTop) v->setCursor(Qt::SizeVerCursor); 01499 01500 if (closeToLeft) { v->setCursor(Qt::SizeHorCursor); m_editMode = LeftBoundary; return; } 01501 if (closeToRight) { v->setCursor(Qt::SizeHorCursor); m_editMode = RightBoundary; return; } 01502 if (closeToTop) { v->setCursor(Qt::CrossCursor); m_editMode = DragNote; return; } 01503 if (closeToBottom) { v->setCursor(Qt::UpArrowCursor); m_editMode = SplitNote; return; } 01504 01505 v->setCursor(Qt::ArrowCursor); 01506 01507 std::cerr << "Mouse moved in edit mode over FlexiNoteLayer" << std::endl; 01508 // v->setCursor(Qt::SizeHorCursor); 01509 01510 } 01511 01512 void 01513 FlexiNoteLayer::getRelativeMousePosition(View *v, FlexiNoteModel::Point ¬e, int x, int y, bool &closeToLeft, bool &closeToRight, bool &closeToTop, bool &closeToBottom) const 01514 { 01515 // GF: TODO: consoloidate the tolerance values 01516 if (!m_model) return; 01517 01518 int ctol = 0; 01519 int noteStartX = v->getXForFrame(note.frame); 01520 int noteEndX = v->getXForFrame(note.frame + note.duration); 01521 int noteValueY = getYForValue(v,note.value); 01522 int noteStartY = noteValueY - (NOTE_HEIGHT / 2); 01523 int noteEndY = noteValueY + (NOTE_HEIGHT / 2); 01524 01525 bool closeToNote = false; 01526 01527 if (y >= noteStartY-ctol && y <= noteEndY+ctol && x >= noteStartX-ctol && x <= noteEndX+ctol) closeToNote = true; 01528 if (!closeToNote) return; 01529 01530 int tol = NOTE_HEIGHT / 2; 01531 01532 if (x >= noteStartX - tol && x <= noteStartX + tol) closeToLeft = true; 01533 if (x >= noteEndX - tol && x <= noteEndX + tol) closeToRight = true; 01534 if (y >= noteStartY - tol && y <= noteStartY + tol) closeToTop = true; 01535 if (y >= noteEndY - tol && y <= noteEndY + tol) closeToBottom = true; 01536 01537 // cerr << "FlexiNoteLayer::getRelativeMousePosition: close to: left " << closeToLeft << " right " << closeToRight << " top " << closeToTop << " bottom " << closeToBottom << endl; 01538 } 01539 01540 01541 bool 01542 FlexiNoteLayer::editOpen(View *v, QMouseEvent *e) 01543 { 01544 std::cerr << "Opening note editor dialog" << std::endl; 01545 if (!m_model) return false; 01546 01547 FlexiNoteModel::Point note(0); 01548 if (!getPointToDrag(v, e->x(), e->y(), note)) return false; 01549 01550 // FlexiNoteModel::Point note = *points.begin(); 01551 01552 ItemEditDialog *dialog = new ItemEditDialog 01553 (m_model->getSampleRate(), 01554 ItemEditDialog::ShowTime | 01555 ItemEditDialog::ShowDuration | 01556 ItemEditDialog::ShowValue | 01557 ItemEditDialog::ShowText, 01558 getScaleUnits()); 01559 01560 dialog->setFrameTime(note.frame); 01561 dialog->setValue(note.value); 01562 dialog->setFrameDuration(note.duration); 01563 dialog->setText(note.label); 01564 01565 if (dialog->exec() == QDialog::Accepted) { 01566 01567 FlexiNoteModel::Point newNote = note; 01568 newNote.frame = dialog->getFrameTime(); 01569 newNote.value = dialog->getValue(); 01570 newNote.duration = dialog->getFrameDuration(); 01571 newNote.label = dialog->getText(); 01572 01573 FlexiNoteModel::EditCommand *command = new FlexiNoteModel::EditCommand 01574 (m_model, tr("Edit Point")); 01575 command->deletePoint(note); 01576 command->addPoint(newNote); 01577 finish(command); 01578 } 01579 01580 delete dialog; 01581 return true; 01582 } 01583 01584 void 01585 FlexiNoteLayer::moveSelection(Selection s, int newStartFrame) 01586 { 01587 if (!m_model) return; 01588 01589 FlexiNoteModel::EditCommand *command = 01590 new FlexiNoteModel::EditCommand(m_model, tr("Drag Selection")); 01591 01592 FlexiNoteModel::PointList points = 01593 m_model->getPoints(s.getStartFrame(), s.getEndFrame()); 01594 01595 for (FlexiNoteModel::PointList::iterator i = points.begin(); 01596 i != points.end(); ++i) { 01597 01598 if (s.contains(i->frame)) { 01599 FlexiNoteModel::Point newPoint(*i); 01600 newPoint.frame = i->frame + newStartFrame - s.getStartFrame(); 01601 command->deletePoint(*i); 01602 command->addPoint(newPoint); 01603 } 01604 } 01605 01606 finish(command); 01607 } 01608 01609 void 01610 FlexiNoteLayer::resizeSelection(Selection s, Selection newSize) 01611 { 01612 if (!m_model) return; 01613 01614 FlexiNoteModel::EditCommand *command = 01615 new FlexiNoteModel::EditCommand(m_model, tr("Resize Selection")); 01616 01617 FlexiNoteModel::PointList points = 01618 m_model->getPoints(s.getStartFrame(), s.getEndFrame()); 01619 01620 double ratio = 01621 double(newSize.getEndFrame() - newSize.getStartFrame()) / 01622 double(s.getEndFrame() - s.getStartFrame()); 01623 01624 for (FlexiNoteModel::PointList::iterator i = points.begin(); 01625 i != points.end(); ++i) { 01626 01627 if (s.contains(i->frame)) { 01628 01629 double targetStart = i->frame; 01630 targetStart = newSize.getStartFrame() + 01631 double(targetStart - s.getStartFrame()) * ratio; 01632 01633 double targetEnd = i->frame + i->duration; 01634 targetEnd = newSize.getStartFrame() + 01635 double(targetEnd - s.getStartFrame()) * ratio; 01636 01637 FlexiNoteModel::Point newPoint(*i); 01638 newPoint.frame = lrint(targetStart); 01639 newPoint.duration = lrint(targetEnd - targetStart); 01640 command->deletePoint(*i); 01641 command->addPoint(newPoint); 01642 } 01643 } 01644 01645 finish(command); 01646 } 01647 01648 void 01649 FlexiNoteLayer::deleteSelection(Selection s) 01650 { 01651 if (!m_model) return; 01652 01653 FlexiNoteModel::EditCommand *command = 01654 new FlexiNoteModel::EditCommand(m_model, tr("Delete Selected Points")); 01655 01656 FlexiNoteModel::PointList points = 01657 m_model->getPoints(s.getStartFrame(), s.getEndFrame()); 01658 01659 for (FlexiNoteModel::PointList::iterator i = points.begin(); 01660 i != points.end(); ++i) { 01661 01662 if (s.contains(i->frame)) { 01663 command->deletePoint(*i); 01664 } 01665 } 01666 01667 finish(command); 01668 } 01669 01670 void 01671 FlexiNoteLayer::deleteSelectionInclusive(Selection s) 01672 { 01673 if (!m_model) return; 01674 01675 FlexiNoteModel::EditCommand *command = 01676 new FlexiNoteModel::EditCommand(m_model, tr("Delete Selected Points")); 01677 01678 FlexiNoteModel::PointList points = 01679 m_model->getPoints(s.getStartFrame(), s.getEndFrame()); 01680 01681 for (FlexiNoteModel::PointList::iterator i = points.begin(); 01682 i != points.end(); ++i) { 01683 bool overlap = !( 01684 ((s.getStartFrame() <= i->frame) && (s.getEndFrame() <= i->frame)) || // selection is left of note 01685 ((s.getStartFrame() >= (i->frame+i->duration)) && (s.getEndFrame() >= (i->frame+i->duration))) // selection is right of note 01686 ); 01687 if (overlap) { 01688 command->deletePoint(*i); 01689 } 01690 } 01691 01692 finish(command); 01693 } 01694 01695 void 01696 FlexiNoteLayer::copy(View *v, Selection s, Clipboard &to) 01697 { 01698 if (!m_model) return; 01699 01700 FlexiNoteModel::PointList points = 01701 m_model->getPoints(s.getStartFrame(), s.getEndFrame()); 01702 01703 for (FlexiNoteModel::PointList::iterator i = points.begin(); 01704 i != points.end(); ++i) { 01705 if (s.contains(i->frame)) { 01706 Clipboard::Point point(i->frame, i->value, i->duration, i->level, i->label); 01707 point.setReferenceFrame(alignToReference(v, i->frame)); 01708 to.addPoint(point); 01709 } 01710 } 01711 } 01712 01713 bool 01714 FlexiNoteLayer::paste(View *v, const Clipboard &from, int /*frameOffset */, bool /* interactive */) 01715 { 01716 if (!m_model) return false; 01717 01718 const Clipboard::PointList &points = from.getPoints(); 01719 01720 bool realign = false; 01721 01722 if (clipboardHasDifferentAlignment(v, from)) { 01723 01724 QMessageBox::StandardButton button = 01725 QMessageBox::question(v, tr("Re-align pasted items?"), 01726 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?"), 01727 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, 01728 QMessageBox::Yes); 01729 01730 if (button == QMessageBox::Cancel) { 01731 return false; 01732 } 01733 01734 if (button == QMessageBox::Yes) { 01735 realign = true; 01736 } 01737 } 01738 01739 FlexiNoteModel::EditCommand *command = 01740 new FlexiNoteModel::EditCommand(m_model, tr("Paste")); 01741 01742 for (Clipboard::PointList::const_iterator i = points.begin(); 01743 i != points.end(); ++i) { 01744 01745 if (!i->haveFrame()) continue; 01746 int frame = 0; 01747 01748 if (!realign) { 01749 01750 frame = i->getFrame(); 01751 01752 } else { 01753 01754 if (i->haveReferenceFrame()) { 01755 frame = i->getReferenceFrame(); 01756 frame = alignFromReference(v, frame); 01757 } else { 01758 frame = i->getFrame(); 01759 } 01760 } 01761 01762 FlexiNoteModel::Point newPoint(frame); 01763 01764 if (i->haveLabel()) newPoint.label = i->getLabel(); 01765 if (i->haveValue()) newPoint.value = i->getValue(); 01766 else newPoint.value = (m_model->getValueMinimum() + 01767 m_model->getValueMaximum()) / 2; 01768 if (i->haveLevel()) newPoint.level = i->getLevel(); 01769 if (i->haveDuration()) newPoint.duration = i->getDuration(); 01770 else { 01771 int nextFrame = frame; 01772 Clipboard::PointList::const_iterator j = i; 01773 for (; j != points.end(); ++j) { 01774 if (!j->haveFrame()) continue; 01775 if (j != i) break; 01776 } 01777 if (j != points.end()) { 01778 nextFrame = j->getFrame(); 01779 } 01780 if (nextFrame == frame) { 01781 newPoint.duration = m_model->getResolution(); 01782 } else { 01783 newPoint.duration = nextFrame - frame; 01784 } 01785 } 01786 01787 command->addPoint(newPoint); 01788 } 01789 01790 finish(command); 01791 return true; 01792 } 01793 01794 void 01795 FlexiNoteLayer::addNoteOn(int frame, int pitch, int velocity) 01796 { 01797 m_pendingNoteOns.insert(FlexiNote(frame, pitch, 0, float(velocity) / 127.0, "")); 01798 } 01799 01800 void 01801 FlexiNoteLayer::addNoteOff(int frame, int pitch) 01802 { 01803 for (FlexiNoteSet::iterator i = m_pendingNoteOns.begin(); 01804 i != m_pendingNoteOns.end(); ++i) { 01805 if (lrintf((*i).value) == pitch) { 01806 FlexiNote note(*i); 01807 m_pendingNoteOns.erase(i); 01808 note.duration = frame - note.frame; 01809 if (m_model) { 01810 FlexiNoteModel::AddPointCommand *c = new FlexiNoteModel::AddPointCommand 01811 (m_model, note, tr("Record FlexiNote")); 01812 // execute and bundle: 01813 CommandHistory::getInstance()->addCommand(c, true, true); 01814 } 01815 break; 01816 } 01817 } 01818 } 01819 01820 void 01821 FlexiNoteLayer::abandonNoteOns() 01822 { 01823 m_pendingNoteOns.clear(); 01824 } 01825 01826 int 01827 FlexiNoteLayer::getDefaultColourHint(bool darkbg, bool &impose) 01828 { 01829 impose = false; 01830 return ColourDatabase::getInstance()->getColourIndex 01831 (QString(darkbg ? "White" : "Black")); 01832 } 01833 01834 void 01835 FlexiNoteLayer::toXml(QTextStream &stream, 01836 QString indent, QString extraAttributes) const 01837 { 01838 SingleColourLayer::toXml(stream, indent, extraAttributes + 01839 QString(" verticalScale=\"%1\" scaleMinimum=\"%2\" scaleMaximum=\"%3\" ") 01840 .arg(m_verticalScale) 01841 .arg(m_scaleMinimum) 01842 .arg(m_scaleMaximum)); 01843 } 01844 01845 void 01846 FlexiNoteLayer::setProperties(const QXmlAttributes &attributes) 01847 { 01848 SingleColourLayer::setProperties(attributes); 01849 01850 bool ok; 01851 VerticalScale scale = (VerticalScale) 01852 attributes.value("verticalScale").toInt(&ok); 01853 if (ok) setVerticalScale(scale); 01854 01855 // bool alsoOk; 01856 // float min = attributes.value("scaleMinimum").toFloat(&ok); 01857 // float max = attributes.value("scaleMaximum").toFloat(&alsoOk); 01858 // if (ok && alsoOk && min != max) setDisplayExtents(min, max); 01859 } 01860 01861 void 01862 FlexiNoteLayer::setVerticalRangeToNoteRange(View *v) 01863 { 01864 float minf = std::numeric_limits<float>::max(); 01865 float maxf = 0; 01866 bool hasNotes = 0; 01867 for (FlexiNoteModel::PointList::const_iterator i = m_model->getPoints().begin(); 01868 i != m_model->getPoints().end(); ++i) { 01869 hasNotes = 1; 01870 FlexiNote note = *i; 01871 if (note.value < minf) minf = note.value; 01872 if (note.value > maxf) maxf = note.value; 01873 } 01874 01875 std::cerr << "min frequency:" << minf << ", max frequency: " << maxf << std::endl; 01876 01877 if (hasNotes) { 01878 v->getLayer(1)->setDisplayExtents(minf*0.66,maxf*1.5); 01879 // MM: this is a hack because we rely on 01880 // * this layer being automatically aligned to layer 1 01881 // * layer one is a log frequency layer. 01882 } 01883 } 01884 01885