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