diff options
author | Tim E. Real <termtech@rogers.com> | 2012-08-15 00:24:29 +0000 |
---|---|---|
committer | Tim E. Real <termtech@rogers.com> | 2012-08-15 00:24:29 +0000 |
commit | 6c642a1cff928c183d80af7ab4fad6910466a091 (patch) | |
tree | ab534d2203486d681398b7197635861ae65160c1 /muse2 | |
parent | 345fb0cc41b94b08134dc1f40020b4bf26e1d46b (diff) |
Introducing: Improved Wave Editor. Class WaveView is replaced by WaveCanvas.
Wave Events can be selected, added, moved and deleted.
TODO: Bunch o' stuff, but currently it should be as good or better than the old editor.
Diffstat (limited to 'muse2')
-rw-r--r-- | muse2/ChangeLog | 5 | ||||
-rw-r--r-- | muse2/muse/functions.cpp | 12 | ||||
-rw-r--r-- | muse2/muse/midiedit/ecanvas.h | 16 | ||||
-rw-r--r-- | muse2/muse/midieditor.cpp | 22 | ||||
-rw-r--r-- | muse2/muse/midieditor.h | 5 | ||||
-rw-r--r-- | muse2/muse/waveedit/CMakeLists.txt | 4 | ||||
-rw-r--r-- | muse2/muse/waveedit/wavecanvas.cpp | 2422 | ||||
-rw-r--r-- | muse2/muse/waveedit/wavecanvas.h | 185 | ||||
-rw-r--r-- | muse2/muse/waveedit/waveedit.cpp | 401 | ||||
-rw-r--r-- | muse2/muse/waveedit/waveedit.h | 27 | ||||
-rw-r--r-- | muse2/muse/widgets/canvas.cpp | 2 | ||||
-rw-r--r-- | muse2/muse/widgets/canvas.h | 2 | ||||
-rw-r--r-- | muse2/muse/widgets/poslabel.cpp | 24 |
13 files changed, 3032 insertions, 95 deletions
diff --git a/muse2/ChangeLog b/muse2/ChangeLog index 8cd17adf..0c3883f7 100644 --- a/muse2/ChangeLog +++ b/muse2/ChangeLog @@ -1,3 +1,8 @@ +14.08.2012: + * Introducing: Improved Wave Editor. (Tim) + Class WaveView is replaced by WaveCanvas. + Wave Events can be selected, added, moved and deleted. + TODO: Bunch o' stuff, but currently it should be as good or better than the old editor. 04.08.2012: * Introducing: More bits for Song Changed SC_* flags. (Tim) Added MusECore::SongChangedFlags_t (in new type_defs.h file). Declared as int64 type. diff --git a/muse2/muse/functions.cpp b/muse2/muse/functions.cpp index 1a6927e8..da5826e5 100644 --- a/muse2/muse/functions.cpp +++ b/muse2/muse/functions.cpp @@ -1286,12 +1286,22 @@ void schedule_resize_all_same_len_clone_parts(Part* part, unsigned new_len, Undo if (op_it->type==UndoOp::ModifyPart || op_it->type==UndoOp::DeletePart) already_done.insert(op_it->nPart); - unsigned old_len=part->lenTick(); + unsigned old_len= part->type() == Pos::FRAMES ? part->lenFrame() : part->lenTick(); if (old_len!=new_len) { Part* part_it=part; do { + if (part->type() == Pos::FRAMES) + { + if (part_it->lenFrame()==old_len && !already_done.contains(part_it)) + { + WavePart* new_part = new WavePart(*(WavePart*)part_it); + new_part->setLenFrame(new_len); + operations.push_back(UndoOp(UndoOp::ModifyPart, part_it, new_part, true, false)); + } + } + else if (part_it->lenTick()==old_len && !already_done.contains(part_it)) { MidiPart* new_part = new MidiPart(*(MidiPart*)part_it); diff --git a/muse2/muse/midiedit/ecanvas.h b/muse2/muse/midiedit/ecanvas.h index 7847e88a..400072dc 100644 --- a/muse2/muse/midiedit/ecanvas.h +++ b/muse2/muse/midiedit/ecanvas.h @@ -87,22 +87,22 @@ class EventCanvas : public Canvas { signals: void pitchChanged(int); // current cursor position void timeChanged(unsigned); - void selectionChanged(int /*tick*/ , MusECore::Event&, MusECore::Part*, bool /*update*/); + void selectionChanged(int /*tick or frame*/ , MusECore::Event&, MusECore::Part*, bool /*update*/); void enterCanvas(); public: EventCanvas(MidiEditor*, QWidget*, int, int, const char* name = 0); MusECore::MidiTrack* track() const; - unsigned start() const { return start_tick; } - unsigned end() const { return end_tick; } + virtual unsigned start() const { return start_tick; } + virtual unsigned end() const { return end_tick; } bool midiin() const { return _midiin; } bool steprec() const { return _steprec; } - QString getCaption() const; - void songChanged(MusECore::SongChangedFlags_t); - void range(int* s, int* e) const { *s = start_tick; *e = end_tick; } + virtual QString getCaption() const; + virtual void songChanged(MusECore::SongChangedFlags_t); + virtual void range(int* s, int* e) const { *s = start_tick; *e = end_tick; } void playEvents(bool flag) { _playEvents = flag; } - void selectAtTick(unsigned int tick); - void viewDropEvent(QDropEvent* event); + virtual void selectAtTick(unsigned int tick); + virtual void viewDropEvent(QDropEvent* event); virtual void modifySelected(NoteInfo::ValType, int /*val*/, bool /*delta_mode*/ = true) {} virtual void keyPress(QKeyEvent*); }; diff --git a/muse2/muse/midieditor.cpp b/muse2/muse/midieditor.cpp index b4830c21..c00a7049 100644 --- a/muse2/muse/midieditor.cpp +++ b/muse2/muse/midieditor.cpp @@ -50,7 +50,7 @@ MidiEditor::MidiEditor(ToplevelType t, int r, MusECore::PartList* pl, _parts.insert(i->second->sn()); _raster = r; canvas = 0; - wview = 0; + //wview = 0; _curDrumInstrument = -1; mainw = new QWidget(this); @@ -194,8 +194,8 @@ void MidiEditor::songChanged(MusECore::SongChangedFlags_t type) } if (canvas) canvas->songChanged(type); - else if (wview) - wview->songChanged(type); + //else if (wview) + // wview->songChanged(type); if (type & (SC_PART_REMOVED | SC_PART_MODIFIED | SC_PART_INSERTED | SC_TRACK_REMOVED)) { @@ -204,8 +204,8 @@ void MidiEditor::songChanged(MusECore::SongChangedFlags_t type) if (canvas) setWindowTitle(canvas->getCaption()); - else if (wview) - setWindowTitle(wview->getCaption()); + //else if (wview) + // setWindowTitle(wview->getCaption()); if (type & SC_SIG) time->update(); @@ -236,18 +236,6 @@ MusECore::Part* MidiEditor::curCanvasPart() } //--------------------------------------------------------- -// curWavePart -//--------------------------------------------------------- - -MusECore::WavePart* MidiEditor::curWavePart() -{ - if(wview) - return wview->part(); - else - return 0; -} - -//--------------------------------------------------------- // setCurCanvasPart //--------------------------------------------------------- diff --git a/muse2/muse/midieditor.h b/muse2/muse/midieditor.h index fd6d255b..541e4e0c 100644 --- a/muse2/muse/midieditor.h +++ b/muse2/muse/midieditor.h @@ -46,7 +46,7 @@ class CtrlEdit; class EventCanvas; class MTScale; class ScrollScale; -class WaveView; +//class WaveView; //--------------------------------------------------------- // MidiEditor @@ -64,7 +64,7 @@ class MidiEditor : public TopWin { MusEGui::ScrollScale* vscroll; MusEGui::MTScale* time; EventCanvas* canvas; - WaveView* wview; + //WaveView* wview; std::list<CtrlEdit*> ctrlEditList; int _raster; @@ -102,7 +102,6 @@ class MidiEditor : public TopWin { MusECore::PartList* parts() { return _pl; } int curDrumInstrument() const { return _curDrumInstrument; } MusECore::Part* curCanvasPart(); - MusECore::WavePart* curWavePart(); void setCurCanvasPart(MusECore::Part*); void addPart(MusECore::Part*); }; diff --git a/muse2/muse/waveedit/CMakeLists.txt b/muse2/muse/waveedit/CMakeLists.txt index f32eb6e1..920c7068 100644 --- a/muse2/muse/waveedit/CMakeLists.txt +++ b/muse2/muse/waveedit/CMakeLists.txt @@ -27,7 +27,7 @@ QT4_WRAP_CPP ( waveedit_mocs editgain.h waveedit.h - waveview.h + wavecanvas.h ) ## @@ -44,7 +44,7 @@ QT4_WRAP_UI (waveedit_ui_headers ${waveedit_ui_files} ) file (GLOB waveedit_source_files editgain.cpp waveedit.cpp - waveview.cpp + wavecanvas.cpp ) ## diff --git a/muse2/muse/waveedit/wavecanvas.cpp b/muse2/muse/waveedit/wavecanvas.cpp new file mode 100644 index 00000000..220c2804 --- /dev/null +++ b/muse2/muse/waveedit/wavecanvas.cpp @@ -0,0 +1,2422 @@ +//========================================================= +// MusE +// Linux Music Editor +// wavecanvas.cpp +// (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) +// +// Based on WaveView.cpp and PianoCanvas.cpp +// (C) Copyright 2000 Werner Schweer (ws@seh.de) +// and others. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +//========================================================= + + +#include <QApplication> +#include <QClipboard> +#include <QPainter> +#include <QDrag> +#include <QDragLeaveEvent> +#include <QDragEnterEvent> +#include <QDragMoveEvent> +#include <QDropEvent> +#include <QMouseEvent> +#include <QList> +#include <QPair> +#include <QMessageBox> +#include <QDir> + +#include <set> + +#include <limits.h> +#include <stdio.h> +#include <math.h> +#include <errno.h> +#include <sys/wait.h> + +#include "xml.h" +#include "wavecanvas.h" +#include "event.h" +#include "globals.h" +#include "cmd.h" +#include "song.h" +#include "audio.h" +#include "functions.h" +#include "gconfig.h" +#include "shortcuts.h" +#include "editgain.h" +#include "wave.h" +#include "waveedit.h" +#include "fastlog.h" +#include "utils.h" +#include "tools.h" + +namespace MusEGui { + +//--------------------------------------------------------- +// WEvent +//--------------------------------------------------------- + +WEvent::WEvent(MusECore::Event& e, MusECore::Part* p, int height) : MusEGui::CItem(e, p) + { + unsigned frame = e.frame() + p->frame(); + setPos(QPoint(frame, 0)); + unsigned len = e.lenFrame(); + if(e.frame() + e.lenFrame() >= p->lenFrame()) + len = p->lenFrame() - e.frame(); + setBBox(QRect(frame, 0, len, height)); + } + +//--------------------------------------------------------- +// addItem +//--------------------------------------------------------- + +CItem* WaveCanvas::addItem(MusECore::Part* part, MusECore::Event& event) + { + if (signed(event.frame())<0) { + printf("ERROR: trying to add event before current part!\n"); + return NULL; + } + + WEvent* ev = new WEvent(event, part, height()); + items.add(ev); + + int diff = event.frame()-part->lenFrame(); + if (diff > 0) {// too short part? extend it + part->setLenFrame(part->lenFrame()+diff); + } + + return ev; + } + +//--------------------------------------------------------- +// WaveCanvas +//--------------------------------------------------------- + +WaveCanvas::WaveCanvas(MidiEditor* pr, QWidget* parent, int sx, int sy) + : EventCanvas(pr, parent, sx, 1) + { + colorMode = 0; + button = 0; + + editor = pr; + setVirt(true); + + setBg(QColor()); + + pos[0] = MusEGlobal::tempomap.tick2frame(MusEGlobal::song->cpos()); + pos[1] = MusEGlobal::tempomap.tick2frame(MusEGlobal::song->lpos()); + pos[2] = MusEGlobal::tempomap.tick2frame(MusEGlobal::song->rpos()); + yScale = sy; + mode = NORMAL; + selectionStart = 0; + selectionStop = 0; + lastGainvalue = 100; + + songChanged(SC_TRACK_INSERTED); + } + +WaveCanvas::~WaveCanvas() +{ + //delete steprec; +} + +//--------------------------------------------------------- +// songChanged(type) +//--------------------------------------------------------- + +void WaveCanvas::songChanged(MusECore::SongChangedFlags_t flags) + { + // Is it simply a midi controller value adjustment? Forget it. + if(flags == SC_MIDI_CONTROLLER) + return; + + if (flags & ~SC_SELECTION) { + // TODO FIXME: don't we actually only want SC_PART_*, and maybe SC_TRACK_DELETED? + // (same in waveview.cpp) + bool curItemNeedsRestore=false; + MusECore::Event storedEvent; + int partSn; + if (curItem) + { + curItemNeedsRestore=true; + storedEvent=curItem->event(); + partSn=curItem->part()->sn(); + } + curItem=NULL; + + items.clearDelete(); + startSample = INT_MAX; + endSample = 0; + curPart = 0; + for (MusECore::iPart p = editor->parts()->begin(); p != editor->parts()->end(); ++p) { + MusECore::WavePart* part = (MusECore::WavePart*)(p->second); + if (part->sn() == curPartId) + curPart = part; + unsigned ssample = part->frame(); + unsigned len = part->lenFrame(); + unsigned esample = ssample + len; + if (ssample < startSample) + startSample = ssample; + if (esample > endSample) + endSample = esample; + + MusECore::EventList* el = part->events(); + for (MusECore::iEvent i = el->begin(); i != el->end(); ++i) { + MusECore::Event e = i->second; + // Do not add events which are past the end of the part. + if(e.frame() > len) + break; + + if (e.type() == MusECore::Wave) { + CItem* temp = addItem(part, e); + + if (temp && curItemNeedsRestore && e==storedEvent && part->sn()==partSn) + { + if (curItem!=NULL) + printf("THIS SHOULD NEVER HAPPEN: curItemNeedsRestore=true, event fits, but there was already a fitting event!?\n"); + + curItem=temp; + } + } + } + } + } + + MusECore::Event event; + MusECore::WavePart* part = 0; + int x = 0; + CItem* nevent = 0; + + int n = 0; // count selections + for (iCItem k = items.begin(); k != items.end(); ++k) { + MusECore::Event ev = k->second->event(); + bool selected = ev.selected(); + if (selected) { + k->second->setSelected(true); + ++n; + if (!nevent) { + nevent = k->second; + MusECore::Event mi = nevent->event(); + } + } + } + + if (flags & SC_CLIP_MODIFIED) { + redraw(); // Boring, but the only thing possible to do + } + if (flags & SC_TEMPO) { + setPos(0, MusEGlobal::song->cpos(), false); + setPos(1, MusEGlobal::song->lpos(), false); + setPos(2, MusEGlobal::song->rpos(), false); + } + + if (n >= 1) + { + x = nevent->x(); + event = nevent->event(); + part = (MusECore::WavePart*)nevent->part(); + if (_setCurPartIfOnlyOneEventIsSelected && n == 1 && curPart != part) { + curPart = part; + curPartId = curPart->sn(); + curPartChanged(); + } + } + + bool f1 = flags & (SC_EVENT_INSERTED | SC_EVENT_MODIFIED | SC_EVENT_REMOVED | + SC_PART_INSERTED | SC_PART_MODIFIED | SC_PART_REMOVED | + SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED | + SC_SIG | SC_TEMPO | SC_KEY | SC_MASTER | SC_CONFIG | SC_DRUMMAP); + bool f2 = flags & SC_SELECTION; + if(f1 || f2) // Try to avoid all unnecessary emissions. + emit selectionChanged(x, event, part, !f1); + + if (curPart == 0) + curPart = (MusECore::WavePart*)(editor->parts()->begin()->second); + redraw(); + } + +//--------------------------------------------------------- +// selectAtTick +//--------------------------------------------------------- + +void WaveCanvas::selectAtTick(unsigned int tick) + { + selectAtFrame(MusEGlobal::tempomap.tick2frame(tick)); + } + +//--------------------------------------------------------- +// selectAtFrame +//--------------------------------------------------------- + +void WaveCanvas::selectAtFrame(unsigned int frame) + { + //Select event nearest frame, if none selected and there are any + if (!items.empty() && selectionSize() == 0) { + iCItem i = items.begin(); + CItem* nearest = i->second; + + while (i != items.end()) { + CItem* cur=i->second; + unsigned int curf=abs(cur->x() + cur->part()->frame() - frame); + unsigned int nearf=abs(nearest->x() + nearest->part()->frame() - frame); + + if (curf < nearf) { + nearest=cur; + } + + i++; + } + + if (!nearest->isSelected()) { + selectItem(nearest, true); + songChanged(SC_SELECTION); + } + } + } + +//--------------------------------------------------------- +// getCaption +//--------------------------------------------------------- + +QString WaveCanvas::getCaption() const + { + int bar1, bar2, xx; + unsigned x; + AL::sigmap.tickValues(curPart->tick(), &bar1, &xx, &x); + AL::sigmap.tickValues(curPart->tick() + curPart->lenTick(), &bar2, &xx, &x); + + return QString("MusE: Part <") + curPart->name() + + QString("> %1-%2").arg(bar1+1).arg(bar2+1); + } + +//--------------------------------------------------------- +// track +//--------------------------------------------------------- + +MusECore::WaveTrack* WaveCanvas::track() const + { + return ((MusECore::WavePart*)curPart)->track(); + } + + +//--------------------------------------------------------- +// keyPress +//--------------------------------------------------------- + +void WaveCanvas::keyPress(QKeyEvent* event) + { + int key = event->key(); + if (((QInputEvent*)event)->modifiers() & Qt::ShiftModifier) + key += Qt::SHIFT; + if (((QInputEvent*)event)->modifiers() & Qt::AltModifier) + key += Qt::ALT; + if (((QInputEvent*)event)->modifiers() & Qt::ControlModifier) + key+= Qt::CTRL; + + // TODO: New WaveCanvas: Convert these to frames, and remove unneeded functions. + + // + // Shortcut for DrumEditor & PianoRoll + // Sets locators to selected events + // + if (key == shortcuts[SHRT_LOCATORS_TO_SELECTION].key) { + int tick_max = 0; + int tick_min = INT_MAX; + bool found = false; + + for (iCItem i= items.begin(); i != items.end(); i++) { + if (!i->second->isSelected()) + continue; + + int tick = i->second->x(); + int len = i->second->event().lenTick(); + found = true; + if (tick + len > tick_max) + tick_max = tick + len; + if (tick < tick_min) + tick_min = tick; + } + if (found) { + MusECore::Pos p1(tick_min, true); + MusECore::Pos p2(tick_max, true); + MusEGlobal::song->setPos(1, p1); + MusEGlobal::song->setPos(2, p2); + } + } + // Select items by key (PianoRoll & DrumEditor) + else if (key == shortcuts[SHRT_SEL_RIGHT].key || key == shortcuts[SHRT_SEL_RIGHT_ADD].key) { + rciCItem i; + + if (items.empty()) + return; + for (i = items.rbegin(); i != items.rend(); ++i) + if (i->second->isSelected()) + break; + + if(i == items.rend()) + i = items.rbegin(); + + if(i != items.rbegin()) + --i; + if(i->second) + { + if (key != shortcuts[SHRT_SEL_RIGHT_ADD].key) + deselectAll(); + CItem* sel = i->second; + sel->setSelected(true); + updateSelection(); + if (sel->x() + sel->width() > mapxDev(width())) + { + int mx = rmapx(sel->x()); + int newx = mx + rmapx(sel->width()) - width(); + // Leave a bit of room for the specially-drawn drum notes. But good for piano too. + emit horizontalScroll( (newx > mx ? mx - 10: newx + 10) - rmapx(xorg) ); + } + } + } + //Select items by key: (PianoRoll & DrumEditor) + else if (key == shortcuts[SHRT_SEL_LEFT].key || key == shortcuts[SHRT_SEL_LEFT_ADD].key) { + ciCItem i; + if (items.empty()) + return; + for (i = items.begin(); i != items.end(); ++i) + if (i->second->isSelected()) + break; + + if(i == items.end()) + i = items.begin(); + + if(i != items.begin()) + --i; + if(i->second) + { + if (key != shortcuts[SHRT_SEL_LEFT_ADD].key) + deselectAll(); + CItem* sel = i->second; + sel->setSelected(true); + updateSelection(); + if (sel->x() <= mapxDev(0)) + emit horizontalScroll(rmapx(sel->x() - xorg) - 10); // Leave a bit of room. + } + } + //else if (key == shortcuts[SHRT_INC_PITCH].key) { + // modifySelected(NoteInfo::VAL_PITCH, 1); + // } + //else if (key == shortcuts[SHRT_DEC_PITCH].key) { + // modifySelected(NoteInfo::VAL_PITCH, -1); + // } + else if (key == shortcuts[SHRT_INC_POS].key) { + // TODO: Check boundaries + modifySelected(NoteInfo::VAL_TIME, editor->raster()); + } + else if (key == shortcuts[SHRT_DEC_POS].key) { + // TODO: Check boundaries + modifySelected(NoteInfo::VAL_TIME, 0 - editor->raster()); + } + + else if (key == shortcuts[SHRT_INCREASE_LEN].key) { + // TODO: Check boundaries + modifySelected(NoteInfo::VAL_LEN, editor->raster()); + } + else if (key == shortcuts[SHRT_DECREASE_LEN].key) { + // TODO: Check boundaries + modifySelected(NoteInfo::VAL_LEN, 0 - editor->raster()); + } + + else + event->ignore(); + } + + +//--------------------------------------------------------- +// setPos +// set one of three markers +// idx - 0-cpos 1-lpos 2-rpos +// flag - emit followEvent() +//--------------------------------------------------------- + +void WaveCanvas::setPos(int idx, unsigned val, bool adjustScrollbar) + { + val = MusEGlobal::tempomap.tick2frame(val); + if (pos[idx] == val) + return; + int opos = mapx(pos[idx]); + int npos = mapx(val); + + if (adjustScrollbar && idx == 0) { + switch (MusEGlobal::song->follow()) { + case MusECore::Song::NO: + break; + case MusECore::Song::JUMP: + if (npos >= width()) { + int ppos = val - xorg - rmapxDev(width()/4); + if (ppos < 0) + ppos = 0; + emit followEvent(ppos); + opos = mapx(pos[idx]); + npos = mapx(val); + } + else if (npos < 0) { + int ppos = val - xorg - rmapxDev(width()*3/4); + if (ppos < 0) + ppos = 0; + emit followEvent(ppos); + opos = mapx(pos[idx]); + npos = mapx(val); + } + break; + case MusECore::Song::CONTINUOUS: + if (npos > (width()*5)/8) { + int ppos = pos[idx] - xorg - rmapxDev(width()*5/8); + if (ppos < 0) + ppos = 0; + emit followEvent(ppos); + opos = mapx(pos[idx]); + npos = mapx(val); + } + else if (npos < (width()*3)/8) { + int ppos = pos[idx] - xorg - rmapxDev(width()*3/8); + if (ppos < 0) + ppos = 0; + emit followEvent(ppos); + opos = mapx(pos[idx]); + npos = mapx(val); + } + break; + } + } + + int x; + int w = 1; + if (opos > npos) { + w += opos - npos; + x = npos; + } + else { + w += npos - opos; + x = opos; + } + pos[idx] = val; + //redraw(QRect(x, 0, w, height())); + redraw(QRect(x-1, 0, w+2, height())); // From Canvas::draw (is otherwise identical). Fix for corruption. (TEST: New WaveCanvas: Still true?) + } + +//--------------------------------------------------------- +// setYScale +//--------------------------------------------------------- + +void WaveCanvas::setYScale(int val) + { + yScale = val; + redraw(); + } + +// TODO: Overridden because markers need tick2frame. +// After BBT/frame mode is added to Canvas, remove this override and let Canvas::draw do it. +// Also add the drawParts calls to Canvas::draw, and add a dummy Canvas::drawParts { }. +//--------------------------------------------------------- +// draw +//--------------------------------------------------------- + +void WaveCanvas::draw(QPainter& p, const QRect& r) + { + int x = r.x() < 0 ? 0 : r.x(); + int y = r.y() < 0 ? 0 : r.y(); + int w = r.width(); + int h = r.height(); + int x2 = x + w; + + std::vector<CItem*> list1; + std::vector<CItem*> list2; + //std::vector<CItem*> list3; + std::vector<CItem*> list4; + + drawCanvas(p, r); + + //--------------------------------------------------- + // draw Canvas Items + //--------------------------------------------------- + + iCItem to(items.lower_bound(x2)); + + for(iCItem i = items.begin(); i != to; ++i) + { + CItem* ci = i->second; + // NOTE Optimization: For each item call this once now, then use cached results later via cachedHasHiddenEvents(). + // Not required for now. + //ci->part()->hasHiddenEvents(); + + // Draw items from other parts behind all others. Only for items with events (not arranger parts). + if(!ci->event().empty() && ci->part() != curPart) + list1.push_back(ci); + else if(!ci->isMoving() && (ci->event().empty() || ci->part() == curPart)) + { + // Draw selected parts in front of all others. + if(ci->isSelected()) + list4.push_back(ci); + // Draw clone parts, and parts with hidden events, in front of others all except selected. + //else if(ci->event().empty() && (ci->part()->events()->arefCount() > 1 || ci->part()->cachedHasHiddenEvents())) + // Draw clone parts in front of others all except selected. + //else if(ci->event().empty() && (ci->part()->events()->arefCount() > 1)) + // list3.push_back(ci); + else + // Draw unselected parts. + list2.push_back(ci); + } + } + + // Draw non-current part backgrounds behind all others: + drawParts(p, r, false); + + int i; + int sz = list1.size(); + for(i = 0; i != sz; ++i) + drawItem(p, list1[i], r); + + // Draw current part background in front of all others: + drawParts(p, r, true); + + sz = list2.size(); + for(i = 0; i != sz; ++i) + drawItem(p, list2[i], r); + + //sz = list3.size(); + //for(i = 0; i != sz; ++i) + // drawItem(p, list3[i], rect); + + sz = list4.size(); + for(i = 0; i != sz; ++i) + drawItem(p, list4[i], r); + + to = moving.lower_bound(x2); + for (iCItem i = moving.begin(); i != to; ++i) + { + drawItem(p, i->second, r); + } + + drawTopItem(p,r); + + + //--------------------------------------------------- + // draw marker + //--------------------------------------------------- + + bool wmtxen = p.worldMatrixEnabled(); + p.setWorldMatrixEnabled(false); + + int my = mapy(y); + int my2 = mapy(y + h); + + MusECore::MarkerList* marker = MusEGlobal::song->marker(); + for (MusECore::iMarker m = marker->begin(); m != marker->end(); ++m) { + int xp = MusEGlobal::tempomap.tick2frame(m->second.tick()); + if (xp >= x && xp < x2) { + p.setPen(Qt::green); + p.drawLine(mapx(xp), my, mapx(xp), my2); + } + } + + //--------------------------------------------------- + // draw location marker + //--------------------------------------------------- + + // Tip: These positions are already in units of frames. + p.setPen(Qt::blue); + int mx; + if (pos[1] >= unsigned(x) && pos[1] < unsigned(x2)) { + mx = mapx(pos[1]); + p.drawLine(mx, my, mx, my2); + } + if (pos[2] >= unsigned(x) && pos[2] < unsigned(x2)) { + mx = mapx(pos[2]); + p.drawLine(mx, my, mx, my2); + } + p.setPen(Qt::red); + if (pos[0] >= unsigned(x) && pos[0] < unsigned(x2)) { + mx = mapx(pos[0]); + p.drawLine(mx, my, mx, my2); + } + + //p.restore(); + //p.setWorldMatrixEnabled(true); + p.setWorldMatrixEnabled(wmtxen); + + //--------------------------------------------------- + // draw lasso + //--------------------------------------------------- + + if (drag == DRAG_LASSO) { + p.setPen(Qt::blue); + p.setBrush(Qt::NoBrush); + p.drawRect(lasso); + } + + //--------------------------------------------------- + // draw moving items + //--------------------------------------------------- + + for(iCItem i = moving.begin(); i != moving.end(); ++i) + drawMoving(p, i->second, r); + + } + +//--------------------------------------------------------- +// drawWaveParts +//--------------------------------------------------------- + +void WaveCanvas::drawParts(QPainter& p, const QRect& r, bool do_cur_part) +{ + //QRect rr = p.transform().mapRect(r); // Gives inconsistent positions. Source shows wrong operation for our needs. + QRect rr = map(r); // Use our own map instead. + + bool wmtxen = p.worldMatrixEnabled(); + p.setWorldMatrixEnabled(false); + + if(do_cur_part) + { + // Draw current part: + if(curPart) + { + QRect mwpr = map(QRect(curPart->frame(), 0, curPart->lenFrame(), height())); + QRect mpbgr = rr & mwpr; + if(!mpbgr.isNull()) + { + QColor c; + switch(colorMode) + { + default: + case 0: + c = MusEGlobal::config.partColors[curPart->colorIndex()]; + break; + case 1: + c = Qt::lightGray; + break; + } + c.setAlpha(MusEGlobal::config.globalAlphaBlend); + QBrush part_bg_brush(MusECore::gGradientFromQColor(c, mwpr.topLeft(), mwpr.bottomLeft())); + p.fillRect(mpbgr, part_bg_brush); + } + } + } + else + { + // Draw non-current parts: + for (MusECore::iPart ip = editor->parts()->begin(); ip != editor->parts()->end(); ++ip) + { + MusECore::WavePart* wp = (MusECore::WavePart*)(ip->second); + if(wp == curPart) + continue; + + QRect mwpr = map(QRect(wp->frame(), 0, wp->lenFrame(), height())); + QRect mpbgr = rr & mwpr; + if(!mpbgr.isNull()) + { + //int cidx = wp->colorIndex(); + //QColor c(MusEGlobal::config.partColors[cidx]); + QColor c(Qt::darkGray); + c.setAlpha(MusEGlobal::config.globalAlphaBlend); + QBrush part_bg_brush(MusECore::gGradientFromQColor(c, mwpr.topLeft(), mwpr.bottomLeft())); + p.fillRect(mpbgr, part_bg_brush); + } + } + } + + p.setWorldMatrixEnabled(wmtxen); +} + +// TODO: Overridden because we're in units of frames. +// After BBT/frame mode is added to Canvas, remove this override and let View or Canvas do it. +//--------------------------------------------------------- +// drawTickRaster +//--------------------------------------------------------- + +void WaveCanvas::drawTickRaster(QPainter& p, int x, int y, int w, int h, int raster) + { + // Changed to draw in device coordinate space instead of virtual, transformed space. Tim. + + //int mx = mapx(x); + int my = mapy(y); + //int mw = mapx(x + w) - mx; + //int mw = mapx(x + w) - mx - 1; + //int mh = mapy(y + h) - my; + //int mh = mapy(y + h) - my - 1; + + //p.save(); + bool wmtxen = p.worldMatrixEnabled(); + p.setWorldMatrixEnabled(false); + + int xx,bar1, bar2, beat; + unsigned tick; +// AL::sigmap.tickValues(x, &bar1, &beat, &tick); +// AL::sigmap.tickValues(x+w, &bar2, &beat, &tick); + AL::sigmap.tickValues(MusEGlobal::tempomap.frame2tick(x), &bar1, &beat, &tick); + AL::sigmap.tickValues(MusEGlobal::tempomap.frame2tick(x+w), &bar2, &beat, &tick); + ++bar2; + ///int y2 = y + h; + //int y2 = my + mh; + int y2 = mapy(y + h) - 1; + //printf("View::drawTickRaster x:%d y:%d w:%d h:%d mx:%d my:%d mw:%d mh:%d y2:%d bar1:%d bar2:%d\n", x, y, w, h, mx, my, mw, mh, y2, bar1, bar2); + //printf("View::drawTickRaster x:%d y:%d w:%d h:%d my:%d mh:%d y2:%d bar1:%d bar2:%d\n", x, y, w, h, my, mh, y2, bar1, bar2); + for (int bar = bar1; bar < bar2; ++bar) { + +// unsigned xb = AL::sigmap.bar2tick(bar, 0, 0); + unsigned xb = AL::sigmap.bar2tick(bar, 0, 0); + int xt = mapx(MusEGlobal::tempomap.tick2frame(xb)); + p.setPen(Qt::black); + p.drawLine(xt, my, xt, y2); + + int z, n; + AL::sigmap.timesig(xb, z, n); + int qq = raster; + if (rmapx(raster) < 8) // grid too dense + qq *= 2; + p.setPen(Qt::lightGray); + if (raster>=4) { + xx = xb + qq; + int xxx = MusEGlobal::tempomap.tick2frame(AL::sigmap.bar2tick(bar, z, 0)); + //while (MusEGlobal::tempomap.tick2frame(xx) <= xxx) { + while (1) { + int xxf = MusEGlobal::tempomap.tick2frame(xx); + if(xxf > xxx) + break; + //int x = mapx(MusEGlobal::tempomap.tick2frame(xx)); + int x = mapx(xxf); + p.drawLine(x, my, x, y2); + xx += qq; + } + } + p.setPen(Qt::gray); + for (int beat = 1; beat < z; beat++) { + xx = mapx(MusEGlobal::tempomap.tick2frame(AL::sigmap.bar2tick(bar, beat, 0))); + //printf(" bar:%d z:%d beat:%d xx:%d\n", bar, z, beat, xx); + p.drawLine(xx, my, xx, y2); + } + + } + p.setWorldMatrixEnabled(wmtxen); + } + +// TODO: Overridden because we're in units of frames. +// After BBT/frame mode is added to Canvas, remove this override and let Canvas do it. +//--------------------------------------------------------- +// raster +//--------------------------------------------------------- + +QPoint WaveCanvas::raster(const QPoint& p) const + { + int x = p.x(); + if (x < 0) + x = 0; + //x = editor->rasterVal(x); + x = MusEGlobal::tempomap.tick2frame(editor->rasterVal(MusEGlobal::tempomap.frame2tick(x))); + int pitch = y2pitch(p.y()); + int y = pitch2y(pitch); + return QPoint(x, y); + } + +#define WHEEL_STEPSIZE 40 +#define WHEEL_DELTA 120 +//--------------------------------------------------------- +// wheelEvent +//--------------------------------------------------------- +void WaveCanvas::wheelEvent(QWheelEvent* ev) +{ + int keyState = ev->modifiers(); + + bool shift = keyState & Qt::ShiftModifier; + bool ctrl = keyState & Qt::ControlModifier; + + if (shift) { // scroll vertically + int delta = -ev->delta() / WHEEL_DELTA; + int xpixelscale = 5*MusECore::fast_log10(rmapxDev(1)); + + + if (xpixelscale <= 0) + xpixelscale = 1; + + int scrollstep = WHEEL_STEPSIZE * (delta); + ///if (ev->state() == Qt::ShiftModifier) + // if (((QInputEvent*)ev)->modifiers() == Qt::ShiftModifier) + scrollstep = scrollstep / 10; + + int newXpos = xpos + xpixelscale * scrollstep; + + if (newXpos < 0) + newXpos = 0; + + //setYPos(newYpos); + emit horizontalScroll((unsigned)newXpos); + + + } else if (ctrl) { // zoom horizontally + if (ev->delta()>0) + emit horizontalZoomIn(); + else + emit horizontalZoomOut(); + + } else { // scroll horizontally + emit mouseWheelMoved(ev->delta() / 10); + } + +} + +//--------------------------------------------------------- +// viewMousePressEvent +//--------------------------------------------------------- + +bool WaveCanvas::mousePress(QMouseEvent* event) + { + if (event->modifiers() & Qt::ControlModifier) { + return true; + } + button = event->button(); + QPoint pt = event->pos(); + //CItem* item = items.find(pt); + unsigned x = event->x(); + + switch (_tool) { + default: + break; + case CursorTool: + switch (button) { + case Qt::LeftButton: + if (mode == NORMAL) { + // redraw and reset: + if (selectionStart != selectionStop) { + selectionStart = selectionStop = 0; + redraw(); + } + mode = DRAG; + dragstartx = x; + selectionStart = selectionStop = x; + drag = DRAG_LASSO_START; + Canvas::start = pt; + return false; + } + break; + + case Qt::MidButton: + case Qt::RightButton: + default: + break; + } + + break; + } + return true; + } + +//--------------------------------------------------------- +// viewMouseReleaseEvent +//--------------------------------------------------------- + +void WaveCanvas::mouseRelease(const QPoint&) + { + button = Qt::NoButton; + if (mode == DRAG) { + mode = NORMAL; + } + } + +//--------------------------------------------------------- +// viewMousevent +//--------------------------------------------------------- + +void WaveCanvas::mouseMove(QMouseEvent* event) + { + int x = event->x(); + if (x < 0) + x = 0; + emit timeChanged(x); + //emit timeChanged(editor->rasterVal(x)); + //emit timeChanged(AL::sigmap.raster(x, *_raster)); + + switch (button) { + case Qt::LeftButton: + if (mode == DRAG) { + int mx = mapx(x); + int mstart = mapx(selectionStart); + int mstop = mapx(selectionStop); + //int mdstart = mapx(dragstartx); + QRect r(0, 0, 0, height()); + + if (x < dragstartx) { + if(x < selectionStart) + { + r.setLeft(mx); + r.setWidth((selectionStop >= dragstartx ? mstop : mstart) - mx); + } + else + { + r.setLeft(mstart); + r.setWidth(mx - mstart); + } + selectionStart = x; + selectionStop = dragstartx; + } + else { + if(x >= selectionStop) + { + r.setLeft(selectionStart < dragstartx ? mstart : mstop); + r.setWidth(mx - (selectionStart < dragstartx ? mstart : mstop)); + } + else + { + r.setLeft(mx); + r.setWidth(mstop - mx); + } + selectionStart = dragstartx; + selectionStop = x; + } + update(r); + } + break; + case Qt::MidButton: + break; + case Qt::RightButton: + break; + default: + return; + } + } + +//--------------------------------------------------------- +// pitch2y +//--------------------------------------------------------- + +int WaveCanvas::pitch2y(int) const + { + return 0; + } + +//--------------------------------------------------------- +// y2pitch +//--------------------------------------------------------- + +int WaveCanvas::y2pitch(int) const + { + return 0; + } + +//--------------------------------------------------------- +// drawItem +// draws a wave +//--------------------------------------------------------- + +void WaveCanvas::drawItem(QPainter& p, const MusEGui::CItem* item, const QRect& rect) +{ + MusECore::WavePart* wp = (MusECore::WavePart*)(item->part()); + if(!wp || !wp->track()) + return; + + //QRect rr = p.transform().mapRect(rect); // Gives inconsistent positions. Source shows wrong operation for our needs. + QRect rr = map(rect); // Use our own map instead. + + QRect mwpr = map(QRect(wp->frame(), 0, wp->lenFrame(), height())); + + QRect r = item->bbox(); + QRect mer = map(r); + QRect mr = rr & mer & mwpr; + if(mr.isNull()) + return; + + MusECore::Event event = item->event(); + if(event.empty()) + return; + + int x1 = mr.x(); + int x2 = mr.right() + 1; + if (x1 < 0) + x1 = 0; + if (x2 > width()) + x2 = width(); + int hh = height(); + int h = hh/2; + int y = mr.y() + h; + + int xScale = xmag; + if (xScale < 0) + xScale = -xScale; + + //int t_channels = wp->track()->channels(); + int px = wp->frame(); + + bool wmtxen = p.worldMatrixEnabled(); + p.setWorldMatrixEnabled(false); + + int sx, ex; + + sx = event.frame() + px + xScale/2; + ex = sx + event.lenFrame(); + sx = sx / xScale - xpos; + ex = ex / xScale - xpos; + + if (sx < x1) + sx = x1; + if (ex > x2) + ex = x2; + + int pos = (xpos + sx) * xScale + event.spos() - event.frame() - px; + + //printf("pos=%d xpos=%d sx=%d ex=%d xScale=%d event.spos=%d event.frame=%d px=%d\n", + // pos, xpos, sx, ex, xScale, event.spos(), event.frame(), px); + + + QBrush brush; + if (item->isMoving()) + { + QColor c(Qt::gray); + c.setAlpha(MusEGlobal::config.globalAlphaBlend); + QLinearGradient gradient(r.topLeft(), r.bottomLeft()); + gradient.setColorAt(0, c); + gradient.setColorAt(1, c.darker()); + brush = QBrush(gradient); + p.fillRect(sx, 0, ex - sx, hh, brush); + } + else + if (item->isSelected()) + { + QColor c(Qt::black); + c.setAlpha(MusEGlobal::config.globalAlphaBlend); + QLinearGradient gradient(r.topLeft(), r.bottomLeft()); + // Use a colour only about 20% lighter than black, rather than the 50% we use in MusECore::gGradientFromQColor + // and is used in darker()/lighter(), so that it is distinguished a bit better from grey non-part tracks. + //c.setRgba(64, 64, 64, c.alpha()); + gradient.setColorAt(0, QColor(51, 51, 51, MusEGlobal::config.globalAlphaBlend)); + gradient.setColorAt(1, c); + brush = QBrush(gradient); + p.fillRect(sx, 0, ex - sx, hh, brush); + } + //else + { + QPen pen(Qt::DashLine); + pen.setColor(Qt::black); + pen.setCosmetic(true); + p.setPen(pen); + p.drawRect(sx, 0, ex - sx, hh); + } + //p.fillRect(sx, 0, ex - sx, hh, brush); + //p.drawRect(sx, 0, ex - sx, hh, brush); + + MusECore::SndFileR f = event.sndFile(); + if(f.isNull()) + { + p.setWorldMatrixEnabled(wmtxen); + return; + } + + int ev_channels = f.channels(); + if (ev_channels == 0) { + p.setWorldMatrixEnabled(wmtxen); + printf("WaveCnvas::drawItem: ev_channels==0! %s\n", f.name().toLatin1().constData()); + return; + } + + h = hh / (ev_channels * 2); + int cc = hh % (ev_channels * 2) ? 0 : 1; + + unsigned peoffset = px + event.frame() - event.spos(); + + for (int i = sx; i < ex; i++) { + y = mr.y() + h; + MusECore::SampleV sa[f.channels()]; + f.read(sa, xScale, pos); + pos += xScale; + if (pos < event.spos()) + continue; + + int selectionStartPos = selectionStart - peoffset; // Offset transformed to event coords + int selectionStopPos = selectionStop - peoffset; + + for (int k = 0; k < ev_channels; ++k) { + int kk = k % f.channels(); + int peak = (sa[kk].peak * (h - 1)) / yScale; + int rms = (sa[kk].rms * (h - 1)) / yScale; + if (peak > h) + peak = h; + if (rms > h) + rms = h; + QColor peak_color = QColor(Qt::darkGray); + QColor rms_color = QColor(Qt::black); + + // Changed by T356. Reduces (but not eliminates) drawing artifacts. (TODO Cause of artifacts gone, correct this now.) + //if (pos > selectionStartPos && pos < selectionStopPos) { + if (pos > selectionStartPos && pos <= selectionStopPos) { + + peak_color = QColor(Qt::lightGray); + rms_color = QColor(Qt::white); + // Draw inverted + p.setPen(QColor(Qt::black)); + p.drawLine(i, y - h + cc, i, y + h - cc ); + } + p.setPen(peak_color); + p.drawLine(i, y - peak - cc, i, y + peak); + p.setPen(rms_color); + p.drawLine(i, y - rms - cc, i, y + rms); + y += 2 * h; + } + } + + + int hn = hh / ev_channels; + int hhn = hn / 2; + for (int i = 0; i < ev_channels; ++i) { + int h2 = hn * i; + int center = hhn + h2; + p.setPen(QColor(i & i ? Qt::red : Qt::blue)); + p.drawLine(sx, center, ex, center); + p.setPen(QColor(Qt::black)); + p.drawLine(sx, h2, ex, h2); + } + + p.setWorldMatrixEnabled(wmtxen); +} + +//--------------------------------------------------------- +// drawTopItem +//--------------------------------------------------------- +void WaveCanvas::drawTopItem(QPainter& , const QRect&) +{} + +//--------------------------------------------------------- +// drawMoving +// draws moving items +//--------------------------------------------------------- + +void WaveCanvas::drawMoving(QPainter& p, const MusEGui::CItem* item, const QRect& rect) + { + QRect mr = QRect(item->mp().x(), item->mp().y(), item->width(), item->height()); + mr = mr.intersected(rect); + if(!mr.isValid()) + return; + p.setPen(Qt::black); + p.setBrush(QColor(0, 128, 0, 128)); // TODO: Pick a better colour, or use part colours, or grey? + p.drawRect(mr); + } + +//--------------------------------------------------------- +// viewMouseDoubleClickEvent +//--------------------------------------------------------- + +void WaveCanvas::viewMouseDoubleClickEvent(QMouseEvent* event) + { + if ((_tool != MusEGui::PointerTool) && (event->button() != Qt::LeftButton)) { + mousePress(event); + return; + } + } + +//--------------------------------------------------------- +// moveCanvasItems +//--------------------------------------------------------- + +MusECore::Undo WaveCanvas::moveCanvasItems(MusEGui::CItemList& items, int /*dp*/, int dx, DragType dtype) +{ + if(editor->parts()->empty()) + return MusECore::Undo(); //return empty list + + MusECore::PartsToChangeMap parts2change; + MusECore::Undo operations; + + for(MusECore::iPart ip = editor->parts()->begin(); ip != editor->parts()->end(); ++ip) + { + MusECore::Part* part = ip->second; + if(!part) + continue; + + int npartoffset = 0; + for(MusEGui::iCItem ici = items.begin(); ici != items.end(); ++ici) + { + MusEGui::CItem* ci = ici->second; + if(ci->part() != part) + continue; + + int x = ci->pos().x() + dx; + //int y = pitch2y(y2pitch(ci->pos().y()) + dp); + int y = 0; + QPoint newpos = raster(QPoint(x, y)); + + // Test moving the item... + WEvent* wevent = (WEvent*) ci; + MusECore::Event event = wevent->event(); + x = newpos.x(); + if(x < 0) + x = 0; + int nframe = MusEGlobal::tempomap.tick2frame(editor->rasterVal(MusEGlobal::tempomap.frame2tick(x))) - part->frame(); + if(nframe < 0) + nframe = 0; + int diff = nframe + event.lenFrame() - part->lenFrame(); + + // If moving the item would require a new part size... + if(diff > npartoffset) + npartoffset = diff; + } + + if(npartoffset > 0) + { + MusECore::iPartToChange ip2c = parts2change.find(part); + if(ip2c == parts2change.end()) + { + MusECore::PartToChange p2c = {0, npartoffset}; + parts2change.insert(std::pair<MusECore::Part*, MusECore::PartToChange> (part, p2c)); + } + else + ip2c->second.xdiff = npartoffset; + } + } + + bool forbidden=false; + for(MusECore::iPartToChange ip2c = parts2change.begin(); ip2c != parts2change.end(); ++ip2c) + { + MusECore::Part* opart = ip2c->first; + if (opart->hasHiddenEvents()) + { + forbidden=true; + break; + } + } + + + if (!forbidden) + { + std::vector< MusEGui::CItem* > doneList; + typedef std::vector< MusEGui::CItem* >::iterator iDoneList; + + for(MusEGui::iCItem ici = items.begin(); ici != items.end(); ++ici) + { + MusEGui::CItem* ci = ici->second; + + int x = ci->pos().x(); + //int y = ci->pos().y(); + int nx = x + dx; + //int ny = pitch2y(y2pitch(y) + dp); + int ny = 0; + QPoint newpos = raster(QPoint(nx, ny)); + selectItem(ci, true); + + iDoneList idl; + for(idl = doneList.begin(); idl != doneList.end(); ++idl) + // This compares EventBase pointers to see if they're the same... + if((*idl)->event() == ci->event()) + break; + + // Do not process if the event has already been processed (meaning it's an event in a clone part)... + if (idl == doneList.end()) + { + moveItem(operations, ci, newpos, dtype); // always returns true. if not, change is necessary here! + doneList.push_back(ci); + } + ci->move(newpos); + + if(moving.size() == 1) + itemReleased(curItem, newpos); + + if(dtype == MOVE_COPY || dtype == MOVE_CLONE) + selectItem(ci, false); + } + + for(MusECore::iPartToChange ip2c = parts2change.begin(); ip2c != parts2change.end(); ++ip2c) + { + MusECore::Part* opart = ip2c->first; + int diff = ip2c->second.xdiff; + + //schedule_resize_all_same_len_clone_parts(opart, opart->lenTick() + diff, operations); + schedule_resize_all_same_len_clone_parts(opart, opart->lenFrame() + diff, operations); + } + + return operations; + } + else + { + return MusECore::Undo(); //return empty list + } +} + +//--------------------------------------------------------- +// moveItem +// called after moving an object +//--------------------------------------------------------- + +bool WaveCanvas::moveItem(MusECore::Undo& operations, MusEGui::CItem* item, const QPoint& pos, DragType dtype) + { + WEvent* wevent = (WEvent*) item; + MusECore::Event event = wevent->event(); + //int npitch = y2pitch(pos.y()); + MusECore::Event newEvent = event.clone(); + int x = pos.x(); + if (x < 0) + x = 0; + + MusECore::Part* part = wevent->part(); + int nframe = MusEGlobal::tempomap.tick2frame(editor->rasterVal(MusEGlobal::tempomap.frame2tick(x))) - part->frame(); + if (nframe < 0) + nframe = 0; + newEvent.setFrame(nframe); + newEvent.setLenFrame(event.lenFrame()); + + // don't check, whether the new event is within the part + // at this place. with operation groups, the part isn't + // resized yet. (flo93) + + if (dtype == MOVE_COPY || dtype == MOVE_CLONE) + operations.push_back(MusECore::UndoOp(MusECore::UndoOp::AddEvent, newEvent, part, false, false)); + else + operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, newEvent, event, part, false, false)); + + return true; +} + +//--------------------------------------------------------- +// newItem(p, state) +//--------------------------------------------------------- + +MusEGui::CItem* WaveCanvas::newItem(const QPoint& p, int) + { + int frame = MusEGlobal::tempomap.tick2frame(editor->rasterVal1(MusEGlobal::tempomap.frame2tick(p.x()))); + int len = p.x() - frame; + frame -= curPart->frame(); + if (frame < 0) + return 0; + MusECore::Event e = MusECore::Event(MusECore::Wave); + e.setFrame(frame); + e.setLenFrame(len); + WEvent* we = new WEvent(e, curPart, height()); + return we; + } + +void WaveCanvas::newItem(MusEGui::CItem* item, bool noSnap) + { + WEvent* wevent = (WEvent*) item; + MusECore::Event event = wevent->event(); + int x = item->x(); + if (x<0) + x=0; + int w = item->width(); + + if (!noSnap) { + //x = editor->rasterVal1(x); //round down + x = MusEGlobal::tempomap.tick2frame(editor->rasterVal1(MusEGlobal::tempomap.frame2tick(x))); //round down + //w = editor->rasterVal(x + w) - x; + w = MusEGlobal::tempomap.tick2frame(editor->rasterVal(MusEGlobal::tempomap.frame2tick(x + w))) - x; + if (w == 0) + //w = editor->raster(); + w = MusEGlobal::tempomap.tick2frame(editor->raster()); + } + MusECore::Part* part = wevent->part(); + event.setFrame(x - part->frame()); + event.setLenFrame(w); + + MusECore::Undo operations; + int diff = event.endFrame() - part->lenFrame(); + + if (! ((diff > 0) && part->hasHiddenEvents()) ) //operation is allowed + { + operations.push_back(MusECore::UndoOp(MusECore::UndoOp::AddEvent,event, part, false, false)); + + if (diff > 0)// part must be extended? + { + //schedule_resize_all_same_len_clone_parts(part, event.endTick(), operations); + schedule_resize_all_same_len_clone_parts(part, event.endFrame(), operations); + printf("newItem: extending\n"); + } + + MusEGlobal::song->applyOperationGroup(operations); + } + else // forbid action by not applying it + songChanged(SC_EVENT_INSERTED); //this forces an update of the itemlist, which is neccessary + //to remove "forbidden" events from the list again + } + +//--------------------------------------------------------- +// resizeItem +//--------------------------------------------------------- + +void WaveCanvas::resizeItem(MusEGui::CItem* item, bool noSnap, bool) // experimental changes to try dynamically extending parts + { + WEvent* wevent = (WEvent*) item; + MusECore::Event event = wevent->event(); + MusECore::Event newEvent = event.clone(); + int len; + + MusECore::Part* part = wevent->part(); + + if (noSnap) + len = wevent->width(); + else { + unsigned frame = event.frame() + part->frame(); + //len = editor->rasterVal(tick + wevent->width()) - tick; + len = MusEGlobal::tempomap.tick2frame(editor->rasterVal(MusEGlobal::tempomap.frame2tick(frame + wevent->width()))) - frame; + if (len <= 0) + //len = editor->raster(); + len = MusEGlobal::tempomap.tick2frame(editor->raster()); + } + + MusECore::Undo operations; + //int diff = event.tick()+len-part->lenTick(); + int diff = event.frame() + len - part->lenFrame(); + + if (! ((diff > 0) && part->hasHiddenEvents()) ) //operation is allowed + { + //newEvent.setLenTick(len); + newEvent.setLenFrame(len); + operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent,newEvent, event, wevent->part(), false, false)); + + if (diff > 0)// part must be extended? + { + //schedule_resize_all_same_len_clone_parts(part, event.tick()+len, operations); + schedule_resize_all_same_len_clone_parts(part, event.frame() + len, operations); + printf("resizeItem: extending\n"); + } + } + //else forbid action by not performing it + MusEGlobal::song->applyOperationGroup(operations); + songChanged(SC_EVENT_MODIFIED); //this forces an update of the itemlist, which is neccessary + //to remove "forbidden" events from the list again + } + +//--------------------------------------------------------- +// deleteItem +//--------------------------------------------------------- + +bool WaveCanvas::deleteItem(MusEGui::CItem* item) + { + WEvent* wevent = (WEvent*) item; + if (wevent->part() == curPart) { + MusECore::Event ev = wevent->event(); + // Indicate do undo, and do not do port controller values and clone parts. + MusEGlobal::audio->msgDeleteEvent(ev, curPart, true, false, false); + return true; + } + return false; + } + +//--------------------------------------------------------- +// draw +//--------------------------------------------------------- + +void WaveCanvas::drawCanvas(QPainter& p, const QRect& rect) + { + int x = rect.x(); + int y = rect.y(); + int w = rect.width(); + int h = rect.height(); + + //--------------------------------------------------- + // vertical lines + //--------------------------------------------------- + + drawTickRaster(p, x, y, w, h, editor->raster()); + } + +//--------------------------------------------------------- +// waveCmd +//--------------------------------------------------------- + +void WaveCanvas::waveCmd(int cmd) + { + // TODO: New WaveCanvas: Convert this routine to frames. + switch(cmd) { + case CMD_LEFT: + { + int spos = pos[0]; + if(spos > 0) + { + spos -= 1; // Nudge by -1, then snap down with raster1. + spos = AL::sigmap.raster1(spos, editor->rasterStep(pos[0])); + } + if(spos < 0) + spos = 0; + MusECore::Pos p(spos,true); + MusEGlobal::song->setPos(0, p, true, true, true); + } + break; + case CMD_RIGHT: + { + int spos = AL::sigmap.raster2(pos[0] + 1, editor->rasterStep(pos[0])); // Nudge by +1, then snap up with raster2. + MusECore::Pos p(spos,true); + MusEGlobal::song->setPos(0, p, true, true, true); + } + break; + case CMD_LEFT_NOSNAP: + { + int spos = pos[0] - editor->rasterStep(pos[0]); + if (spos < 0) + spos = 0; + MusECore::Pos p(spos,true); + MusEGlobal::song->setPos(0, p, true, true, true); //CDW + } + break; + case CMD_RIGHT_NOSNAP: + { + MusECore::Pos p(pos[0] + editor->rasterStep(pos[0]), true); + MusEGlobal::song->setPos(0, p, true, true, true); //CDW + } + break; + case CMD_INSERT: + { + if (pos[0] < start() || pos[0] >= end()) + break; + MusECore::MidiPart* part = (MusECore::MidiPart*)curPart; + + if (part == 0) + break; + + MusECore::EventList* el = part->events(); + MusECore::Undo operations; + + std::list <MusECore::Event> elist; + for (MusECore::iEvent e = el->lower_bound(pos[0] - part->tick()); e != el->end(); ++e) + elist.push_back((MusECore::Event)e->second); + for (std::list<MusECore::Event>::iterator i = elist.begin(); i != elist.end(); ++i) { + MusECore::Event event = *i; + MusECore::Event newEvent = event.clone(); + newEvent.setTick(event.tick() + editor->raster());// - part->tick()); DELETETHIS + // Do not do port controller values and clone parts. + operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, newEvent, event, part, false, false)); + } + MusEGlobal::song->applyOperationGroup(operations); + + MusECore::Pos p(editor->rasterVal(pos[0] + editor->rasterStep(pos[0])), true); + MusEGlobal::song->setPos(0, p, true, false, true); + } + return; + case CMD_BACKSPACE: + if (pos[0] < start() || pos[0] >= end()) + break; + { + MusECore::MidiPart* part = (MusECore::MidiPart*)curPart; + if (part == 0) + break; + + MusECore::Undo operations; + MusECore::EventList* el = part->events(); + + std::list<MusECore::Event> elist; + for (MusECore::iEvent e = el->lower_bound(pos[0]); e != el->end(); ++e) + elist.push_back((MusECore::Event)e->second); + for (std::list<MusECore::Event>::iterator i = elist.begin(); i != elist.end(); ++i) { + MusECore::Event event = *i; + MusECore::Event newEvent = event.clone(); + newEvent.setTick(event.tick() - editor->raster() - part->tick()); + // Do not do port controller values and clone parts. + operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, newEvent, event, part, false, false)); + } + MusEGlobal::song->applyOperationGroup(operations); + MusECore::Pos p(editor->rasterVal(pos[0] - editor->rasterStep(pos[0])), true); + MusEGlobal::song->setPos(0, p, true, false, true); + } + break; + } + } + +//--------------------------------------------------------- +// cmd +// pulldown menu commands +//--------------------------------------------------------- + +void WaveCanvas::cmd(int cmd) + { + int modifyoperation = -1; + double paramA = 0.0; + switch (cmd) { + case CMD_SELECT_ALL: // select all + if (tool() == MusEGui::CursorTool) + { + if (!editor->parts()->empty()) { + MusECore::iPart iBeg = editor->parts()->begin(); + MusECore::iPart iEnd = editor->parts()->end(); + iEnd--; + MusECore::WavePart* beg = (MusECore::WavePart*) iBeg->second; + MusECore::WavePart* end = (MusECore::WavePart*) iEnd->second; + selectionStart = beg->frame(); + selectionStop = end->frame() + end->lenFrame(); + redraw(); + } + } + for (MusEGui::iCItem k = items.begin(); k != items.end(); ++k) { + if (!k->second->isSelected()) + selectItem(k->second, true); + } + break; + case CMD_SELECT_NONE: // select none + selectionStart = selectionStop = 0; + deselectAll(); + break; + case CMD_SELECT_INVERT: // invert selection + for (MusEGui::iCItem k = items.begin(); k != items.end(); ++k) { + selectItem(k->second, !k->second->isSelected()); + } + break; + case CMD_SELECT_ILOOP: // select inside loop + for (MusEGui::iCItem k = items.begin(); k != items.end(); ++k) { + WEvent* wevent = (WEvent*)(k->second); + MusECore::Part* part = wevent->part(); + MusECore::Event event = wevent->event(); + unsigned tick = event.tick() + part->tick(); + if (tick < MusEGlobal::song->lpos() || tick >= MusEGlobal::song->rpos()) + selectItem(k->second, false); + else + selectItem(k->second, true); + } + break; + case CMD_SELECT_OLOOP: // select outside loop + for (MusEGui::iCItem k = items.begin(); k != items.end(); ++k) { + WEvent* wevent = (WEvent*)(k->second); + MusECore::Part* part = wevent->part(); + MusECore::Event event = wevent->event(); + unsigned tick = event.tick() + part->tick(); + if (tick < MusEGlobal::song->lpos() || tick >= MusEGlobal::song->rpos()) + selectItem(k->second, true); + else + selectItem(k->second, false); + } + break; + case CMD_SELECT_PREV_PART: // select previous part + { + MusECore::Part* pt = editor->curCanvasPart(); + MusECore::Part* newpt = pt; + MusECore::PartList* pl = editor->parts(); + for(MusECore::iPart ip = pl->begin(); ip != pl->end(); ++ip) + if(ip->second == pt) + { + if(ip == pl->begin()) + ip = pl->end(); + --ip; + newpt = ip->second; + break; + } + if(newpt != pt) + editor->setCurCanvasPart(newpt); + } + break; + case CMD_SELECT_NEXT_PART: // select next part + { + MusECore::Part* pt = editor->curCanvasPart(); + MusECore::Part* newpt = pt; + MusECore::PartList* pl = editor->parts(); + for(MusECore::iPart ip = pl->begin(); ip != pl->end(); ++ip) + if(ip->second == pt) + { + ++ip; + if(ip == pl->end()) + ip = pl->begin(); + newpt = ip->second; + break; + } + if(newpt != pt) + editor->setCurCanvasPart(newpt); + } + break; + + + case CMD_EDIT_EXTERNAL: + modifyoperation = EDIT_EXTERNAL; + break; + + case CMD_EDIT_COPY: + modifyoperation = COPY; + break; + case CMD_EDIT_CUT: + modifyoperation = CUT; + break; + case CMD_EDIT_PASTE: + modifyoperation = PASTE; + break; + + case CMD_MUTE: + modifyoperation = MUTE; + break; + + case CMD_NORMALIZE: + modifyoperation = NORMALIZE; + break; + + case CMD_FADE_IN: + modifyoperation = FADE_IN; + break; + + case CMD_FADE_OUT: + modifyoperation = FADE_OUT; + break; + + case CMD_REVERSE: + modifyoperation = REVERSE; + break; + + case CMD_GAIN_FREE: { + EditGain* editGain = new EditGain(this, lastGainvalue); + if (editGain->exec() == QDialog::Accepted) { + lastGainvalue = editGain->getGain(); + modifyoperation = GAIN; + paramA = (double)lastGainvalue / 100.0; + } + delete editGain; + } + break; + + case CMD_GAIN_200: + modifyoperation = GAIN; + paramA = 2.0; + break; + + case CMD_GAIN_150: + modifyoperation = GAIN; + paramA = 1.5; + break; + + case CMD_GAIN_75: + modifyoperation = GAIN; + paramA = 0.75; + break; + + case CMD_GAIN_50: + modifyoperation = GAIN; + paramA = 0.5; + break; + + case CMD_GAIN_25: + modifyoperation = GAIN; + paramA = 0.25; + break; + + + + case CMD_ERASE_MEASURE: + case CMD_DELETE_MEASURE: + case CMD_CREATE_MEASURE: + break; + default: +// printf("unknown ecanvas cmd %d\n", cmd); + break; + } + + if (modifyoperation != -1) { + if (selectionStart == selectionStop && modifyoperation!=PASTE) { + printf("No selection. Ignoring\n"); //@!TODO: Disable menu options when no selection + QMessageBox::information(this, + QString("MusE"), + QWidget::tr("No selection. Ignoring")); + + return; + } + + //if(!modifyWarnedYet) + //{ + // modifyWarnedYet = true; + // if(QMessageBox::warning(this, QString("Muse"), + // tr("Warning! Muse currently operates directly on the sound file.\n" + // "Undo is supported, but NOT after exit, WITH OR WITHOUT A SAVE!\n" + // "If you are stuck, try deleting the associated .wca file and reloading."), tr("&Ok"), tr("&Cancel"), + // QString::null, 0, 1 ) != 0) + // return; + //} + modifySelection(modifyoperation, selectionStart, selectionStop, paramA); + } + + updateSelection(); + redraw(); + } + +//--------------------------------------------------------- +// getSelection +//--------------------------------------------------------- +MusECore::WaveSelectionList WaveCanvas::getSelection(unsigned startpos, unsigned stoppos) + { + MusECore::WaveSelectionList selection; + + for (MusECore::iPart ip = editor->parts()->begin(); ip != editor->parts()->end(); ++ip) { + MusECore::WavePart* wp = (MusECore::WavePart*)(ip->second); + unsigned part_offset = wp->frame(); + + MusECore::EventList* el = wp->events(); + //printf("eventlist length=%d\n",el->size()); + + for (MusECore::iEvent e = el->begin(); e != el->end(); ++e) { + MusECore::Event event = e->second; + if (event.empty()) + continue; + MusECore::SndFileR file = event.sndFile(); + if (file.isNull()) + continue; + + // Respect part end: Don't modify stuff outside of part boundary. + unsigned elen = event.lenFrame(); + if(event.frame() + event.lenFrame() >= wp->lenFrame()) + { + // Adjust apparent operation length: + if(event.frame() > wp->lenFrame()) + elen = 0; + else + elen = wp->lenFrame() - event.frame(); + } + + unsigned event_offset = event.frame() + part_offset; + unsigned event_startpos = event.spos(); + unsigned event_length = elen + event.spos(); + unsigned event_end = event_offset + event_length; + //printf("startpos=%d stoppos=%d part_offset=%d event_offset=%d event_startpos=%d event_length=%d event_end=%d\n", startpos, stoppos, part_offset, event_offset, event_startpos, event_length, event_end); + + if (!(event_end <= startpos || event_offset > stoppos)) { + int tmp_sx = startpos - event_offset + event_startpos; + int tmp_ex = stoppos - event_offset + event_startpos; + unsigned sx; + unsigned ex; + + tmp_sx < (int)event_startpos ? sx = event_startpos : sx = tmp_sx; + tmp_ex > (int)event_length ? ex = event_length : ex = tmp_ex; + + //printf("Event data affected: %d->%d filename:%s\n", sx, ex, file.name().toLatin1().constData()); + MusECore::WaveEventSelection s; + s.file = file; + s.startframe = sx; + s.endframe = ex+1; + //printf("sx=%d ex=%d\n",sx,ex); + selection.push_back(s); + } + } + } + + return selection; + } + +//--------------------------------------------------------- +// modifySelection +//--------------------------------------------------------- +void WaveCanvas::modifySelection(int operation, unsigned startpos, unsigned stoppos, double paramA) + { + MusEGlobal::song->startUndo(); + + if (operation == PASTE) { + // we need to redefine startpos and stoppos + if (copiedPart =="") + return; + MusECore::SndFile pasteFile(copiedPart); + pasteFile.openRead(); + startpos = pos[0]; + stoppos = startpos+ pasteFile.samples(); // possibly this is wrong if there are tempo changes + pasteFile.close(); + pos[0]=stoppos; + } + + MusECore::WaveSelectionList selection = getSelection(startpos, stoppos); + for (MusECore::iWaveSelection i = selection.begin(); i != selection.end(); i++) { + MusECore::WaveEventSelection w = *i; + MusECore::SndFileR& file = w.file; + unsigned sx = w.startframe; + unsigned ex = w.endframe; + unsigned file_channels = file.channels(); + + QString tmpWavFile = QString::null; + if (!MusEGlobal::getUniqueTmpfileName("tmp_musewav",".wav", tmpWavFile)) { + break; + } + + MusEGlobal::audio->msgIdle(true); // Not good with playback during operations + MusECore::SndFile tmpFile(tmpWavFile); + tmpFile.setFormat(file.format(), file_channels, file.samplerate()); + if (tmpFile.openWrite()) { + MusEGlobal::audio->msgIdle(false); + printf("Could not open temporary file...\n"); + break; + } + + // + // Write out data that will be changed to temp file + // + unsigned tmpdatalen = ex - sx; + off_t tmpdataoffset = sx; + float* tmpdata[file_channels]; + + for (unsigned i=0; i<file_channels; i++) { + tmpdata[i] = new float[tmpdatalen]; + } + file.seek(tmpdataoffset, 0); + file.readWithHeap(file_channels, tmpdata, tmpdatalen); + file.close(); + tmpFile.write(file_channels, tmpdata, tmpdatalen); + tmpFile.close(); + + switch(operation) + { + case MUTE: + muteSelection(file_channels, tmpdata, tmpdatalen); + break; + + case NORMALIZE: + normalizeSelection(file_channels, tmpdata, tmpdatalen); + break; + + case FADE_IN: + fadeInSelection(file_channels, tmpdata, tmpdatalen); + break; + + case FADE_OUT: + fadeOutSelection(file_channels, tmpdata, tmpdatalen); + break; + + case REVERSE: + reverseSelection(file_channels, tmpdata, tmpdatalen); + break; + + case GAIN: + applyGain(file_channels, tmpdata, tmpdatalen, paramA); + break; + case CUT: + copySelection(file_channels, tmpdata, tmpdatalen, true, file.format(), file.samplerate()); + break; + case COPY: + copySelection(file_channels, tmpdata, tmpdatalen, false, file.format(), file.samplerate()); + break; + case PASTE: + { + MusECore::SndFile pasteFile(copiedPart); + pasteFile.openRead(); + pasteFile.seek(tmpdataoffset, 0); + pasteFile.readWithHeap(file_channels, tmpdata, tmpdatalen); + } + break; + + case EDIT_EXTERNAL: + editExternal(file.format(), file.samplerate(), file_channels, tmpdata, tmpdatalen); + break; + + default: + printf("Error: Default state reached in modifySelection\n"); + break; + + } + + file.openWrite(); + file.seek(tmpdataoffset, 0); + file.write(file_channels, tmpdata, tmpdatalen); + file.update(); + file.close(); + file.openRead(); + + for (unsigned i=0; i<file_channels; i++) { + delete[] tmpdata[i]; + } + + // Undo handling + MusEGlobal::song->cmdChangeWave(file.dirPath() + "/" + file.name(), tmpWavFile, sx, ex); + MusEGlobal::audio->msgIdle(false); // Not good with playback during operations + } + MusEGlobal::song->endUndo(SC_CLIP_MODIFIED); + redraw(); + } + +//--------------------------------------------------------- +// copySelection +//--------------------------------------------------------- +void WaveCanvas::copySelection(unsigned file_channels, float** tmpdata, unsigned length, bool blankData, unsigned format, unsigned sampleRate) +{ + if (copiedPart!="") { + QFile::remove(copiedPart); + } + if (!MusEGlobal::getUniqueTmpfileName("tmp_musewav",".wav", copiedPart)) { + return; + } + + MusECore::SndFile tmpFile(copiedPart); + tmpFile.setFormat(format, file_channels, sampleRate); + tmpFile.openWrite(); + tmpFile.write(file_channels, tmpdata, length); + tmpFile.close(); + + if (blankData) { + // Set everything to 0! + for (unsigned i=0; i<file_channels; i++) { + for (unsigned j=0; j<length; j++) { + tmpdata[i][j] = 0; + } + } + } +} + +//--------------------------------------------------------- +// muteSelection +//--------------------------------------------------------- +void WaveCanvas::muteSelection(unsigned channels, float** data, unsigned length) + { + // Set everything to 0! + for (unsigned i=0; i<channels; i++) { + for (unsigned j=0; j<length; j++) { + data[i][j] = 0; + } + } + } + +//--------------------------------------------------------- +// normalizeSelection +//--------------------------------------------------------- +void WaveCanvas::normalizeSelection(unsigned channels, float** data, unsigned length) + { + float loudest = 0.0; + + for (unsigned i=0; i<channels; i++) { + for (unsigned j=0; j<length; j++) { + if (data[i][j] > loudest) + loudest = data[i][j]; + } + } + + double scale = 0.99 / (double)loudest; + + for (unsigned i=0; i<channels; i++) { + for (unsigned j=0; j<length; j++) { + data[i][j] = (float) ((double)data[i][j] * scale); + } + } + } + +//--------------------------------------------------------- +// fadeInSelection +//--------------------------------------------------------- +void WaveCanvas::fadeInSelection(unsigned channels, float** data, unsigned length) + { + for (unsigned i=0; i<channels; i++) { + for (unsigned j=0; j<length; j++) { + double scale = (double) j / (double)length ; + data[i][j] = (float) ((double)data[i][j] * scale); + } + } + } + +//--------------------------------------------------------- +// fadeOutSelection +//--------------------------------------------------------- +void WaveCanvas::fadeOutSelection(unsigned channels, float** data, unsigned length) + { + for (unsigned i=0; i<channels; i++) { + for (unsigned j=0; j<length; j++) { + double scale = (double) (length - j) / (double)length ; + data[i][j] = (float) ((double)data[i][j] * scale); + } + } + } + +//--------------------------------------------------------- +// reverseSelection +//--------------------------------------------------------- +void WaveCanvas::reverseSelection(unsigned channels, float** data, unsigned length) + { + if(length <= 1) + return; + for (unsigned i=0; i<channels; i++) { + for (unsigned j=0; j<length/2; j++) { + float tmpl = data[i][j]; + float tmpr = data[i][length - j - 1]; + data[i][j] = tmpr; + data[i][length - j - 1] = tmpl; + } + } + } +//--------------------------------------------------------- +// applyGain +//--------------------------------------------------------- +void WaveCanvas::applyGain(unsigned channels, float** data, unsigned length, double gain) + { + for (unsigned i=0; i<channels; i++) { + for (unsigned j=0; j<length; j++) { + data[i][j] = (float) ((double)data[i][j] * gain); + } + } + } + +//--------------------------------------------------------- +// editExternal +//--------------------------------------------------------- +void WaveCanvas::editExternal(unsigned file_format, unsigned file_samplerate, unsigned file_channels, float** tmpdata, unsigned tmpdatalen) + { + // Create yet another tmp-file + QString exttmpFileName; + if (!MusEGlobal::getUniqueTmpfileName("tmp_musewav",".wav", exttmpFileName)) { + printf("Could not create temp file - aborting...\n"); + return; + } + + MusECore::SndFile exttmpFile(exttmpFileName); + exttmpFile.setFormat(file_format, file_channels, file_samplerate); + if (exttmpFile.openWrite()) { + printf("Could not open temporary file...\n"); + return; + } + // Write out change-data to this file: + exttmpFile.write(file_channels, tmpdata, tmpdatalen); + exttmpFile.close(); + + // Forkaborkabork + int pid = fork(); + if (pid == 0) { + if (execlp(MusEGlobal::config.externalWavEditor.toLatin1().constData(), MusEGlobal::config.externalWavEditor.toLatin1().constData(), exttmpFileName.toLatin1().constData(), NULL) == -1) { + perror("Failed to launch external editor"); + // Get out of here + + + // cannot report error through gui, we are in another fork! + //@!TODO: Handle unsuccessful attempts + exit(99); + } + exit(0); + } + else if (pid == -1) { + perror("fork failed"); + } + else { + int status; + waitpid(pid, &status, 0); + //printf ("status=%d\n",status); + if( WEXITSTATUS(status) != 0 ){ + QMessageBox::warning(this, tr("MusE - external editor failed"), + tr("MusE was unable to launch the external editor\ncheck if the editor setting in:\n" + "Global Settings->Audio:External Waveditor\nis set to a valid editor.")); + } + + if (exttmpFile.openRead()) { + printf("Could not reopen temporary file!\n"); + } + else { + // Re-read file again + exttmpFile.seek(0, 0); + size_t sz = exttmpFile.readWithHeap(file_channels, tmpdata, tmpdatalen); + if (sz != tmpdatalen) { + // File must have been shrunken - not good. Alert user. + QMessageBox::critical(this, tr("MusE - file size changed"), + tr("When editing in external editor - you should not change the filesize\nsince it must fit the selected region.\n\nMissing data is muted")); + for (unsigned i=0; i<file_channels; i++) { + for (unsigned j=sz; j<tmpdatalen; j++) { + tmpdata[i][j] = 0; + } + } + } + } + QDir dir = exttmpFile.dirPath(); + dir.remove(exttmpFileName); + dir.remove(exttmpFile.basename() + ".wca"); + } + } + + + +//--------------------------------------------------------- +// startDrag +//--------------------------------------------------------- + +void WaveCanvas::startDrag(MusEGui::CItem* /* item*/, bool copymode) + { + QMimeData* md = MusECore::selected_events_to_mime(MusECore::partlist_to_set(editor->parts()), 1); + + if (md) { + // "Note that setMimeData() assigns ownership of the QMimeData object to the QDrag object. + // The QDrag must be constructed on the heap with a parent QWidget to ensure that Qt can + // clean up after the drag and drop operation has been completed. " + QDrag* drag = new QDrag(this); + drag->setMimeData(md); + + if (copymode) + drag->exec(Qt::CopyAction); + else + drag->exec(Qt::MoveAction); + } + } + +//--------------------------------------------------------- +// dragEnterEvent +//--------------------------------------------------------- + +void WaveCanvas::dragEnterEvent(QDragEnterEvent* event) + { + //event->accept(Q3TextDrag::canDecode(event)); + event->acceptProposedAction(); // TODO CHECK Tim. + } + +//--------------------------------------------------------- +// dragMoveEvent +//--------------------------------------------------------- + +void WaveCanvas::dragMoveEvent(QDragMoveEvent*) + { + //printf("drag move %x\n", this); DELETETHIS (whole function?) + //event->acceptProposedAction(); + } + +//--------------------------------------------------------- +// dragLeaveEvent +//--------------------------------------------------------- + +void WaveCanvas::dragLeaveEvent(QDragLeaveEvent*) + { + //printf("drag leave\n"); DELETETHIS (whole function?) + //event->acceptProposedAction(); + } + +//--------------------------------------------------------- +// itemPressed +//--------------------------------------------------------- + +void WaveCanvas::itemPressed(const MusEGui::CItem*) + { + } + +//--------------------------------------------------------- +// itemReleased +//--------------------------------------------------------- + +void WaveCanvas::itemReleased(const MusEGui::CItem*, const QPoint&) + { + } + +//--------------------------------------------------------- +// itemMoved +//--------------------------------------------------------- + +void WaveCanvas::itemMoved(const MusEGui::CItem*, const QPoint&) + { + } + +//--------------------------------------------------------- +// curPartChanged +//--------------------------------------------------------- + +void WaveCanvas::curPartChanged() + { + EventCanvas::curPartChanged(); + editor->setWindowTitle(getCaption()); + } + +//--------------------------------------------------------- +// modifySelected +//--------------------------------------------------------- + +void WaveCanvas::modifySelected(MusEGui::NoteInfo::ValType type, int val, bool delta_mode) + { + // TODO: New WaveCanvas: Convert this routine to frames and remove unneeded operations. + QList< QPair<MusECore::EventList*,MusECore::Event> > already_done; + MusEGlobal::audio->msgIdle(true); + MusEGlobal::song->startUndo(); + for (MusEGui::iCItem i = items.begin(); i != items.end(); ++i) { + if (!(i->second->isSelected())) + continue; + WEvent* e = (WEvent*)(i->second); + MusECore::Event event = e->event(); + if (event.type() != MusECore::Note) + continue; + + MusECore::WavePart* part = (MusECore::WavePart*)(e->part()); + + if (already_done.contains(QPair<MusECore::EventList*,MusECore::Event>(part->events(), event))) + continue; + + MusECore::Event newEvent = event.clone(); + + switch (type) { + case MusEGui::NoteInfo::VAL_TIME: + { + int newTime = val; + if(delta_mode) + newTime += event.tick(); + else + newTime -= part->tick(); + if (newTime < 0) + newTime = 0; + newEvent.setTick(newTime); + } + break; + case MusEGui::NoteInfo::VAL_LEN: + { + int len = val; + if(delta_mode) + len += event.lenTick(); + if (len < 1) + len = 1; + newEvent.setLenTick(len); + } + break; + case MusEGui::NoteInfo::VAL_VELON: + { + int velo = val; + if(delta_mode) + velo += event.velo(); + if (velo > 127) + velo = 127; + else if (velo < 0) + velo = 0; + newEvent.setVelo(velo); + } + break; + case MusEGui::NoteInfo::VAL_VELOFF: + { + int velo = val; + if(delta_mode) + velo += event.veloOff(); + if (velo > 127) + velo = 127; + else if (velo < 0) + velo = 0; + newEvent.setVeloOff(velo); + } + break; + case MusEGui::NoteInfo::VAL_PITCH: + { + int pitch = val; + if(delta_mode) + pitch += event.pitch(); + if (pitch > 127) + pitch = 127; + else if (pitch < 0) + pitch = 0; + newEvent.setPitch(pitch); + } + break; + } + + MusEGlobal::song->changeEvent(event, newEvent, part); + // Indicate do not do port controller values and clone parts. + MusEGlobal::song->addUndo(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, newEvent, event, part, false, false)); + + already_done.append(QPair<MusECore::EventList*,MusECore::Event>(part->events(), event)); + } + MusEGlobal::song->endUndo(SC_EVENT_MODIFIED); + MusEGlobal::audio->msgIdle(false); + } + +//--------------------------------------------------------- +// resizeEvent +//--------------------------------------------------------- + +void WaveCanvas::resizeEvent(QResizeEvent* ev) + { + // Readjust all wave canvas item heights + bool do_redraw = false; + for (iCItem k = items.begin(); k != items.end(); ++k) + { + if(k->second->height() != ev->size().height()) + { + k->second->setHeight(ev->size().height()); + do_redraw = true; + } + } + + if (ev->size().width() != ev->oldSize().width()) + emit newWidth(ev->size().width()); + EventCanvas::resizeEvent(ev); + + if(do_redraw) + redraw(); + } + +} // namespace MusEGui diff --git a/muse2/muse/waveedit/wavecanvas.h b/muse2/muse/waveedit/wavecanvas.h new file mode 100644 index 00000000..df285abc --- /dev/null +++ b/muse2/muse/waveedit/wavecanvas.h @@ -0,0 +1,185 @@ +//========================================================= +// MusE +// Linux Music Editor +// wavecanvas.h +// (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) +// +// Based on WaveView.cpp and PianoCanvas.cpp +// (C) Copyright 2000 Werner Schweer (ws@seh.de) +// and others. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; version 2 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +//========================================================= + +#ifndef __WAVECANVAS_H__ +#define __WAVECANVAS_H__ + +#include "type_defs.h" +#include "ecanvas.h" +#include <QDragEnterEvent> +#include <QDropEvent> +#include <QMouseEvent> +#include <QDragMoveEvent> +#include <QDragLeaveEvent> +#include <QWheelEvent> +#include <QResizeEvent> +#include <QTimer> + +class QRect; + +namespace MusECore { +class SndFileR; +class WavePart; +class WaveTrack; + +struct WaveEventSelection { + SndFileR file; + unsigned startframe; + unsigned endframe; + }; + +typedef std::list<WaveEventSelection> WaveSelectionList; +typedef std::list<WaveEventSelection>::iterator iWaveSelection; +} + +namespace MusEGui { + +//--------------------------------------------------------- +// WEvent +// ''visual'' Wave Event +//--------------------------------------------------------- + +class WEvent : public CItem { + public: + WEvent(MusECore::Event& e, MusECore::Part* p, int height); + }; + +//--------------------------------------------------------- +// WaveCanvas +//--------------------------------------------------------- + +class WaveCanvas : public EventCanvas { + Q_OBJECT + + enum { NORMAL, DRAG } mode; + enum { MUTE = 0, NORMALIZE, FADE_IN, FADE_OUT, REVERSE, GAIN, EDIT_EXTERNAL, CUT, COPY, PASTE }; //!< Modify operations + + int yScale; + int button; + unsigned startSample; + unsigned endSample; + int colorMode; + int selectionStart, selectionStop, dragstartx; + int lastGainvalue; //!< Stores the last used gainvalue when specifiying gain value in the editgain dialog + QString copiedPart; + + //bool getUniqueTmpfileName(QString& newFilename); //!< Generates unique filename for temporary SndFile + MusECore::WaveSelectionList getSelection(unsigned startpos, unsigned stoppos); + void modifySelection(int operation, unsigned startpos, unsigned stoppos, double paramA); //!< Modifies selection + void muteSelection(unsigned channels, float** data, unsigned length); //!< Mutes selection + void normalizeSelection(unsigned channels, float** data, unsigned length); //!< Normalizes selection + void fadeInSelection(unsigned channels, float** data, unsigned length); //!< Linear fade in of selection + void fadeOutSelection(unsigned channels, float** data, unsigned length); //!< Linear fade out of selection + void reverseSelection(unsigned channels, float** data, unsigned length); //!< Reverse selection + void applyGain(unsigned channels, float** data, unsigned length, double gain); //!< Apply gain to selection + void copySelection(unsigned file_channels, float** tmpdata, unsigned tmpdatalen, bool blankData, unsigned format, unsigned sampleRate); + void editExternal(unsigned file_format, unsigned file_samplerate, unsigned channels, float** data, unsigned length); + //void applyLadspa(unsigned channels, float** data, unsigned length); //!< Apply LADSPA plugin on selection + + + protected: + virtual QPoint raster(const QPoint&) const; + void drawTickRaster(QPainter& p, int x, int y, int w, int h, int raster); + void drawParts(QPainter&, const QRect&, bool do_cur_part); + + // REMOVE Tim. + //virtual void pdraw(QPainter&, const QRect&); + virtual void draw(QPainter&, const QRect&); + virtual void viewMouseDoubleClickEvent(QMouseEvent*); + virtual void wheelEvent(QWheelEvent*); + virtual bool mousePress(QMouseEvent*); + virtual void mouseMove(QMouseEvent* event); + virtual void mouseRelease(const QPoint&); + virtual void drawItem(QPainter&, const CItem*, const QRect&); + void drawTopItem(QPainter &p, const QRect &rect); + virtual void drawMoving(QPainter&, const CItem*, const QRect&); + virtual MusECore::Undo moveCanvasItems(CItemList&, int, int, DragType); + virtual bool moveItem(MusECore::Undo&, CItem*, const QPoint&, DragType); + virtual CItem* newItem(const QPoint&, int); + virtual void resizeItem(CItem*, bool noSnap, bool); + virtual void newItem(CItem*, bool noSnap); + virtual bool deleteItem(CItem*); + virtual void startDrag(CItem* item, bool copymode); + virtual void dragEnterEvent(QDragEnterEvent* event); + virtual void dragMoveEvent(QDragMoveEvent*); + virtual void dragLeaveEvent(QDragLeaveEvent*); + virtual CItem* addItem(MusECore::Part*, MusECore::Event&); + + int y2pitch(int) const; + int pitch2y(int) const; + virtual void drawCanvas(QPainter&, const QRect&); + virtual void itemPressed(const CItem*); + virtual void itemReleased(const CItem*, const QPoint&); + virtual void itemMoved(const CItem*, const QPoint&); + virtual void curPartChanged(); + virtual void resizeEvent(QResizeEvent*); + + private slots: + void setPos(int idx, unsigned val, bool adjustScrollbar); + + signals: + void quantChanged(int); + void rasterChanged(int); + void newWidth(int); + void mouseWheelMoved(int); + + public slots: + void setYScale(int); + void waveCmd(int); + + public: + + enum { CMD_MUTE=0, CMD_NORMALIZE, CMD_FADE_IN, CMD_FADE_OUT, CMD_REVERSE, + CMD_GAIN_FREE, CMD_GAIN_200, CMD_GAIN_150, CMD_GAIN_75, CMD_GAIN_50, CMD_GAIN_25, + CMD_EDIT_COPY, CMD_EDIT_CUT, CMD_EDIT_PASTE, CMD_PASTE_DIALOG, CMD_DEL, + CMD_EDIT_EXTERNAL, + CMD_QUANTIZE, + CMD_SELECT_ALL, CMD_SELECT_NONE, CMD_SELECT_INVERT, + CMD_SELECT_ILOOP, CMD_SELECT_OLOOP, CMD_SELECT_PREV_PART, CMD_SELECT_NEXT_PART, + CMD_ERASE_MEASURE, CMD_DELETE_MEASURE, CMD_CREATE_MEASURE + }; + + WaveCanvas(MidiEditor*, QWidget*, int, int); + virtual ~WaveCanvas(); + MusECore::WaveTrack* track() const; + void cmd(int cmd); + void setColorMode(int mode) { + colorMode = mode; + redraw(); + } + QString getCaption() const; + void songChanged(MusECore::SongChangedFlags_t); + void range(int* s, int* e) const { *s = startSample; *e = endSample; } + void selectAtTick(unsigned int tick); + void selectAtFrame(unsigned int frame); + void modifySelected(NoteInfo::ValType type, int val, bool delta_mode = true); + void keyPress(QKeyEvent*); + }; + +} // namespace MusEGui + +#endif + diff --git a/muse2/muse/waveedit/waveedit.cpp b/muse2/muse/waveedit/waveedit.cpp index e1f18722..8290128f 100644 --- a/muse2/muse/waveedit/waveedit.cpp +++ b/muse2/muse/waveedit/waveedit.cpp @@ -3,6 +3,7 @@ // Linux Music Editor // $Id: waveedit.cpp,v 1.5.2.12 2009/04/06 01:24:54 terminator356 Exp $ // (C) Copyright 2000 Werner Schweer (ws@seh.de) +// (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -20,6 +21,8 @@ // //========================================================= +#include <limits.h> + #include <QMenu> #include <QSignalMapper> #include <QToolBar> @@ -41,7 +44,8 @@ #include "waveedit.h" #include "mtscale.h" #include "scrollscale.h" -#include "waveview.h" +//#include "waveview.h" +#include "wavecanvas.h" #include "ttoolbar.h" #include "globals.h" #include "audio.h" @@ -51,6 +55,7 @@ #include "gconfig.h" #include "icons.h" #include "shortcuts.h" +#include "cmd.h" namespace MusECore { extern QColor readColor(MusECore::Xml& xml); @@ -58,6 +63,12 @@ extern QColor readColor(MusECore::Xml& xml); namespace MusEGui { +static int waveEditTools = MusEGui::PointerTool | MusEGui::PencilTool | MusEGui::RubberTool | + MusEGui::CutTool | MusEGui::CursorTool; + +int WaveEdit::_rasterInit = 96; +int WaveEdit::colorModeInit = 0; + //--------------------------------------------------------- // closeEvent //--------------------------------------------------------- @@ -81,8 +92,10 @@ WaveEdit::WaveEdit(MusECore::PartList* pl) : MidiEditor(TopWin::WAVE, 1, pl) { setFocusPolicy(Qt::NoFocus); + colorMode = colorModeInit; QSignalMapper* mapper = new QSignalMapper(this); + QSignalMapper* colorMapper = new QSignalMapper(this); QAction* act; //---------Pulldown Menu---------------------------- @@ -95,27 +108,27 @@ WaveEdit::WaveEdit(MusECore::PartList* pl) menuGain = menuFunctions->addMenu(tr("&Gain")); act = menuGain->addAction("200%"); - mapper->setMapping(act, CMD_GAIN_200); + mapper->setMapping(act, WaveCanvas::CMD_GAIN_200); connect(act, SIGNAL(triggered()), mapper, SLOT(map())); act = menuGain->addAction("150%"); - mapper->setMapping(act, CMD_GAIN_150); + mapper->setMapping(act, WaveCanvas::CMD_GAIN_150); connect(act, SIGNAL(triggered()), mapper, SLOT(map())); act = menuGain->addAction("75%"); - mapper->setMapping(act, CMD_GAIN_75); + mapper->setMapping(act, WaveCanvas::CMD_GAIN_75); connect(act, SIGNAL(triggered()), mapper, SLOT(map())); act = menuGain->addAction("50%"); - mapper->setMapping(act, CMD_GAIN_50); + mapper->setMapping(act, WaveCanvas::CMD_GAIN_50); connect(act, SIGNAL(triggered()), mapper, SLOT(map())); act = menuGain->addAction("25%"); - mapper->setMapping(act, CMD_GAIN_25); + mapper->setMapping(act, WaveCanvas::CMD_GAIN_25); connect(act, SIGNAL(triggered()), mapper, SLOT(map())); act = menuGain->addAction(tr("Other")); - mapper->setMapping(act, CMD_GAIN_FREE); + mapper->setMapping(act, WaveCanvas::CMD_GAIN_FREE); connect(act, SIGNAL(triggered()), mapper, SLOT(map())); connect(mapper, SIGNAL(mapped(int)), this, SLOT(cmd(int))); @@ -123,65 +136,99 @@ WaveEdit::WaveEdit(MusECore::PartList* pl) menuFunctions->addSeparator(); copyAction = menuEdit->addAction(tr("&Copy")); - mapper->setMapping(copyAction, CMD_EDIT_COPY); + mapper->setMapping(copyAction, WaveCanvas::CMD_EDIT_COPY); connect(copyAction, SIGNAL(triggered()), mapper, SLOT(map())); cutAction = menuEdit->addAction(tr("C&ut")); - mapper->setMapping(cutAction, CMD_EDIT_CUT); + mapper->setMapping(cutAction, WaveCanvas::CMD_EDIT_CUT); connect(cutAction, SIGNAL(triggered()), mapper, SLOT(map())); pasteAction = menuEdit->addAction(tr("&Paste")); - mapper->setMapping(pasteAction, CMD_EDIT_PASTE); + mapper->setMapping(pasteAction, WaveCanvas::CMD_EDIT_PASTE); connect(pasteAction, SIGNAL(triggered()), mapper, SLOT(map())); act = menuEdit->addAction(tr("Edit in E&xternal Editor")); - mapper->setMapping(act, CMD_EDIT_EXTERNAL); + mapper->setMapping(act, WaveCanvas::CMD_EDIT_EXTERNAL); connect(act, SIGNAL(triggered()), mapper, SLOT(map())); act = menuFunctions->addAction(tr("Mute Selection")); - mapper->setMapping(act, CMD_MUTE); + mapper->setMapping(act, WaveCanvas::CMD_MUTE); connect(act, SIGNAL(triggered()), mapper, SLOT(map())); act = menuFunctions->addAction(tr("Normalize Selection")); - mapper->setMapping(act, CMD_NORMALIZE); + mapper->setMapping(act, WaveCanvas::CMD_NORMALIZE); connect(act, SIGNAL(triggered()), mapper, SLOT(map())); act = menuFunctions->addAction(tr("Fade In Selection")); - mapper->setMapping(act, CMD_FADE_IN); + mapper->setMapping(act, WaveCanvas::CMD_FADE_IN); connect(act, SIGNAL(triggered()), mapper, SLOT(map())); act = menuFunctions->addAction(tr("Fade Out Selection")); - mapper->setMapping(act, CMD_FADE_OUT); + mapper->setMapping(act, WaveCanvas::CMD_FADE_OUT); connect(act, SIGNAL(triggered()), mapper, SLOT(map())); act = menuFunctions->addAction(tr("Reverse Selection")); - mapper->setMapping(act, CMD_REVERSE); + mapper->setMapping(act, WaveCanvas::CMD_REVERSE); connect(act, SIGNAL(triggered()), mapper, SLOT(map())); select = menuEdit->addMenu(QIcon(*selectIcon), tr("Select")); selectAllAction = select->addAction(QIcon(*select_allIcon), tr("Select &All")); - mapper->setMapping(selectAllAction, CMD_SELECT_ALL); + mapper->setMapping(selectAllAction, WaveCanvas::CMD_SELECT_ALL); connect(selectAllAction, SIGNAL(triggered()), mapper, SLOT(map())); selectNoneAction = select->addAction(QIcon(*select_allIcon), tr("&Deselect All")); - mapper->setMapping(selectNoneAction, CMD_SELECT_NONE); + mapper->setMapping(selectNoneAction, WaveCanvas::CMD_SELECT_NONE); connect(selectNoneAction, SIGNAL(triggered()), mapper, SLOT(map())); + select->addSeparator(); + + selectPrevPartAction = select->addAction(QIcon(*select_all_parts_on_trackIcon), tr("&Previous Part")); + mapper->setMapping(selectPrevPartAction, WaveCanvas::CMD_SELECT_PREV_PART); + connect(selectPrevPartAction, SIGNAL(triggered()), mapper, SLOT(map())); + + selectNextPartAction = select->addAction(QIcon(*select_all_parts_on_trackIcon), tr("&Next Part")); + mapper->setMapping(selectNextPartAction, WaveCanvas::CMD_SELECT_NEXT_PART); + connect(selectNextPartAction, SIGNAL(triggered()), mapper, SLOT(map())); + QMenu* settingsMenu = menuBar()->addMenu(tr("Window &Config")); + + eventColor = settingsMenu->addMenu(tr("&Event Color")); + + QActionGroup* actgrp = new QActionGroup(this); + actgrp->setExclusive(true); + + evColorNormalAction = actgrp->addAction(tr("&Part colors")); + evColorNormalAction->setCheckable(true); + colorMapper->setMapping(evColorNormalAction, 0); + + evColorPartsAction = actgrp->addAction(tr("&Gray")); + evColorPartsAction->setCheckable(true); + colorMapper->setMapping(evColorPartsAction, 1); + + connect(evColorNormalAction, SIGNAL(triggered()), colorMapper, SLOT(map())); + connect(evColorPartsAction, SIGNAL(triggered()), colorMapper, SLOT(map())); + + eventColor->addActions(actgrp->actions()); + + connect(colorMapper, SIGNAL(mapped(int)), this, SLOT(eventColorModeChanged(int))); + + settingsMenu->addSeparator(); settingsMenu->addAction(subwinAction); settingsMenu->addAction(shareAction); settingsMenu->addAction(fullscreenAction); - connect(MusEGlobal::muse, SIGNAL(configChanged()), SLOT(configChanged())); //-------------------------------------------------- // ToolBar: Solo Cursor1 Cursor2 + tools2 = new MusEGui::EditToolBar(this, waveEditTools); + addToolBar(tools2); + addToolBarBreak(); tb1 = addToolBar(tr("WaveEdit tools")); tb1->setObjectName("WaveEdit tools"); @@ -222,8 +269,9 @@ WaveEdit::WaveEdit(MusECore::PartList* pl) } hscroll = new ScrollScale(-32768, 1, xscale, 10000, Qt::Horizontal, mainw, 0, false, 10000.0); - view = new WaveView(this, mainw, xscale, yscale); - wview = view; // HACK! + //view = new WaveView(this, mainw, xscale, yscale); + canvas = new WaveCanvas(this, mainw, xscale, yscale); + //wview = canvas; // HACK! QSizeGrip* corner = new QSizeGrip(mainw); ymag = new QSlider(Qt::Vertical, mainw); @@ -235,10 +283,10 @@ WaveEdit::WaveEdit(MusECore::PartList* pl) time = new MTScale(&_raster, mainw, xscale, true); ymag->setFixedWidth(16); - connect(view, SIGNAL(mouseWheelMoved(int)), this, SLOT(moveVerticalSlider(int))); - connect(ymag, SIGNAL(valueChanged(int)), view, SLOT(setYScale(int))); - connect(view, SIGNAL(horizontalZoomIn()), SLOT(horizontalZoomIn())); - connect(view, SIGNAL(horizontalZoomOut()), SLOT(horizontalZoomOut())); + connect(canvas, SIGNAL(mouseWheelMoved(int)), this, SLOT(moveVerticalSlider(int))); + connect(ymag, SIGNAL(valueChanged(int)), canvas, SLOT(setYScale(int))); + connect(canvas, SIGNAL(horizontalZoomIn()), SLOT(horizontalZoomIn())); + connect(canvas, SIGNAL(horizontalZoomOut()), SLOT(horizontalZoomOut())); time->setOrigin(0, 0); @@ -247,28 +295,38 @@ WaveEdit::WaveEdit(MusECore::PartList* pl) mainGrid->addWidget(time, 0, 0, 1, 2); mainGrid->addWidget(MusECore::hLine(mainw), 1, 0, 1, 2); - mainGrid->addWidget(view, 2, 0); + mainGrid->addWidget(canvas, 2, 0); mainGrid->addWidget(ymag, 2, 1); mainGrid->addWidget(hscroll, 3, 0); mainGrid->addWidget(corner, 3, 1, Qt::AlignBottom | Qt::AlignRight); - view->setFocus(); + canvas->setFocus(); - connect(hscroll, SIGNAL(scrollChanged(int)), view, SLOT(setXPos(int))); - connect(hscroll, SIGNAL(scaleChanged(int)), view, SLOT(setXMag(int))); - setWindowTitle(view->getCaption()); - connect(view, SIGNAL(followEvent(int)), hscroll, SLOT(setOffset(int))); + connect(canvas, SIGNAL(toolChanged(int)), tools2, SLOT(set(int))); + connect(tools2, SIGNAL(toolChanged(int)), canvas, SLOT(setTool(int))); + + connect(hscroll, SIGNAL(scrollChanged(int)), canvas, SLOT(setXPos(int))); + connect(hscroll, SIGNAL(scaleChanged(int)), canvas, SLOT(setXMag(int))); + setWindowTitle(canvas->getCaption()); + connect(canvas, SIGNAL(followEvent(int)), hscroll, SLOT(setOffset(int))); connect(hscroll, SIGNAL(scrollChanged(int)), time, SLOT(setXPos(int))); connect(hscroll, SIGNAL(scaleChanged(int)), time, SLOT(setXMag(int))); -// connect(time, SIGNAL(timeChanged(unsigned)), SLOT(setTime(unsigned))); - connect(view, SIGNAL(timeChanged(unsigned)), SLOT(setTime(unsigned))); + connect(time, SIGNAL(timeChanged(unsigned)), SLOT(timeChanged(unsigned))); + connect(canvas, SIGNAL(timeChanged(unsigned)), SLOT(setTime(unsigned))); - connect(view, SIGNAL(horizontalScroll(unsigned)),hscroll, SLOT(setPos(unsigned))); + connect(canvas, SIGNAL(horizontalScroll(unsigned)),hscroll, SLOT(setPos(unsigned))); + connect(canvas, SIGNAL(horizontalScrollNoLimit(unsigned)),hscroll, SLOT(setPosNoLimit(unsigned))); connect(hscroll, SIGNAL(scaleChanged(int)), SLOT(updateHScrollRange())); connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedFlags_t)), SLOT(songChanged1(MusECore::SongChangedFlags_t))); + // For the wave editor, let's start with the operation range selection tool. + canvas->setTool(MusEGui::CursorTool); + tools2->set(MusEGui::CursorTool); + + setEventColorMode(colorMode); + initShortcuts(); updateHScrollRange(); @@ -291,6 +349,17 @@ void WaveEdit::initShortcuts() pasteAction->setShortcut(shortcuts[SHRT_PASTE].key); selectAllAction->setShortcut(shortcuts[SHRT_SELECT_ALL].key); selectNoneAction->setShortcut(shortcuts[SHRT_SELECT_NONE].key); + + //selectInvertAction->setShortcut(shortcuts[SHRT_SELECT_INVERT].key); + //selectInsideLoopAction->setShortcut(shortcuts[SHRT_SELECT_ILOOP].key); + //selectOutsideLoopAction->setShortcut(shortcuts[SHRT_SELECT_OLOOP].key); + selectPrevPartAction->setShortcut(shortcuts[SHRT_SELECT_PREV_PART].key); + selectNextPartAction->setShortcut(shortcuts[SHRT_SELECT_NEXT_PART].key); + + eventColor->menuAction()->setShortcut(shortcuts[SHRT_EVENT_COLOR].key); + //evColorNormalAction->setShortcut(shortcuts[ ].key); + //evColorPartsAction->setShortcut(shortcuts[ ].key); + } //--------------------------------------------------------- @@ -299,9 +368,15 @@ void WaveEdit::initShortcuts() void WaveEdit::configChanged() { - view->setBg(MusEGlobal::config.waveEditBackgroundColor); - selectAllAction->setShortcut(shortcuts[SHRT_SELECT_ALL].key); - selectNoneAction->setShortcut(shortcuts[SHRT_SELECT_NONE].key); + if (MusEGlobal::config.canvasBgPixmap.isEmpty()) { + canvas->setBg(MusEGlobal::config.waveEditBackgroundColor); + canvas->setBg(QPixmap()); + } + else { + canvas->setBg(QPixmap(MusEGlobal::config.canvasBgPixmap)); + } + + initShortcuts(); } //--------------------------------------------------------- @@ -310,17 +385,39 @@ void WaveEdit::configChanged() void WaveEdit::updateHScrollRange() { int s, e; - wview->range(&s, &e); - // Show one more measure. - e += AL::sigmap.ticksMeasure(e); - // Show another quarter measure due to imprecise drawing at canvas end point. - e += AL::sigmap.ticksMeasure(e) / 4; + canvas->range(&s, &e); // Range in frames + unsigned tm = AL::sigmap.ticksMeasure(MusEGlobal::tempomap.frame2tick(e)); + + // Show one more measure, and show another quarter measure due to imprecise drawing at canvas end point. + //e += MusEGlobal::tempomap.tick2frame(tm + tm / 4); // TODO: Try changing scrollbar to use units of frames? + e += (tm + tm / 4); + // Compensate for the vscroll width. //e += wview->rmapxDev(-vscroll->width()); int s1, e1; - hscroll->range(&s1, &e1); + hscroll->range(&s1, &e1); // ... if(s != s1 || e != e1) - hscroll->setRange(s, e); + hscroll->setRange(s, e); // ... +} + +//--------------------------------------------------------- +// timeChanged +//--------------------------------------------------------- + +void WaveEdit::timeChanged(unsigned t) +{ + if(t == INT_MAX) + { + // Let the PosLabels disable themselves with INT_MAX. + pos1->setValue(t); + pos2->setValue(t); + return; + } + + unsigned frame = MusEGlobal::tempomap.tick2frame(t); + pos1->setValue(t); + pos2->setValue(frame); + time->setPos(3, t, false); } //--------------------------------------------------------- @@ -329,10 +426,16 @@ void WaveEdit::updateHScrollRange() void WaveEdit::setTime(unsigned samplepos) { -// printf("setTime %d %x\n", samplepos, samplepos); + if(samplepos == INT_MAX) + { + // Let the PosLabels disable themselves with INT_MAX. + pos1->setValue(samplepos); + pos2->setValue(samplepos); + return; + } + unsigned tick = MusEGlobal::tempomap.frame2tick(samplepos); pos1->setValue(tick); - //pos2->setValue(tick); pos2->setValue(samplepos); time->setPos(3, tick, false); } @@ -343,7 +446,6 @@ void WaveEdit::setTime(unsigned samplepos) WaveEdit::~WaveEdit() { - // MusEGlobal::undoRedo->removeFrom(tools); // p4.0.6 Removed } //--------------------------------------------------------- @@ -352,7 +454,7 @@ WaveEdit::~WaveEdit() void WaveEdit::cmd(int n) { - view->cmd(n); + ((WaveCanvas*)canvas)->cmd(n); } //--------------------------------------------------------- @@ -368,6 +470,10 @@ void WaveEdit::readConfiguration(MusECore::Xml& xml) case MusECore::Xml::TagStart: if (tag == "bgcolor") MusEGlobal::config.waveEditBackgroundColor = readColor(xml); + else if (tag == "raster") + _rasterInit = xml.parseInt(); + else if (tag == "colormode") + colorModeInit = xml.parseInt(); else if (tag == "topwin") TopWin::readConfiguration(WAVE, xml); else @@ -393,6 +499,8 @@ void WaveEdit::writeConfiguration(int level, MusECore::Xml& xml) { xml.tag(level++, "waveedit"); xml.colorTag(level, "bgcolor", MusEGlobal::config.waveEditBackgroundColor); + xml.intTag(level, "raster", _rasterInit); + xml.intTag(level, "colormode", colorModeInit); TopWin::writeConfiguration(WAVE, level,xml); xml.tag(level, "/waveedit"); } @@ -406,6 +514,7 @@ void WaveEdit::writeStatus(int level, MusECore::Xml& xml) const writePartList(level, xml); xml.tag(level++, "waveedit"); MidiEditor::writeStatus(level, xml); + xml.intTag(level, "tool", int(canvas->tool())); xml.intTag(level, "xpos", hscroll->pos()); xml.intTag(level, "xmag", hscroll->mag()); xml.intTag(level, "ymag", ymag->value()); @@ -427,6 +536,11 @@ void WaveEdit::readStatus(MusECore::Xml& xml) case MusECore::Xml::TagStart: if (tag == "midieditor") MidiEditor::readStatus(xml); + else if (tag == "tool") { + int tool = xml.parseInt(); + canvas->setTool(tool); + tools2->set(tool); + } else if (tag == "xmag") hscroll->setMag(xml.parseInt()); else if (tag == "ymag") @@ -486,14 +600,170 @@ void WaveEdit::soloChanged(bool flag) void WaveEdit::keyPressEvent(QKeyEvent* event) { +// TODO: Raster: +// int index; +// int n = sizeof(rasterTable)/sizeof(*rasterTable); +// for (index = 0; index < n; ++index) +// if (rasterTable[index] == raster()) +// break; +// if (index == n) { +// index = 0; +// // raster 1 is not in table +// } +// int off = (index / 9) * 9; +// index = index % 9; + +// int val = 0; + + WaveCanvas* wc = (WaveCanvas*)canvas; int key = event->key(); + + if (((QInputEvent*)event)->modifiers() & Qt::ShiftModifier) + key += Qt::SHIFT; + if (((QInputEvent*)event)->modifiers() & Qt::AltModifier) + key += Qt::ALT; + if (((QInputEvent*)event)->modifiers() & Qt::ControlModifier) + key+= Qt::CTRL; + if (key == Qt::Key_Escape) { close(); return; } - else { + + else if (key == shortcuts[SHRT_POS_INC].key) { + wc->waveCmd(CMD_RIGHT); + return; + } + else if (key == shortcuts[SHRT_POS_DEC].key) { + wc->waveCmd(CMD_LEFT); + return; + } + else if (key == shortcuts[SHRT_POS_INC_NOSNAP].key) { + wc->waveCmd(CMD_RIGHT_NOSNAP); + return; + } + else if (key == shortcuts[SHRT_POS_DEC_NOSNAP].key) { + wc->waveCmd(CMD_LEFT_NOSNAP); + return; + } + else if (key == shortcuts[SHRT_INSERT_AT_LOCATION].key) { + wc->waveCmd(CMD_INSERT); + return; + } + else if (key == Qt::Key_Backspace) { + wc->waveCmd(CMD_BACKSPACE); + return; + } + + else if (key == shortcuts[SHRT_TOOL_POINTER].key) { + tools2->set(MusEGui::PointerTool); + return; + } + else if (key == shortcuts[SHRT_TOOL_PENCIL].key) { + tools2->set(MusEGui::PencilTool); + return; + } + else if (key == shortcuts[SHRT_TOOL_RUBBER].key) { + tools2->set(MusEGui::RubberTool); + return; + } + else if (key == shortcuts[SHRT_TOOL_SCISSORS].key) { + tools2->set(MusEGui::CutTool); + return; + } + else if (key == shortcuts[SHRT_TOOL_CURSOR].key) { + tools2->set(MusEGui::CursorTool); + return; + } + else if (key == shortcuts[SHRT_EVENT_COLOR].key) { + if (colorMode == 0) + colorMode = 1; + else if (colorMode == 1) + colorMode = 0; + setEventColorMode(colorMode); + return; + } + + // TODO: New WaveCanvas: Convert some of these to use frames. + else if (key == shortcuts[SHRT_ZOOM_IN].key) { + int mag = hscroll->mag(); + int zoomlvl = MusEGui::ScrollScale::getQuickZoomLevel(mag); + if (zoomlvl < MusEGui::ScrollScale::zoomLevels-1) + zoomlvl++; + + int newmag = MusEGui::ScrollScale::convertQuickZoomLevelToMag(zoomlvl); + hscroll->setMag(newmag); + //printf("mag = %d zoomlvl = %d newmag = %d\n", mag, zoomlvl, newmag); + return; + } + else if (key == shortcuts[SHRT_ZOOM_OUT].key) { + int mag = hscroll->mag(); + int zoomlvl = MusEGui::ScrollScale::getQuickZoomLevel(mag); + if (zoomlvl > 1) + zoomlvl--; + + int newmag = MusEGui::ScrollScale::convertQuickZoomLevelToMag(zoomlvl); + hscroll->setMag(newmag); + //printf("mag = %d zoomlvl = %d newmag = %d\n", mag, zoomlvl, newmag); + return; + } + else if (key == shortcuts[SHRT_GOTO_CPOS].key) { + MusECore::PartList* p = this->parts(); + MusECore::Part* first = p->begin()->second; + hscroll->setPos(MusEGlobal::song->cpos() - first->tick() ); + return; + } + else if (key == shortcuts[SHRT_SCROLL_LEFT].key) { + int pos = hscroll->pos() - MusEGlobal::config.division; + if (pos < 0) + pos = 0; + hscroll->setPos(pos); + return; + } + else if (key == shortcuts[SHRT_SCROLL_RIGHT].key) { + int pos = hscroll->pos() + MusEGlobal::config.division; + hscroll->setPos(pos); + return; + } + +// TODO: Raster: +// else if (key == shortcuts[SHRT_SET_QUANT_1].key) +// val = rasterTable[8 + off]; +// else if (key == shortcuts[SHRT_SET_QUANT_2].key) +// val = rasterTable[7 + off]; +// else if (key == shortcuts[SHRT_SET_QUANT_3].key) +// val = rasterTable[6 + off]; +// else if (key == shortcuts[SHRT_SET_QUANT_4].key) +// val = rasterTable[5 + off]; +// else if (key == shortcuts[SHRT_SET_QUANT_5].key) +// val = rasterTable[4 + off]; +// else if (key == shortcuts[SHRT_SET_QUANT_6].key) +// val = rasterTable[3 + off]; +// else if (key == shortcuts[SHRT_SET_QUANT_7].key) +// val = rasterTable[2 + off]; +// else if (key == shortcuts[SHRT_TOGGLE_TRIOL].key) +// val = rasterTable[index + ((off == 0) ? 9 : 0)]; +// else if (key == shortcuts[SHRT_TOGGLE_PUNCT].key) +// val = rasterTable[index + ((off == 18) ? 9 : 18)]; +// else if (key == shortcuts[SHRT_TOGGLE_PUNCT2].key) {//CDW +// if ((off == 18) && (index > 2)) { +// val = rasterTable[index + 9 - 1]; +// } +// else if ((off == 9) && (index < 8)) { +// val = rasterTable[index + 18 + 1]; +// } +// else +// return; +// } + + else { //Default: event->ignore(); + return; } + + // TODO: Raster: + //setRaster(val); + //toolbar->setRaster(_raster); } //--------------------------------------------------------- @@ -540,9 +810,38 @@ void WaveEdit::focusCanvas() { if(MusEGlobal::config.smartFocus) { - view->setFocus(); - view->activateWindow(); + canvas->setFocus(); + canvas->activateWindow(); } } +//--------------------------------------------------------- +// eventColorModeChanged +//--------------------------------------------------------- + +void WaveEdit::eventColorModeChanged(int mode) + { + colorMode = mode; + colorModeInit = colorMode; + + ((WaveCanvas*)(canvas))->setColorMode(colorMode); + } + +//--------------------------------------------------------- +// setEventColorMode +//--------------------------------------------------------- + +void WaveEdit::setEventColorMode(int mode) + { + colorMode = mode; + colorModeInit = colorMode; + + evColorNormalAction->setChecked(mode == 0); + evColorPartsAction->setChecked(mode == 1); + + ((WaveCanvas*)(canvas))->setColorMode(colorMode); + } + + + } // namespace MusEGui diff --git a/muse2/muse/waveedit/waveedit.h b/muse2/muse/waveedit/waveedit.h index d52eb986..2d74391a 100644 --- a/muse2/muse/waveedit/waveedit.h +++ b/muse2/muse/waveedit/waveedit.h @@ -3,6 +3,7 @@ // Linux Music Editor // $Id: waveedit.h,v 1.3.2.8 2008/01/26 07:23:21 terminator356 Exp $ // (C) Copyright 2000 Werner Schweer (ws@seh.de) +// (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -33,6 +34,7 @@ #include "type_defs.h" #include "midieditor.h" +#include "tools.h" class QAction; class QResizeEvent; @@ -48,7 +50,7 @@ namespace MusEGui { class PosLabel; class ScrollScale; class SNode; -class WaveView; +class WaveCanvas; //--------------------------------------------------------- // WaveEdit @@ -57,7 +59,6 @@ class WaveView; class WaveEdit : public MidiEditor { Q_OBJECT - WaveView* view; QSlider* ymag; QToolBar* tb1; QToolButton* solo; @@ -68,22 +69,32 @@ class WaveEdit : public MidiEditor { QAction* cutAction; QAction* copyAction; QAction* pasteAction; - + QAction* selectPrevPartAction; + QAction* selectNextPartAction; + QAction* evColorNormalAction; + QAction* evColorPartsAction; + MusEGui::EditToolBar* tools2; + QMenu* menuFunctions, *select, *menuGain, *eventColor; + int colorMode; + static int _rasterInit; + static int colorModeInit; + virtual void closeEvent(QCloseEvent*); virtual void keyPressEvent(QKeyEvent*); - QMenu* menuFunctions, *select, *menuGain; - void initShortcuts(); + void setEventColorMode(int); private slots: void cmd(int); + void timeChanged(unsigned t); void setTime(unsigned t); void songChanged1(MusECore::SongChangedFlags_t); void soloChanged(bool flag); void moveVerticalSlider(int val); + void eventColorModeChanged(int); public slots: void configChanged(); @@ -102,12 +113,6 @@ class WaveEdit : public MidiEditor { virtual void writeStatus(int, MusECore::Xml&) const; static void readConfiguration(MusECore::Xml&); static void writeConfiguration(int, MusECore::Xml&); - - enum { CMD_MUTE=0, CMD_NORMALIZE, CMD_FADE_IN, CMD_FADE_OUT, CMD_REVERSE, - CMD_GAIN_FREE, CMD_GAIN_200, CMD_GAIN_150, CMD_GAIN_75, CMD_GAIN_50, CMD_GAIN_25, - CMD_EDIT_COPY, CMD_EDIT_CUT, CMD_EDIT_PASTE, - CMD_EDIT_EXTERNAL, - CMD_SELECT_ALL, CMD_SELECT_NONE }; }; } // namespace MusEGui diff --git a/muse2/muse/widgets/canvas.cpp b/muse2/muse/widgets/canvas.cpp index a4617d53..f9280ab5 100644 --- a/muse2/muse/widgets/canvas.cpp +++ b/muse2/muse/widgets/canvas.cpp @@ -374,7 +374,7 @@ void Canvas::draw(QPainter& p, const QRect& rect) // Draw unselected parts. list2.push_back(ci); } - } + } int i; int sz = list1.size(); for(i = 0; i != sz; ++i) diff --git a/muse2/muse/widgets/canvas.h b/muse2/muse/widgets/canvas.h index fe26c25a..74de63c5 100644 --- a/muse2/muse/widgets/canvas.h +++ b/muse2/muse/widgets/canvas.h @@ -176,7 +176,7 @@ class Canvas : public View { public slots: void setTool(int t); - void setPos(int, unsigned, bool adjustScrollbar); + virtual void setPos(int, unsigned, bool adjustScrollbar); void scrollTimerDone(void); void redirectedWheelEvent(QWheelEvent*); diff --git a/muse2/muse/widgets/poslabel.cpp b/muse2/muse/widgets/poslabel.cpp index b893c58d..5687089c 100644 --- a/muse2/muse/widgets/poslabel.cpp +++ b/muse2/muse/widgets/poslabel.cpp @@ -123,6 +123,14 @@ void PosLabel::updateValue() void PosLabel::setSampleValue(unsigned val) { + if (val == INT_MAX) + { + setEnabled(false); + return; + } + if(!isEnabled()) + setEnabled(true); + if (val == _sampleValue) return; _sampleValue = val; @@ -135,6 +143,14 @@ void PosLabel::setSampleValue(unsigned val) void PosLabel::setTickValue(unsigned val) { + if (val == INT_MAX) + { + setEnabled(false); + return; + } + if(!isEnabled()) + setEnabled(true); + if (val == _tickValue) return; if (val >= MAX_TICK) @@ -153,6 +169,14 @@ void PosLabel::setTickValue(unsigned val) void PosLabel::setValue(unsigned val) { + if (val == INT_MAX) + { + setEnabled(false); + return; + } + if(!isEnabled()) + setEnabled(true); + unsigned oval = _smpte ? _sampleValue : _tickValue; if (val == oval) return; |