1 //=========================================================
2 // MusE
3 // Linux Music Editor
4 // wavecanvas.cpp
5 // (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net)
6 //
7 // Based on WaveView.cpp and PianoCanvas.cpp
8 // (C) Copyright 2000 Werner Schweer (ws@seh.de)
9 // and others.
10 //
11 // This program is free software; you can redistribute it and/or
12 // modify it under the terms of the GNU General Public License
13 // as published by the Free Software Foundation; version 2 of
14 // the License, or (at your option) any later version.
15 //
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with this program; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24 //
25 //=========================================================
26
27
28 #include <QApplication>
29 #include <QClipboard>
30 #include <QDrag>
31 #include <QDragLeaveEvent>
32 #include <QDragEnterEvent>
33 #include <QDragMoveEvent>
34 #include <QDropEvent>
35 #include <QFile>
36 #include <QInputDialog>
37 #include <QMouseEvent>
38 #include <QPair>
39 #include <QMessageBox>
40 #include <QDir>
41 #include <QLine>
42 #include <QVector>
43 #include <QProcess>
44 #include <QColor>
45 #include <QPen>
46
47 #include <set>
48
49 #include <limits.h>
50 #include <stdio.h>
51 #include "muse_math.h"
52 #include <set>
53
54 #include "app.h"
55 #include "icons.h"
56 #include "xml.h"
57 #include "wavecanvas.h"
58 #include "event.h"
59 #include "globals.h"
60 #include "cmd.h"
61 #include "song.h"
62 #include "audio.h"
63 #include "functions.h"
64 #include "gconfig.h"
65 #include "shortcuts.h"
66 #include "editgain.h"
67 #include "wave.h"
68 #include "waveedit.h"
69 #include "fastlog.h"
70 #include "utils.h"
71 #include "tools.h"
72 #include "copy_on_write.h"
73 #include "helper.h"
74 #include "sig.h"
75 #include "wave_helper.h"
76 #include "config.h"
77
78 #include "menutitleitem.h"
79 #include "audio_converter_settings.h"
80 #include "audio_convert/audio_converter_plugin.h"
81 #include "audio_convert/audio_converter_settings_group.h"
82 #include "sndfile.h"
83 #include "operations.h"
84
85 // Forwards from header:
86 #include <QDragEnterEvent>
87 #include <QDropEvent>
88 #include <QMouseEvent>
89 #include <QDragMoveEvent>
90 #include <QDragLeaveEvent>
91 #include <QWheelEvent>
92 #include <QResizeEvent>
93 #include <QPainter>
94 #include "part.h"
95 #include "track.h"
96
97 #define ABS(x) (abs(x))
98 #define FABS(x) (fabs(x))
99
100 // For debugging output: Uncomment the fprintf section.
101 #define ERROR_WAVECANVAS(dev, format, args...) fprintf(dev, format, ##args)
102 #define INFO_WAVECANVAS(dev, format, args...) // fprintf(dev, format, ##args)
103 #define DEBUG_WAVECANVAS(dev, format, args...) // fprintf(dev, format, ##args)
104
105
106 namespace MusEGui {
107
108 const int WaveCanvas::_stretchAutomationPointDetectDist = 4;
109 const int WaveCanvas::_stretchAutomationPointWidthUnsel = 2;
110 const int WaveCanvas::_stretchAutomationPointWidthSel = 3;
111
112 //---------------------------------------------------------
113 // WEvent
114 //---------------------------------------------------------
115
WEvent(const MusECore::Event & e,MusECore::Part * p,int height)116 WEvent::WEvent(const MusECore::Event& e, MusECore::Part* p, int height) : EItem(e, p)
117 {
118 unsigned frame = e.frame() + p->frame();
119 setPos(QPoint(frame, 0));
120 unsigned len = e.lenFrame();
121 if(e.frame() + e.lenFrame() >= p->lenFrame())
122 len = p->lenFrame() - e.frame();
123 setBBox(QRect(frame, 0, len, height));
124 // Give the moving point an initial value.
125 setMp(pos());
126 }
127
128 //---------------------------------------------------------
129 // addItem
130 //---------------------------------------------------------
131
addItem(MusECore::Part * part,const MusECore::Event & event)132 CItem* WaveCanvas::addItem(MusECore::Part* part, const MusECore::Event& event)
133 {
134 WEvent* ev = new WEvent(event, part, height());
135 items.add(ev);
136 return ev;
137 }
138
139 //---------------------------------------------------------
140 // WaveCanvas
141 //---------------------------------------------------------
142
WaveCanvas(MidiEditor * pr,QWidget * parent,int sx,int sy)143 WaveCanvas::WaveCanvas(MidiEditor* pr, QWidget* parent, int sx, int sy)
144 : EventCanvas(pr, parent, sx, 1)
145 {
146 setObjectName("WaveCanvas");
147
148 setStatusTip(tr("Wave canvas: Use Pencil tool to edit wave events, Pointer tool to select and edit. Press F1 for help."));
149
150 colorMode = 0;
151 button = 0;
152 supportsResizeToTheLeft = true;
153
154 editor = pr;
155 setVirt(true);
156
157 setBg(QColor());
158
159 pos[0] = MusEGlobal::tempomap.tick2frame(MusEGlobal::song->cpos());
160 pos[1] = MusEGlobal::tempomap.tick2frame(MusEGlobal::song->lpos());
161 pos[2] = MusEGlobal::tempomap.tick2frame(MusEGlobal::song->rpos());
162 yScale = sy;
163 mode = NORMAL;
164 selectionStart = 0;
165 selectionStop = 0;
166 lastGainvalue = 100;
167
168 songChanged(SC_TRACK_INSERTED);
169 }
170
~WaveCanvas()171 WaveCanvas::~WaveCanvas()
172 {
173 //delete steprec;
174 }
175
176 //---------------------------------------------------------
177 // updateItems
178 //---------------------------------------------------------
179
updateItems()180 void WaveCanvas::updateItems()
181 {
182 bool curItemNeedsRestore=false;
183 MusECore::Event storedEvent;
184 int partSn = 0;
185 if (curItem)
186 {
187 curItemNeedsRestore=true;
188 storedEvent=curItem->event();
189 partSn=curItem->part()->sn();
190 }
191 curItem=NULL;
192
193 items.clearDelete();
194 startSample = INT_MAX;
195 endSample = 0;
196 curPart = 0;
197 for (MusECore::iPart p = editor->parts()->begin(); p != editor->parts()->end(); ++p) {
198 MusECore::WavePart* part = (MusECore::WavePart*)(p->second);
199 if (part->sn() == curPartId)
200 curPart = part;
201 unsigned ssample = part->frame();
202 unsigned len = part->lenFrame();
203 unsigned esample = ssample + len;
204 if (ssample < startSample)
205 startSample = ssample;
206 if (esample > endSample)
207 endSample = esample;
208
209 for (MusECore::ciEvent i = part->events().begin(); i != part->events().end(); ++i) {
210 const MusECore::Event& e = i->second;
211 // Do not add events which are past the end of the part.
212 #ifdef ALLOW_LEFT_HIDDEN_EVENTS
213 if((int)e.frame() + (int)e.lenFrame() < 0)
214 continue;
215 if((int)e.frame() >= (int)len)
216 break;
217 #else
218 if(e.frame() > len)
219 break;
220 #endif
221
222 if (e.type() == MusECore::Wave) {
223 CItem* temp = addItem(part, e);
224
225 if (temp && curItemNeedsRestore && e==storedEvent && part->sn()==partSn)
226 {
227 if (curItem!=NULL)
228 printf("THIS SHOULD NEVER HAPPEN: curItemNeedsRestore=true, event fits, but there was already a fitting event!?\n");
229
230 curItem=temp;
231 }
232 }
233 }
234 }
235 }
236
237 //---------------------------------------------------------
238 // songChanged(type)
239 //---------------------------------------------------------
240
songChanged(MusECore::SongChangedStruct_t flags)241 void WaveCanvas::songChanged(MusECore::SongChangedStruct_t flags)
242 {
243 if (flags & ~(SC_SELECTION | SC_PART_SELECTION | SC_TRACK_SELECTION)) {
244 // TODO FIXME: don't we actually only want SC_PART_*, and maybe SC_TRACK_DELETED?
245 // (same in waveview.cpp)
246 updateItems();
247 }
248
249 MusECore::Event event;
250 MusECore::WavePart* part = 0;
251 int x = 0;
252 CItem* nevent = 0;
253
254 int n = 0; // count selections
255 for (iCItem k = items.begin(); k != items.end(); ++k) {
256 if (k->second->event().selected()) {
257 ++n;
258 if (!nevent) {
259 nevent = k->second;
260 }
261 }
262 }
263
264 if (flags & SC_AUDIO_STRETCH) {
265 for(iStretchSelectedItem is = _stretchAutomation._stretchSelectedList.begin();
266 is != _stretchAutomation._stretchSelectedList.end(); )
267 {
268 MusECore::MuseFrame_t frame = is->first;
269 StretchSelectedItem& ssi = is->second;
270 MusECore::StretchList* sl = ssi._sndFile.stretchList();
271 if(!sl)
272 continue;
273
274 MusEGui::ciCItem i;
275 for(i = items.begin(); i != items.end(); ++i)
276 {
277 WEvent* we = static_cast<WEvent*>(i->second);
278 MusECore::Event e = we->event();
279 if(MusECore::StretchList* e_sl = e.sndFile().stretchList())
280 {
281 if(e_sl == sl && e_sl->find(frame) != e_sl->end())
282 break;
283 }
284 }
285
286 if(i == items.end())
287 {
288 iStretchSelectedItem is_save = is;
289 _stretchAutomation._stretchSelectedList.erase(is);
290 is = is_save;
291 }
292 else
293 ++is;
294 }
295 }
296
297
298 if (flags & SC_CLIP_MODIFIED) {
299 redraw(); // Boring, but the only thing possible to do
300 }
301 if (flags & SC_TEMPO) {
302 setPos(0, MusEGlobal::song->cpos(), false);
303 setPos(1, MusEGlobal::song->lpos(), false);
304 setPos(2, MusEGlobal::song->rpos(), false);
305 }
306
307 if (n >= 1)
308 {
309 x = nevent->x();
310 event = nevent->event();
311 part = (MusECore::WavePart*)nevent->part();
312 if (_setCurPartIfOnlyOneEventIsSelected && n == 1 && curPart != part) {
313 curPart = part;
314 curPartId = curPart->sn();
315 curPartChanged();
316 }
317 }
318
319 if(flags & (SC_SELECTION))
320 {
321 // Prevent race condition: Ignore if the change was ultimately sent by the canvas itself.
322 if(flags._sender != this)
323 updateItemSelections();
324 }
325
326 bool f1 = static_cast<bool>(flags & (SC_EVENT_INSERTED | SC_EVENT_MODIFIED | SC_EVENT_REMOVED |
327 SC_PART_INSERTED | SC_PART_MODIFIED | SC_PART_REMOVED |
328 SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED |
329 SC_SIG | SC_TEMPO | SC_KEY | SC_MASTER | SC_CONFIG | SC_DRUMMAP));
330 bool f2 = static_cast<bool>(flags & SC_SELECTION);
331 if(f1 || f2) // Try to avoid all unnecessary emissions.
332 emit selectionChanged(x, event, part, !f1);
333
334 if (curPart == 0)
335 curPart = (MusECore::WavePart*)(editor->parts()->begin()->second);
336 redraw();
337 }
338
339 //---------------------------------------------------------
340 // selectAtTick
341 //---------------------------------------------------------
342
selectAtTick(unsigned int tick)343 void WaveCanvas::selectAtTick(unsigned int tick)
344 {
345 selectAtFrame(MusEGlobal::tempomap.tick2frame(tick));
346 }
347
348 //---------------------------------------------------------
349 // selectAtFrame
350 //---------------------------------------------------------
351
selectAtFrame(unsigned int frame)352 void WaveCanvas::selectAtFrame(unsigned int frame)
353 {
354 //Select event nearest frame, if none selected and there are any
355 if (!items.empty() && selectionSize() == 0) {
356 iCItem i = items.begin();
357 CItem* nearest = i->second;
358
359 while (i != items.end()) {
360 CItem* cur=i->second;
361 unsigned int curf=abs(cur->x() + (int)cur->part()->frame() - (int)frame);
362 unsigned int nearf=abs(nearest->x() + (int)nearest->part()->frame() - (int)frame);
363
364 if (curf < nearf) {
365 nearest=cur;
366 }
367
368 i++;
369 }
370
371 if (!nearest->isSelected()) {
372 selectItem(nearest, true);
373 songChanged(SC_SELECTION);
374 }
375 }
376 }
377
378 ////---------------------------------------------------------
379 //// getCaption
380 ////---------------------------------------------------------
381
382 //QString WaveCanvas::getCaption() const
383 // {
384 // int bar1, bar2, xx;
385 // unsigned x;
386 // MusEGlobal::sigmap.tickValues(curPart->tick(), &bar1, &xx, &x);
387 // MusEGlobal::sigmap.tickValues(curPart->tick() + curPart->lenTick(), &bar2, &xx, &x);
388
389 // return QString("Part <") + curPart->name()
390 // + QString("> %1-%2").arg(bar1+1).arg(bar2+1);
391 // }
392
393 //---------------------------------------------------------
394 // track
395 //---------------------------------------------------------
396
track() const397 MusECore::WaveTrack* WaveCanvas::track() const
398 {
399 return ((MusECore::WavePart*)curPart)->track();
400 }
401
402
403 //---------------------------------------------------------
404 // keyPress
405 //---------------------------------------------------------
406
keyPress(QKeyEvent * event)407 void WaveCanvas::keyPress(QKeyEvent* event)
408 {
409 int key = event->key();
410 if (((QInputEvent*)event)->modifiers() & Qt::ShiftModifier)
411 key += Qt::SHIFT;
412 if (((QInputEvent*)event)->modifiers() & Qt::AltModifier)
413 key += Qt::ALT;
414 if (((QInputEvent*)event)->modifiers() & Qt::ControlModifier)
415 key+= Qt::CTRL;
416
417 if (key == shortcuts[SHRT_DELETE].key)
418 {
419 switch (_tool)
420 {
421 case StretchTool:
422 case SamplerateTool:
423 {
424 MusECore::PendingOperationList operations;
425 StretchSelectedList_t& ssl = _stretchAutomation._stretchSelectedList;
426 for(iStretchSelectedItem isi = ssl.begin(); isi != ssl.end(); ++isi)
427 {
428 StretchSelectedItem& ssi = isi->second;
429 // ssi._sndFile.delAtStretchListOperation(ssi._type, isi->first, operations);
430 MusEGlobal::song->delAtStretchListOperation(ssi._sndFile, ssi._type, isi->first, operations);
431 }
432 ssl.clear();
433 MusEGlobal::audio->msgExecutePendingOperations(operations, true);
434 }
435 break;
436
437 default:
438 break;
439 }
440
441 return;
442 }
443
444 // TODO: New WaveCanvas: Convert these to frames, and remove unneeded functions.
445
446 // Select items by key (PianoRoll & DrumEditor)
447 else if (key == shortcuts[SHRT_SEL_RIGHT].key || key == shortcuts[SHRT_SEL_RIGHT_ADD].key) {
448 rciCItem i;
449
450 if (items.empty())
451 return;
452 for (i = items.rbegin(); i != items.rend(); ++i)
453 if (i->second->isSelected())
454 break;
455
456 if(i == items.rend())
457 i = items.rbegin();
458
459 if(i != items.rbegin())
460 --i;
461 if(i->second)
462 {
463 if (key != shortcuts[SHRT_SEL_RIGHT_ADD].key)
464 deselectAll();
465 CItem* sel = i->second;
466 sel->setSelected(true);
467 redraw();
468 if (sel->x() + sel->width() > mapxDev(width()))
469 {
470 int mx = rmapx(sel->x());
471 int newx = mx + rmapx(sel->width()) - width();
472 // Leave a bit of room for the specially-drawn drum notes. But good for piano too.
473 emit horizontalScroll( (newx > mx ? mx - 10: newx + 10) - rmapx(xorg) );
474 }
475 }
476 }
477 //Select items by key: (PianoRoll & DrumEditor)
478 else if (key == shortcuts[SHRT_SEL_LEFT].key || key == shortcuts[SHRT_SEL_LEFT_ADD].key) {
479 ciCItem i;
480 if (items.empty())
481 return;
482 for (i = items.begin(); i != items.end(); ++i)
483 if (i->second->isSelected())
484 break;
485
486 if(i == items.end())
487 i = items.begin();
488
489 if(i != items.begin())
490 --i;
491 if(i->second)
492 {
493 if (key != shortcuts[SHRT_SEL_LEFT_ADD].key)
494 deselectAll();
495 CItem* sel = i->second;
496 sel->setSelected(true);
497 redraw();
498 if (sel->x() <= mapxDev(0))
499 emit horizontalScroll(rmapx(sel->x() - xorg) - 10); // Leave a bit of room.
500 }
501 }
502 //else if (key == shortcuts[SHRT_INC_PITCH].key) {
503 // modifySelected(NoteInfo::VAL_PITCH, 1);
504 // }
505 //else if (key == shortcuts[SHRT_DEC_PITCH].key) {
506 // modifySelected(NoteInfo::VAL_PITCH, -1);
507 // }
508 else if (key == shortcuts[SHRT_INC_POS].key) {
509 // TODO: Check boundaries
510 modifySelected(NoteInfo::VAL_TIME, editor->raster());
511 }
512 else if (key == shortcuts[SHRT_DEC_POS].key) {
513 // TODO: Check boundaries
514 modifySelected(NoteInfo::VAL_TIME, 0 - editor->raster());
515 }
516
517 else if (key == shortcuts[SHRT_INCREASE_LEN].key) {
518 // TODO: Check boundaries
519 modifySelected(NoteInfo::VAL_LEN, editor->raster());
520 }
521 else if (key == shortcuts[SHRT_DECREASE_LEN].key) {
522 // TODO: Check boundaries
523 modifySelected(NoteInfo::VAL_LEN, 0 - editor->raster());
524 }
525
526 else
527 event->ignore();
528 }
529
530 //---------------------------------------------------------
531 // keyRelease
532 //---------------------------------------------------------
533
keyRelease(QKeyEvent * event)534 void WaveCanvas::keyRelease(QKeyEvent* event)
535 {
536 const int key = event->key();
537
538 // We do not want auto-repeat events.
539 // It does press and release repeatedly. Wait till the last release comes.
540 if(!event->isAutoRepeat())
541 {
542 // Select part to the right
543 if(key == shortcuts[SHRT_SEL_RIGHT].key || key == shortcuts[SHRT_SEL_RIGHT_ADD].key ||
544 // Select part to the left
545 key == shortcuts[SHRT_SEL_LEFT].key || key == shortcuts[SHRT_SEL_LEFT_ADD].key)
546 {
547 itemSelectionsChanged();
548 }
549 return;
550 }
551
552 EventCanvas::keyRelease(event);
553 }
554
555
556 //---------------------------------------------------------
557 // setPos
558 // set one of three markers
559 // idx - 0-cpos 1-lpos 2-rpos
560 // flag - emit followEvent()
561 //---------------------------------------------------------
562
setPos(int idx,unsigned val,bool adjustScrollbar)563 void WaveCanvas::setPos(int idx, unsigned val, bool adjustScrollbar)
564 {
565 val = MusEGlobal::tempomap.tick2frame(val);
566 if (pos[idx] == val)
567 return;
568 int opos = mapx(pos[idx]);
569 int npos = mapx(val);
570
571 if (adjustScrollbar && idx == 0) {
572 switch (MusEGlobal::song->follow()) {
573 case MusECore::Song::NO:
574 break;
575 case MusECore::Song::JUMP:
576 if (npos >= width()) {
577 int ppos = val - xorg - rmapxDev(width()/4);
578 if (ppos < 0)
579 ppos = 0;
580 emit followEvent(ppos);
581 opos = mapx(pos[idx]);
582 npos = mapx(val);
583 }
584 else if (npos < 0) {
585 int ppos = val - xorg - rmapxDev(width()*3/4);
586 if (ppos < 0)
587 ppos = 0;
588 emit followEvent(ppos);
589 opos = mapx(pos[idx]);
590 npos = mapx(val);
591 }
592 break;
593 case MusECore::Song::CONTINUOUS:
594 if (npos > (width()*5)/8) {
595 int ppos = pos[idx] - xorg - rmapxDev(width()*5/8);
596 if (ppos < 0)
597 ppos = 0;
598 emit followEvent(ppos);
599 opos = mapx(pos[idx]);
600 npos = mapx(val);
601 }
602 else if (npos < (width()*3)/8) {
603 int ppos = pos[idx] - xorg - rmapxDev(width()*3/8);
604 if (ppos < 0)
605 ppos = 0;
606 emit followEvent(ppos);
607 opos = mapx(pos[idx]);
608 npos = mapx(val);
609 }
610 break;
611 }
612 }
613
614 int x;
615 int w = 1;
616 if (opos > npos) {
617 w += opos - npos;
618 x = npos;
619 }
620 else {
621 w += npos - opos;
622 x = opos;
623 }
624 pos[idx] = val;
625 redraw(QRect(x-1, 0, w+2, height())); // From Canvas::draw (is otherwise identical). Fix for corruption. (TEST: New WaveCanvas: Still true?)
626 }
627
628 //---------------------------------------------------------
629 // setYScale
630 //---------------------------------------------------------
631
setYScale(int val)632 void WaveCanvas::setYScale(int val)
633 {
634 yScale = val;
635 redraw();
636 }
637
638 //---------------------------------------------------------
639 // drawMarkers
640 //---------------------------------------------------------
641
drawMarkers(QPainter & p,const QRect & mr,const QRegion &)642 void WaveCanvas::drawMarkers(QPainter& p, const QRect& mr, const QRegion&)
643 {
644 const int mx = mr.x();
645 const int my = mr.y();
646 const int mw = mr.width();
647 const int mh = mr.height();
648 const int my_2 = my + mh;
649
650 const ViewXCoordinate vx(mx, true);
651 const ViewWCoordinate vw(mw, true);
652 const ViewXCoordinate vx_2(mx + mw, true);
653
654 QPen pen;
655 pen.setCosmetic(true);
656
657 MusECore::MarkerList* marker = MusEGlobal::song->marker();
658 pen.setColor(MusEGlobal::config.markerColor);
659 p.setPen(pen);
660 for (MusECore::iMarker m = marker->begin(); m != marker->end(); ++m) {
661 const ViewXCoordinate xp(MusEGlobal::tempomap.tick2frame(m->second.tick()), false);
662 if (isXInRange(xp, vx, vx_2)) {
663 const int mxp = asMapped(xp)._value;
664 p.drawLine(mxp, my, mxp, my_2);
665 }
666 }
667 }
668
669 //---------------------------------------------------------
670 // drawWaveParts
671 //---------------------------------------------------------
672
drawParts(QPainter & p,bool do_cur_part,const QRect & mr,const QRegion &)673 void WaveCanvas::drawParts(QPainter& p, bool do_cur_part, const QRect& mr, const QRegion&)
674 {
675 bool wmtxen = p.worldMatrixEnabled();
676 p.setWorldMatrixEnabled(false);
677
678 if(do_cur_part)
679 {
680 // Draw current part:
681 if(curPart)
682 {
683 QRect mwpr = map(QRect(curPart->frame(), 0, curPart->lenFrame(), height()));
684 QRect mpbgr = mr & mwpr;
685 if(!mpbgr.isNull())
686 {
687 QColor c;
688 switch(colorMode)
689 {
690 default:
691 case 0:
692 if (curPart->colorIndex() == 0 && MusEGlobal::config.useTrackColorForParts)
693 c = curPart->track()->color();
694 else
695 c = MusEGlobal::config.partColors[curPart->colorIndex()];
696 break;
697 case 1:
698 c = Qt::lightGray;
699 break;
700 }
701 c.setAlpha(MusEGlobal::config.globalAlphaBlend);
702 QBrush part_bg_brush(MusECore::gGradientFromQColor(c, mwpr.topLeft(), mwpr.bottomLeft()));
703 p.fillRect(mpbgr, part_bg_brush);
704 }
705 }
706 }
707 else
708 {
709 // Draw non-current parts:
710 for (MusECore::iPart ip = editor->parts()->begin(); ip != editor->parts()->end(); ++ip)
711 {
712 MusECore::WavePart* wp = (MusECore::WavePart*)(ip->second);
713 if(wp == curPart)
714 continue;
715
716 QRect mwpr = map(QRect(wp->frame(), 0, wp->lenFrame(), height()));
717 QRect mpbgr = mr & mwpr;
718 if(!mpbgr.isNull())
719 {
720 QColor c(MusEGlobal::config.waveNonselectedPart);
721 c.setAlpha(MusEGlobal::config.globalAlphaBlend);
722 QBrush part_bg_brush(MusECore::gGradientFromQColor(c, mwpr.topLeft(), mwpr.bottomLeft()));
723 p.fillRect(mpbgr, part_bg_brush);
724 }
725 }
726 }
727
728 p.setWorldMatrixEnabled(wmtxen);
729 }
730
731 // TODO: Overridden because we're in units of frames.
732 // After BBT/frame mode is added to Canvas, remove this override and let Canvas do it.
733 //---------------------------------------------------------
734 // raster
735 //---------------------------------------------------------
736
raster(const QPoint & p) const737 QPoint WaveCanvas::raster(const QPoint& p) const
738 {
739 int x = p.x();
740 if (x < 0)
741 x = 0;
742 // Normally frame to tick methods round down. But here we need it to 'snap'
743 // the frame from either side of a tick to the tick. So round to nearest.
744 x = MusEGlobal::tempomap.tick2frame(editor->rasterVal(MusEGlobal::tempomap.frame2tick(x, 0, MusECore::LargeIntRoundNearest)));
745 int pitch = y2pitch(p.y());
746 int y = pitch2y(pitch);
747 return QPoint(x, y);
748 }
749
750 #define WHEEL_STEPSIZE 50
751 #define WHEEL_DELTA 120
752 //---------------------------------------------------------
753 // wheelEvent
754 //---------------------------------------------------------
wheelEvent(QWheelEvent * ev)755 void WaveCanvas::wheelEvent(QWheelEvent* ev)
756 {
757 int keyState = ev->modifiers();
758
759 bool shift = keyState & Qt::ShiftModifier;
760 bool ctrl = keyState & Qt::ControlModifier;
761
762 const QPoint pixelDelta = ev->pixelDelta();
763 const QPoint angleDegrees = ev->angleDelta() / 8;
764 int delta = 0;
765 if(!pixelDelta.isNull())
766 delta = pixelDelta.y();
767 else if(!angleDegrees.isNull())
768 delta = angleDegrees.y() / 15;
769 else
770 return;
771
772 if (shift) { // scroll horizontally
773 int d = -delta / WHEEL_DELTA;
774 int xpixelscale = 5*MusECore::fast_log10(rmapxDev(1));
775 if (xpixelscale <= 0)
776 xpixelscale = 1;
777 int scrollstep = WHEEL_STEPSIZE * ( d );
778 scrollstep = scrollstep / 10;
779 int newXpos = xpos + xpixelscale * scrollstep;
780 if (newXpos < 0)
781 newXpos = 0;
782 emit horizontalScroll((unsigned)newXpos);
783 } else if (ctrl) { // zoom horizontally
784 #if QT_VERSION >= 0x050e00
785 emit horizontalZoom(delta>0, ev->globalPosition().toPoint());
786 #else
787 emit horizontalZoom(delta>0, ev->globalPos());
788 #endif
789 } else { // scroll horizontally
790 emit mouseWheelMoved(delta / 10);
791 }
792 }
793
794 //---------------------------------------------------------
795 // viewMousePressEvent
796 //---------------------------------------------------------
797
mousePress(QMouseEvent * event)798 bool WaveCanvas::mousePress(QMouseEvent* event)
799 {
800 // if (event->modifiers() & Qt::ControlModifier) {
801 // return true;
802 // }
803 const bool ctl = event->modifiers() & Qt::ControlModifier;
804 button = event->button();
805 QPoint pt = event->pos();
806 unsigned x = event->x();
807
808 switch (_tool)
809 {
810 default:
811 break;
812 case RangeTool:
813 if (ctl)
814 return true;
815 switch (button)
816 {
817 case Qt::LeftButton:
818 if (mode == NORMAL)
819 {
820 // redraw and reset:
821 if (selectionStart != selectionStop)
822 {
823 selectionStart = selectionStop = 0;
824 redraw();
825 }
826 mode = DRAG;
827 dragstartx = x;
828 selectionStart = selectionStop = x;
829 drag = DRAG_LASSO_START;
830 Canvas::start = pt;
831 return false;
832 }
833 break;
834
835 case Qt::MidButton:
836 case Qt::RightButton:
837 default:
838 break;
839 }
840
841 break;
842
843 case StretchTool:
844 case SamplerateTool:
845 {
846 if(button != Qt::LeftButton)
847 return true;
848
849 StretchSelectedList_t& ssl = _stretchAutomation._stretchSelectedList;
850 //if(!ctl)
851 //{
852 // ssl.clear();
853 // update();
854 //}
855
856 // TODO Look properly through the whole list instead of just current one.
857 //if(!curItem)
858 // break;
859 //WEvent* wevent = static_cast<WEvent*>(curItem);
860 CItem* item = items.find(pt);
861 if(!item)
862 break;
863 WEvent* wevent = static_cast<WEvent*>(item);
864
865 const MusECore::Event event = wevent->event();
866 if(event.type() != MusECore::Wave)
867 break;
868
869 MusECore::SndFileR sf = event.sndFile();
870 if(sf.isNull())
871 break;
872
873 MusECore::StretchList* sl = sf.stretchList();
874 if(!sl)
875 break;
876
877 const double sf_sr_ratio = sf->sampleRateRatio();
878
879 MusECore::StretchListItem::StretchEventType type;
880 if(_tool == StretchTool)
881 type = MusECore::StretchListItem::StretchEvent;
882 else //if(_tool == SamplerateTool)
883 type = MusECore::StretchListItem::SamplerateEvent;
884
885 MusECore::iStretchListItem isli_hit_test = stretchListHitTest(type, pt, wevent);
886 if(isli_hit_test == sl->end())
887 {
888 if(!ctl)
889 {
890 ssl.clear();
891 update();
892 }
893
894 double newframe = sl->unSquish(sf_sr_ratio * double(x - wevent->x()));
895
896 MusECore::PendingOperationList operations;
897 MusEGlobal::song->addAtStretchListOperation(sf, type, newframe, sl->ratioAt(type, newframe), operations);
898 MusEGlobal::audio->msgExecutePendingOperations(operations, true);
899 ssl.insert(StretchSelectedItemInsertPair_t(newframe, StretchSelectedItem(type, sf)));
900 _stretchAutomation._startMovePoint = pt;
901 _stretchAutomation._controllerState = stretchStartMove;
902 QWidget::setCursor(Qt::SizeHorCursor);
903 break;
904 }
905
906 iStretchSelectedItemPair res = ssl.equal_range(isli_hit_test->first);
907 iStretchSelectedItem isi;
908 for(isi = res.first; isi != res.second; ++isi)
909 if(isi->second._sndFile.stretchList() == sl && isi->second._type)
910 break;
911
912 if(isi != res.second)
913 {
914 if(ctl)
915 {
916 ssl.erase(isi);
917 //setCursor();
918 update();
919 }
920 else
921 {
922 _stretchAutomation._startMovePoint = pt;
923 _stretchAutomation._controllerState = stretchStartMove;
924 QWidget::setCursor(Qt::SizeHorCursor);
925 }
926 }
927 else
928 {
929 if(!ctl)
930 ssl.clear();
931 ssl.insert(std::pair<MusECore::MuseFrame_t, StretchSelectedItem>(isli_hit_test->first,
932 StretchSelectedItem(type, sf)));
933 _stretchAutomation._startMovePoint = pt;
934 _stretchAutomation._controllerState = stretchStartMove;
935 QWidget::setCursor(Qt::SizeHorCursor);
936 update();
937 }
938 }
939 break;
940
941 }
942
943 return true;
944 }
945
946 //---------------------------------------------------------
947 // viewMouseReleaseEvent
948 //---------------------------------------------------------
949
mouseRelease(QMouseEvent * ev)950 void WaveCanvas::mouseRelease(QMouseEvent* ev)
951 {
952 QPoint pt = ev->pos();
953 const bool ctl = ev->modifiers() & Qt::ControlModifier;
954
955 switch(_tool)
956 {
957 case StretchTool:
958 case SamplerateTool:
959 {
960 if(button != Qt::LeftButton)
961 {
962 _stretchAutomation._controllerState = stretchDoNothing;
963 setStretchAutomationCursor(pt);
964 return;
965 }
966
967 StretchSelectedList_t& ssl = _stretchAutomation._stretchSelectedList;
968 switch(_stretchAutomation._controllerState)
969 {
970 case stretchMovingController:
971 case stretchAddNewController:
972 //setCursor();
973 break;
974
975 case stretchDoNothing:
976 case stretchStartMove:
977 if(!ctl)
978 {
979 ssl.clear();
980 update();
981 }
982
983 // TODO Look properly through the whole list instead of just current one.
984 //if(!curItem)
985 // break;
986 //WEvent* wevent = static_cast<WEvent*>(curItem);
987 CItem* item = items.find(pt);
988 if(!item)
989 break;
990 WEvent* wevent = static_cast<WEvent*>(item);
991
992 const MusECore::Event event = wevent->event();
993 if(event.type() != MusECore::Wave)
994 break;
995
996 const MusECore::SndFileR sf = event.sndFile();
997 if(sf.isNull())
998 break;
999
1000 MusECore::StretchList* sl = sf.stretchList();
1001 if(!sl)
1002 break;
1003
1004 MusECore::StretchListItem::StretchEventType type;
1005 if(_tool == StretchTool)
1006 type = MusECore::StretchListItem::StretchEvent;
1007 else // if(_tool == SamplerateTool)
1008 type = MusECore::StretchListItem::SamplerateEvent;
1009
1010 MusECore::iStretchListItem isli_hit_test = stretchListHitTest(type, pt, wevent);
1011 if(isli_hit_test == sl->end())
1012 break;
1013
1014 iStretchSelectedItemPair res = ssl.equal_range(isli_hit_test->first);
1015 iStretchSelectedItem isi;
1016 for(isi = res.first; isi != res.second; ++isi)
1017 if(isi->second._sndFile.stretchList() == sl && isi->second._type)
1018 break;
1019
1020 if(isi == res.second)
1021 {
1022 ssl.insert(std::pair<MusECore::MuseFrame_t, StretchSelectedItem>(isli_hit_test->first,
1023 StretchSelectedItem(type, sf)));
1024 update();
1025 }
1026
1027 break;
1028 }
1029
1030
1031 }
1032 break;
1033
1034 default:
1035 break;
1036 }
1037
1038 _stretchAutomation._controllerState = stretchDoNothing;
1039
1040 button = Qt::NoButton;
1041 if(mode == DRAG)
1042 mode = NORMAL;
1043
1044 setStretchAutomationCursor(pt);
1045 }
1046
1047 //---------------------------------------------------------
1048 // viewMousevent
1049 //---------------------------------------------------------
1050
mouseMove(QMouseEvent * event)1051 void WaveCanvas::mouseMove(QMouseEvent* event)
1052 {
1053 QPoint pt = event->pos();
1054 int x = pt.x();
1055 if (x < 0)
1056 x = 0;
1057
1058 // Special for wave canvas: Normally in the base class EventCanvas we would rasterize the tick before emitting,
1059 // but since these units are in frames we handle displaying the position specially in the WaveEditor.
1060 // One reason is that we may need the precise frame, such as for the editing 'functions' and the stretching below.
1061 // Snapping those things, even to the minimum 1 tick ('off'), might be undesirable when we're editing at wave level.
1062 emit timeChanged(x);
1063
1064 switch(_tool)
1065 {
1066 case StretchTool:
1067 case SamplerateTool:
1068 {
1069 event->accept();
1070 //bool slowMotion = event->modifiers() & Qt::ShiftModifier;
1071 //processStretchAutomationMovements(event->pos(), slowMotion);
1072
1073 //if(button != Qt::LeftButton)
1074 //{
1075 // _stretchAutomation._controllerState = stretchDoNothing;
1076 // setCursor();
1077 // return;
1078 //}
1079
1080 switch(_stretchAutomation._controllerState)
1081 {
1082 case stretchDoNothing:
1083 case stretchAddNewController:
1084 setStretchAutomationCursor(pt);
1085 break;
1086
1087 case stretchStartMove:
1088 _stretchAutomation._controllerState = stretchMovingController;
1089 // NOTE: Error suppressor for new gcc 7 'fallthrough' level 3 and 4:
1090 // FALLTHROUGH
1091 case stretchMovingController:
1092 {
1093 if(button != Qt::LeftButton)
1094 {
1095 _stretchAutomation._controllerState = stretchDoNothing;
1096 //setCursor();
1097 break;;
1098 }
1099 QPoint delta_pt = QPoint(pt.x() - _stretchAutomation._startMovePoint.x(),
1100 pt.y() - _stretchAutomation._startMovePoint.y());
1101 if(delta_pt.x() == 0)
1102 break;
1103 double prevNewVal = 0, thisNewVal = 0;
1104 double prevFrame, thisFrame, nextFrame, delta_fr, next_delta_fr;
1105 MusECore::PendingOperationList operations;
1106 StretchSelectedList_t& ssl = _stretchAutomation._stretchSelectedList;
1107 for(ciStretchSelectedItem iss = ssl.begin(); iss != ssl.end(); ++iss)
1108 {
1109 const StretchSelectedItem& ssi = iss->second;
1110 MusECore::SndFileR sf = ssi._sndFile;
1111 if(sf.isNull())
1112 continue;
1113 MusECore::StretchList* sl = sf.stretchList();
1114 if(!sl)
1115 continue;
1116
1117 MusECore::iStretchListItem isli_typed = sl->findEvent(ssi._type, iss->first);
1118 if(isli_typed == sl->end())
1119 continue;
1120
1121 const double wave_sr_ratio = sf.sampleRateRatio();
1122
1123 thisFrame = double(isli_typed->first);
1124
1125 const MusECore::StretchListItem& sli_typed = isli_typed->second;
1126
1127 MusECore::iStretchListItem prev_isli_typed = sl->previousEvent(ssi._type, isli_typed);
1128 if(prev_isli_typed == sl->end())
1129 continue;
1130
1131 prevFrame = double(prev_isli_typed->first);
1132
1133 const MusECore::StretchListItem& prev_sli_typed = prev_isli_typed->second;
1134
1135 MusECore::iStretchListItem next_isli_typed = sl->nextEvent(ssi._type, isli_typed);
1136 if(next_isli_typed == sl->end())
1137 nextFrame = double(sf.samples());
1138 else
1139 nextFrame = double(next_isli_typed->first);
1140
1141 next_delta_fr = nextFrame - thisFrame;
1142 // FIXME: Comparing double with zero.
1143 if(next_delta_fr <= 0)
1144 continue;
1145
1146 delta_fr = thisFrame - prevFrame;
1147 // FIXME: Comparing double with zero.
1148 if(delta_fr <= 0)
1149 continue;
1150
1151 const double minStretchRatio = sf.minStretchRatio();
1152 const double maxStretchRatio = sf.maxStretchRatio();
1153 const double minSamplerateRatio = sf.minSamplerateRatio();
1154 const double maxSamplerateRatio = sf.maxSamplerateRatio();
1155
1156 switch(ssi._type)
1157 {
1158 case MusECore::StretchListItem::StretchEvent:
1159 {
1160 const double left_prev_smpx = prev_sli_typed._samplerateSquishedFrame;
1161 const double left_this_smpx = sl->squish(thisFrame, MusECore::StretchListItem::SamplerateEvent);
1162 const double left_dsmpx = left_this_smpx - left_prev_smpx;
1163 const double left_effective_sr = left_dsmpx / delta_fr;
1164
1165 const double right_this_smpx = sli_typed._samplerateSquishedFrame;
1166 const double right_next_smpx = sl->squish(nextFrame, MusECore::StretchListItem::SamplerateEvent);
1167 const double right_dsmpx = right_next_smpx - right_this_smpx;
1168 const double right_effective_sr = right_dsmpx / next_delta_fr;
1169
1170 INFO_WAVECANVAS(stderr, "WaveCanvas::mouseMove StretchEvent delta_pt.x:%d\n", delta_pt.x());
1171
1172 double left_min_stretch_delta_x =
1173 (minStretchRatio - prev_sli_typed._stretchRatio) * left_effective_sr * delta_fr;
1174 if(left_min_stretch_delta_x > 0.0)
1175 left_min_stretch_delta_x = 0.0;
1176 if((double)delta_pt.x() < left_min_stretch_delta_x)
1177 delta_pt.setX(left_min_stretch_delta_x);
1178
1179 double right_min_stretch_delta_x =
1180 (minStretchRatio - sli_typed._stretchRatio) * right_effective_sr * next_delta_fr;
1181 if(right_min_stretch_delta_x > 0.0)
1182 right_min_stretch_delta_x = 0.0;
1183 if(-(double)delta_pt.x() < right_min_stretch_delta_x)
1184 delta_pt.setX(right_min_stretch_delta_x);
1185
1186 INFO_WAVECANVAS(stderr, " left_min_stretch_delta_x:%f right_min_stretch_delta_x:%f\n",
1187 left_min_stretch_delta_x, right_min_stretch_delta_x);
1188
1189 if(maxStretchRatio > 0.0)
1190 {
1191 double left_max_stretch_delta_x =
1192 (maxStretchRatio - prev_sli_typed._stretchRatio) * left_effective_sr * delta_fr;
1193 if(left_max_stretch_delta_x < 0.0)
1194 left_max_stretch_delta_x = 0.0;
1195 if((double)delta_pt.x() > left_max_stretch_delta_x)
1196 delta_pt.setX(left_max_stretch_delta_x);
1197
1198 double right_max_stretch_delta_x =
1199 (maxStretchRatio - sli_typed._stretchRatio) * right_effective_sr * next_delta_fr;
1200 if(right_max_stretch_delta_x < 0.0)
1201 right_max_stretch_delta_x = 0.0;
1202 if((double)delta_pt.x() > right_max_stretch_delta_x)
1203 delta_pt.setX(right_max_stretch_delta_x);
1204 }
1205
1206 const double left_effective_dx = wave_sr_ratio * (double)delta_pt.x() / left_effective_sr;
1207 prevNewVal = prev_sli_typed._stretchRatio + (left_effective_dx / delta_fr);
1208
1209 const double right_effective_dx = wave_sr_ratio * (double)delta_pt.x() / right_effective_sr;
1210 thisNewVal = sli_typed._stretchRatio - (right_effective_dx / next_delta_fr);
1211 }
1212 break;
1213 case MusECore::StretchListItem::SamplerateEvent:
1214 {
1215 const double left_prev_strx = prev_sli_typed._stretchSquishedFrame;
1216 const double left_this_strx = sl->squish(thisFrame, MusECore::StretchListItem::StretchEvent);
1217 const double left_dstrx = left_this_strx - left_prev_strx;
1218 const double left_effective_str = left_dstrx / delta_fr;
1219
1220 const double right_this_strx = sli_typed._stretchSquishedFrame;
1221 const double right_next_strx = sl->squish(nextFrame, MusECore::StretchListItem::StretchEvent);
1222 const double right_dstrx = right_next_strx - right_this_strx;
1223 const double right_effective_str = right_dstrx / next_delta_fr;
1224
1225 INFO_WAVECANVAS(stderr, "WaveCanvas::mouseMove SamplerateEvent delta_pt.x:%d\n", delta_pt.x());
1226
1227 double left_min_samplerate_delta_x =
1228 (minSamplerateRatio - prev_sli_typed._samplerateRatio) * left_effective_str * delta_fr;
1229 if(left_min_samplerate_delta_x > 0.0)
1230 left_min_samplerate_delta_x = 0.0;
1231 if((double)delta_pt.x() < left_min_samplerate_delta_x)
1232 delta_pt.setX(left_min_samplerate_delta_x);
1233
1234 double right_min_samplerate_delta_x =
1235 (minSamplerateRatio - sli_typed._samplerateRatio) * right_effective_str * next_delta_fr;
1236 if(right_min_samplerate_delta_x > 0.0)
1237 right_min_samplerate_delta_x = 0.0;
1238 if(-(double)delta_pt.x() < right_min_samplerate_delta_x)
1239 delta_pt.setX(right_min_samplerate_delta_x);
1240
1241 INFO_WAVECANVAS(stderr, " left_min_samplerate_delta_x:%f right_min_samplerate_delta_x:%f\n",
1242 left_min_samplerate_delta_x, right_min_samplerate_delta_x);
1243
1244 if(maxSamplerateRatio > 0.0)
1245 {
1246 double left_max_samplerate_delta_x =
1247 (maxSamplerateRatio - prev_sli_typed._samplerateRatio) * left_effective_str * delta_fr;
1248 if(left_max_samplerate_delta_x < 0.0)
1249 left_max_samplerate_delta_x = 0.0;
1250 if((double)delta_pt.x() > left_max_samplerate_delta_x)
1251 delta_pt.setX(left_max_samplerate_delta_x);
1252
1253 double right_max_samplerate_delta_x =
1254 (maxSamplerateRatio - sli_typed._samplerateRatio) * right_effective_str * next_delta_fr;
1255 if(right_max_samplerate_delta_x < 0.0)
1256 right_max_samplerate_delta_x = 0.0;
1257 if((double)delta_pt.x() > right_max_samplerate_delta_x)
1258 delta_pt.setX(right_max_samplerate_delta_x);
1259 }
1260
1261 const double left_effective_dx = wave_sr_ratio * (double)delta_pt.x() / left_effective_str;
1262 prevNewVal =
1263 1.0 / ((1.0 / prev_sli_typed._samplerateRatio) + (left_effective_dx / delta_fr));
1264
1265 const double right_effective_dx = wave_sr_ratio * (double)delta_pt.x() / right_effective_str;
1266 thisNewVal =
1267 1.0 / ((1.0 / sli_typed._samplerateRatio) - (right_effective_dx / next_delta_fr));
1268 }
1269 break;
1270 case MusECore::StretchListItem::PitchEvent:
1271 prevNewVal = prev_sli_typed._pitchRatio; // TODO
1272 break;
1273 }
1274
1275 MusEGlobal::song->modifyAtStretchListOperation(sf, ssi._type, prevFrame, prevNewVal, operations);
1276 MusEGlobal::song->modifyAtStretchListOperation(sf, ssi._type, thisFrame, thisNewVal, operations);
1277 }
1278 MusEGlobal::audio->msgExecutePendingOperations(operations, true);
1279 _stretchAutomation._startMovePoint = pt;
1280 }
1281 break;
1282 }
1283 }
1284 break;
1285
1286
1287 default:
1288 {
1289 event->ignore();
1290
1291 switch (button)
1292 {
1293 case Qt::LeftButton:
1294 if (mode == DRAG)
1295 {
1296 int mx = mapx(x);
1297 int mstart = mapx(selectionStart);
1298 int mstop = mapx(selectionStop);
1299 //int mdstart = mapx(dragstartx);
1300 QRect r(0, 0, 0, height());
1301
1302 if (x < dragstartx) {
1303 if(x < selectionStart)
1304 {
1305 r.setLeft(mx);
1306 r.setWidth((selectionStop >= dragstartx ? mstop : mstart) - mx);
1307 }
1308 else
1309 {
1310 r.setLeft(mstart);
1311 r.setWidth(mx - mstart);
1312 }
1313 selectionStart = x;
1314 selectionStop = dragstartx;
1315 }
1316 else {
1317 if(x >= selectionStop)
1318 {
1319 r.setLeft(selectionStart < dragstartx ? mstart : mstop);
1320 r.setWidth(mx - (selectionStart < dragstartx ? mstart : mstop));
1321 }
1322 else
1323 {
1324 r.setLeft(mx);
1325 r.setWidth(mstop - mx);
1326 }
1327 selectionStart = dragstartx;
1328 selectionStop = x;
1329 }
1330 update(r);
1331 }
1332 break;
1333 case Qt::MidButton:
1334 break;
1335 case Qt::RightButton:
1336 break;
1337 default:
1338 return;
1339 }
1340
1341 }
1342 break;
1343 }
1344 }
1345
1346 //---------------------------------------------------------
1347 // pitch2y
1348 //---------------------------------------------------------
1349
pitch2y(int) const1350 int WaveCanvas::pitch2y(int) const
1351 {
1352 return 0;
1353 }
1354
1355 //---------------------------------------------------------
1356 // y2pitch
1357 //---------------------------------------------------------
1358
y2pitch(int) const1359 int WaveCanvas::y2pitch(int) const
1360 {
1361 return 0;
1362 }
1363
1364 //---------------------------------------------------------
1365 // drawItem
1366 // draws a wave
1367 //---------------------------------------------------------
1368
drawItem(QPainter & p,const CItem * item,const QRect & mr,const QRegion &)1369 void WaveCanvas::drawItem(QPainter& p, const CItem* item, const QRect& mr, const QRegion&)
1370 {
1371 MusECore::WavePart* wp = (MusECore::WavePart*)(item->part());
1372 if(!wp || !wp->track())
1373 return;
1374
1375 MusECore::Event event = item->event();
1376 if(event.empty())
1377 return;
1378
1379 //QRect rr = p.transform().mapRect(rect); // Gives inconsistent positions. Source shows wrong operation for our needs.
1380 const QRect ur = mapDev(mr); // Use our own map instead.
1381 const int ux = ur.x();
1382 const int uw = ur.width();
1383 const int ux_2 = ux + uw;
1384 QRect uwpr = QRect(wp->frame(), 0, wp->lenFrame(), height());
1385 const QRect ubbr = item->bbox();
1386 const QRect ubbr_exp = item->bbox().adjusted(0, 0, rmapxDev(1), 0);
1387 const QRect mbbr = map(ubbr);
1388 const int ubbx = ubbr.x();
1389 const int ubbx_2 = ubbr.x() + ubbr.width();
1390 const int mbbx = mbbr.x();
1391 const int mbbx_2 = mapx(ubbr.x() + ubbr.width());
1392 const QRect ubr = ur & ubbr;
1393 const QRect ubr_exp = ur & ubbr_exp;
1394 const int uby_exp = ubr_exp.y();
1395 const int uby_2exp = ubr_exp.y() + ubr_exp.height();
1396 const int mby_exp = mapy(uby_exp);
1397 const int mby_2exp = mapy(uby_2exp);
1398 const QRect ubrwp = ubr & uwpr;
1399 const QRect mbrwp = map(ubrwp);
1400
1401 QPen pen;
1402 pen.setCosmetic(true);
1403 const QColor left_ch_color(0, 170, 255);
1404 const QColor right_ch_color(Qt::red);
1405
1406 int x1 = mapx(ubrwp.x());
1407 int x2 = mapx(ubrwp.x() + ubrwp.width());
1408 if (x1 < 0)
1409 x1 = 0;
1410 if (x2 > width())
1411 x2 = width();
1412 int hh = height();
1413 int y1 = mapy(ubrwp.y());
1414 int y2 = mapy(ubrwp.y() + ubrwp.height());
1415
1416 int xScale = xmag;
1417 if (xScale < 0)
1418 xScale = -xScale;
1419
1420 int px = wp->frame();
1421
1422 bool wmtxen = p.worldMatrixEnabled();
1423 p.setWorldMatrixEnabled(false);
1424
1425 int sx, ex;
1426
1427 // Changed. Possible BUG ? Why the half nudge?
1428 // sx = event.frame() + px + xScale/2;
1429 sx = event.frame() + px;
1430 ex = sx + event.lenFrame();
1431 sx = sx / xScale - xpos - xorg;
1432 ex = ex / xScale - xpos - xorg;
1433
1434 if(sx >= x2 || ex < x1)
1435 return;
1436
1437 if (sx < x1)
1438 sx = x1;
1439 if (ex > x2)
1440 ex = x2;
1441
1442 const int ev_spos = event.spos();
1443
1444 int pos = (xpos + xorg + sx) * xScale - event.frame() - px;
1445
1446 // fprintf(stderr, "\nWaveCanvas::drawItem:\nmr:\nx:%8d\t\ty:%8d\t\tw:%8d\t\th:%8d\n\n",
1447 // mr.x(), mr.y(), mr.width(), mr.height());
1448 // fprintf(stderr, "\nur:\nx:%8d\t\ty:%8d\t\tw:%8d\t\th:%8d\n\n",
1449 // ur.x(), ur.y(), ur.width(), ur.height());
1450 // fprintf(stderr, "\nubbr:\nx:%8d\t\ty:%8d\t\tw:%8d\t\th:%8d\n\n",
1451 // ubbr.x(), ubbr.y(), ubbr.width(), ubbr.height());
1452 // fprintf(stderr, "\nmbbr:\nx:%8d\t\ty:%8d\t\tw:%8d\t\th:%8d\n\n",
1453 // mbbr.x(), mbbr.y(), mbbr.width(), mbbr.height());
1454
1455 QBrush brush;
1456 if (item->isMoving())
1457 {
1458 QColor c(Qt::gray);
1459 c.setAlpha(MusEGlobal::config.globalAlphaBlend);
1460 QLinearGradient gradient(ubbr.topLeft(), ubbr.bottomLeft());
1461 gradient.setColorAt(0, c);
1462 gradient.setColorAt(1, c.darker());
1463 brush = QBrush(gradient);
1464 p.fillRect(sx, y1, ex - sx + 1, y2, brush);
1465 }
1466 else
1467 if (item->isSelected())
1468 {
1469 QColor c(Qt::black);
1470 c.setAlpha(MusEGlobal::config.globalAlphaBlend);
1471 QLinearGradient gradient(ubbr.topLeft(), ubbr.bottomLeft());
1472 // Use a colour only about 20% lighter than black, rather than the 50% we use in MusECore::gGradientFromQColor
1473 // and is used in darker()/lighter(), so that it is distinguished a bit better from grey non-part tracks.
1474 gradient.setColorAt(0, QColor(51, 51, 51, MusEGlobal::config.globalAlphaBlend));
1475 gradient.setColorAt(1, c);
1476 brush = QBrush(gradient);
1477 p.fillRect(sx, y1, ex - sx + 1, y2, brush);
1478 }
1479
1480 int ev_channels = 0;
1481 int wav_sx = 0;
1482 int wav_ex = 0;
1483 int wsx = 0;
1484 int wex = 0;
1485 bool wave_visible = false;
1486
1487 MusECore::SndFileR f = event.sndFile();
1488 if(!f.isNull())
1489 {
1490 ev_channels = f.channels();
1491 if (ev_channels > 0) {
1492
1493 int h = hh / (ev_channels * 2);
1494 int cc = hh % (ev_channels * 2) ? 0 : 1;
1495
1496 unsigned peoffset = px + event.frame() - ev_spos;
1497
1498 const sf_count_t smps = f.samples();
1499
1500 if(-ev_spos < smps && ev_spos <= smps)
1501 {
1502 wave_visible = true;
1503 wav_sx = -ev_spos;
1504 wav_ex = smps - ev_spos;
1505 if(wav_sx < 0)
1506 wav_sx = 0;
1507 wav_sx += event.frame() + wp->frame();
1508
1509 wav_ex = f.unConvertPosition(wav_ex);
1510 if(wav_ex >= (int)event.lenFrame())
1511 {
1512 wav_ex = event.lenFrame();
1513 if(wav_ex > 0)
1514 --wav_ex;
1515 }
1516 wav_ex += event.frame() + wp->frame();
1517
1518 wav_sx = wav_sx / xScale - xpos - xorg;
1519 wav_ex = wav_ex / xScale - xpos - xorg;
1520 wsx = wav_sx < x1 ? x1 : wav_sx;
1521 wex = wav_ex > x2 ? x2 : wav_ex;
1522 }
1523
1524 // fprintf(stderr, "WaveCanvas::drawItem: rect x:%d w:%d rr x:%d w:%d mr x:%d w:%d pos:%d sx:%d ex:%d\n",
1525 // rect.x(), rect.width(),
1526 // rr.x(), rr.width(),
1527 // mr.x(), mr.width(),
1528 // pos,
1529 // sx, ex);
1530
1531 for (int i = sx; i < ex; i++) {
1532 int y = h;
1533 MusECore::SampleV sa[f.channels()];
1534 if((ev_spos + f.convertPosition(pos)) > smps)
1535 break;
1536 // Seek the file only once, not with every read!
1537 if(i == sx)
1538 {
1539 if(f.seekUIConverted(pos, SEEK_SET | SFM_READ, ev_spos) == -1)
1540 break;
1541 }
1542 f.readConverted(sa, xScale, pos, ev_spos);
1543
1544 pos += xScale;
1545 if (pos < 0)
1546 continue;
1547
1548 int selectionStartPos = selectionStart - peoffset; // Offset transformed to event coords
1549 int selectionStopPos = selectionStop - peoffset;
1550
1551 for (int k = 0; k < ev_channels; ++k) {
1552 int kk = k % f.channels();
1553 int peak = (sa[kk].peak * (h - 1)) / yScale;
1554 int rms = (sa[kk].rms * (h - 1)) / yScale;
1555 if (peak > h)
1556 peak = h;
1557 if (rms > h)
1558 rms = h;
1559 QColor peak_color = MusEGlobal::config.wavePeakColor;
1560 QColor rms_color = MusEGlobal::config.waveRmsColor;
1561
1562 if ((ev_spos + pos) > selectionStartPos && (ev_spos + pos) <= selectionStopPos) {
1563 peak_color = MusEGlobal::config.wavePeakColorSelected;
1564 rms_color = MusEGlobal::config.waveRmsColorSelected;
1565 QLine l_inv = clipQLine(i, y - h + cc, i, y + h - cc, mbrwp);
1566 if(!l_inv.isNull())
1567 {
1568 // Draw inverted
1569 pen.setColor(QColor(Qt::black));
1570 p.setPen(pen);
1571 p.drawLine(l_inv);
1572 }
1573 }
1574
1575 QLine l_peak = clipQLine(i, y - peak - cc, i, y + peak, mbrwp);
1576 if(!l_peak.isNull())
1577 {
1578 pen.setColor(peak_color);
1579 p.setPen(pen);
1580 p.drawLine(l_peak);
1581 }
1582
1583 QLine l_rms = clipQLine(i, y - rms - cc, i, y + rms, mbrwp);
1584 if(!l_rms.isNull())
1585 {
1586 pen.setColor(rms_color);
1587 p.setPen(pen);
1588 p.drawLine(l_rms);
1589 }
1590
1591 y += 2 * h;
1592 }
1593 }
1594
1595 // Only if there's something to draw.
1596 if(wave_visible && wsx <= wex && wsx < x2 && wex >= x1)
1597 {
1598 const int hn = hh / ev_channels;
1599 const int hhn = hn / 2;
1600 for (int i = 0; i < ev_channels; ++i) {
1601 const int h2 = hn * i;
1602 const int center = hhn + h2;
1603 if(center >= y1 && center < y2)
1604 {
1605 pen.setColor(QColor(i & 1 ? right_ch_color : left_ch_color));
1606 p.setPen(pen);
1607 p.drawLine(wsx, center, wex, center);
1608 }
1609 }
1610 }
1611 }
1612 }
1613
1614 const int h2 = hh / 2;
1615 if(h2 >= y1 && h2 < y2)
1616 {
1617 pen.setColor(QColor(Qt::black));
1618 p.setPen(pen);
1619 // Draw the complete line only if there are an even number of channels (space for the line in the middle).
1620 // Ensure a complete line is drawn even if there is no sound file or channels.
1621 if((ev_channels & 1) == 0)
1622 p.drawLine(sx, h2, ex, h2);
1623 else
1624 {
1625 // Draw only the required two segments of the line.
1626 if(wave_visible)
1627 {
1628 if(sx < wsx)
1629 p.drawLine(sx, h2, wsx - 1, h2);
1630 if(wex < ex)
1631 p.drawLine(wex + 1, h2, ex, h2);
1632 }
1633 }
1634 }
1635
1636 //
1637 // Draw custom dashed borders around the wave event
1638 //
1639
1640 QColor color(item->isSelected() ? Qt::white : Qt::black);
1641 QPen penH(color);
1642 QPen penV(color);
1643 penH.setCosmetic(true);
1644 penV.setCosmetic(true);
1645 QVector<qreal> customDashPattern;
1646 customDashPattern << 4.0 << 6.0;
1647 penH.setDashPattern(customDashPattern);
1648 penV.setDashPattern(customDashPattern);
1649 penV.setDashOffset(2.0);
1650 // FIXME: Some shifting still going on. Values likely not quite right here.
1651 //int xdiff = sx - r.x();
1652 int xdiff = sx - mbbx;
1653 if(xdiff > 0)
1654 {
1655 int doff = xdiff % 10;
1656 penH.setDashOffset(doff);
1657 }
1658 // Tested OK. Each segment drawn only when necessary.
1659 if(y1 <= 0)
1660 {
1661 p.setPen(penH);
1662 p.drawLine(sx, 0, ex, 0);
1663 }
1664 if(y2 >= hh - 1)
1665 {
1666 p.setPen(penH);
1667 p.drawLine(sx, hh - 1, ex, hh - 1);
1668 }
1669
1670 //fprintf(stderr, "...Checking left edge: ubbx:%d ux:%d ux_2:%d\n", ubbx, ux, ux_2);
1671 if(ubbx >= ux && ubbx < ux_2)
1672 {
1673 //fprintf(stderr, "...Drawing left edge at mbbx:%d mby_exp:%d mby_2exp:%d\n", mbbx, mby_exp, mby_2exp);
1674
1675 p.setPen(penV);
1676 p.drawLine(mbbx, mby_exp, mbbx, mby_2exp);
1677 }
1678
1679
1680 //fprintf(stderr, "...Checking right edge: ubbx_2:%d ux:%d ux_2:%d\n", ubbx_2, ux, ux_2);
1681 if(ubbx_2 >= ux && ubbx_2 < ux_2)
1682 {
1683 //fprintf(stderr, "...Drawing right edge at mbbx_2:%d mby_exp:%d mby_2exp:%d\n", mbbx_2, mby_exp, mby_2exp);
1684
1685 p.setPen(penV);
1686 p.drawLine(mbbx_2, mby_exp, mbbx_2, mby_2exp);
1687 }
1688
1689 // Done. Restore and return.
1690 p.setWorldMatrixEnabled(wmtxen);
1691 }
1692
1693 //---------------------------------------------------------
1694 // drawTopItem
1695 //---------------------------------------------------------
1696
drawTopItem(QPainter & p,const QRect & rect,const QRegion &)1697 void WaveCanvas::drawTopItem(QPainter& p, const QRect& rect, const QRegion&)
1698 {
1699
1700 // TODO TODO: Convert this routine to new drawing system pulled from master? 20190121
1701
1702 QRect mr = map(rect);
1703
1704 DEBUG_WAVECANVAS(stderr, "WaveCanvas::drawTopItem: rect.x:%d rect.w:%d mr.x:%d mr.w:%d\n",
1705 rect.x(), rect.width(), mr.x(), mr.width());
1706
1707 p.save();
1708 p.setWorldMatrixEnabled(false);
1709
1710 for(MusEGui::ciCItem i = items.begin(); i != items.end(); ++i)
1711 {
1712 //if(!(i->second->isSelected()))
1713 // continue;
1714 WEvent* e = static_cast<WEvent*>(i->second);
1715 drawStretchAutomation(p, mr, e);
1716 }
1717
1718 p.restore();
1719
1720 }
1721
1722 //---------------------------------------------------------
1723 // drawStretchAutomation
1724 //---------------------------------------------------------
1725
drawStretchAutomation(QPainter & p,const QRect & rr,WEvent * item) const1726 void WaveCanvas::drawStretchAutomation(QPainter& p, const QRect& rr, WEvent* item) const
1727 {
1728 const MusECore::Event event = item->event();
1729 if(event.type() != MusECore::Wave)
1730 return;
1731
1732 const MusECore::SndFileR sf = event.sndFile();
1733 if(sf.isNull())
1734 return;
1735
1736 const MusECore::StretchList* sl = sf.stretchList();
1737 if(!sl)
1738 return;
1739
1740
1741 //const bool wave_sr_differs = sf.sampleRateDiffers();
1742 const double wave_sr_ratio = sf.sampleRateRatio();
1743
1744 p.setBrush(Qt::NoBrush);
1745
1746 QColor c;
1747 QPen pen;
1748 int xpixel;
1749 QVector<qreal> pattern;
1750 pattern << 4 << 4;
1751 const StretchSelectedList_t& ssl = _stretchAutomation._stretchSelectedList;
1752 ciStretchSelectedItemPair res;
1753 for(MusECore::ciStretchListItem is = sl->begin(); is != sl->end(); ++is)
1754 {
1755 // Do not recognize or draw the item at zeroth frame.
1756 if(is->first == 0)
1757 continue;
1758
1759 const MusECore::StretchListItem& sli = is->second;
1760 //if((_tool == StretchTool && (sli._type & MusECore::StretchListItem::StretchEvent)) ||
1761 // (_tool == SamplerateTool && (sli._type & MusECore::StretchListItem::SamplerateEvent)))
1762 {
1763 xpixel = mapx(sl->squish((double)is->first) / wave_sr_ratio + item->x());
1764
1765 DEBUG_WAVECANVAS(stderr, "drawStretchAutomation: rr.x:%d rr.w:%d xpixel:%d\n", rr.x(), rr.width(), xpixel);
1766
1767 if(sli._type & MusECore::StretchListItem::StretchEvent)
1768 {
1769 //if(_tool == StretchTool)
1770 c = Qt::magenta;
1771 //else
1772 // c = Qt::darkMagenta;
1773
1774 res = ssl.equal_range(is->first); // FIXME Calls non-constant version? Want constant version.
1775 for(ciStretchSelectedItem ise = res.first; ise != res.second; ++ise)
1776 {
1777 if(ise->first == is->first && ise->second._sndFile.stretchList() == sl && ise->second._type == MusECore::StretchListItem::StretchEvent)
1778 {
1779 c = Qt::white;
1780 break;
1781 }
1782 }
1783
1784 //c.setAlpha(200);
1785 pen.setColor(c);
1786 pen.setDashPattern(pattern);
1787 p.setPen(pen);
1788 p.drawLine(xpixel, rr.top() - 2, xpixel, rr.bottom() - 2);
1789 }
1790
1791 if(sli._type & MusECore::StretchListItem::SamplerateEvent)
1792 {
1793 //if(_tool == SamplerateTool)
1794 c = Qt::cyan;
1795 //else
1796 // c = Qt::darkCyan;
1797
1798 res = ssl.equal_range(is->first); // FIXME Calls non-constant version? Want constant version.
1799 for(ciStretchSelectedItem ise = res.first; ise != res.second; ++ise)
1800 {
1801 if(ise->first == is->first && ise->second._sndFile.stretchList() == sl && ise->second._type == MusECore::StretchListItem::SamplerateEvent)
1802 {
1803 c = Qt::white;
1804 break;
1805 }
1806 }
1807
1808 //c.setAlpha(200);
1809 pen.setColor(c);
1810 pen.setDashPattern(pattern);
1811 // Offset to help distinguish from stretch lines.
1812 pen.setDashOffset(4.0);
1813 p.setPen(pen);
1814 // Draw reverse direction to help distinguish from stretch lines.
1815 p.drawLine(xpixel, rr.bottom() - 2, xpixel, rr.top() - 2);
1816 }
1817 }
1818 }
1819 }
1820
stretchListHitTest(int types,QPoint pt,WEvent * wevent)1821 MusECore::iStretchListItem WaveCanvas::stretchListHitTest(int types, QPoint pt, WEvent* wevent)
1822 {
1823 const MusECore::Event event = wevent->event();
1824 if(event.type() != MusECore::Wave)
1825 return MusECore::iStretchListItem();
1826
1827 const MusECore::SndFileR sf = event.sndFile();
1828 if(sf.isNull())
1829 return MusECore::iStretchListItem();
1830
1831 MusECore::StretchList* stretchList = sf.stretchList();
1832 if(!stretchList)
1833 return MusECore::iStretchListItem();
1834
1835 //const bool wave_sr_differs = sf.sampleRateDiffers();
1836 const double wave_sr_ratio = sf.sampleRateRatio();
1837
1838 const int pt_x = pt.x();
1839 const int wevent_x = wevent->x();
1840 int closest_dist = _stretchAutomationPointDetectDist;
1841 MusECore::iStretchListItem closest_ev = stretchList->end();
1842 for(MusECore::iStretchListItem is = stretchList->begin(); is != stretchList->end(); ++is)
1843 {
1844 // Do not recognize or draw the item at zeroth frame.
1845 if(is->first == 0)
1846 continue;
1847
1848 const MusECore::StretchListItem& se = is->second;
1849 if(!(se._type & types))
1850 continue;
1851
1852 const double newSqFrame = se._finSquishedFrame / wave_sr_ratio;
1853 const int xpixel = mapx(newSqFrame + wevent_x);
1854 const int pt_pixel = mapx(pt_x);
1855
1856 const int x_diff = (xpixel > pt_pixel) ? (xpixel - pt_pixel) : (pt_pixel - xpixel);
1857 if(x_diff <= closest_dist)
1858 {
1859 closest_dist = x_diff;
1860 closest_ev = is;
1861 }
1862 }
1863
1864 return closest_ev;
1865 }
1866
setStretchAutomationCursor(QPoint pt)1867 void WaveCanvas::setStretchAutomationCursor(QPoint pt)
1868 {
1869 if(_tool != StretchTool && _tool != SamplerateTool)
1870 return;
1871
1872 CItem* item = items.find(pt);
1873 if(!item)
1874 {
1875 setCursor();
1876 return;
1877 }
1878 WEvent* wevent = static_cast<WEvent*>(item);
1879
1880 const MusECore::Event event = wevent->event();
1881 if(event.type() != MusECore::Wave)
1882 {
1883 setCursor();
1884 return;
1885 }
1886
1887 MusECore::SndFileR sf = event.sndFile();
1888 if(sf.isNull())
1889 {
1890 setCursor();
1891 return;
1892 }
1893
1894 MusECore::StretchList* sl = sf.stretchList();
1895 if(!sl)
1896 {
1897 setCursor();
1898 return;
1899 }
1900
1901 MusECore::StretchListItem::StretchEventType type;
1902 if(_tool == StretchTool)
1903 type = MusECore::StretchListItem::StretchEvent;
1904 else // if(_tool == SamplerateTool)
1905 type = MusECore::StretchListItem::SamplerateEvent;
1906
1907 MusECore::iStretchListItem isli_hit_test = stretchListHitTest(type, pt, wevent);
1908 if(isli_hit_test == sl->end())
1909 setCursor();
1910 else
1911 QWidget::setCursor(Qt::SizeHorCursor);
1912 }
1913
1914 //---------------------------------------------------------
1915 // drawMoving
1916 // draws moving items
1917 //---------------------------------------------------------
1918
drawMoving(QPainter & p,const CItem * item,const QRect & mr,const QRegion &)1919 void WaveCanvas::drawMoving(QPainter& p, const CItem* item, const QRect& mr, const QRegion&)
1920 {
1921 const QRect ur = mapDev(mr);
1922 QRect ur_item = QRect(item->mp().x(), item->mp().y(), item->width(), item->height());
1923 ur_item = ur_item.intersected(ur);
1924 if(!ur_item.isValid())
1925 return;
1926 QPen pen;
1927 pen.setCosmetic(true);
1928 pen.setColor(Qt::black);
1929 p.setPen(pen);
1930 p.setBrush(QColor(0, 128, 0, 128)); // TODO: Pick a better colour, or use part colours, or grey?
1931 p.drawRect(ur_item);
1932 }
1933
1934 //---------------------------------------------------------
1935 // viewMouseDoubleClickEvent
1936 //---------------------------------------------------------
1937
viewMouseDoubleClickEvent(QMouseEvent * event)1938 void WaveCanvas::viewMouseDoubleClickEvent(QMouseEvent* event)
1939 {
1940 if ((_tool != PointerTool) && (event->button() != Qt::LeftButton)) {
1941 mousePress(event);
1942 return;
1943 }
1944 }
1945
1946 //---------------------------------------------------------
1947 // moveCanvasItems
1948 //---------------------------------------------------------
1949
moveCanvasItems(CItemMap & items,int,int dx,DragType dtype,bool rasterize)1950 MusECore::Undo WaveCanvas::moveCanvasItems(CItemMap& items, int /*dp*/, int dx, DragType dtype, bool rasterize)
1951 {
1952 if(editor->parts()->empty())
1953 return MusECore::Undo(); //return empty list
1954
1955 MusECore::PartsToChangeMap parts2change;
1956 MusECore::Undo operations;
1957
1958 for(MusECore::iPart ip = editor->parts()->begin(); ip != editor->parts()->end(); ++ip)
1959 {
1960 MusECore::Part* part = ip->second;
1961 if(!part)
1962 continue;
1963
1964 int npartoffset = 0;
1965 for(iCItem ici = items.begin(); ici != items.end(); ++ici)
1966 {
1967 CItem* ci = ici->second;
1968 ci->setMoving(false);
1969
1970 if(ci->part() != part)
1971 continue;
1972
1973 int x = ci->pos().x() + dx;
1974 int y = 0;
1975 QPoint newpos = QPoint(x, y);
1976 if(rasterize)
1977 newpos = raster(newpos);
1978
1979 // Test moving the item...
1980 WEvent* wevent = (WEvent*) ci;
1981 MusECore::Event event = wevent->event();
1982 x = newpos.x();
1983 if(x < 0)
1984 x = 0;
1985 // Normally frame to tick methods round down. But here we need it to 'snap'
1986 // the frame from either side of a tick to the tick. So round to nearest.
1987 int nframe = (rasterize ? MusEGlobal::tempomap.tick2frame(
1988 editor->rasterVal(MusEGlobal::tempomap.frame2tick(x, 0, MusECore::LargeIntRoundNearest))) : x) - part->frame();
1989 if(nframe < 0)
1990 nframe = 0;
1991 int diff = nframe + event.lenFrame() - part->lenFrame();
1992
1993 // If moving the item would require a new part size...
1994 if(diff > npartoffset)
1995 npartoffset = diff;
1996 }
1997
1998 if(npartoffset > 0)
1999 {
2000 MusECore::iPartToChange ip2c = parts2change.find(part);
2001 if(ip2c == parts2change.end())
2002 {
2003 MusECore::PartToChange p2c = {0, npartoffset};
2004 parts2change.insert(std::pair<MusECore::Part*, MusECore::PartToChange> (part, p2c));
2005 }
2006 else
2007 ip2c->second.xdiff = npartoffset;
2008 }
2009 }
2010
2011 bool forbidden=false;
2012 for(MusECore::iPartToChange ip2c = parts2change.begin(); ip2c != parts2change.end(); ++ip2c)
2013 {
2014 MusECore::Part* opart = ip2c->first;
2015 if (opart->hasHiddenEvents() & MusECore::Part::RightEventsHidden)
2016 {
2017 forbidden=true;
2018 break;
2019 }
2020 }
2021
2022
2023 if (!forbidden)
2024 {
2025 std::vector< CItem* > doneList;
2026 typedef std::vector< CItem* >::iterator iDoneList;
2027
2028 for(iCItem ici = items.begin(); ici != items.end(); ++ici)
2029 {
2030 CItem* ci = ici->second;
2031
2032 int x = ci->pos().x();
2033 int nx = x + dx;
2034 int ny = 0;
2035 QPoint newpos = QPoint(nx, ny);
2036 if(rasterize)
2037 newpos = raster(newpos);
2038 selectItem(ci, true);
2039
2040 iDoneList idl;
2041 for(idl = doneList.begin(); idl != doneList.end(); ++idl)
2042 // This compares EventBase pointers to see if they're the same...
2043 if((*idl)->event() == ci->event())
2044 break;
2045
2046 // Do not process if the event has already been processed (meaning it's an event in a clone part)...
2047 if (idl == doneList.end())
2048 {
2049 moveItem(operations, ci, newpos, dtype, rasterize); // always returns true. if not, change is necessary here!
2050 doneList.push_back(ci);
2051 }
2052 ci->move(newpos);
2053
2054 if(moving.size() == 1)
2055 itemReleased(curItem, newpos);
2056
2057 if(dtype == MOVE_COPY || dtype == MOVE_CLONE)
2058 selectItem(ci, false);
2059 }
2060
2061 for(MusECore::iPartToChange ip2c = parts2change.begin(); ip2c != parts2change.end(); ++ip2c)
2062 {
2063 MusECore::Part* opart = ip2c->first;
2064 int diff = ip2c->second.xdiff;
2065
2066 //schedule_resize_all_same_len_clone_parts(opart, opart->lenTick() + diff, operations);
2067 schedule_resize_all_same_len_clone_parts(opart, opart->lenFrame() + diff, operations);
2068 }
2069
2070 return operations;
2071 }
2072 else
2073 {
2074 return MusECore::Undo(); //return empty list
2075 }
2076 }
2077
2078 //---------------------------------------------------------
2079 // moveItem
2080 // called after moving an object
2081 //---------------------------------------------------------
2082
moveItem(MusECore::Undo & operations,CItem * item,const QPoint & pos,DragType dtype,bool rasterize)2083 bool WaveCanvas::moveItem(MusECore::Undo& operations, CItem* item, const QPoint& pos, DragType dtype, bool rasterize)
2084 {
2085 WEvent* wevent = (WEvent*) item;
2086 MusECore::Event event = wevent->event();
2087 MusECore::Event newEvent = event.clone();
2088 int x = pos.x();
2089 if (x < 0)
2090 x = 0;
2091
2092 MusECore::Part* part = wevent->part();
2093 // Normally frame to tick methods round down. But here we need it to 'snap'
2094 // the frame from either side of a tick to the tick. So round to nearest.
2095 int nframe =
2096 (rasterize ? MusEGlobal::tempomap.tick2frame(
2097 editor->rasterVal(MusEGlobal::tempomap.frame2tick(x, 0, MusECore::LargeIntRoundNearest))) : x) - part->frame();
2098 if (nframe < 0)
2099 nframe = 0;
2100 newEvent.setFrame(nframe);
2101 newEvent.setLenFrame(event.lenFrame());
2102
2103 // don't check, whether the new event is within the part
2104 // at this place. with operation groups, the part isn't
2105 // resized yet. (flo93)
2106
2107 if (dtype == MOVE_COPY || dtype == MOVE_CLONE)
2108 operations.push_back(MusECore::UndoOp(MusECore::UndoOp::AddEvent, newEvent, part, false, false));
2109 else
2110 operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, newEvent, event, part, false, false));
2111
2112 return true;
2113 }
2114
2115 //---------------------------------------------------------
2116 // newItem(p, state)
2117 //---------------------------------------------------------
2118
newItem(const QPoint & p,int key_modifiers)2119 CItem* WaveCanvas::newItem(const QPoint& p, int key_modifiers)
2120 {
2121 int frame = p.x();
2122 if(frame < 0)
2123 frame = 0;
2124 // Normally frame to tick methods round down. But here we need it to 'snap'
2125 // the frame from either side of a tick to the tick. So round to nearest.
2126 if(!(key_modifiers & Qt::ShiftModifier))
2127 frame = MusEGlobal::tempomap.tick2frame(
2128 editor->rasterVal1(MusEGlobal::tempomap.frame2tick(frame, 0, MusECore::LargeIntRoundNearest)));
2129 int len = p.x() - frame;
2130 frame -= curPart->frame();
2131 if (frame < 0)
2132 return 0;
2133 MusECore::Event e = MusECore::Event(MusECore::Wave);
2134 e.setFrame(frame);
2135 e.setLenFrame(len);
2136 WEvent* we = new WEvent(e, curPart, height());
2137 return we;
2138 }
2139
newItem(CItem * item,bool noSnap)2140 void WaveCanvas::newItem(CItem* item, bool noSnap)
2141 {
2142 WEvent* wevent = (WEvent*) item;
2143 MusECore::Event event = wevent->event();
2144 MusECore::Part* part = wevent->part();
2145 int pframe = part->frame();
2146 int x = item->x();
2147 if (x<pframe)
2148 x=pframe;
2149 int w = item->width();
2150
2151 if (!noSnap) {
2152 // Normally frame to tick methods round down. But here we need it to 'snap'
2153 // the frame from either side of a tick to the tick. So round to nearest.
2154 x = MusEGlobal::tempomap.tick2frame(
2155 editor->rasterVal1(MusEGlobal::tempomap.frame2tick(x, 0, MusECore::LargeIntRoundNearest)));
2156 w = MusEGlobal::tempomap.tick2frame(
2157 editor->rasterVal(MusEGlobal::tempomap.frame2tick(x + w, 0, MusECore::LargeIntRoundNearest))) - x;
2158 if (w == 0)
2159 w = MusEGlobal::tempomap.tick2frame(editor->raster());
2160 }
2161 if (x<pframe)
2162 x=pframe;
2163 event.setFrame(x - pframe);
2164 event.setLenFrame(w);
2165 event.setSelected(true);
2166
2167 MusECore::Undo operations;
2168 int diff = event.endFrame() - part->lenFrame();
2169
2170 if (! ((diff > 0) && (part->hasHiddenEvents() & MusECore::Part::RightEventsHidden)) ) //operation is allowed
2171 {
2172 operations.push_back(MusECore::UndoOp(MusECore::UndoOp::AddEvent,event, part, false, false));
2173
2174 if (diff > 0)// part must be extended?
2175 {
2176 schedule_resize_all_same_len_clone_parts(part, event.endFrame(), operations);
2177 printf("newItem: extending\n");
2178 }
2179
2180 MusEGlobal::song->applyOperationGroup(operations);
2181 }
2182 else // forbid action by not applying it
2183 songChanged(SC_EVENT_INSERTED); //this forces an update of the itemlist, which is necessary
2184 //to remove "forbidden" events from the list again
2185 }
2186
2187 //---------------------------------------------------------
2188 // resizeItem
2189 //---------------------------------------------------------
2190
resizeItem(CItem * item,bool noSnap,bool)2191 void WaveCanvas::resizeItem(CItem* item, bool noSnap, bool) // experimental changes to try dynamically extending parts
2192 {
2193 WEvent* wevent = (WEvent*) item;
2194 MusECore::Event event = wevent->event();
2195 MusECore::Event newEvent = event.clone();
2196 int len;
2197
2198 MusECore::Part* part = wevent->part();
2199
2200 if (noSnap)
2201 len = wevent->width();
2202 else
2203 {
2204 unsigned frame = event.frame() + part->frame();
2205
2206 // Normally frame to tick methods round down. But here we need it to 'snap'
2207 // the frame from either side of a tick to the tick. So round to nearest.
2208 len = MusEGlobal::tempomap.tick2frame(
2209 editor->rasterVal(MusEGlobal::tempomap.frame2tick(
2210 frame + wevent->width(), 0, MusECore::LargeIntRoundNearest))) - frame;
2211 if (len <= 0)
2212 len = MusEGlobal::tempomap.tick2frame(editor->raster());
2213 }
2214
2215 MusECore::Undo operations;
2216 int diff = event.frame() + len - part->lenFrame();
2217
2218 if (resizeDirection == MusECore::ResizeDirection::RESIZE_TO_THE_LEFT) {
2219 int x = qMax(0, wevent->x());
2220 int nframe = qMax(0u, x - part->frame());
2221 newEvent.setFrame(nframe);
2222 }
2223
2224 if (! ((diff > 0) && (part->hasHiddenEvents() & MusECore::Part::RightEventsHidden)) ) //operation is allowed
2225 {
2226 newEvent.setLenFrame(len);
2227 operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent,newEvent, event, wevent->part(), false, false));
2228
2229 if (diff > 0)// part must be extended?
2230 {
2231 //schedule_resize_all_same_len_clone_parts(part, event.tick()+len, operations);
2232 schedule_resize_all_same_len_clone_parts(part, event.frame() + len, operations);
2233 printf("resizeItem: extending\n");
2234 }
2235 }
2236 //else forbid action by not performing it
2237 MusEGlobal::song->applyOperationGroup(operations);
2238 songChanged(SC_EVENT_MODIFIED); //this forces an update of the itemlist, which is necessary
2239 //to remove "forbidden" events from the list again
2240 }
2241
2242
2243 //---------------------------------------------------------
2244 // deleteItem
2245 //---------------------------------------------------------
2246
deleteItem(CItem * item)2247 bool WaveCanvas::deleteItem(CItem* item)
2248 {
2249 WEvent* wevent = (WEvent*) item;
2250 if (wevent->part() == curPart) {
2251 MusECore::Event ev = wevent->event();
2252 // Indicate do undo, and do not do port controller values and clone parts.
2253 MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::DeleteEvent, ev, curPart, false, false));
2254 return true;
2255 }
2256 return false;
2257 }
2258
2259 //---------------------------------------------------------
2260 // adjustWaveOffset
2261 //---------------------------------------------------------
2262
adjustWaveOffset()2263 void WaveCanvas::adjustWaveOffset()
2264 {
2265 bool have_selected = false;
2266 int init_offset = 0;
2267
2268 for (iCItem k = items.begin(); k != items.end(); ++k)
2269 {
2270 if (k->second->isSelected())
2271 {
2272 have_selected = true;
2273 init_offset = k->second->event().spos();
2274 break;
2275 }
2276 }
2277
2278 if(!have_selected)
2279 {
2280 QMessageBox::information(this,
2281 QString("MusE"),
2282 QWidget::tr("No wave events selected."));
2283 return;
2284 }
2285
2286 bool ok = false;
2287 int offset = QInputDialog::getInt(this,
2288 tr("Adjust Wave Offset"),
2289 tr("Wave offset (frames)"),
2290 init_offset,
2291 0, 2147483647, 1,
2292 &ok);
2293 if(!ok)
2294 return;
2295
2296 MusECore::Undo operations;
2297
2298 // FIXME: Respect clones! If operating on two selected clones of the same part, an extra event is created!
2299 // Check - Is it really this code's problem? Seems so, other operations like moving an event seem OK.
2300 for(iCItem ici = items.begin(); ici != items.end(); ++ici)
2301 {
2302 if(ici->second->isSelected())
2303 {
2304 MusECore::Event oldEvent = ici->second->event();
2305 if(oldEvent.spos() != offset)
2306 {
2307 MusECore::Part* part = ici->second->part();
2308 MusECore::Event newEvent = oldEvent.clone();
2309 newEvent.setSpos(offset);
2310 // Do not do port controller values and clone parts.
2311 operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, newEvent, oldEvent, part, false, false));
2312 }
2313 }
2314 }
2315
2316 MusEGlobal::song->applyOperationGroup(operations);
2317
2318 redraw();
2319 }
2320
2321 //---------------------------------------------------------
2322 // draw
2323 //---------------------------------------------------------
2324
drawCanvas(QPainter & p,const QRect & rect,const QRegion & rg)2325 void WaveCanvas::drawCanvas(QPainter& p, const QRect& rect, const QRegion& rg)
2326 {
2327 if (MusEGlobal::config.canvasShowGrid)
2328 {
2329 //---------------------------------------------------
2330 // vertical lines
2331 //---------------------------------------------------
2332
2333 drawTickRaster(p, rect, rg, editor->raster(), true, false, false,
2334 MusEGlobal::config.midiCanvasBeatColor, // color sequence slightly done by trial and error..
2335 MusEGlobal::config.midiCanvasBeatColor,
2336 MusEGlobal::config.midiCanvasFineColor,
2337 MusEGlobal::config.midiCanvasBarColor
2338 );
2339 }
2340 }
2341
2342 //---------------------------------------------------------
2343 // waveCmd
2344 //---------------------------------------------------------
2345
waveCmd(int cmd)2346 void WaveCanvas::waveCmd(int cmd)
2347 {
2348 // TODO: New WaveCanvas: Convert this routine to frames.
2349 switch(cmd) {
2350 case CMD_LEFT:
2351 {
2352 int spos = pos[0];
2353 if(spos > 0)
2354 {
2355 spos -= 1; // Nudge by -1, then snap down with raster1.
2356 spos = MusEGlobal::sigmap.raster1(spos, editor->rasterStep(pos[0]));
2357 }
2358 if(spos < 0)
2359 spos = 0;
2360 MusECore::Pos p(spos,true);
2361 MusEGlobal::song->setPos(MusECore::Song::CPOS, p, true, true, true);
2362 }
2363 break;
2364 case CMD_RIGHT:
2365 {
2366 int spos = MusEGlobal::sigmap.raster2(pos[0] + 1, editor->rasterStep(pos[0])); // Nudge by +1, then snap up with raster2.
2367 MusECore::Pos p(spos,true);
2368 MusEGlobal::song->setPos(MusECore::Song::CPOS, p, true, true, true);
2369 }
2370 break;
2371 case CMD_LEFT_NOSNAP:
2372 {
2373 int spos = pos[0] - editor->rasterStep(pos[0]);
2374 if (spos < 0)
2375 spos = 0;
2376 MusECore::Pos p(spos,true);
2377 MusEGlobal::song->setPos(MusECore::Song::CPOS, p, true, true, true); //CDW
2378 }
2379 break;
2380 case CMD_RIGHT_NOSNAP:
2381 {
2382 MusECore::Pos p(pos[0] + editor->rasterStep(pos[0]), true);
2383 MusEGlobal::song->setPos(MusECore::Song::CPOS, p, true, true, true); //CDW
2384 }
2385 break;
2386 case CMD_INSERT:
2387 {
2388 if (pos[0] < start() || pos[0] >= end())
2389 break;
2390 MusECore::MidiPart* part = (MusECore::MidiPart*)curPart;
2391
2392 if (part == 0)
2393 break;
2394
2395 const MusECore::EventList& el = part->events();
2396 MusECore::Undo operations;
2397
2398 std::list <MusECore::Event> elist;
2399 for (MusECore::ciEvent e = el.lower_bound(pos[0] - part->tick()); e != el.end(); ++e)
2400 elist.push_back((MusECore::Event)e->second);
2401 for (std::list<MusECore::Event>::iterator i = elist.begin(); i != elist.end(); ++i) {
2402 MusECore::Event event = *i;
2403 MusECore::Event newEvent = event.clone();
2404 newEvent.setTick(event.tick() + editor->raster());// - part->tick()); DELETETHIS
2405 // Do not do port controller values and clone parts.
2406 operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, newEvent, event, part, false, false));
2407 }
2408 MusEGlobal::song->applyOperationGroup(operations);
2409
2410 MusECore::Pos p(editor->rasterVal(pos[0] + editor->rasterStep(pos[0])), true);
2411 MusEGlobal::song->setPos(MusECore::Song::CPOS, p, true, false, true);
2412 }
2413 return;
2414 case CMD_BACKSPACE:
2415 if (pos[0] < start() || pos[0] >= end())
2416 break;
2417 {
2418 MusECore::MidiPart* part = (MusECore::MidiPart*)curPart;
2419 if (part == 0)
2420 break;
2421
2422 MusECore::Undo operations;
2423 const MusECore::EventList& el = part->events();
2424
2425 std::list<MusECore::Event> elist;
2426 for (MusECore::ciEvent e = el.lower_bound(pos[0]); e != el.end(); ++e)
2427 elist.push_back((MusECore::Event)e->second);
2428 for (std::list<MusECore::Event>::iterator i = elist.begin(); i != elist.end(); ++i) {
2429 MusECore::Event event = *i;
2430 MusECore::Event newEvent = event.clone();
2431 newEvent.setTick(event.tick() - editor->raster() - part->tick());
2432 // Do not do port controller values and clone parts.
2433 operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, newEvent, event, part, false, false));
2434 }
2435 MusEGlobal::song->applyOperationGroup(operations);
2436 MusECore::Pos p(editor->rasterVal(pos[0] - editor->rasterStep(pos[0])), true);
2437 MusEGlobal::song->setPos(MusECore::Song::CPOS, p, true, false, true);
2438 }
2439 break;
2440 }
2441 }
2442
2443 //---------------------------------------------------------
2444 // cmd
2445 // pulldown menu commands
2446 //---------------------------------------------------------
2447
cmd(int cmd)2448 void WaveCanvas::cmd(int cmd)
2449 {
2450 int modifyoperation = -1;
2451 double paramA = 0.0;
2452 switch (cmd) {
2453 case CMD_SELECT_ALL: // select all
2454 if (tool() == RangeTool)
2455 {
2456 if (!editor->parts()->empty()) {
2457 MusECore::iPart iBeg = editor->parts()->begin();
2458 MusECore::iPart iEnd = editor->parts()->end();
2459 iEnd--;
2460 MusECore::WavePart* beg = (MusECore::WavePart*) iBeg->second;
2461 MusECore::WavePart* end = (MusECore::WavePart*) iEnd->second;
2462 selectionStart = beg->frame();
2463 selectionStop = end->frame() + end->lenFrame();
2464 redraw();
2465 }
2466 }
2467 for (iCItem k = items.begin(); k != items.end(); ++k) {
2468 if (!k->second->isSelected())
2469 selectItem(k->second, true);
2470 }
2471 break;
2472 case CMD_SELECT_NONE: // select none
2473 selectionStart = selectionStop = 0;
2474 deselectAll();
2475 break;
2476 case CMD_SELECT_INVERT: // invert selection
2477 for (iCItem k = items.begin(); k != items.end(); ++k) {
2478 selectItem(k->second, !k->second->isSelected());
2479 }
2480 break;
2481 case CMD_SELECT_ILOOP: // select inside loop
2482 for (iCItem k = items.begin(); k != items.end(); ++k) {
2483 WEvent* wevent = (WEvent*)(k->second);
2484 MusECore::Part* part = wevent->part();
2485 MusECore::Event event = wevent->event();
2486 unsigned tick = event.tick() + part->tick();
2487 if (tick < MusEGlobal::song->lpos() || tick >= MusEGlobal::song->rpos())
2488 selectItem(k->second, false);
2489 else
2490 selectItem(k->second, true);
2491 }
2492 break;
2493 case CMD_SELECT_OLOOP: // select outside loop
2494 for (iCItem k = items.begin(); k != items.end(); ++k) {
2495 WEvent* wevent = (WEvent*)(k->second);
2496 MusECore::Part* part = wevent->part();
2497 MusECore::Event event = wevent->event();
2498 unsigned tick = event.tick() + part->tick();
2499 if (tick < MusEGlobal::song->lpos() || tick >= MusEGlobal::song->rpos())
2500 selectItem(k->second, true);
2501 else
2502 selectItem(k->second, false);
2503 }
2504 break;
2505 case CMD_RANGE_TO_SELECTION:
2506 setRangeToSelection();
2507 break;
2508
2509 case CMD_SELECT_PREV_PART: // select previous part
2510 {
2511 MusECore::Part* pt = editor->curCanvasPart();
2512 MusECore::Part* newpt = pt;
2513 MusECore::PartList* pl = editor->parts();
2514 for(MusECore::iPart ip = pl->begin(); ip != pl->end(); ++ip)
2515 if(ip->second == pt)
2516 {
2517 if(ip == pl->begin())
2518 ip = pl->end();
2519 --ip;
2520 newpt = ip->second;
2521 break;
2522 }
2523 if(newpt != pt)
2524 editor->setCurCanvasPart(newpt);
2525 }
2526 break;
2527 case CMD_SELECT_NEXT_PART: // select next part
2528 {
2529 MusECore::Part* pt = editor->curCanvasPart();
2530 MusECore::Part* newpt = pt;
2531 MusECore::PartList* pl = editor->parts();
2532 for(MusECore::iPart ip = pl->begin(); ip != pl->end(); ++ip)
2533 if(ip->second == pt)
2534 {
2535 ++ip;
2536 if(ip == pl->end())
2537 ip = pl->begin();
2538 newpt = ip->second;
2539 break;
2540 }
2541 if(newpt != pt)
2542 editor->setCurCanvasPart(newpt);
2543 }
2544 break;
2545
2546 case CMD_ADJUST_WAVE_OFFSET:
2547 adjustWaveOffset();
2548 break;
2549
2550 case CMD_EDIT_EXTERNAL:
2551 modifyoperation = EDIT_EXTERNAL;
2552 break;
2553
2554 case CMD_EDIT_COPY:
2555 modifyoperation = COPY;
2556 break;
2557 case CMD_EDIT_CUT:
2558 modifyoperation = CUT;
2559 break;
2560 case CMD_EDIT_PASTE:
2561 modifyoperation = PASTE;
2562 break;
2563
2564 case CMD_MUTE:
2565 modifyoperation = MUTE;
2566 break;
2567
2568 case CMD_NORMALIZE:
2569 modifyoperation = NORMALIZE;
2570 break;
2571
2572 case CMD_FADE_IN:
2573 modifyoperation = FADE_IN;
2574 break;
2575
2576 case CMD_FADE_OUT:
2577 modifyoperation = FADE_OUT;
2578 break;
2579
2580 case CMD_REVERSE:
2581 modifyoperation = REVERSE;
2582 break;
2583
2584 case CMD_GAIN_FREE: {
2585 EditGain* editGain = new EditGain(this, lastGainvalue);
2586 if (editGain->exec() == QDialog::Accepted) {
2587 lastGainvalue = editGain->getGain();
2588 modifyoperation = GAIN;
2589 paramA = (double)lastGainvalue / 100.0;
2590 }
2591 delete editGain;
2592 }
2593 break;
2594
2595 case CMD_GAIN_200:
2596 modifyoperation = GAIN;
2597 paramA = 2.0;
2598 break;
2599
2600 case CMD_GAIN_150:
2601 modifyoperation = GAIN;
2602 paramA = 1.5;
2603 break;
2604
2605 case CMD_GAIN_75:
2606 modifyoperation = GAIN;
2607 paramA = 0.75;
2608 break;
2609
2610 case CMD_GAIN_50:
2611 modifyoperation = GAIN;
2612 paramA = 0.5;
2613 break;
2614
2615 case CMD_GAIN_25:
2616 modifyoperation = GAIN;
2617 paramA = 0.25;
2618 break;
2619
2620 case CMD_CREATE_PART_REGION:
2621 {
2622 // create a new part and put in the copy buffer
2623 MusECore::Part* pt = editor->curCanvasPart();
2624 if (pt == 0 || pt->track()->type() != MusECore::Track::WAVE)
2625 return;
2626 MusECore::WavePart *origPart = (MusECore::WavePart*)pt;
2627 if (MusEGlobal::song->lpos() < origPart->tick() || MusEGlobal::song->rpos() > origPart->endTick())
2628 {
2629 QMessageBox::warning(this, tr("Part creation failed"),
2630 tr("Left and right position markers must be placed inside the current part."),
2631 QMessageBox::Ok, QMessageBox::Ok);
2632 return;
2633 }
2634 MusECore::WavePart *tempPart = new MusECore::WavePart(origPart->track());
2635 unsigned origFrame = origPart->frame();
2636 unsigned frameDistance = MusEGlobal::song->lPos().frame() - origFrame;
2637 tempPart->setPos(MusEGlobal::song->lpos());
2638 tempPart->setLenTick(MusEGlobal::song->rpos() - MusEGlobal::song->lpos());
2639 // loop through the events and set them accordingly
2640 for (MusECore::ciEvent iWaveEvent = origPart->events().begin(); iWaveEvent != origPart->events().end(); iWaveEvent++)
2641 {
2642 // TODO: handle multiple events correctly,
2643 // the math for subsequent events isn't correct
2644 const MusECore::Event& ev = iWaveEvent->second;
2645 MusECore::Event *newEvent = new MusECore::Event(ev.clone());
2646 newEvent->setSpos(ev.spos() + frameDistance);
2647 newEvent->setLenTick(MusEGlobal::song->rpos() - MusEGlobal::song->lpos());
2648 tempPart->addEvent(*newEvent);
2649 }
2650 std::set<const MusECore::Part*> partList;
2651 partList.insert(tempPart);
2652
2653 QMimeData *mimeData = MusECore::parts_to_mime(partList);
2654 QApplication::clipboard()->setMimeData(mimeData, QClipboard::Clipboard);
2655 QMessageBox::information(this, tr("Part created"),
2656 tr("The selected region has been copied to the clipboard and can be pasted in the arranger."),
2657 QMessageBox::Ok, QMessageBox::Ok);
2658 }
2659 break;
2660 case CMD_ERASE_MEASURE:
2661 case CMD_DELETE_MEASURE:
2662 case CMD_CREATE_MEASURE:
2663 break;
2664 default:
2665 // printf("unknown ecanvas cmd %d\n", cmd);
2666 break;
2667 }
2668
2669 if (modifyoperation != -1) {
2670 if (selectionStart == selectionStop && modifyoperation!=PASTE) {
2671 printf("No selection. Ignoring\n"); //@!TODO: Disable menu options when no selection
2672 QMessageBox::information(this,
2673 QString("MusE"),
2674 QWidget::tr("No selection. Ignoring"));
2675
2676 return;
2677 }
2678
2679 //if(!modifyWarnedYet)
2680 //{
2681 // modifyWarnedYet = true;
2682 // if(QMessageBox::warning(this, QString("Muse"),
2683 // tr("Warning! Muse currently operates directly on the sound file.\n"
2684 // "Undo is supported, but NOT after exit, WITH OR WITHOUT A SAVE!"), tr("&Ok"), tr("&Cancel"),
2685 // QString::null, 0, 1 ) != 0)
2686 // return;
2687 //}
2688 modifySelection(modifyoperation, selectionStart, selectionStop, paramA);
2689 }
2690
2691 itemSelectionsChanged();
2692 redraw();
2693 }
2694
2695 //---------------------------------------------------------
2696 // getSelection
2697 //---------------------------------------------------------
getSelection(unsigned startpos,unsigned stoppos)2698 MusECore::WaveSelectionList WaveCanvas::getSelection(unsigned startpos, unsigned stoppos)
2699 {
2700 MusECore::WaveSelectionList selection;
2701
2702 for (MusECore::iPart ip = editor->parts()->begin(); ip != editor->parts()->end(); ++ip) {
2703 MusECore::WavePart* wp = (MusECore::WavePart*)(ip->second);
2704 unsigned part_offset = wp->frame();
2705
2706 const MusECore::EventList& el = wp->events();
2707
2708 for (MusECore::ciEvent e = el.begin(); e != el.end(); ++e) {
2709 MusECore::Event event = e->second;
2710 if (event.empty())
2711 continue;
2712 MusECore::SndFileR file = event.sndFile();
2713 if (file.isNull())
2714 continue;
2715
2716 // Respect part end: Don't modify stuff outside of part boundary.
2717 unsigned elen = event.lenFrame();
2718 if(event.frame() + event.lenFrame() >= wp->lenFrame())
2719 {
2720 // Adjust apparent operation length:
2721 if(event.frame() > wp->lenFrame())
2722 elen = 0;
2723 else
2724 elen = wp->lenFrame() - event.frame();
2725 }
2726
2727 unsigned event_offset = event.frame() + part_offset;
2728 unsigned event_startpos = event.spos();
2729 unsigned event_length = elen + event.spos();
2730 unsigned event_end = event_offset + event_length;
2731 //printf("startpos=%d stoppos=%d part_offset=%d event_offset=%d event_startpos=%d event_length=%d event_end=%d\n",
2732 // startpos, stoppos, part_offset, event_offset, event_startpos, event_length, event_end);
2733
2734 if (!(event_end <= startpos || event_offset > stoppos)) {
2735 int tmp_sx = startpos - event_offset + event_startpos;
2736 int tmp_ex = stoppos - event_offset + event_startpos;
2737 unsigned sx;
2738 unsigned ex;
2739
2740 tmp_sx < (int)event_startpos ? sx = event_startpos : sx = tmp_sx;
2741 tmp_ex > (int)event_length ? ex = event_length : ex = tmp_ex;
2742
2743 //printf("Event data affected: %d->%d filename:%s\n", sx, ex, file.name().toLatin1().constData());
2744 MusECore::WaveEventSelection s;
2745 s.event = event;
2746 s.startframe = sx;
2747 s.endframe = ex+1;
2748 //printf("sx=%d ex=%d\n",sx,ex);
2749 selection.push_back(s);
2750 }
2751 }
2752 }
2753
2754 return selection;
2755 }
2756
2757 //---------------------------------------------------------
2758 // modifySelection
2759 //---------------------------------------------------------
modifySelection(int operation,unsigned startpos,unsigned stoppos,double paramA)2760 void WaveCanvas::modifySelection(int operation, unsigned startpos, unsigned stoppos, double paramA)
2761 {
2762 if (operation == PASTE) {
2763 // we need to redefine startpos and stoppos
2764 if (copiedPart =="")
2765 return;
2766 MusECore::SndFile pasteFile(copiedPart);
2767 pasteFile.openRead();
2768 startpos = pos[0];
2769 stoppos = startpos+ pasteFile.samples(); // possibly this is wrong if there are tempo changes
2770 pasteFile.close();
2771 pos[0]=stoppos;
2772 }
2773
2774 //
2775 // Copy on Write: Check if some files need to be copied, either because they are not
2776 // writable, or more than one independent (non-clone) wave event shares a wave file.
2777 //
2778
2779 MusECore::WaveSelectionList selection = getSelection(startpos, stoppos);
2780 std::vector<MusECore::SndFileR> copy_files_proj_dir;
2781 for(MusECore::iWaveSelection i = selection.begin(); i != selection.end(); i++)
2782 {
2783 MusECore::WaveEventSelection w = *i;
2784 if(w.event.empty())
2785 continue;
2786 MusECore::SndFileR file = w.event.sndFile();
2787 if(file.isNull())
2788 continue;
2789 if(sndFileCheckCopyOnWrite(file))
2790 {
2791 std::vector<MusECore::SndFileR>::iterator i = copy_files_proj_dir.begin();
2792 for( ; i != copy_files_proj_dir.end(); ++i)
2793 {
2794 if(i->canonicalPath() == file.canonicalPath())
2795 break;
2796 }
2797 if(i == copy_files_proj_dir.end())
2798 copy_files_proj_dir.push_back(file);
2799 }
2800 }
2801 if(!copy_files_proj_dir.empty())
2802 {
2803 CopyOnWriteDialog* dlg = new CopyOnWriteDialog();
2804 for(std::vector<MusECore::SndFileR>::iterator i = copy_files_proj_dir.begin(); i != copy_files_proj_dir.end(); ++i)
2805 {
2806 qint64 sz = QFile(i->canonicalPath()).size();
2807 QString s;
2808 if(sz > 1048576)
2809 s += QString::number(sz / 1048576) + "MB ";
2810 else
2811 if(sz > 1024)
2812 s += QString::number(sz / 1024) + "KB ";
2813 else
2814 s += QString::number(sz) + "B ";
2815 s += i->canonicalPath();
2816 dlg->addProjDirFile(s);
2817 }
2818 int rv = dlg->exec();
2819 delete dlg;
2820 if(rv != QDialog::Accepted)
2821 return;
2822 // Has a project been created yet?
2823 if(MusEGlobal::museProject == MusEGlobal::museProjectInitPath) // && MusEGlobal::config.useProjectSaveDialog
2824 {
2825 // No project, we need to create one.
2826 if(!MusEGlobal::muse->saveAs())
2827 return; // No project, don't want to copy without one.
2828 //setFocus(); // For some reason focus is given away to Arranger
2829 }
2830 for(MusECore::iWaveSelection i = selection.begin(); i != selection.end(); i++)
2831 {
2832 MusECore::WaveEventSelection w = *i;
2833 if(w.event.empty())
2834 continue;
2835 MusECore::SndFileR file = w.event.sndFile();
2836 if(file.isNull() || !sndFileCheckCopyOnWrite(file)) // Make sure to re-check
2837 continue;
2838 QString filePath = MusEGlobal::museProject + QString("/") + file.name();
2839 QString newFilePath;
2840 if(MusECore::getUniqueFileName(filePath, newFilePath))
2841 {
2842 {
2843 QFile qf(file.canonicalPath());
2844 if(!qf.copy(newFilePath)) // Copy the file
2845 {
2846 printf("MusE Error: Could not copy to new sound file (file exists?): %s\n", newFilePath.toLatin1().constData());
2847 continue; // Let's not overwrite an existing file
2848 }
2849 }
2850 QFile nqf(newFilePath);
2851 // Need to make sure some permissions are set...
2852 QFile::Permissions pm = nqf.permissions();
2853 if(!(pm & QFile::ReadOwner))
2854 {
2855 pm |= QFile::ReadOwner;
2856 if(!nqf.setPermissions(pm))
2857 {
2858 printf("MusE Error: Could not set read owner permissions on new sound file: %s\n", newFilePath.toLatin1().constData());
2859 continue;
2860 }
2861 }
2862 if(!(pm & QFile::WriteOwner))
2863 {
2864 pm |= QFile::WriteOwner;
2865 if(!nqf.setPermissions(pm))
2866 {
2867 printf("MusE Error: Could not set write owner permissions on new sound file: %s\n", newFilePath.toLatin1().constData());
2868 continue;
2869 }
2870 }
2871 if(!(pm & QFile::ReadUser))
2872 {
2873 pm |= QFile::ReadUser;
2874 if(!nqf.setPermissions(pm))
2875 {
2876 printf("MusE Error: Could not set read user permissions on new sound file: %s\n", newFilePath.toLatin1().constData());
2877 continue;
2878 }
2879 }
2880 if(!(pm & QFile::WriteUser))
2881 {
2882 pm |= QFile::WriteUser;
2883 if(!nqf.setPermissions(pm))
2884 {
2885 printf("MusE Error: Could not set write user permissions on new sound file: %s\n", newFilePath.toLatin1().constData());
2886 continue;
2887 }
2888 }
2889 MusECore::SndFile* newSF = new MusECore::SndFile(newFilePath);
2890 MusECore::SndFileR newSFR(newSF); // Create a sndFileR for the new file
2891 if(newSFR.openRead())
2892 {
2893 printf("MusE Error: Could not open new sound file: %s\n", newSFR.canonicalPath().toLatin1().constData());
2894 continue; // newSF will be deleted when newSFR goes out of scope and is deleted
2895 }
2896 MusEGlobal::audio->msgIdle(true);
2897 w.event.sndFile().close(); // Close the old file.
2898 // NOTE: For now, don't bother providing undo for this. Reason: If the user undoes
2899 // and then modifies again, it will prompt to create new copies each time. There is
2900 // no mechanism ("touched"?) to tell if an existing copy would be suitable to just 'drop in'.
2901 // It would help if we deleted the wave file copies upon undo, but not too crazy about that.
2902 // So since the copy has already been created and "there it is", we might as well use it.
2903 // It means that events and even undo items BEFORE this operation will point to this
2904 // NEW wave file (as if they always did). It also means the user CANNOT change back
2905 // to the old file... Oh well, this IS Copy On Write.
2906 // FIXME: Find a conceptual way to make undo work with or without deleting the copies.
2907 w.event.setSndFile(newSFR); // Set the new file.
2908 MusEGlobal::audio->msgIdle(false);
2909 }
2910 }
2911 }
2912
2913 MusEGlobal::song->startUndo();
2914 for (MusECore::iWaveSelection i = selection.begin(); i != selection.end(); i++) {
2915 MusECore::WaveEventSelection w = *i;
2916 if(w.event.empty())
2917 continue;
2918 MusECore::SndFileR file = w.event.sndFile();
2919 if(file.isNull())
2920 continue;
2921 unsigned sx = w.startframe;
2922 unsigned ex = w.endframe;
2923 unsigned file_channels = file.channels();
2924
2925 QString tmpWavFile;
2926 if (!MusEGlobal::getUniqueTmpfileName("tmp_musewav",".wav", tmpWavFile)) {
2927 break;
2928 }
2929
2930 MusEGlobal::audio->msgIdle(true); // Not good with playback during operations
2931 MusECore::SndFile tmpFile(tmpWavFile);
2932 tmpFile.setFormat(file.format(), file_channels, file.samplerate());
2933 if (tmpFile.openWrite()) {
2934 MusEGlobal::audio->msgIdle(false);
2935 printf("Could not open temporary file...\n");
2936 break;
2937 }
2938
2939 //
2940 // Write out data that will be changed to temp file
2941 //
2942 unsigned tmpdatalen = ex - sx;
2943 off_t tmpdataoffset = sx;
2944 float* tmpdata[file_channels];
2945
2946 for (unsigned i=0; i<file_channels; i++) {
2947 tmpdata[i] = new float[tmpdatalen];
2948 }
2949 file.seek(tmpdataoffset, 0);
2950 file.readWithHeap(file_channels, tmpdata, tmpdatalen);
2951 file.close();
2952 tmpFile.write(file_channels, tmpdata, tmpdatalen, MusEGlobal::config.liveWaveUpdate);
2953 tmpFile.close();
2954
2955 switch(operation)
2956 {
2957 case MUTE:
2958 muteSelection(file_channels, tmpdata, tmpdatalen);
2959 break;
2960
2961 case NORMALIZE:
2962 normalizeSelection(file_channels, tmpdata, tmpdatalen);
2963 break;
2964
2965 case FADE_IN:
2966 fadeInSelection(file_channels, tmpdata, tmpdatalen);
2967 break;
2968
2969 case FADE_OUT:
2970 fadeOutSelection(file_channels, tmpdata, tmpdatalen);
2971 break;
2972
2973 case REVERSE:
2974 reverseSelection(file_channels, tmpdata, tmpdatalen);
2975 break;
2976
2977 case GAIN:
2978 applyGain(file_channels, tmpdata, tmpdatalen, paramA);
2979 break;
2980 case CUT:
2981 copySelection(file_channels, tmpdata, tmpdatalen, true, file.format(), file.samplerate());
2982 break;
2983 case COPY:
2984 copySelection(file_channels, tmpdata, tmpdatalen, false, file.format(), file.samplerate());
2985 break;
2986 case PASTE:
2987 {
2988 MusECore::SndFile pasteFile(copiedPart);
2989 pasteFile.openRead();
2990 pasteFile.seek(tmpdataoffset, 0);
2991 pasteFile.readWithHeap(file_channels, tmpdata, tmpdatalen);
2992 }
2993 break;
2994
2995 case EDIT_EXTERNAL:
2996 editExternal(file.format(), file.samplerate(), file_channels, tmpdata, tmpdatalen);
2997 break;
2998
2999 default:
3000 printf("Error: Default state reached in modifySelection\n");
3001 break;
3002
3003 }
3004
3005 file.openWrite();
3006 file.seek(tmpdataoffset, 0);
3007 file.write(file_channels, tmpdata, tmpdatalen, MusEGlobal::config.liveWaveUpdate);
3008 file.update();
3009 file.close();
3010 file.openRead();
3011
3012 for (unsigned i=0; i<file_channels; i++) {
3013 delete[] tmpdata[i];
3014 }
3015
3016 // Undo handling
3017 MusEGlobal::song->cmdChangeWave(w.event, tmpWavFile, sx, ex);
3018 MusEGlobal::audio->msgIdle(false); // Not good with playback during operations
3019 }
3020 MusEGlobal::song->endUndo(SC_CLIP_MODIFIED);
3021 redraw();
3022 }
3023
3024 //---------------------------------------------------------
3025 // copySelection
3026 //---------------------------------------------------------
copySelection(unsigned file_channels,float ** tmpdata,unsigned length,bool blankData,unsigned format,unsigned sampleRate)3027 void WaveCanvas::copySelection(unsigned file_channels, float** tmpdata, unsigned length, bool blankData, unsigned format, unsigned sampleRate)
3028 {
3029 if (copiedPart!="") {
3030 QFile::remove(copiedPart);
3031 }
3032 if (!MusEGlobal::getUniqueTmpfileName("tmp_musewav",".wav", copiedPart)) {
3033 return;
3034 }
3035
3036 MusECore::SndFile tmpFile(copiedPart);
3037 tmpFile.setFormat(format, file_channels, sampleRate);
3038 tmpFile.openWrite();
3039 tmpFile.write(file_channels, tmpdata, length, MusEGlobal::config.liveWaveUpdate);
3040 tmpFile.close();
3041
3042 if (blankData) {
3043 // Set everything to 0!
3044 for (unsigned i=0; i<file_channels; i++) {
3045 for (unsigned j=0; j<length; j++) {
3046 tmpdata[i][j] = 0;
3047 }
3048 }
3049 }
3050 }
3051
3052 //---------------------------------------------------------
3053 // muteSelection
3054 //---------------------------------------------------------
muteSelection(unsigned channels,float ** data,unsigned length)3055 void WaveCanvas::muteSelection(unsigned channels, float** data, unsigned length)
3056 {
3057 // Set everything to 0!
3058 for (unsigned i=0; i<channels; i++) {
3059 for (unsigned j=0; j<length; j++) {
3060 data[i][j] = 0;
3061 }
3062 }
3063 }
3064
3065 //---------------------------------------------------------
3066 // normalizeSelection
3067 //---------------------------------------------------------
normalizeSelection(unsigned channels,float ** data,unsigned length)3068 void WaveCanvas::normalizeSelection(unsigned channels, float** data, unsigned length)
3069 {
3070 float loudest = 0.0;
3071
3072 for (unsigned i=0; i<channels; i++) {
3073 for (unsigned j=0; j<length; j++) {
3074 if (data[i][j] > loudest)
3075 loudest = data[i][j];
3076 }
3077 }
3078
3079 double scale = 0.99 / (double)loudest;
3080
3081 for (unsigned i=0; i<channels; i++) {
3082 for (unsigned j=0; j<length; j++) {
3083 data[i][j] = (float) ((double)data[i][j] * scale);
3084 }
3085 }
3086 }
3087
3088 //---------------------------------------------------------
3089 // fadeInSelection
3090 //---------------------------------------------------------
fadeInSelection(unsigned channels,float ** data,unsigned length)3091 void WaveCanvas::fadeInSelection(unsigned channels, float** data, unsigned length)
3092 {
3093 for (unsigned i=0; i<channels; i++) {
3094 for (unsigned j=0; j<length; j++) {
3095 double scale = (double) j / (double)length ;
3096 data[i][j] = (float) ((double)data[i][j] * scale);
3097 }
3098 }
3099 }
3100
3101 //---------------------------------------------------------
3102 // fadeOutSelection
3103 //---------------------------------------------------------
fadeOutSelection(unsigned channels,float ** data,unsigned length)3104 void WaveCanvas::fadeOutSelection(unsigned channels, float** data, unsigned length)
3105 {
3106 for (unsigned i=0; i<channels; i++) {
3107 for (unsigned j=0; j<length; j++) {
3108 double scale = (double) (length - j) / (double)length ;
3109 data[i][j] = (float) ((double)data[i][j] * scale);
3110 }
3111 }
3112 }
3113
3114 //---------------------------------------------------------
3115 // reverseSelection
3116 //---------------------------------------------------------
reverseSelection(unsigned channels,float ** data,unsigned length)3117 void WaveCanvas::reverseSelection(unsigned channels, float** data, unsigned length)
3118 {
3119 if(length <= 1)
3120 return;
3121 for (unsigned i=0; i<channels; i++) {
3122 for (unsigned j=0; j<length/2; j++) {
3123 float tmpl = data[i][j];
3124 float tmpr = data[i][length - j - 1];
3125 data[i][j] = tmpr;
3126 data[i][length - j - 1] = tmpl;
3127 }
3128 }
3129 }
3130 //---------------------------------------------------------
3131 // applyGain
3132 //---------------------------------------------------------
applyGain(unsigned channels,float ** data,unsigned length,double gain)3133 void WaveCanvas::applyGain(unsigned channels, float** data, unsigned length, double gain)
3134 {
3135 for (unsigned i=0; i<channels; i++) {
3136 for (unsigned j=0; j<length; j++) {
3137 data[i][j] = (float) ((double)data[i][j] * gain);
3138 }
3139 }
3140 }
3141
3142 //---------------------------------------------------------
3143 // editExternal
3144 //---------------------------------------------------------
editExternal(unsigned file_format,unsigned file_samplerate,unsigned file_channels,float ** tmpdata,unsigned tmpdatalen)3145 void WaveCanvas::editExternal(unsigned file_format, unsigned file_samplerate, unsigned file_channels, float** tmpdata, unsigned tmpdatalen)
3146 {
3147 // Create yet another tmp-file
3148 QString exttmpFileName;
3149 if (!MusEGlobal::getUniqueTmpfileName("tmp_musewav",".wav", exttmpFileName)) {
3150 printf("Could not create temp file - aborting...\n");
3151 return;
3152 }
3153
3154 MusECore::SndFile exttmpFile(exttmpFileName);
3155 exttmpFile.setFormat(file_format, file_channels, file_samplerate);
3156 if (exttmpFile.openWrite()) {
3157 printf("Could not open temporary file...\n");
3158 return;
3159 }
3160 // Write out change-data to this file:
3161 exttmpFile.write(file_channels, tmpdata, tmpdatalen, MusEGlobal::config.liveWaveUpdate);
3162 exttmpFile.close();
3163
3164 printf("Temporary file: %s\n", qPrintable(exttmpFileName));
3165
3166 QProcess proc;
3167 QStringList arguments;
3168 arguments << exttmpFileName;
3169 proc.start(MusEGlobal::config.externalWavEditor, arguments);
3170
3171 // Wait forever. This freezes MusE until returned.
3172 // FIXME TODO: Try to make something that does it asynchronously while MusE still runs?
3173 // Hm, that'd be quite hard... (More like inter-app 'live collaboration' support).
3174 if(!proc.waitForFinished(-1))
3175 {
3176 QMessageBox::warning(this, tr("MusE - external editor failed"),
3177 tr("MusE was unable to launch the external editor\ncheck if the editor setting in:\n"
3178 "Global Settings->Audio:External Waveditor\nis set to a valid editor."));
3179 }
3180
3181 if(proc.exitStatus() != QProcess::NormalExit)
3182 {
3183 std::fprintf(stderr, "\nError: Launch external wave editor: Exit status: %d File: %s\n",
3184 proc.exitStatus(), MusEGlobal::config.externalWavEditor.toLatin1().constData());
3185 }
3186
3187 if(proc.exitCode() != 0)
3188 {
3189 std::fprintf(stderr, "\nError: Launch external wave editor: Exit code: %d File: %s\n",
3190 proc.exitCode(), MusEGlobal::config.externalWavEditor.toLatin1().constData());
3191 }
3192
3193 if (exttmpFile.openRead()) {
3194 printf("Could not reopen temporary file!\n");
3195 }
3196 else {
3197 // Re-read file again
3198 exttmpFile.seek(0, 0);
3199 size_t sz = exttmpFile.readWithHeap(file_channels, tmpdata, tmpdatalen);
3200 if (sz != tmpdatalen) {
3201 // File must have been shrunken - not good. Alert user.
3202 QMessageBox::critical(this, tr("MusE - file size changed"),
3203 tr("When editing in external editor - you should not change the filesize\nsince it must fit the selected region.\n\nMissing data is muted"));
3204 for (unsigned i=0; i<file_channels; i++) {
3205 for (unsigned j=sz; j<tmpdatalen; j++) {
3206 tmpdata[i][j] = 0;
3207 }
3208 }
3209 }
3210 }
3211 QDir dir = exttmpFile.dirPath();
3212 dir.remove(exttmpFileName);
3213 dir.remove(exttmpFile.basename() + ".wca");
3214 }
3215
3216
3217
3218 //---------------------------------------------------------
3219 // startDrag
3220 //---------------------------------------------------------
3221
startDrag(CItem *,DragType t)3222 void WaveCanvas::startDrag(CItem* /* item*/, DragType t)
3223 {
3224 QMimeData* md = MusECore::selected_events_to_mime(MusECore::partlist_to_set(editor->parts()), 1);
3225
3226 if (md) {
3227 // "Note that setMimeData() assigns ownership of the QMimeData object to the QDrag object.
3228 // The QDrag must be constructed on the heap with a parent QWidget to ensure that Qt can
3229 // clean up after the drag and drop operation has been completed. "
3230 QDrag* drag = new QDrag(this);
3231 drag->setMimeData(md);
3232
3233 if (t == MOVE_COPY || t == MOVE_CLONE)
3234 drag->exec(Qt::CopyAction);
3235 else
3236 drag->exec(Qt::MoveAction);
3237 }
3238 }
3239
3240 //---------------------------------------------------------
3241 // dragEnterEvent
3242 //---------------------------------------------------------
3243
dragEnterEvent(QDragEnterEvent * event)3244 void WaveCanvas::dragEnterEvent(QDragEnterEvent* event)
3245 {
3246 //event->accept(Q3TextDrag::canDecode(event));
3247 event->acceptProposedAction(); // TODO CHECK Tim.
3248 }
3249
3250 //---------------------------------------------------------
3251 // dragMoveEvent
3252 //---------------------------------------------------------
3253
dragMoveEvent(QDragMoveEvent *)3254 void WaveCanvas::dragMoveEvent(QDragMoveEvent*)
3255 {
3256 //printf("drag move %x\n", this); DELETETHIS (whole function?)
3257 //event->acceptProposedAction();
3258 }
3259
3260 //---------------------------------------------------------
3261 // dragLeaveEvent
3262 //---------------------------------------------------------
3263
dragLeaveEvent(QDragLeaveEvent *)3264 void WaveCanvas::dragLeaveEvent(QDragLeaveEvent*)
3265 {
3266 //printf("drag leave\n"); DELETETHIS (whole function?)
3267 //event->acceptProposedAction();
3268 }
3269
3270 //---------------------------------------------------------
3271 // curPartChanged
3272 //---------------------------------------------------------
3273
curPartChanged()3274 void WaveCanvas::curPartChanged()
3275 {
3276 EventCanvas::curPartChanged();
3277 editor->setWindowTitle(getCaption());
3278 }
3279
3280 //---------------------------------------------------------
3281 // modifySelected
3282 //---------------------------------------------------------
3283
modifySelected(NoteInfo::ValType type,int val,bool delta_mode)3284 void WaveCanvas::modifySelected(NoteInfo::ValType type, int val, bool delta_mode)
3285 {
3286 // TODO: New WaveCanvas: Convert this routine to frames and remove unneeded operations.
3287 QList< QPair<int,MusECore::Event> > already_done;
3288 MusECore::Undo operations;
3289 for (iCItem i = items.begin(); i != items.end(); ++i) {
3290 if (!(i->second->isSelected()))
3291 continue;
3292 WEvent* e = (WEvent*)(i->second);
3293 MusECore::Event event = e->event();
3294 if (event.type() != MusECore::Note)
3295 continue;
3296
3297 MusECore::WavePart* part = (MusECore::WavePart*)(e->part());
3298
3299 if (already_done.contains(QPair<int,MusECore::Event>(part->clonemaster_sn(), event)))
3300 continue;
3301
3302 MusECore::Event newEvent = event.clone();
3303
3304 switch (type) {
3305 case NoteInfo::VAL_TIME:
3306 {
3307 int newTime = val;
3308 if(delta_mode)
3309 newTime += event.tick();
3310 else
3311 newTime -= part->tick();
3312 if (newTime < 0)
3313 newTime = 0;
3314 newEvent.setTick(newTime);
3315 }
3316 break;
3317 case NoteInfo::VAL_LEN:
3318 {
3319 int len = val;
3320 if(delta_mode)
3321 len += event.lenTick();
3322 if (len < 1)
3323 len = 1;
3324 newEvent.setLenTick(len);
3325 }
3326 break;
3327 case NoteInfo::VAL_VELON:
3328 {
3329 int velo = val;
3330 if(delta_mode)
3331 velo += event.velo();
3332 if (velo > 127)
3333 velo = 127;
3334 else if (velo < 0)
3335 // REMOVE Tim. Noteoff. Changed. Zero note on vel is not allowed now.
3336 // velo = 0;
3337 velo = 1;
3338 newEvent.setVelo(velo);
3339 }
3340 break;
3341 case NoteInfo::VAL_VELOFF:
3342 {
3343 int velo = val;
3344 if(delta_mode)
3345 velo += event.veloOff();
3346 if (velo > 127)
3347 velo = 127;
3348 else if (velo < 0)
3349 velo = 0;
3350 newEvent.setVeloOff(velo);
3351 }
3352 break;
3353 case NoteInfo::VAL_PITCH:
3354 {
3355 int pitch = val;
3356 if(delta_mode)
3357 pitch += event.pitch();
3358 if (pitch > 127)
3359 pitch = 127;
3360 else if (pitch < 0)
3361 pitch = 0;
3362 newEvent.setPitch(pitch);
3363 }
3364 break;
3365 }
3366
3367 operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, newEvent, event, part, false, false));
3368
3369 already_done.append(QPair<int,MusECore::Event>(part->clonemaster_sn(), event));
3370 }
3371 MusEGlobal::song->applyOperationGroup(operations);
3372 }
3373
3374 //---------------------------------------------------------
3375 // resizeEvent
3376 //---------------------------------------------------------
3377
resizeEvent(QResizeEvent * ev)3378 void WaveCanvas::resizeEvent(QResizeEvent* ev)
3379 {
3380 // Readjust all wave canvas item heights
3381 bool do_redraw = false;
3382 for (iCItem k = items.begin(); k != items.end(); ++k)
3383 {
3384 if(k->second->height() != ev->size().height())
3385 {
3386 k->second->setHeight(ev->size().height());
3387 do_redraw = true;
3388 }
3389 }
3390
3391 if (ev->size().width() != ev->oldSize().width())
3392 emit newWidth(ev->size().width());
3393 EventCanvas::resizeEvent(ev);
3394
3395 if(do_redraw)
3396 redraw();
3397 }
3398
3399 //---------------------------------------------------------
3400 // genItemPopup
3401 //---------------------------------------------------------
3402
genItemPopup(CItem * item)3403 QMenu* WaveCanvas::genItemPopup(CItem* item)
3404 {
3405 QMenu* eventPopup = new QMenu(this);
3406
3407 eventPopup->addAction(new MenuTitleItem(tr("Wave event:"), eventPopup));
3408
3409 eventPopup->addSeparator();
3410 QAction *act_settings = eventPopup->addAction(tr("Converter settings"));
3411 act_settings->setData(0);
3412 act_settings->setEnabled(item && !item->event().sndFile().isNull());
3413
3414 genCanvasPopup(eventPopup);
3415 return eventPopup;
3416 }
3417
3418 //---------------------------------------------------------
3419 // itemPopup
3420 //---------------------------------------------------------
3421
itemPopup(CItem *,int n,const QPoint &)3422 void WaveCanvas::itemPopup(CItem* /*item*/, int n, const QPoint& /*pt*/)
3423 {
3424 if(n >= TOOLS_ID_BASE)
3425 {
3426 canvasPopup(n);
3427 return;
3428 }
3429
3430 switch(n)
3431 {
3432 case 0: // Settings
3433 if(curItem && !curItem->event().sndFile().isNull())
3434 {
3435 if(MusECore::AudioConverterSettingsGroup* settings = curItem->event().sndFile().audioConverterSettings())
3436 {
3437 MusECore::AudioConverterSettingsGroup* wrk_set = new MusECore::AudioConverterSettingsGroup(true); // Local settings.
3438 wrk_set->assign(*settings);
3439 AudioConverterSettingsDialog dialog(this,
3440 &MusEGlobal::audioConverterPluginList,
3441 wrk_set,
3442 true); // Local settings.
3443 if(dialog.exec() == QDialog::Accepted)
3444 {
3445 MusECore::PendingOperationList operations;
3446 MusEGlobal::song->modifyAudioConverterSettingsOperation(
3447 curItem->event().sndFile(),
3448 wrk_set,
3449 MusEGlobal::defaultAudioConverterSettings,
3450 true, // Local settings.
3451 operations);
3452
3453 if(operations.empty())
3454 {
3455 delete wrk_set;
3456 }
3457 else
3458 {
3459 MusEGlobal::audio->msgExecutePendingOperations(operations, true);
3460 //MusEGlobal::song->update(SC_);
3461 }
3462 }
3463 else
3464 {
3465 delete wrk_set;
3466 }
3467
3468 }
3469 }
3470 break;
3471
3472 default:
3473 printf("unknown action %d\n", n);
3474 break;
3475 }
3476 }
3477
3478
setRangeToSelection()3479 void WaveCanvas::setRangeToSelection() {
3480
3481 if (selectionStop > selectionStart) {
3482 int tick_min = MusEGlobal::tempomap.frame2tick(selectionStart, 0, MusECore::LargeIntRoundNearest);
3483 int tick_max = MusEGlobal::tempomap.frame2tick(selectionStop, 0, MusECore::LargeIntRoundNearest);
3484 MusECore::Pos p1(tick_min, true);
3485 MusECore::Pos p2(tick_max, true);
3486
3487 if (p1 < MusEGlobal::song->lPos()) {
3488 MusEGlobal::song->setPos(MusECore::Song::LPOS, p1);
3489 MusEGlobal::song->setPos(MusECore::Song::RPOS, p2);
3490 } else {
3491 MusEGlobal::song->setPos(MusECore::Song::RPOS, p2);
3492 MusEGlobal::song->setPos(MusECore::Song::LPOS, p1);
3493 }
3494 }
3495 }
3496
3497
3498 } // namespace MusEGui
3499
3500