Mercurial > hg > easaier-soundaccess
comparison layer/NoteLayer.cpp @ 0:fc9323a41f5a
start base : Sonic Visualiser sv1-1.0rc1
author | lbajardsilogic |
---|---|
date | Fri, 11 May 2007 09:08:14 +0000 |
parents | |
children | d8e6709e9075 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:fc9323a41f5a |
---|---|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ | |
2 | |
3 /* | |
4 Sonic Visualiser | |
5 An audio file viewer and annotation editor. | |
6 Centre for Digital Music, Queen Mary, University of London. | |
7 This file copyright 2006 Chris Cannam. | |
8 | |
9 This program is free software; you can redistribute it and/or | |
10 modify it under the terms of the GNU General Public License as | |
11 published by the Free Software Foundation; either version 2 of the | |
12 License, or (at your option) any later version. See the file | |
13 COPYING included with this distribution for more information. | |
14 */ | |
15 | |
16 #include "NoteLayer.h" | |
17 | |
18 #include "data/model/Model.h" | |
19 #include "base/RealTime.h" | |
20 #include "base/Profiler.h" | |
21 #include "base/Pitch.h" | |
22 #include "base/LogRange.h" | |
23 #include "view/View.h" | |
24 | |
25 #include "data/model/NoteModel.h" | |
26 | |
27 #include "widgets/ItemEditDialog.h" | |
28 | |
29 #include "SpectrogramLayer.h" // for optional frequency alignment | |
30 | |
31 #include <QPainter> | |
32 #include <QPainterPath> | |
33 #include <QMouseEvent> | |
34 | |
35 #include <iostream> | |
36 #include <cmath> | |
37 | |
38 NoteLayer::NoteLayer() : | |
39 Layer(), | |
40 m_model(0), | |
41 m_editing(false), | |
42 m_originalPoint(0, 0.0, 0, tr("New Point")), | |
43 m_editingPoint(0, 0.0, 0, tr("New Point")), | |
44 m_editingCommand(0), | |
45 m_colour(Qt::black), | |
46 m_verticalScale(AutoAlignScale) | |
47 { | |
48 | |
49 } | |
50 | |
51 void | |
52 NoteLayer::setModel(NoteModel *model) | |
53 { | |
54 if (m_model == model) return; | |
55 m_model = model; | |
56 | |
57 connect(m_model, SIGNAL(modelChanged()), this, SIGNAL(modelChanged())); | |
58 connect(m_model, SIGNAL(modelChanged(size_t, size_t)), | |
59 this, SIGNAL(modelChanged(size_t, size_t))); | |
60 | |
61 connect(m_model, SIGNAL(completionChanged()), | |
62 this, SIGNAL(modelCompletionChanged())); | |
63 | |
64 // std::cerr << "NoteLayer::setModel(" << model << ")" << std::endl; | |
65 | |
66 emit modelReplaced(); | |
67 } | |
68 | |
69 Layer::PropertyList | |
70 NoteLayer::getProperties() const | |
71 { | |
72 PropertyList list; | |
73 list.push_back("Colour"); | |
74 list.push_back("Vertical Scale"); | |
75 list.push_back("Scale Units"); | |
76 return list; | |
77 } | |
78 | |
79 QString | |
80 NoteLayer::getPropertyLabel(const PropertyName &name) const | |
81 { | |
82 if (name == "Colour") return tr("Colour"); | |
83 if (name == "Vertical Scale") return tr("Vertical Scale"); | |
84 if (name == "Scale Units") return tr("Scale Units"); | |
85 return ""; | |
86 } | |
87 | |
88 Layer::PropertyType | |
89 NoteLayer::getPropertyType(const PropertyName &name) const | |
90 { | |
91 if (name == "Scale Units") return UnitsProperty; | |
92 return ValueProperty; | |
93 } | |
94 | |
95 QString | |
96 NoteLayer::getPropertyGroupName(const PropertyName &name) const | |
97 { | |
98 if (name == "Vertical Scale" || name == "Scale Units") { | |
99 return tr("Scale"); | |
100 } | |
101 return QString(); | |
102 } | |
103 | |
104 int | |
105 NoteLayer::getPropertyRangeAndValue(const PropertyName &name, | |
106 int *min, int *max, int *deflt) const | |
107 { | |
108 //!!! factor this colour handling stuff out into a colour manager class | |
109 | |
110 int val = 0; | |
111 | |
112 if (name == "Colour") { | |
113 | |
114 if (min) *min = 0; | |
115 if (max) *max = 5; | |
116 if (deflt) *deflt = 0; | |
117 | |
118 if (m_colour == Qt::black) val = 0; | |
119 else if (m_colour == Qt::darkRed) val = 1; | |
120 else if (m_colour == Qt::darkBlue) val = 2; | |
121 else if (m_colour == Qt::darkGreen) val = 3; | |
122 else if (m_colour == QColor(200, 50, 255)) val = 4; | |
123 else if (m_colour == QColor(255, 150, 50)) val = 5; | |
124 | |
125 } else if (name == "Vertical Scale") { | |
126 | |
127 if (min) *min = 0; | |
128 if (max) *max = 3; | |
129 if (deflt) *deflt = int(AutoAlignScale); | |
130 | |
131 val = int(m_verticalScale); | |
132 | |
133 } else if (name == "Scale Units") { | |
134 | |
135 if (deflt) *deflt = 0; | |
136 if (m_model) { | |
137 val = UnitDatabase::getInstance()->getUnitId | |
138 (m_model->getScaleUnits()); | |
139 } | |
140 | |
141 } else { | |
142 | |
143 val = Layer::getPropertyRangeAndValue(name, min, max, deflt); | |
144 } | |
145 | |
146 return val; | |
147 } | |
148 | |
149 QString | |
150 NoteLayer::getPropertyValueLabel(const PropertyName &name, | |
151 int value) const | |
152 { | |
153 if (name == "Colour") { | |
154 switch (value) { | |
155 default: | |
156 case 0: return tr("Black"); | |
157 case 1: return tr("Red"); | |
158 case 2: return tr("Blue"); | |
159 case 3: return tr("Green"); | |
160 case 4: return tr("Purple"); | |
161 case 5: return tr("Orange"); | |
162 } | |
163 } else if (name == "Vertical Scale") { | |
164 switch (value) { | |
165 default: | |
166 case 0: return tr("Auto-Align"); | |
167 case 1: return tr("Linear"); | |
168 case 2: return tr("Log"); | |
169 case 3: return tr("MIDI Notes"); | |
170 } | |
171 } | |
172 return tr("<unknown>"); | |
173 } | |
174 | |
175 void | |
176 NoteLayer::setProperty(const PropertyName &name, int value) | |
177 { | |
178 if (name == "Colour") { | |
179 switch (value) { | |
180 default: | |
181 case 0: setBaseColour(Qt::black); break; | |
182 case 1: setBaseColour(Qt::darkRed); break; | |
183 case 2: setBaseColour(Qt::darkBlue); break; | |
184 case 3: setBaseColour(Qt::darkGreen); break; | |
185 case 4: setBaseColour(QColor(200, 50, 255)); break; | |
186 case 5: setBaseColour(QColor(255, 150, 50)); break; | |
187 } | |
188 } else if (name == "Vertical Scale") { | |
189 setVerticalScale(VerticalScale(value)); | |
190 } else if (name == "Scale Units") { | |
191 if (m_model) { | |
192 m_model->setScaleUnits | |
193 (UnitDatabase::getInstance()->getUnitById(value)); | |
194 emit modelChanged(); | |
195 } | |
196 } | |
197 } | |
198 | |
199 void | |
200 NoteLayer::setBaseColour(QColor colour) | |
201 { | |
202 if (m_colour == colour) return; | |
203 m_colour = colour; | |
204 emit layerParametersChanged(); | |
205 } | |
206 | |
207 void | |
208 NoteLayer::setVerticalScale(VerticalScale scale) | |
209 { | |
210 if (m_verticalScale == scale) return; | |
211 m_verticalScale = scale; | |
212 emit layerParametersChanged(); | |
213 } | |
214 | |
215 bool | |
216 NoteLayer::isLayerScrollable(const View *v) const | |
217 { | |
218 QPoint discard; | |
219 return !v->shouldIlluminateLocalFeatures(this, discard); | |
220 } | |
221 | |
222 bool | |
223 NoteLayer::shouldConvertMIDIToHz() const | |
224 { | |
225 QString unit = m_model->getScaleUnits(); | |
226 return (unit != "Hz"); | |
227 // if (unit == "" || | |
228 // unit.startsWith("MIDI") || | |
229 // unit.startsWith("midi")) return true; | |
230 // return false; | |
231 } | |
232 | |
233 bool | |
234 NoteLayer::getValueExtents(float &min, float &max, | |
235 bool &logarithmic, QString &unit) const | |
236 { | |
237 if (!m_model) return false; | |
238 min = m_model->getValueMinimum(); | |
239 max = m_model->getValueMaximum(); | |
240 | |
241 if (shouldConvertMIDIToHz()) { | |
242 unit = "Hz"; | |
243 min = Pitch::getFrequencyForPitch(lrintf(min)); | |
244 max = Pitch::getFrequencyForPitch(lrintf(max + 1)); | |
245 } else unit = m_model->getScaleUnits(); | |
246 | |
247 if (m_verticalScale == MIDIRangeScale || | |
248 m_verticalScale == LogScale) logarithmic = true; | |
249 | |
250 return true; | |
251 } | |
252 | |
253 bool | |
254 NoteLayer::getDisplayExtents(float &min, float &max) const | |
255 { | |
256 if (!m_model || m_verticalScale == AutoAlignScale) return false; | |
257 | |
258 if (m_verticalScale == MIDIRangeScale) { | |
259 min = Pitch::getFrequencyForPitch(0); | |
260 max = Pitch::getFrequencyForPitch(127); | |
261 return true; | |
262 } | |
263 | |
264 min = m_model->getValueMinimum(); | |
265 max = m_model->getValueMaximum(); | |
266 | |
267 if (shouldConvertMIDIToHz()) { | |
268 min = Pitch::getFrequencyForPitch(lrintf(min)); | |
269 max = Pitch::getFrequencyForPitch(lrintf(max + 1)); | |
270 } | |
271 | |
272 return true; | |
273 } | |
274 | |
275 NoteModel::PointList | |
276 NoteLayer::getLocalPoints(View *v, int x) const | |
277 { | |
278 if (!m_model) return NoteModel::PointList(); | |
279 | |
280 long frame = v->getFrameForX(x); | |
281 | |
282 NoteModel::PointList onPoints = | |
283 m_model->getPoints(frame); | |
284 | |
285 if (!onPoints.empty()) { | |
286 return onPoints; | |
287 } | |
288 | |
289 NoteModel::PointList prevPoints = | |
290 m_model->getPreviousPoints(frame); | |
291 NoteModel::PointList nextPoints = | |
292 m_model->getNextPoints(frame); | |
293 | |
294 NoteModel::PointList usePoints = prevPoints; | |
295 | |
296 if (prevPoints.empty()) { | |
297 usePoints = nextPoints; | |
298 } else if (long(prevPoints.begin()->frame) < v->getStartFrame() && | |
299 !(nextPoints.begin()->frame > v->getEndFrame())) { | |
300 usePoints = nextPoints; | |
301 } else if (long(nextPoints.begin()->frame) - frame < | |
302 frame - long(prevPoints.begin()->frame)) { | |
303 usePoints = nextPoints; | |
304 } | |
305 | |
306 if (!usePoints.empty()) { | |
307 int fuzz = 2; | |
308 int px = v->getXForFrame(usePoints.begin()->frame); | |
309 if ((px > x && px - x > fuzz) || | |
310 (px < x && x - px > fuzz + 1)) { | |
311 usePoints.clear(); | |
312 } | |
313 } | |
314 | |
315 return usePoints; | |
316 } | |
317 | |
318 QString | |
319 NoteLayer::getFeatureDescription(View *v, QPoint &pos) const | |
320 { | |
321 int x = pos.x(); | |
322 | |
323 if (!m_model || !m_model->getSampleRate()) return ""; | |
324 | |
325 NoteModel::PointList points = getLocalPoints(v, x); | |
326 | |
327 if (points.empty()) { | |
328 if (!m_model->isReady()) { | |
329 return tr("In progress"); | |
330 } else { | |
331 return tr("No local points"); | |
332 } | |
333 } | |
334 | |
335 Note note(0); | |
336 NoteModel::PointList::iterator i; | |
337 | |
338 for (i = points.begin(); i != points.end(); ++i) { | |
339 | |
340 int y = getYForValue(v, i->value); | |
341 int h = 3; | |
342 | |
343 if (m_model->getValueQuantization() != 0.0) { | |
344 h = y - getYForValue(v, i->value + m_model->getValueQuantization()); | |
345 if (h < 3) h = 3; | |
346 } | |
347 | |
348 if (pos.y() >= y - h && pos.y() <= y) { | |
349 note = *i; | |
350 break; | |
351 } | |
352 } | |
353 | |
354 if (i == points.end()) return tr("No local points"); | |
355 | |
356 RealTime rt = RealTime::frame2RealTime(note.frame, | |
357 m_model->getSampleRate()); | |
358 RealTime rd = RealTime::frame2RealTime(note.duration, | |
359 m_model->getSampleRate()); | |
360 | |
361 QString pitchText; | |
362 | |
363 if (shouldConvertMIDIToHz()) { | |
364 | |
365 int mnote = lrintf(note.value); | |
366 int cents = lrintf((note.value - mnote) * 100); | |
367 float freq = Pitch::getFrequencyForPitch(mnote, cents); | |
368 pitchText = tr("%1 (%2 Hz)") | |
369 .arg(Pitch::getPitchLabel(mnote, cents)).arg(freq); | |
370 | |
371 } else if (m_model->getScaleUnits() == "Hz") { | |
372 | |
373 pitchText = tr("%1 Hz (%2)") | |
374 .arg(note.value) | |
375 .arg(Pitch::getPitchLabelForFrequency(note.value)); | |
376 | |
377 } else { | |
378 pitchText = tr("%1 %2") | |
379 .arg(note.value).arg(m_model->getScaleUnits()); | |
380 } | |
381 | |
382 QString text; | |
383 | |
384 if (note.label == "") { | |
385 text = QString(tr("Time:\t%1\nPitch:\t%2\nDuration:\t%3\nNo label")) | |
386 .arg(rt.toText(true).c_str()) | |
387 .arg(pitchText) | |
388 .arg(rd.toText(true).c_str()); | |
389 } else { | |
390 text = QString(tr("Time:\t%1\nPitch:\t%2\nDuration:\t%3\nLabel:\t%4")) | |
391 .arg(rt.toText(true).c_str()) | |
392 .arg(pitchText) | |
393 .arg(rd.toText(true).c_str()) | |
394 .arg(note.label); | |
395 } | |
396 | |
397 pos = QPoint(v->getXForFrame(note.frame), | |
398 getYForValue(v, note.value)); | |
399 return text; | |
400 } | |
401 | |
402 bool | |
403 NoteLayer::snapToFeatureFrame(View *v, int &frame, | |
404 size_t &resolution, | |
405 SnapType snap) const | |
406 { | |
407 if (!m_model) { | |
408 return Layer::snapToFeatureFrame(v, frame, resolution, snap); | |
409 } | |
410 | |
411 resolution = m_model->getResolution(); | |
412 NoteModel::PointList points; | |
413 | |
414 if (snap == SnapNeighbouring) { | |
415 | |
416 points = getLocalPoints(v, v->getXForFrame(frame)); | |
417 if (points.empty()) return false; | |
418 frame = points.begin()->frame; | |
419 return true; | |
420 } | |
421 | |
422 points = m_model->getPoints(frame, frame); | |
423 int snapped = frame; | |
424 bool found = false; | |
425 | |
426 for (NoteModel::PointList::const_iterator i = points.begin(); | |
427 i != points.end(); ++i) { | |
428 | |
429 if (snap == SnapRight) { | |
430 | |
431 if (i->frame > frame) { | |
432 snapped = i->frame; | |
433 found = true; | |
434 break; | |
435 } | |
436 | |
437 } else if (snap == SnapLeft) { | |
438 | |
439 if (i->frame <= frame) { | |
440 snapped = i->frame; | |
441 found = true; // don't break, as the next may be better | |
442 } else { | |
443 break; | |
444 } | |
445 | |
446 } else { // nearest | |
447 | |
448 NoteModel::PointList::const_iterator j = i; | |
449 ++j; | |
450 | |
451 if (j == points.end()) { | |
452 | |
453 snapped = i->frame; | |
454 found = true; | |
455 break; | |
456 | |
457 } else if (j->frame >= frame) { | |
458 | |
459 if (j->frame - frame < frame - i->frame) { | |
460 snapped = j->frame; | |
461 } else { | |
462 snapped = i->frame; | |
463 } | |
464 found = true; | |
465 break; | |
466 } | |
467 } | |
468 } | |
469 | |
470 frame = snapped; | |
471 return found; | |
472 } | |
473 | |
474 void | |
475 NoteLayer::getScaleExtents(View *v, float &min, float &max, bool &log) const | |
476 { | |
477 min = 0.0; | |
478 max = 0.0; | |
479 log = false; | |
480 | |
481 QString queryUnits; | |
482 if (shouldConvertMIDIToHz()) queryUnits = "Hz"; | |
483 else queryUnits = m_model->getScaleUnits(); | |
484 | |
485 if (m_verticalScale == AutoAlignScale) { | |
486 | |
487 if (!v->getValueExtents(queryUnits, min, max, log)) { | |
488 | |
489 min = m_model->getValueMinimum(); | |
490 max = m_model->getValueMaximum(); | |
491 | |
492 if (shouldConvertMIDIToHz()) { | |
493 min = Pitch::getFrequencyForPitch(lrintf(min)); | |
494 max = Pitch::getFrequencyForPitch(lrintf(max + 1)); | |
495 } | |
496 | |
497 std::cerr << "NoteLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << std::endl; | |
498 | |
499 } else if (log) { | |
500 | |
501 LogRange::mapRange(min, max); | |
502 | |
503 std::cerr << "NoteLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << std::endl; | |
504 | |
505 } | |
506 | |
507 } else { | |
508 | |
509 min = m_model->getValueMinimum(); | |
510 max = m_model->getValueMaximum(); | |
511 | |
512 if (m_verticalScale == MIDIRangeScale) { | |
513 min = Pitch::getFrequencyForPitch(0); | |
514 max = Pitch::getFrequencyForPitch(127); | |
515 } else if (shouldConvertMIDIToHz()) { | |
516 min = Pitch::getFrequencyForPitch(lrintf(min)); | |
517 max = Pitch::getFrequencyForPitch(lrintf(max + 1)); | |
518 } | |
519 | |
520 if (m_verticalScale == LogScale || m_verticalScale == MIDIRangeScale) { | |
521 LogRange::mapRange(min, max); | |
522 log = true; | |
523 } | |
524 } | |
525 | |
526 if (max == min) max = min + 1.0; | |
527 } | |
528 | |
529 int | |
530 NoteLayer::getYForValue(View *v, float val) const | |
531 { | |
532 float min = 0.0, max = 0.0; | |
533 bool logarithmic = false; | |
534 int h = v->height(); | |
535 | |
536 getScaleExtents(v, min, max, logarithmic); | |
537 | |
538 // std::cerr << "NoteLayer[" << this << "]::getYForValue(" << val << "): min = " << min << ", max = " << max << ", log = " << logarithmic << std::endl; | |
539 | |
540 if (shouldConvertMIDIToHz()) { | |
541 val = Pitch::getFrequencyForPitch(lrintf(val), | |
542 lrintf((val - lrintf(val)) * 100)); | |
543 // std::cerr << "shouldConvertMIDIToHz true, val now = " << val << std::endl; | |
544 } | |
545 | |
546 if (logarithmic) { | |
547 val = LogRange::map(val); | |
548 // std::cerr << "logarithmic true, val now = " << val << std::endl; | |
549 } | |
550 | |
551 int y = int(h - ((val - min) * h) / (max - min)) - 1; | |
552 // std::cerr << "y = " << y << std::endl; | |
553 return y; | |
554 } | |
555 | |
556 float | |
557 NoteLayer::getValueForY(View *v, int y) const | |
558 { | |
559 float min = 0.0, max = 0.0; | |
560 bool logarithmic = false; | |
561 int h = v->height(); | |
562 | |
563 getScaleExtents(v, min, max, logarithmic); | |
564 | |
565 float val = min + (float(h - y) * float(max - min)) / h; | |
566 | |
567 if (logarithmic) { | |
568 val = powf(10.f, val); | |
569 } | |
570 | |
571 if (shouldConvertMIDIToHz()) { | |
572 val = Pitch::getPitchForFrequency(val); | |
573 } | |
574 | |
575 return val; | |
576 } | |
577 | |
578 void | |
579 NoteLayer::paint(View *v, QPainter &paint, QRect rect) const | |
580 { | |
581 if (!m_model || !m_model->isOK()) return; | |
582 | |
583 int sampleRate = m_model->getSampleRate(); | |
584 if (!sampleRate) return; | |
585 | |
586 // Profiler profiler("NoteLayer::paint", true); | |
587 | |
588 int x0 = rect.left(), x1 = rect.right(); | |
589 long frame0 = v->getFrameForX(x0); | |
590 long frame1 = v->getFrameForX(x1); | |
591 | |
592 NoteModel::PointList points(m_model->getPoints(frame0, frame1)); | |
593 if (points.empty()) return; | |
594 | |
595 paint.setPen(m_colour); | |
596 | |
597 QColor brushColour(m_colour); | |
598 brushColour.setAlpha(80); | |
599 | |
600 // std::cerr << "NoteLayer::paint: resolution is " | |
601 // << m_model->getResolution() << " frames" << std::endl; | |
602 | |
603 float min = m_model->getValueMinimum(); | |
604 float max = m_model->getValueMaximum(); | |
605 if (max == min) max = min + 1.0; | |
606 | |
607 QPoint localPos; | |
608 long illuminateFrame = -1; | |
609 | |
610 if (v->shouldIlluminateLocalFeatures(this, localPos)) { | |
611 NoteModel::PointList localPoints = | |
612 getLocalPoints(v, localPos.x()); | |
613 if (!localPoints.empty()) illuminateFrame = localPoints.begin()->frame; | |
614 } | |
615 | |
616 paint.save(); | |
617 paint.setRenderHint(QPainter::Antialiasing, false); | |
618 | |
619 for (NoteModel::PointList::const_iterator i = points.begin(); | |
620 i != points.end(); ++i) { | |
621 | |
622 const NoteModel::Point &p(*i); | |
623 | |
624 int x = v->getXForFrame(p.frame); | |
625 int y = getYForValue(v, p.value); | |
626 int w = v->getXForFrame(p.frame + p.duration) - x; | |
627 int h = 3; | |
628 | |
629 if (m_model->getValueQuantization() != 0.0) { | |
630 h = y - getYForValue(v, p.value + m_model->getValueQuantization()); | |
631 if (h < 3) h = 3; | |
632 } | |
633 | |
634 if (w < 1) w = 1; | |
635 paint.setPen(m_colour); | |
636 paint.setBrush(brushColour); | |
637 | |
638 if (illuminateFrame == p.frame) { | |
639 if (localPos.y() >= y - h && localPos.y() < y) { | |
640 paint.setPen(Qt::black);//!!! | |
641 paint.setBrush(Qt::black);//!!! | |
642 } | |
643 } | |
644 | |
645 paint.drawRect(x, y - h/2, w, h); | |
646 | |
647 /// if (p.label != "") { | |
648 /// paint.drawText(x + 5, y - paint.fontMetrics().height() + paint.fontMetrics().ascent(), p.label); | |
649 /// } | |
650 } | |
651 | |
652 paint.restore(); | |
653 } | |
654 | |
655 void | |
656 NoteLayer::drawStart(View *v, QMouseEvent *e) | |
657 { | |
658 // std::cerr << "NoteLayer::drawStart(" << e->x() << "," << e->y() << ")" << std::endl; | |
659 | |
660 if (!m_model) return; | |
661 | |
662 long frame = v->getFrameForX(e->x()); | |
663 if (frame < 0) frame = 0; | |
664 frame = frame / m_model->getResolution() * m_model->getResolution(); | |
665 | |
666 float value = getValueForY(v, e->y()); | |
667 | |
668 m_editingPoint = NoteModel::Point(frame, value, 0, tr("New Point")); | |
669 m_originalPoint = m_editingPoint; | |
670 | |
671 if (m_editingCommand) m_editingCommand->finish(); | |
672 m_editingCommand = new NoteModel::EditCommand(m_model, | |
673 tr("Draw Point")); | |
674 m_editingCommand->addPoint(m_editingPoint); | |
675 | |
676 m_editing = true; | |
677 } | |
678 | |
679 void | |
680 NoteLayer::drawDrag(View *v, QMouseEvent *e) | |
681 { | |
682 // std::cerr << "NoteLayer::drawDrag(" << e->x() << "," << e->y() << ")" << std::endl; | |
683 | |
684 if (!m_model || !m_editing) return; | |
685 | |
686 long frame = v->getFrameForX(e->x()); | |
687 if (frame < 0) frame = 0; | |
688 frame = frame / m_model->getResolution() * m_model->getResolution(); | |
689 | |
690 float newValue = getValueForY(v, e->y()); | |
691 | |
692 long newFrame = m_editingPoint.frame; | |
693 long newDuration = frame - newFrame; | |
694 if (newDuration < 0) { | |
695 newFrame = frame; | |
696 newDuration = -newDuration; | |
697 } else if (newDuration == 0) { | |
698 newDuration = 1; | |
699 } | |
700 | |
701 m_editingCommand->deletePoint(m_editingPoint); | |
702 m_editingPoint.frame = newFrame; | |
703 m_editingPoint.value = newValue; | |
704 m_editingPoint.duration = newDuration; | |
705 m_editingCommand->addPoint(m_editingPoint); | |
706 } | |
707 | |
708 void | |
709 NoteLayer::drawEnd(View *, QMouseEvent *) | |
710 { | |
711 // std::cerr << "NoteLayer::drawEnd(" << e->x() << "," << e->y() << ")" << std::endl; | |
712 if (!m_model || !m_editing) return; | |
713 m_editingCommand->finish(); | |
714 m_editingCommand = 0; | |
715 m_editing = false; | |
716 } | |
717 | |
718 void | |
719 NoteLayer::editStart(View *v, QMouseEvent *e) | |
720 { | |
721 // std::cerr << "NoteLayer::editStart(" << e->x() << "," << e->y() << ")" << std::endl; | |
722 | |
723 if (!m_model) return; | |
724 | |
725 NoteModel::PointList points = getLocalPoints(v, e->x()); | |
726 if (points.empty()) return; | |
727 | |
728 m_editingPoint = *points.begin(); | |
729 m_originalPoint = m_editingPoint; | |
730 | |
731 if (m_editingCommand) { | |
732 m_editingCommand->finish(); | |
733 m_editingCommand = 0; | |
734 } | |
735 | |
736 m_editing = true; | |
737 } | |
738 | |
739 void | |
740 NoteLayer::editDrag(View *v, QMouseEvent *e) | |
741 { | |
742 // std::cerr << "NoteLayer::editDrag(" << e->x() << "," << e->y() << ")" << std::endl; | |
743 | |
744 if (!m_model || !m_editing) return; | |
745 | |
746 long frame = v->getFrameForX(e->x()); | |
747 if (frame < 0) frame = 0; | |
748 frame = frame / m_model->getResolution() * m_model->getResolution(); | |
749 | |
750 float value = getValueForY(v, e->y()); | |
751 | |
752 if (!m_editingCommand) { | |
753 m_editingCommand = new NoteModel::EditCommand(m_model, | |
754 tr("Drag Point")); | |
755 } | |
756 | |
757 m_editingCommand->deletePoint(m_editingPoint); | |
758 m_editingPoint.frame = frame; | |
759 m_editingPoint.value = value; | |
760 m_editingCommand->addPoint(m_editingPoint); | |
761 } | |
762 | |
763 void | |
764 NoteLayer::editEnd(View *, QMouseEvent *) | |
765 { | |
766 // std::cerr << "NoteLayer::editEnd(" << e->x() << "," << e->y() << ")" << std::endl; | |
767 if (!m_model || !m_editing) return; | |
768 | |
769 if (m_editingCommand) { | |
770 | |
771 QString newName = m_editingCommand->getName(); | |
772 | |
773 if (m_editingPoint.frame != m_originalPoint.frame) { | |
774 if (m_editingPoint.value != m_originalPoint.value) { | |
775 newName = tr("Edit Point"); | |
776 } else { | |
777 newName = tr("Relocate Point"); | |
778 } | |
779 } else { | |
780 newName = tr("Change Point Value"); | |
781 } | |
782 | |
783 m_editingCommand->setName(newName); | |
784 m_editingCommand->finish(); | |
785 } | |
786 | |
787 m_editingCommand = 0; | |
788 m_editing = false; | |
789 } | |
790 | |
791 void | |
792 NoteLayer::editOpen(View *v, QMouseEvent *e) | |
793 { | |
794 if (!m_model) return; | |
795 | |
796 NoteModel::PointList points = getLocalPoints(v, e->x()); | |
797 if (points.empty()) return; | |
798 | |
799 NoteModel::Point note = *points.begin(); | |
800 | |
801 ItemEditDialog *dialog = new ItemEditDialog | |
802 (m_model->getSampleRate(), | |
803 ItemEditDialog::ShowTime | | |
804 ItemEditDialog::ShowDuration | | |
805 ItemEditDialog::ShowValue | | |
806 ItemEditDialog::ShowText, | |
807 m_model->getScaleUnits()); | |
808 | |
809 dialog->setFrameTime(note.frame); | |
810 dialog->setValue(note.value); | |
811 dialog->setFrameDuration(note.duration); | |
812 dialog->setText(note.label); | |
813 | |
814 if (dialog->exec() == QDialog::Accepted) { | |
815 | |
816 NoteModel::Point newNote = note; | |
817 newNote.frame = dialog->getFrameTime(); | |
818 newNote.value = dialog->getValue(); | |
819 newNote.duration = dialog->getFrameDuration(); | |
820 newNote.label = dialog->getText(); | |
821 | |
822 NoteModel::EditCommand *command = new NoteModel::EditCommand | |
823 (m_model, tr("Edit Point")); | |
824 command->deletePoint(note); | |
825 command->addPoint(newNote); | |
826 command->finish(); | |
827 } | |
828 | |
829 delete dialog; | |
830 } | |
831 | |
832 void | |
833 NoteLayer::moveSelection(Selection s, size_t newStartFrame) | |
834 { | |
835 if (!m_model) return; | |
836 | |
837 NoteModel::EditCommand *command = | |
838 new NoteModel::EditCommand(m_model, tr("Drag Selection")); | |
839 | |
840 NoteModel::PointList points = | |
841 m_model->getPoints(s.getStartFrame(), s.getEndFrame()); | |
842 | |
843 for (NoteModel::PointList::iterator i = points.begin(); | |
844 i != points.end(); ++i) { | |
845 | |
846 if (s.contains(i->frame)) { | |
847 NoteModel::Point newPoint(*i); | |
848 newPoint.frame = i->frame + newStartFrame - s.getStartFrame(); | |
849 command->deletePoint(*i); | |
850 command->addPoint(newPoint); | |
851 } | |
852 } | |
853 | |
854 command->finish(); | |
855 } | |
856 | |
857 void | |
858 NoteLayer::resizeSelection(Selection s, Selection newSize) | |
859 { | |
860 if (!m_model) return; | |
861 | |
862 NoteModel::EditCommand *command = | |
863 new NoteModel::EditCommand(m_model, tr("Resize Selection")); | |
864 | |
865 NoteModel::PointList points = | |
866 m_model->getPoints(s.getStartFrame(), s.getEndFrame()); | |
867 | |
868 double ratio = | |
869 double(newSize.getEndFrame() - newSize.getStartFrame()) / | |
870 double(s.getEndFrame() - s.getStartFrame()); | |
871 | |
872 for (NoteModel::PointList::iterator i = points.begin(); | |
873 i != points.end(); ++i) { | |
874 | |
875 if (s.contains(i->frame)) { | |
876 | |
877 double targetStart = i->frame; | |
878 targetStart = newSize.getStartFrame() + | |
879 double(targetStart - s.getStartFrame()) * ratio; | |
880 | |
881 double targetEnd = i->frame + i->duration; | |
882 targetEnd = newSize.getStartFrame() + | |
883 double(targetEnd - s.getStartFrame()) * ratio; | |
884 | |
885 NoteModel::Point newPoint(*i); | |
886 newPoint.frame = lrint(targetStart); | |
887 newPoint.duration = lrint(targetEnd - targetStart); | |
888 command->deletePoint(*i); | |
889 command->addPoint(newPoint); | |
890 } | |
891 } | |
892 | |
893 command->finish(); | |
894 } | |
895 | |
896 void | |
897 NoteLayer::deleteSelection(Selection s) | |
898 { | |
899 if (!m_model) return; | |
900 | |
901 NoteModel::EditCommand *command = | |
902 new NoteModel::EditCommand(m_model, tr("Delete Selected Points")); | |
903 | |
904 NoteModel::PointList points = | |
905 m_model->getPoints(s.getStartFrame(), s.getEndFrame()); | |
906 | |
907 for (NoteModel::PointList::iterator i = points.begin(); | |
908 i != points.end(); ++i) { | |
909 | |
910 if (s.contains(i->frame)) { | |
911 command->deletePoint(*i); | |
912 } | |
913 } | |
914 | |
915 command->finish(); | |
916 } | |
917 | |
918 void | |
919 NoteLayer::copy(Selection s, Clipboard &to) | |
920 { | |
921 if (!m_model) return; | |
922 | |
923 NoteModel::PointList points = | |
924 m_model->getPoints(s.getStartFrame(), s.getEndFrame()); | |
925 | |
926 for (NoteModel::PointList::iterator i = points.begin(); | |
927 i != points.end(); ++i) { | |
928 if (s.contains(i->frame)) { | |
929 Clipboard::Point point(i->frame, i->value, i->duration, i->label); | |
930 to.addPoint(point); | |
931 } | |
932 } | |
933 } | |
934 | |
935 bool | |
936 NoteLayer::paste(const Clipboard &from, int frameOffset, bool /* interactive */) | |
937 { | |
938 if (!m_model) return false; | |
939 | |
940 const Clipboard::PointList &points = from.getPoints(); | |
941 | |
942 NoteModel::EditCommand *command = | |
943 new NoteModel::EditCommand(m_model, tr("Paste")); | |
944 | |
945 for (Clipboard::PointList::const_iterator i = points.begin(); | |
946 i != points.end(); ++i) { | |
947 | |
948 if (!i->haveFrame()) continue; | |
949 size_t frame = 0; | |
950 if (frameOffset > 0 || -frameOffset < i->getFrame()) { | |
951 frame = i->getFrame() + frameOffset; | |
952 } | |
953 NoteModel::Point newPoint(frame); | |
954 | |
955 if (i->haveLabel()) newPoint.label = i->getLabel(); | |
956 if (i->haveValue()) newPoint.value = i->getValue(); | |
957 else newPoint.value = (m_model->getValueMinimum() + | |
958 m_model->getValueMaximum()) / 2; | |
959 if (i->haveDuration()) newPoint.duration = i->getDuration(); | |
960 else { | |
961 size_t nextFrame = frame; | |
962 Clipboard::PointList::const_iterator j = i; | |
963 for (; j != points.end(); ++j) { | |
964 if (!j->haveFrame()) continue; | |
965 if (j != i) break; | |
966 } | |
967 if (j != points.end()) { | |
968 nextFrame = j->getFrame(); | |
969 } | |
970 if (nextFrame == frame) { | |
971 newPoint.duration = m_model->getResolution(); | |
972 } else { | |
973 newPoint.duration = nextFrame - frame; | |
974 } | |
975 } | |
976 | |
977 command->addPoint(newPoint); | |
978 } | |
979 | |
980 command->finish(); | |
981 return true; | |
982 } | |
983 | |
984 QString | |
985 NoteLayer::toXmlString(QString indent, QString extraAttributes) const | |
986 { | |
987 return Layer::toXmlString(indent, extraAttributes + | |
988 QString(" colour=\"%1\" verticalScale=\"%2\"") | |
989 .arg(encodeColour(m_colour)).arg(m_verticalScale)); | |
990 } | |
991 | |
992 void | |
993 NoteLayer::setProperties(const QXmlAttributes &attributes) | |
994 { | |
995 QString colourSpec = attributes.value("colour"); | |
996 if (colourSpec != "") { | |
997 QColor colour(colourSpec); | |
998 if (colour.isValid()) { | |
999 setBaseColour(QColor(colourSpec)); | |
1000 } | |
1001 } | |
1002 | |
1003 bool ok; | |
1004 VerticalScale scale = (VerticalScale) | |
1005 attributes.value("verticalScale").toInt(&ok); | |
1006 if (ok) setVerticalScale(scale); | |
1007 } | |
1008 | |
1009 |