diff options
author | Florian Jung <flo@windfisch.org> | 2011-04-10 15:13:37 +0000 |
---|---|---|
committer | Florian Jung <flo@windfisch.org> | 2011-04-10 15:13:37 +0000 |
commit | e7505b3824fc0ad83c2520a48d26cb0e92ebb583 (patch) | |
tree | 91e03b58aa1c93b6b8567c361c3419e7f3659c95 /muse2/muse | |
parent | d1a740d54d4b2438681656970ce0ec5014652804 (diff) |
initial commit on branch scoreedit
Diffstat (limited to 'muse2/muse')
-rw-r--r-- | muse2/muse/midiedit/scoreedit.cpp | 3038 | ||||
-rw-r--r-- | muse2/muse/midiedit/scoreedit.h | 566 |
2 files changed, 2191 insertions, 1413 deletions
diff --git a/muse2/muse/midiedit/scoreedit.cpp b/muse2/muse/midiedit/scoreedit.cpp index f236a0d5..6a302486 100644 --- a/muse2/muse/midiedit/scoreedit.cpp +++ b/muse2/muse/midiedit/scoreedit.cpp @@ -1,13 +1,18 @@ +//change FONT_PATH to the correct directory +#define FONT_PATH "/home/flo/muse-glyphs/" + //========================================================= // MusE // Linux Music Editor -// $Id: ScoreEdit.cpp,v 1.25.2.15 2009/11/16 11:29:33 lunar_shuttle Exp $ -// (C) Copyright 1999 Werner Schweer (ws@seh.de) +// scoreedit.cpp +// (C) Copyright 2011 Florian Jung (florian.a.jung@web.de) //========================================================= + #include <QLayout> #include <QSizeGrip> #include <QLabel> +#include <QScrollBar> #include <QPushButton> #include <QToolButton> #include <QToolTip> @@ -28,6 +33,11 @@ #include <QSettings> #include <stdio.h> +#include <math.h> + +#include <iostream> +#include <sstream> +using namespace std; #include "xml.h" #include "mtscale.h" @@ -51,20 +61,8 @@ #include "mtrackinfo.h" -int ScoreEdit::_quantInit = 96; -int ScoreEdit::_rasterInit = 96; -int ScoreEdit::_widthInit = 600; -int ScoreEdit::_heightInit = 400; -int ScoreEdit::_quantStrengthInit = 80; // 1 - 100% -int ScoreEdit::_quantLimitInit = 50; // tick value -bool ScoreEdit::_quantLenInit = false; -int ScoreEdit::_toInit = 0; -int ScoreEdit::colorModeInit = 0; +#include "sig.h" -static const int xscale = -10; -static const int yscale = 1; -static const int pianoWidth = 40; -static int ScoreEditTools = PointerTool | PencilTool | RubberTool | DrawTool; //--------------------------------------------------------- @@ -72,1340 +70,1850 @@ static int ScoreEditTools = PointerTool | PencilTool | RubberTool | DrawTool; //--------------------------------------------------------- ScoreEdit::ScoreEdit(PartList* pl, QWidget* parent, const char* name, unsigned initPos) - : MidiEditor(_quantInit, _rasterInit, pl, parent, name) - { - deltaMode = false; - resize(_widthInit, _heightInit); - selPart = 0; - quantConfig = 0; - _playEvents = false; - _quantStrength = _quantStrengthInit; - _quantLimit = _quantLimitInit; - _quantLen = _quantLenInit; - _to = _toInit; - colorMode = colorModeInit; - - QSignalMapper* mapper = new QSignalMapper(this); - QSignalMapper* colorMapper = new QSignalMapper(this); - - //---------Menu---------------------------------- - - menuEdit = menuBar()->addMenu(tr("&Edit")); - - menuEdit->addActions(undoRedo->actions()); - - menuEdit->addSeparator(); - - editCutAction = menuEdit->addAction(QIcon(*editcutIconSet), tr("C&ut")); - mapper->setMapping(editCutAction, PianoCanvas::CMD_CUT); - connect(editCutAction, SIGNAL(triggered()), mapper, SLOT(map())); - - editCopyAction = menuEdit->addAction(QIcon(*editcopyIconSet), tr("&Copy")); - mapper->setMapping(editCopyAction, PianoCanvas::CMD_COPY); - connect(editCopyAction, SIGNAL(triggered()), mapper, SLOT(map())); - - editPasteAction = menuEdit->addAction(QIcon(*editpasteIconSet), tr("&Paste")); - mapper->setMapping(editPasteAction, PianoCanvas::CMD_PASTE); - connect(editPasteAction, SIGNAL(triggered()), mapper, SLOT(map())); - - menuEdit->addSeparator(); - - editDelEventsAction = menuEdit->addAction(tr("Delete &Events")); - mapper->setMapping(editDelEventsAction, PianoCanvas::CMD_DEL); - connect(editDelEventsAction, SIGNAL(triggered()), mapper, SLOT(map())); - - menuEdit->addSeparator(); - - menuSelect = menuEdit->addMenu(QIcon(*selectIcon), tr("&Select")); - - selectAllAction = menuSelect->addAction(QIcon(*select_allIcon), tr("Select &All")); - mapper->setMapping(selectAllAction, PianoCanvas::CMD_SELECT_ALL); - connect(selectAllAction, SIGNAL(triggered()), mapper, SLOT(map())); - - selectNoneAction = menuSelect->addAction(QIcon(*select_deselect_allIcon), tr("&Deselect All")); - mapper->setMapping(selectNoneAction, PianoCanvas::CMD_SELECT_NONE); - connect(selectNoneAction, SIGNAL(triggered()), mapper, SLOT(map())); - - selectInvertAction = menuSelect->addAction(QIcon(*select_invert_selectionIcon), tr("Invert &Selection")); - mapper->setMapping(selectInvertAction, PianoCanvas::CMD_SELECT_INVERT); - connect(selectInvertAction, SIGNAL(triggered()), mapper, SLOT(map())); - - menuSelect->addSeparator(); - - selectInsideLoopAction = menuSelect->addAction(QIcon(*select_inside_loopIcon), tr("&Inside Loop")); - mapper->setMapping(selectInsideLoopAction, PianoCanvas::CMD_SELECT_ILOOP); - connect(selectInsideLoopAction, SIGNAL(triggered()), mapper, SLOT(map())); - - selectOutsideLoopAction = menuSelect->addAction(QIcon(*select_outside_loopIcon), tr("&Outside Loop")); - mapper->setMapping(selectOutsideLoopAction, PianoCanvas::CMD_SELECT_OLOOP); - connect(selectOutsideLoopAction, SIGNAL(triggered()), mapper, SLOT(map())); - - menuSelect->addSeparator(); - - //selectPrevPartAction = select->addAction(tr("&Previous Part")); - selectPrevPartAction = menuSelect->addAction(QIcon(*select_all_parts_on_trackIcon), tr("&Previous Part")); - mapper->setMapping(selectPrevPartAction, PianoCanvas::CMD_SELECT_PREV_PART); - connect(selectPrevPartAction, SIGNAL(triggered()), mapper, SLOT(map())); - - //selNextPartAction = select->addAction(tr("&Next Part")); - selectNextPartAction = menuSelect->addAction(QIcon(*select_all_parts_on_trackIcon), tr("&Next Part")); - mapper->setMapping(selectNextPartAction, PianoCanvas::CMD_SELECT_NEXT_PART); - connect(selectNextPartAction, SIGNAL(triggered()), mapper, SLOT(map())); - - menuConfig = menuBar()->addMenu(tr("&Config")); - - eventColor = menuConfig->addMenu(tr("&Event Color")); - - QActionGroup* actgrp = new QActionGroup(this); - actgrp->setExclusive(true); - - //evColorBlueAction = eventColor->addAction(tr("&Blue")); - evColorBlueAction = actgrp->addAction(tr("&Blue")); - evColorBlueAction->setCheckable(true); - colorMapper->setMapping(evColorBlueAction, 0); - - //evColorPitchAction = eventColor->addAction(tr("&Pitch colors")); - evColorPitchAction = actgrp->addAction(tr("&Pitch colors")); - evColorPitchAction->setCheckable(true); - colorMapper->setMapping(evColorPitchAction, 1); - - //evColorVelAction = eventColor->addAction(tr("&Velocity colors")); - evColorVelAction = actgrp->addAction(tr("&Velocity colors")); - evColorVelAction->setCheckable(true); - colorMapper->setMapping(evColorVelAction, 2); - - connect(evColorBlueAction, SIGNAL(triggered()), colorMapper, SLOT(map())); - connect(evColorPitchAction, SIGNAL(triggered()), colorMapper, SLOT(map())); - connect(evColorVelAction, SIGNAL(triggered()), colorMapper, SLOT(map())); - - eventColor->addActions(actgrp->actions()); - - connect(colorMapper, SIGNAL(mapped(int)), this, SLOT(eventColorModeChanged(int))); - - menuFunctions = menuBar()->addMenu(tr("&Functions")); - - menuFunctions->setTearOffEnabled(true); - - funcOverQuantAction = menuFunctions->addAction(tr("Over Quantize")); - mapper->setMapping(funcOverQuantAction, PianoCanvas::CMD_OVER_QUANTIZE); - connect(funcOverQuantAction, SIGNAL(triggered()), mapper, SLOT(map())); - - funcNoteOnQuantAction = menuFunctions->addAction(tr("Note On Quantize")); - mapper->setMapping(funcNoteOnQuantAction, PianoCanvas::CMD_ON_QUANTIZE); - connect(funcNoteOnQuantAction, SIGNAL(triggered()), mapper, SLOT(map())); - - funcNoteOnOffQuantAction = menuFunctions->addAction(tr("Note On/Off Quantize")); - mapper->setMapping(funcNoteOnOffQuantAction, PianoCanvas::CMD_ONOFF_QUANTIZE); - connect(funcNoteOnOffQuantAction, SIGNAL(triggered()), mapper, SLOT(map())); - - funcIterQuantAction = menuFunctions->addAction(tr("Iterative Quantize")); - mapper->setMapping(funcIterQuantAction, PianoCanvas::CMD_ITERATIVE_QUANTIZE); - connect(funcIterQuantAction, SIGNAL(triggered()), mapper, SLOT(map())); - - menuFunctions->addSeparator(); - - funcConfigQuantAction = menuFunctions->addAction(tr("Config Quant...")); - connect(funcConfigQuantAction, SIGNAL(triggered()), this, SLOT(configQuant())); - - menuFunctions->addSeparator(); - - funcGateTimeAction = menuFunctions->addAction(tr("Modify Gate Time")); - mapper->setMapping(funcGateTimeAction, PianoCanvas::CMD_MODIFY_GATE_TIME); - connect(funcGateTimeAction, SIGNAL(triggered()), mapper, SLOT(map())); - - funcModVelAction = menuFunctions->addAction(tr("Modify Velocity")); - mapper->setMapping(funcModVelAction, PianoCanvas::CMD_MODIFY_VELOCITY); - connect(funcModVelAction, SIGNAL(triggered()), mapper, SLOT(map())); - - funcCrescendoAction = menuFunctions->addAction(tr("Crescendo")); - mapper->setMapping(funcCrescendoAction, PianoCanvas::CMD_CRESCENDO); - funcCrescendoAction->setEnabled(false); - connect(funcCrescendoAction, SIGNAL(triggered()), mapper, SLOT(map())); - - funcTransposeAction = menuFunctions->addAction(tr("Transpose")); - mapper->setMapping(funcTransposeAction, PianoCanvas::CMD_TRANSPOSE); - funcTransposeAction->setEnabled(false); - connect(funcTransposeAction, SIGNAL(triggered()), mapper, SLOT(map())); - - funcThinOutAction = menuFunctions->addAction(tr("Thin Out")); - mapper->setMapping(funcThinOutAction, PianoCanvas::CMD_THIN_OUT); - funcThinOutAction->setEnabled(false); - connect(funcThinOutAction, SIGNAL(triggered()), mapper, SLOT(map())); - - funcEraseEventAction = menuFunctions->addAction(tr("Erase Event")); - mapper->setMapping(funcEraseEventAction, PianoCanvas::CMD_ERASE_EVENT); - funcEraseEventAction->setEnabled(false); - connect(funcEraseEventAction, SIGNAL(triggered()), mapper, SLOT(map())); - - funcNoteShiftAction = menuFunctions->addAction(tr("Note Shift")); - mapper->setMapping(funcNoteShiftAction, PianoCanvas::CMD_NOTE_SHIFT); - funcNoteShiftAction->setEnabled(false); - connect(funcNoteShiftAction, SIGNAL(triggered()), mapper, SLOT(map())); - - funcMoveClockAction = menuFunctions->addAction(tr("Move Clock")); - mapper->setMapping(funcMoveClockAction, PianoCanvas::CMD_MOVE_CLOCK); - funcMoveClockAction->setEnabled(false); - connect(funcMoveClockAction, SIGNAL(triggered()), mapper, SLOT(map())); - - funcCopyMeasureAction = menuFunctions->addAction(tr("Copy Measure")); - mapper->setMapping(funcCopyMeasureAction, PianoCanvas::CMD_COPY_MEASURE); - funcCopyMeasureAction->setEnabled(false); - connect(funcCopyMeasureAction, SIGNAL(triggered()), mapper, SLOT(map())); - - funcEraseMeasureAction = menuFunctions->addAction(tr("Erase Measure")); - mapper->setMapping(funcEraseMeasureAction, PianoCanvas::CMD_ERASE_MEASURE); - funcEraseMeasureAction->setEnabled(false); - connect(funcEraseMeasureAction, SIGNAL(triggered()), mapper, SLOT(map())); - - funcDelMeasureAction = menuFunctions->addAction(tr("Delete Measure")); - mapper->setMapping(funcDelMeasureAction, PianoCanvas::CMD_DELETE_MEASURE); - funcDelMeasureAction->setEnabled(false); - connect(funcDelMeasureAction, SIGNAL(triggered()), mapper, SLOT(map())); - - funcCreateMeasureAction = menuFunctions->addAction(tr("Create Measure")); - mapper->setMapping(funcCreateMeasureAction, PianoCanvas::CMD_CREATE_MEASURE); - funcCreateMeasureAction->setEnabled(false); - connect(funcCreateMeasureAction, SIGNAL(triggered()), mapper, SLOT(map())); - - funcSetFixedLenAction = menuFunctions->addAction(tr("Set Fixed Length")); - mapper->setMapping(funcSetFixedLenAction, PianoCanvas::CMD_FIXED_LEN); - connect(funcSetFixedLenAction, SIGNAL(triggered()), mapper, SLOT(map())); - - funcDelOverlapsAction = menuFunctions->addAction(tr("Delete Overlaps")); - mapper->setMapping(funcDelOverlapsAction, PianoCanvas::CMD_DELETE_OVERLAPS); - connect(funcDelOverlapsAction, SIGNAL(triggered()), mapper, SLOT(map())); - - menuPlugins = menuBar()->addMenu(tr("&Plugins")); - song->populateScriptMenu(menuPlugins, this); - - connect(mapper, SIGNAL(mapped(int)), this, SLOT(cmd(int))); - - //---------ToolBar---------------------------------- - tools = addToolBar(tr("ScoreEdit tools")); - tools->setObjectName("ScoreEdit tools"); - tools->addActions(undoRedo->actions()); - tools->addSeparator(); - - srec = new QToolButton(); - srec->setToolTip(tr("Step Record")); - srec->setIcon(*steprecIcon); - srec->setCheckable(true); - tools->addWidget(srec); - - midiin = new QToolButton(); - midiin->setToolTip(tr("Midi Input")); - midiin->setIcon(*midiinIcon); - midiin->setCheckable(true); - tools->addWidget(midiin); - - speaker = new QToolButton(); - speaker->setToolTip(tr("Play Events")); - speaker->setIcon(*speakerIcon); - speaker->setCheckable(true); - tools->addWidget(speaker); - - tools2 = new EditToolBar(this, ScoreEditTools); - addToolBar(tools2); - - QToolBar* panicToolbar = addToolBar(tr("panic")); - panicToolbar->setObjectName("panic"); - panicToolbar->addAction(panicAction); - - //------------------------------------------------------------- - // Transport Bar - QToolBar* transport = addToolBar(tr("transport")); - transport->setObjectName("transport"); - transport->addActions(transportAction->actions()); - - addToolBarBreak(); - toolbar = new Toolbar1(this, _rasterInit, _quantInit); - addToolBar(toolbar); - - addToolBarBreak(); - info = new NoteInfo(this); - addToolBar(info); - - //--------------------------------------------------- - // split - //--------------------------------------------------- - - splitter = new Splitter(Qt::Vertical, mainw, "splitter"); - splitter->setHandleWidth(2); - - hsplitter = new Splitter(Qt::Horizontal, mainw, "hsplitter"); - hsplitter->setChildrenCollapsible(true); - hsplitter->setHandleWidth(2); - - QPushButton* ctrl = new QPushButton(tr("ctrl"), mainw); - //QPushButton* ctrl = new QPushButton(tr("C"), mainw); // Tim. - ctrl->setObjectName("Ctrl"); - ctrl->setFont(config.fonts[3]); - ctrl->setToolTip(tr("Add Controller View")); - hscroll = new ScrollScale(-25, -2, xscale, 20000, Qt::Horizontal, mainw); - ctrl->setFixedSize(pianoWidth, hscroll->sizeHint().height()); - //ctrl->setFixedSize(pianoWidth / 2, hscroll->sizeHint().height()); // Tim. - - // Tim. - /* - QPushButton* trackInfoButton = new QPushButton(tr("T"), mainw); - trackInfoButton->setObjectName("TrackInfo"); - trackInfoButton->setFont(config.fonts[3]); - trackInfoButton->setToolTip(tr("Show track info")); - trackInfoButton->setFixedSize(pianoWidth / 2, hscroll->sizeHint().height()); - */ - - QSizeGrip* corner = new QSizeGrip(mainw); - - midiTrackInfo = new MidiTrackInfo(mainw); - int mtiw = midiTrackInfo->width(); // Save this. - midiTrackInfo->setMinimumWidth(100); - //midiTrackInfo->setMaximumWidth(150); - - midiTrackInfo->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Expanding)); - infoScroll = new QScrollArea; - infoScroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - infoScroll->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); - infoScroll->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding)); - infoScroll->setWidget(midiTrackInfo); - infoScroll->setWidgetResizable(true); - //infoScroll->setVisible(false); - //infoScroll->setEnabled(false); - - //hsplitter->addWidget(midiTrackInfo); - hsplitter->addWidget(infoScroll); // Tim. - hsplitter->addWidget(splitter); - + : MidiEditor(0, 0, pl, parent, name) +{ +// Splitter* hsplitter; + QPushButton* ctrl; +/* + hsplitter = new Splitter(Qt::Vertical, mainw, "hsplitter"); + hsplitter->setHandleWidth(2); + + ctrl = new QPushButton(tr("ctrl"), mainw); + ctrl->setObjectName("Ctrl"); + ctrl->setFont(config.fonts[3]); + ctrl->setToolTip(tr("Add Controller View")); + + hsplitter->addWidget(ctrl); +*/ + /* + QGridLayout* gridS1 = new QGridLayout(mainw); + gridS1->setContentsMargins(0, 0, 0, 0); + mainGrid->setRowStretch(0, 100); mainGrid->setColumnStretch(1, 100); - mainGrid->addWidget(hsplitter, 0, 1, 1, 3); - - // Original. - /* - mainGrid->setColumnStretch(1, 100); - mainGrid->addWidget(splitter, 0, 0, 1, 3); - mainGrid->addWidget(ctrl, 1, 0); - mainGrid->addWidget(hscroll, 1, 1); - mainGrid->addWidget(corner, 1, 2, Qt::AlignBottom|Qt::AlignRight); - */ - - - // Tim. - /* - mainGrid->setColumnStretch(2, 100); - mainGrid->addWidget(splitter, 0, 0, 1, 4); - mainGrid->addWidget(trackInfoButton, 1, 0); - mainGrid->addWidget(ctrl, 1, 1); - mainGrid->addWidget(hscroll, 1, 2); - mainGrid->addWidget(corner, 1, 3, Qt::AlignBottom|Qt::AlignRight); - */ - - //mainGrid->addRowSpacing(1, hscroll->sizeHint().height()); - //mainGrid->addItem(new QSpacerItem(0, hscroll->sizeHint().height()), 1, 0); // Orig + Tim. - - QWidget* split1 = new QWidget(splitter); - split1->setObjectName("split1"); - QGridLayout* gridS1 = new QGridLayout(split1); - gridS1->setContentsMargins(0, 0, 0, 0); - gridS1->setSpacing(0); - //Defined and configure your program change bar here. - //This may well be a copy of MTScale extended for our needs - time = new MTScale(&_raster, split1, xscale); - Piano* piano = new Piano(split1, yscale); - canvas = new PianoCanvas(this, split1, xscale, yscale); - vscroll = new ScrollScale(-3, 7, yscale, KH * 75, Qt::Vertical, split1); - - //setFocusProxy(canvas); // Tim. - - int offset = -(config.division/4); - canvas->setOrigin(offset, 0); - canvas->setCanvasTools(ScoreEditTools); - canvas->setFocus(); - connect(canvas, SIGNAL(toolChanged(int)), tools2, SLOT(set(int))); - time->setOrigin(offset, 0); gridS1->setRowStretch(2, 100); gridS1->setColumnStretch(1, 100); - //gridS1->setColumnStretch(2, 100); // Tim. - - gridS1->addWidget(time, 0, 1, 1, 2); - gridS1->addWidget(hLine(split1), 1, 0, 1, 3); - gridS1->addWidget(piano, 2, 0); - gridS1->addWidget(canvas, 2, 1); - gridS1->addWidget(vscroll, 2, 2); - - // Tim. - /* - gridS1->addWidget(time, 0, 2, 1, 3); - gridS1->addWidget(hLine(split1), 1, 1, 1, 4); - //gridS1->addWidget(infoScroll, 2, 0); - gridS1->addWidget(infoScroll, 0, 0, 3, 1); - gridS1->addWidget(piano, 2, 1); - gridS1->addWidget(canvas, 2, 2); - gridS1->addWidget(vscroll, 2, 3); - */ - - ctrlLane = new Splitter(Qt::Vertical, splitter, "ctrllane"); - QWidget* split2 = new QWidget(splitter); - split2->setMaximumHeight(hscroll->sizeHint().height()); - split2->setMinimumHeight(hscroll->sizeHint().height()); - QGridLayout* gridS2 = new QGridLayout(split2); - gridS2->setContentsMargins(0, 0, 0, 0); - gridS2->setSpacing(0); - gridS2->setRowStretch(0, 100); - gridS2->setColumnStretch(1, 100); - gridS2->addWidget(ctrl, 0, 0); - gridS2->addWidget(hscroll, 0, 1); - gridS2->addWidget(corner, 0, 2, Qt::AlignBottom|Qt::AlignRight); - //splitter->setCollapsible(0, true); - - piano->setFixedWidth(pianoWidth); - - // Tim. - QList<int> mops; - mops.append(mtiw + 30); // 30 for possible scrollbar - mops.append(width() - mtiw - 30); - hsplitter->setSizes(mops); - - connect(tools2, SIGNAL(toolChanged(int)), canvas, SLOT(setTool(int))); - - connect(ctrl, SIGNAL(clicked()), SLOT(addCtrl())); - //connect(trackInfoButton, SIGNAL(clicked()), SLOT(toggleTrackInfo())); Tim. - connect(info, SIGNAL(valueChanged(NoteInfo::ValType, int)), SLOT(noteinfoChanged(NoteInfo::ValType, int))); - connect(vscroll, SIGNAL(scrollChanged(int)), piano, SLOT(setYPos(int))); - connect(vscroll, SIGNAL(scrollChanged(int)), canvas, SLOT(setYPos(int))); - connect(vscroll, SIGNAL(scaleChanged(int)), canvas, SLOT(setYMag(int))); - connect(vscroll, SIGNAL(scaleChanged(int)), piano, SLOT(setYMag(int))); - - connect(hscroll, SIGNAL(scrollChanged(int)), canvas, SLOT(setXPos(int))); - connect(hscroll, SIGNAL(scrollChanged(int)), time, SLOT(setXPos(int))); - - connect(hscroll, SIGNAL(scaleChanged(int)), canvas, SLOT(setXMag(int))); - connect(hscroll, SIGNAL(scaleChanged(int)), time, SLOT(setXMag(int))); - - connect(canvas, SIGNAL(newWidth(int)), SLOT(newCanvasWidth(int))); - connect(canvas, SIGNAL(pitchChanged(int)), piano, SLOT(setPitch(int))); - connect(canvas, SIGNAL(verticalScroll(unsigned)), vscroll, SLOT(setPos(unsigned))); - connect(canvas, SIGNAL(horizontalScroll(unsigned)),hscroll, SLOT(setPos(unsigned))); - connect(canvas, SIGNAL(horizontalScrollNoLimit(unsigned)),hscroll, SLOT(setPosNoLimit(unsigned))); - connect(canvas, SIGNAL(selectionChanged(int, Event&, Part*)), this, - SLOT(setSelection(int, Event&, Part*))); - - connect(piano, SIGNAL(keyPressed(int, int, bool)), canvas, SLOT(pianoPressed(int, int, bool))); - connect(piano, SIGNAL(keyReleased(int, bool)), canvas, SLOT(pianoReleased(int, bool))); - connect(srec, SIGNAL(toggled(bool)), SLOT(setSteprec(bool))); - connect(midiin, SIGNAL(toggled(bool)), canvas, SLOT(setMidiin(bool))); - connect(speaker, SIGNAL(toggled(bool)), SLOT(setSpeaker(bool))); - connect(canvas, SIGNAL(followEvent(int)), SLOT(follow(int))); - - connect(hscroll, SIGNAL(scaleChanged(int)), SLOT(updateHScrollRange())); - piano->setYPos(KH * 30); - canvas->setYPos(KH * 30); - vscroll->setPos(KH * 30); - //setSelection(0, 0, 0); //Really necessary? Causes segfault when only 1 item selected, replaced by the following: - info->setEnabled(false); - - connect(song, SIGNAL(songChanged(int)), SLOT(songChanged1(int))); - - setWindowTitle(canvas->getCaption()); - - updateHScrollRange(); - // connect to toolbar - connect(canvas, SIGNAL(pitchChanged(int)), toolbar, SLOT(setPitch(int))); - connect(canvas, SIGNAL(timeChanged(unsigned)), SLOT(setTime(unsigned))); - connect(piano, SIGNAL(pitchChanged(int)), toolbar, SLOT(setPitch(int))); - connect(time, SIGNAL(timeChanged(unsigned)), SLOT(setTime(unsigned))); - connect(toolbar, SIGNAL(quantChanged(int)), SLOT(setQuant(int))); - connect(toolbar, SIGNAL(rasterChanged(int)),SLOT(setRaster(int))); - connect(toolbar, SIGNAL(toChanged(int)), SLOT(setTo(int))); - connect(toolbar, SIGNAL(soloChanged(bool)), SLOT(soloChanged(bool))); - - setFocusPolicy(Qt::StrongFocus); - setEventColorMode(colorMode); - - QClipboard* cb = QApplication::clipboard(); - connect(cb, SIGNAL(dataChanged()), SLOT(clipboardChanged())); - - clipboardChanged(); // enable/disable "Paste" - selectionChanged(); // enable/disable "Copy" & "Paste" - initShortcuts(); // initialize shortcuts - - const Pos cpos=song->cPos(); - canvas->setPos(0, cpos.tick(), true); - canvas->selectAtTick(cpos.tick()); - //canvas->selectFirst();// - - unsigned pos=0; - if(initPos >= MAXINT) - pos = song->cpos(); - if(pos > MAXINT) - pos = MAXINT; - if (pos) - hscroll->setOffset((int)pos); - - if(canvas->track()) - { - updateTrackInfo(); - toolbar->setSolo(canvas->track()->solo()); - } - - QSettings settings("MusE", "MusE-qt"); - //restoreGeometry(settings.value("ScoreEdit/geometry").toByteArray()); - restoreState(settings.value("ScoreEdit/windowState").toByteArray()); - - } +*/ -//--------------------------------------------------------- -// songChanged1 -//--------------------------------------------------------- + ScoreCanvas* test=new ScoreCanvas(this, mainw, 1, 1); + QScrollBar* hscroll = new QScrollBar(Qt::Horizontal, mainw); -void ScoreEdit::songChanged1(int bits) - { - - if (bits & SC_SOLO) - { - toolbar->setSolo(canvas->track()->solo()); - return; - } - songChanged(bits); - //trackInfo->songChanged(bits); - // We'll receive SC_SELECTION if a different part is selected. - if (bits & SC_SELECTION) - updateTrackInfo(); - } -//--------------------------------------------------------- -// configChanged -//--------------------------------------------------------- +connect(hscroll, SIGNAL(valueChanged(int)), test, SLOT(scroll_event(int))); +connect(song, SIGNAL(songChanged(int)), test, SLOT(song_changed(int))); +// mainGrid->setRowStretch(0, 100); +// mainGrid->setColumnStretch(1, 100); + mainGrid->addWidget(test, 0, 0); + mainGrid->addWidget(hscroll,1,0); + +hscroll->setMinimum(0); +hscroll->setMaximum(1000); +hscroll->setPageStep(100); +// gridS1->addWidget(test,0,0); + +// gridS1->addWidget(canvas, 0, 0); + // hsplitter->addWidget(test); +} -void ScoreEdit::configChanged() - { - initShortcuts(); - //trackInfo->updateTrackInfo(); - } //--------------------------------------------------------- -// updateHScrollRange +// ~ScoreEdit //--------------------------------------------------------- -void ScoreEdit::updateHScrollRange() +ScoreEdit::~ScoreEdit() { - int s, e; - canvas->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; - // Compensate for the fixed piano and vscroll widths. - e += canvas->rmapxDev(pianoWidth - vscroll->width()); - int s1, e1; - hscroll->range(&s1, &e1); - if(s != s1 || e != e1) - hscroll->setRange(s, e); + } -void ScoreEdit::updateTrackInfo() + +ScoreCanvas::ScoreCanvas(MidiEditor* pr, QWidget* parent, + int sx, int sy) : View(parent, sx, sy) { - selected = curCanvasPart()->track(); - if (selected->isMidiTrack()) { - midiTrackInfo->setTrack(selected); - ///midiTrackInfo->updateTrackInfo(-1); - } + editor = pr; + setFocusPolicy(Qt::StrongFocus); + setBg(Qt::white); + + setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding); +// setMinimumSize(400,600); + + load_pixmaps(); + + x_pos=0; + + song_changed(0); +//fertig mit aufbereiten + cout << "---------------- CALCULATING DONE ------------------" << endl; + } -//--------------------------------------------------------- -// follow -//--------------------------------------------------------- +void ScoreCanvas::song_changed(int) +{ + cout << "song changed!" << endl; + pos_add_list.clear(); + eventlist=createAppropriateEventList(editor->parts(), editor->parts()->begin()->second->track()); + itemlist=create_itemlist(eventlist); + process_itemlist(itemlist); // do note- and rest-grouping and collision avoiding + calc_item_pos(itemlist); + redraw(); + cout << "song had changed, recalculation complete" << endl; +} -void ScoreEdit::follow(int pos) - { - int s, e; - canvas->range(&s, &e); +//flo code starting here - if (pos < e && pos >= s) - hscroll->setOffset(pos); - if (pos < s) - hscroll->setOffset(s); - } +string IntToStr(int i) +{ + ostringstream s; + s<<i; + return s.str(); +} -//--------------------------------------------------------- -// setTime -//--------------------------------------------------------- -void ScoreEdit::setTime(unsigned tick) - { - toolbar->setTime(tick); - time->setPos(3, tick, false); - } -//--------------------------------------------------------- -// ~ScoreEdit -//--------------------------------------------------------- +void ScoreCanvas::load_pixmaps() +{ + pix_whole.load(FONT_PATH "whole.png"); + pix_half.load(FONT_PATH "half.png"); + pix_quarter.load(FONT_PATH "quarter.png"); + pix_r1.load(FONT_PATH "rest1.png"); + pix_r2.load(FONT_PATH "rest2.png"); + pix_r4.load(FONT_PATH "rest4.png"); + pix_r8.load(FONT_PATH "rest8.png"); + pix_r16.load(FONT_PATH "rest16.png"); + pix_dot.load(FONT_PATH "dot.png"); + pix_flag_up[0].load(FONT_PATH "flags8u.png"); + pix_flag_up[1].load(FONT_PATH "flags16u.png"); + pix_flag_up[2].load(FONT_PATH "flags32u.png"); + pix_flag_up[3].load(FONT_PATH "flags64u.png"); + pix_flag_down[0].load(FONT_PATH "flags8d.png"); + pix_flag_down[1].load(FONT_PATH "flags16d.png"); + pix_flag_down[2].load(FONT_PATH "flags32d.png"); + pix_flag_down[3].load(FONT_PATH "flags64d.png"); + pix_noacc.load(FONT_PATH "acc_none.png"); + pix_sharp.load(FONT_PATH "acc_sharp.png"); + pix_b.load(FONT_PATH "acc_b.png"); + + for (int i=0;i<10;i++) + pix_num[i].load(QString((string(FONT_PATH "")+IntToStr(i)+string(".png")).c_str())); +} -ScoreEdit::~ScoreEdit() - { - // undoRedo->removeFrom(tools); // p4.0.6 Removed - } -//--------------------------------------------------------- -// cmd -// pulldown menu commands -//--------------------------------------------------------- -void ScoreEdit::cmd(int cmd) - { - ((PianoCanvas*)canvas)->cmd(cmd, _quantStrength, _quantLimit, _quantLen, _to); - } +int modulo(int a, int b) // similar to a % b +{ + return (((a%b)+b)%b); +} -//--------------------------------------------------------- -// setSelection -// update Info Line -//--------------------------------------------------------- +#define DEFAULT_REST_HEIGHT 6 // TODO -void ScoreEdit::setSelection(int tick, Event& e, Part* p) - { - int selections = canvas->selectionSize(); - - selEvent = e; - selPart = (MidiPart*)p; - selTick = tick; - - if (selections > 1) { - info->setEnabled(true); - info->setDeltaMode(true); - if (!deltaMode) { - deltaMode = true; - info->setValues(0, 0, 0, 0, 0); - tickOffset = 0; - lenOffset = 0; - pitchOffset = 0; - veloOnOffset = 0; - veloOffOffset = 0; - } - } - else if (selections == 1) { - deltaMode = false; - info->setEnabled(true); - info->setDeltaMode(false); - info->setValues(tick, - selEvent.lenTick(), - selEvent.pitch(), - selEvent.velo(), - selEvent.veloOff()); - } - else { - deltaMode = false; - info->setEnabled(false); - } - selectionChanged(); - } -//--------------------------------------------------------- -// edit currently selected Event -//--------------------------------------------------------- +bool operator< (const note_pos_t& a, const note_pos_t& b) +{ + if (a.height<b.height) return true; + if (a.height>b.height) return false; + return a.vorzeichen<b.vorzeichen; +} -void ScoreEdit::noteinfoChanged(NoteInfo::ValType type, int val) - { - int selections = canvas->selectionSize(); - - if (selections == 0) { - printf("noteinfoChanged while nothing selected\n"); - } - else if (selections == 1) { - Event event = selEvent.clone(); - switch(type) { - case NoteInfo::VAL_TIME: - event.setTick(val - selPart->tick()); - break; - case NoteInfo::VAL_LEN: - event.setLenTick(val); - break; - case NoteInfo::VAL_VELON: - event.setVelo(val); - break; - case NoteInfo::VAL_VELOFF: - event.setVeloOff(val); - break; - case NoteInfo::VAL_PITCH: - event.setPitch(val); - break; - } - // Indicate do undo, and do not do port controller values and clone parts. - //audio->msgChangeEvent(selEvent, event, selPart); - audio->msgChangeEvent(selEvent, event, selPart, true, false, false); - } - else { - // multiple events are selected; treat noteinfo values - // as offsets to event values - - int delta = 0; - switch (type) { - case NoteInfo::VAL_TIME: - delta = val - tickOffset; - tickOffset = val; - break; - case NoteInfo::VAL_LEN: - delta = val - lenOffset; - lenOffset = val; - break; - case NoteInfo::VAL_VELON: - delta = val - veloOnOffset; - veloOnOffset = val; - break; - case NoteInfo::VAL_VELOFF: - delta = val - veloOffOffset; - veloOffOffset = val; - break; - case NoteInfo::VAL_PITCH: - delta = val - pitchOffset; - pitchOffset = val; - break; - } - if (delta) - canvas->modifySelected(type, delta); - } - } -//--------------------------------------------------------- -// addCtrl -//--------------------------------------------------------- +//TODO: all das unten richtig machen! +#define TICKS_PER_WHOLE (config.division*4) +#define SONG_LENGTH (TICKS_PER_WHOLE*8) + +#define quant_max 3 //whole, half, quarter = 0,1,2 +#define quant_max_fraction (1 << quant_max) //whole, half, quarter= 1,2,4 +#define FLO_QUANT (TICKS_PER_WHOLE/quant_max_fraction) +//FLO_QUANT = how many ticks has a single quantisation area? + + +//FINDMICH MARKER +//TODO: quant_max richtig setzen! + +/* builds the event list used by the score editor. + * that list contains only note-on and -off, time-sig- and + * key-change events. + * it stores them sorted by their time (quantized); if more + * events with the same time occur, the NOTE-OFFs are + * put before the NOTE-ONs + * it only operates on parts which a) are selected by the + * editor and b) belong to track + * + * this abstracts the rest of the renderer from muse's internal + * data structures, making this easy to port to another application + */ +ScoreEventList ScoreCanvas::createAppropriateEventList(PartList* pl, Track* track) +{ + using AL::sigmap; + using AL::iSigEvent; + + ScoreEventList result; + + //insert note on/off events + for (iPart partIt=pl->begin(); partIt!=pl->end(); partIt++) + { + Part* part=partIt->second; + if (part->track()==track) + { + EventList* el=part->events(); + + for (iEvent it=el->begin(); it!=el->end(); it++) + { + Event& event=it->second; + + if (event.isNote() && !event.isNoteOff()) + { + unsigned begin, end; + //TODO quantizing must be done with the proper functions! + begin=int(rint((float)event.tick() / FLO_QUANT))*FLO_QUANT; + end=int(rint((float)event.endTick() / FLO_QUANT))*FLO_QUANT; + cout <<"inserting note on at "<<begin<<" with pitch="<<event.pitch()<<" and len="<<end-begin<<endl; + cout<< "\tinserting corresponding note off at "<<endl; + result.insert(pair<unsigned, FloEvent>(begin, FloEvent(begin,event.pitch(), event.velo(),end-begin,FloEvent::NOTE_ON,part,&it->second))); + result.insert(pair<unsigned, FloEvent>(end, FloEvent(end,event.pitch(), event.veloOff(),0,FloEvent::NOTE_OFF,part,&it->second))); + } + //else ignore it + } + } + //else ignore it + } + + //insert bars and time signatures + for (iSigEvent it=sigmap.begin(); it!=sigmap.end(); it++) + { + unsigned from=it->second->tick; + unsigned to=it->first; + unsigned ticks_per_measure=sigmap.ticksMeasure(it->second->tick); + + if (to > unsigned(SONG_LENGTH)) + { + cout << "time signature's end-of-validness is outside of our song, limiting it." << endl; + to=SONG_LENGTH; + } + + cout << "new signature from tick "<<from<<" to " << to << ": "<<it->second->sig.z<<"/"<<it->second->sig.n<<"; ticks per measure = "<<ticks_per_measure<<endl; + result.insert(pair<unsigned, FloEvent>(from, FloEvent(from, FloEvent::TIME_SIG, it->second->sig.z, it->second->sig.n) ) ); + for (unsigned t=from; t<to; t+=ticks_per_measure) + result.insert(pair<unsigned, FloEvent>(t, FloEvent(t,0,0,ticks_per_measure,FloEvent::BAR) ) ); + } + + + //TODO FINDMICH MARKER + result.insert(pair<unsigned, FloEvent>(0, FloEvent(0,FloEvent::KEY_CHANGE, C ) ) ); + + return result; +} -CtrlEdit* ScoreEdit::addCtrl() - { - ///CtrlEdit* ctrlEdit = new CtrlEdit(splitter, this, xscale, false, "pianoCtrlEdit"); - CtrlEdit* ctrlEdit = new CtrlEdit(ctrlLane/*splitter*/, this, xscale, false, "pianoCtrlEdit"); // ccharrett - connect(tools2, SIGNAL(toolChanged(int)), ctrlEdit, SLOT(setTool(int))); - connect(hscroll, SIGNAL(scrollChanged(int)), ctrlEdit, SLOT(setXPos(int))); - connect(hscroll, SIGNAL(scaleChanged(int)), ctrlEdit, SLOT(setXMag(int))); - connect(ctrlEdit, SIGNAL(timeChanged(unsigned)), SLOT(setTime(unsigned))); - connect(ctrlEdit, SIGNAL(destroyedCtrl(CtrlEdit*)), SLOT(removeCtrl(CtrlEdit*))); - connect(ctrlEdit, SIGNAL(yposChanged(int)), toolbar, SLOT(setInt(int))); - - ctrlEdit->setTool(tools2->curTool()); - ctrlEdit->setXPos(hscroll->pos()); - ctrlEdit->setXMag(hscroll->getScaleValue()); - - ctrlEdit->show(); - ctrlEditList.push_back(ctrlEdit); - return ctrlEdit; - } -//--------------------------------------------------------- -// removeCtrl -//--------------------------------------------------------- +bool is_sharp_key(tonart_t t) +{ + return ((t>=SHARP_BEGIN) && (t<=SHARP_END)); +} +bool is_b_key(tonart_t t) +{ + return ((t>=B_BEGIN) && (t<=B_END)); +} -void ScoreEdit::removeCtrl(CtrlEdit* ctrl) - { - for (std::list<CtrlEdit*>::iterator i = ctrlEditList.begin(); - i != ctrlEditList.end(); ++i) { - if (*i == ctrl) { - ctrlEditList.erase(i); - break; - } - } - } +int n_accidentials(tonart_t t) +{ + if (is_sharp_key(t)) + return t-SHARP_BEGIN-1; + else + return t-B_BEGIN-1; +} -//--------------------------------------------------------- -// closeEvent -//--------------------------------------------------------- -void ScoreEdit::closeEvent(QCloseEvent* e) - { - QSettings settings("MusE", "MusE-qt"); - //settings.setValue("ScoreEdit/geometry", saveGeometry()); - settings.setValue("ScoreEdit/windowState", saveState()); +//note needs to be 0..11 +note_pos_t ScoreCanvas::note_pos_(int note, tonart_t key) +{ + note_pos_t result; + //C CIS D DIS E F FIS G GIS A AIS H + int foo[12]={0,-1, 1,-1, 2,3,-1, 4,-1, 5, -1,6}; + + if ((note<0) || (note>=12)) + cout << "WARNING: ILLEGAL FUNCTION CALL (note_pos, note out of range)" << endl; + + if (foo[note]!=-1) + { + result.height=foo[note]; + result.vorzeichen=NONE; + } + else + { + if (is_sharp_key(key)) + { + result.height=foo[note-1]; + result.vorzeichen=SHARP; + } + else // if is_b_key + { + result.height=foo[note+1]; + result.vorzeichen=B; + } + } + + // Special cases for GES / FIS keys + if (key==GES) + { + // convert a H to a Ces + if (note==11) + { + result.height=12; + result.vorzeichen=B; + } + } + else if (key==FIS) + { + // convert a F to an Eis + if (note==5) + { + result.height=2; + result.vorzeichen=SHARP; + } + } + + return result; +} - emit deleted((unsigned long)this); - e->accept(); - } -//--------------------------------------------------------- -// readConfiguration -//--------------------------------------------------------- +// V -------------------------- <-- height=10 +// I C -------------------------- <-- height=8 +// O L -------------------------- <-- height=6 +// L E -------------------------- <-- height=4 +// I F -------------------------- <-- height=2 +// N --o-- <-- this is C4. height=0 -void ScoreEdit::readConfiguration(Xml& xml) - { - for (;;) { - Xml::Token token = xml.parse(); - if (token == Xml::Error || token == Xml::End) - break; - const QString& tag = xml.s1(); - switch (token) { - case Xml::TagStart: - if (tag == "quant") - _quantInit = xml.parseInt(); - else if (tag == "raster") - _rasterInit = xml.parseInt(); - else if (tag == "quantStrength") - _quantStrengthInit = xml.parseInt(); - else if (tag == "quantLimit") - _quantLimitInit = xml.parseInt(); - else if (tag == "quantLen") - _quantLenInit = xml.parseInt(); - else if (tag == "to") - _toInit = xml.parseInt(); - else if (tag == "colormode") - colorModeInit = xml.parseInt(); - else if (tag == "width") - _widthInit = xml.parseInt(); - else if (tag == "height") - _heightInit = xml.parseInt(); - else - xml.unknown("ScoreEdit"); - break; - case Xml::TagEnd: - if (tag == "ScoreEdit") - return; - default: - break; - } - } - } +// the "spaces" in between the lines have odd numbers. +// that is, the space between line 2 and 4 is numbered 3. -//--------------------------------------------------------- -// writeConfiguration -//--------------------------------------------------------- +// these numbers do not change when clef changes. line 2 +// is always the "bottom line" of the system. +// in violin clef, line 2 is E4 +// in bass clef, line 2 is G2 -void ScoreEdit::writeConfiguration(int level, Xml& xml) - { - xml.tag(level++, "ScoreEdit"); - xml.intTag(level, "quant", _quantInit); - xml.intTag(level, "raster", _rasterInit); - xml.intTag(level, "quantStrength", _quantStrengthInit); - xml.intTag(level, "quantLimit", _quantLimitInit); - xml.intTag(level, "quantLen", _quantLenInit); - xml.intTag(level, "to", _toInit); - xml.intTag(level, "width", _widthInit); - xml.intTag(level, "height", _heightInit); - xml.intTag(level, "colormode", colorModeInit); - xml.etag(level, "ScoreEdit"); - } +note_pos_t ScoreCanvas::note_pos (int note, tonart_t key, clef_t clef) +{ + int octave=(note/12)-1; //integer division + note=note%12; + + //now octave contains the octave the note is in + //(A4 is the 440Hz tone. C4 is the "low C" in the violin clef + //and the "high C" in the bass clef. + //note contains 0 for C, 1 for Cis, ..., 11 for H (or B if you're not german) + + note_pos_t pos=note_pos_(note,key); + + switch (clef) + { + case VIOLIN: + pos.height=pos.height + (octave-4)*7; + break; + + case BASS: + pos.height=pos.height + (octave-3)*7 + 5; + break; + } + + return pos; +} -//--------------------------------------------------------- -// soloChanged -// signal from solo button -//--------------------------------------------------------- -void ScoreEdit::soloChanged(bool flag) - { - audio->msgSetSolo(canvas->track(), flag); - song->update(SC_SOLO); - } +int ScoreCanvas::calc_len(int l, int d) +{ + // l=0,1,2 -> whole, half, quarter (think of 2^0, 2^1, 2^2) + // d=number of dots + + int tmp=0; + for (int i=0;i<=d;i++) + tmp+=TICKS_PER_WHOLE / pow(2, l+i); + + return tmp; +} -//--------------------------------------------------------- -// setRaster -//--------------------------------------------------------- +bool operator< (const note_len_t& a,const note_len_t& b) //TODO sane sorting order +{ + if (a.len<b.len) return true; + else if (a.dots<b.dots) return true; + else return false; +} -void ScoreEdit::setRaster(int val) - { - _rasterInit = val; - MidiEditor::setRaster(val); - canvas->redrawGrid(); - canvas->setFocus(); // give back focus after kb input - } +//quant_max must be in log(len), that is +//whole, half, quarter, eighth = 0,1,2,3 +//NOT: 1,2,4,8! (think of 2^foo) +//len is in ticks +list<note_len_t> ScoreCanvas::parse_note_len(int len_ticks, bool allow_dots, bool allow_normal, int begin_tick) +{ + list<note_len_t> retval; + + if (allow_normal) + { + int dot_max = allow_dots ? quant_max : 0; + + for (int i=0;i<=quant_max;i++) + for (int j=0;j<=dot_max-i;j++) + if (calc_len(i,j) == len_ticks) + { + retval.push_back(note_len_t (i,j)); + return retval; + } + } + + //if !allow_normal or if the above failed + + // 1 e + e 2 e + e 3 e + e 4 e + e + int foo[]={1,7,6,7,5,7,6,7,4,7,6,7,5,7,6,7,3,7,6,7,5,7,6,7,4,7,6,7,5,7,6,7,2,7,6,7,5,7,6,7,4,7,6,7,5,7,6,7,3,7,6,7,5,7,6,7,4,7,6,7,5,7,6,7}; + #define foo_len (sizeof(foo)/sizeof(*foo)) + + int begin=begin_tick * 64 / TICKS_PER_WHOLE; + int len=len_ticks * 64 / TICKS_PER_WHOLE; + + int pos=begin; + int len_done=0; + + while (len_done<len) + { + int len_now=0; + int last_number=foo[pos]; + + while (! ((foo[pos]<last_number) || (len_done==len) || (pos==foo_len)) ) {pos++;len_done++;len_now++;} + + len_now=len_now*TICKS_PER_WHOLE/64; + + cout << "add " << len_now << " ticks" << endl; + for (int i=0; i<=quant_max; i++) + { + int tmp=calc_len(i,0); + if (tmp <= len_now) + { + retval.push_back(note_len_t(i)); + len_now-=tmp; + if (len_now==0) break; + } + } + + if (len_now!=0) + cout << "WARNING: THIS SHOULD NEVER HAPPEN. wasn't able to split note len properly; len_now="<<len_now << endl; + + if (pos==foo_len) //we cross measure boundaries? + pos=0; + } + + + return retval; +} -//--------------------------------------------------------- -// setQuant -//--------------------------------------------------------- -void ScoreEdit::setQuant(int val) - { - _quantInit = val; - MidiEditor::setQuant(val); - canvas->setFocus(); - } -//--------------------------------------------------------- -// writeStatus -//--------------------------------------------------------- +#define YLEN 10 +#define YDIST (2*YLEN) +#define NOTE_XLEN 10 +#define NOTE_SHIFT 3 +#define PIXELS_PER_WHOLE (320) //how many px are between two wholes? +#define PIXELS_PER_NOTEPOS (PIXELS_PER_WHOLE/quant_max_fraction) //how many px are between the smallest drawn beats? -void ScoreEdit::writeStatus(int level, Xml& xml) const - { - writePartList(level, xml); - xml.tag(level++, "ScoreEdit"); - MidiEditor::writeStatus(level, xml); - splitter->writeStatus(level, xml); - hsplitter->writeStatus(level, xml); - - for (std::list<CtrlEdit*>::const_iterator i = ctrlEditList.begin(); - i != ctrlEditList.end(); ++i) { - (*i)->writeStatus(level, xml); - } - - xml.intTag(level, "steprec", canvas->steprec()); - xml.intTag(level, "midiin", canvas->midiin()); - xml.intTag(level, "tool", int(canvas->tool())); - xml.intTag(level, "quantStrength", _quantStrength); - xml.intTag(level, "quantLimit", _quantLimit); - xml.intTag(level, "quantLen", _quantLen); - xml.intTag(level, "playEvents", _playEvents); - xml.intTag(level, "xpos", hscroll->pos()); - xml.intTag(level, "xmag", hscroll->mag()); - xml.intTag(level, "ypos", vscroll->pos()); - xml.intTag(level, "ymag", vscroll->mag()); - xml.tag(level, "/ScoreEdit"); - } +//PIXELS_PER_NOTEPOS must be greater or equal to 3*NOTE_XLEN + 2*NOTE_SHIFT +//because if tick 0 is at x=0: the notes can be shifted by NOTE_SHIFT. +//additionally, they can be moved by NOTE_XLEN (collision avoiding) +//then, they have their own width, which is NOTE_XLEN/2 into the x>0-area +//the same thing applies to the x<0-area -//--------------------------------------------------------- -// readStatus -//--------------------------------------------------------- +// OOO +// | +// ^actual calculated x, without shifting or moving +// ^ and +// ^ : moved note (by XLEN) +// additionally, a shift is possible +// total_width = shift + move + note_xlen + move + shift, where move==note_xlen +// total_width = 2*shift + 3*note_xlen +// if total_width is greater than px_per_notepos, there will be collisions! -void ScoreEdit::readStatus(Xml& xml) - { - for (;;) { - Xml::Token token = xml.parse(); - if (token == Xml::Error || token == Xml::End) - break; - const QString& tag = xml.s1(); - switch (token) { - case Xml::TagStart: - if (tag == "steprec") { - int val = xml.parseInt(); - canvas->setSteprec(val); - srec->setChecked(val); - } - else if (tag == "midiin") { - int val = xml.parseInt(); - canvas->setMidiin(val); - midiin->setChecked(val); - } - else if (tag == "tool") { - int tool = xml.parseInt(); - canvas->setTool(tool); - tools2->set(tool); - } - else if (tag == "midieditor") - MidiEditor::readStatus(xml); - else if (tag == "ctrledit") { - CtrlEdit* ctrl = addCtrl(); - ctrl->readStatus(xml); - } - else if (tag == splitter->objectName()) - splitter->readStatus(xml); - else if (tag == hsplitter->objectName()) - hsplitter->readStatus(xml); - else if (tag == "quantStrength") - _quantStrength = xml.parseInt(); - else if (tag == "quantLimit") - _quantLimit = xml.parseInt(); - else if (tag == "quantLen") - _quantLen = xml.parseInt(); - else if (tag == "playEvents") { - _playEvents = xml.parseInt(); - canvas->playEvents(_playEvents); - speaker->setChecked(_playEvents); - } - else if (tag == "xmag") - hscroll->setMag(xml.parseInt()); - else if (tag == "xpos") - hscroll->setPos(xml.parseInt()); - else if (tag == "ymag") - vscroll->setMag(xml.parseInt()); - else if (tag == "ypos") - vscroll->setPos(xml.parseInt()); - else - xml.unknown("ScoreEdit"); - break; - case Xml::TagEnd: - if (tag == "ScoreEdit") { - _quantInit = _quant; - _rasterInit = _raster; - toolbar->setRaster(_raster); - toolbar->setQuant(_quant); - canvas->redrawGrid(); - return; - } - default: - break; - } - } - } - -static int rasterTable[] = { - //-9----8- 7 6 5 4 3(1/4) 2 1 - 4, 8, 16, 32, 64, 128, 256, 512, 1024, // triple - 6, 12, 24, 48, 96, 192, 384, 768, 1536, - 9, 18, 36, 72, 144, 288, 576, 1152, 2304 // dot - }; +#define NOTE_MOVE_X (PIXELS_PER_NOTEPOS/2) +//TODO richtige werte finden! +#define REST_AUSWEICH_X 10 +#define DOT_XDIST 6 +#define DOT_XBEGIN 10 +#define DOT_XBEGIN_REST 10 -//--------------------------------------------------------- -// viewKeyPressEvent -//--------------------------------------------------------- +#define TIMESIG_POSADD 20 +#define NUMBER_HEIGHT (pix_num[0].height()) -void ScoreEdit::keyPressEvent(QKeyEvent* event) - { - if (info->hasFocus()) { - event->ignore(); - return; - } - - 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; - - PianoCanvas* pc = (PianoCanvas*)canvas; - int key = event->key(); - - //if (event->state() & Qt::ShiftButton) - if (((QInputEvent*)event)->modifiers() & Qt::ShiftModifier) - key += Qt::SHIFT; - //if (event->state() & Qt::AltButton) - if (((QInputEvent*)event)->modifiers() & Qt::AltModifier) - key += Qt::ALT; - //if (event->state() & Qt::ControlButton) - if (((QInputEvent*)event)->modifiers() & Qt::ControlModifier) - key+= Qt::CTRL; - - if (key == Qt::Key_Escape) { - close(); - return; - } - else if (key == shortcuts[SHRT_TOOL_POINTER].key) { - tools2->set(PointerTool); - return; - } - else if (key == shortcuts[SHRT_TOOL_PENCIL].key) { - tools2->set(PencilTool); - return; - } - else if (key == shortcuts[SHRT_TOOL_RUBBER].key) { - tools2->set(RubberTool); - return; - } - else if (key == shortcuts[SHRT_TOOL_LINEDRAW].key) { - tools2->set(DrawTool); - return; - } - else if (key == shortcuts[SHRT_POS_INC].key) { - pc->pianoCmd(CMD_RIGHT); - return; - } - else if (key == shortcuts[SHRT_POS_DEC].key) { - pc->pianoCmd(CMD_LEFT); - return; - } - else if (key == shortcuts[SHRT_POS_INC_NOSNAP].key) { - pc->pianoCmd(CMD_RIGHT_NOSNAP); - return; - } - else if (key == shortcuts[SHRT_POS_DEC_NOSNAP].key) { - pc->pianoCmd(CMD_LEFT_NOSNAP); - return; - } - else if (key == shortcuts[SHRT_INSERT_AT_LOCATION].key) { - pc->pianoCmd(CMD_INSERT); - return; - } - else if (key == Qt::Key_Delete) { - pc->pianoCmd(CMD_DELETE); - return; - } - else if (key == shortcuts[SHRT_ZOOM_IN].key) { - int mag = hscroll->mag(); - int zoomlvl = ScrollScale::getQuickZoomLevel(mag); - if (zoomlvl < 23) - zoomlvl++; - - int newmag = 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 = ScrollScale::getQuickZoomLevel(mag); - if (zoomlvl > 1) - zoomlvl--; - - int newmag = 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) { - PartList* p = this->parts(); - Part* first = p->begin()->second; - hscroll->setPos(song->cpos() - first->tick() ); - return; - } - else if (key == shortcuts[SHRT_SCROLL_LEFT].key) { - int pos = hscroll->pos() - config.division; - if (pos < 0) - pos = 0; - hscroll->setPos(pos); - return; - } - else if (key == shortcuts[SHRT_SCROLL_RIGHT].key) { - int pos = hscroll->pos() + config.division; - hscroll->setPos(pos); - return; - } - 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_EVENT_COLOR].key) { - if (colorMode == 0) - colorMode = 1; - else if (colorMode == 1) - colorMode = 2; - else - colorMode = 0; - setEventColorMode(colorMode); - return; - } - 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; - } - setQuant(val); - setRaster(val); - toolbar->setQuant(_quant); - toolbar->setRaster(_raster); - } +//kann 0 oder 1 sein: +//bei notenkollisionen mit ungerader anzahl von kollidierenden +//wird immer so ausgewichen, dass möglichst wenige ausweichen müssen +//wenn die anzahl aber gerade ist, gibt es keine "bessere" lösung +//in dem fall werden immer die geraden (0) bzw. ungeraden (1) +//ausweichen. +#define AUSWEICHEN_BEVORZUGT 0 -//--------------------------------------------------------- -// configQuant -//--------------------------------------------------------- +#define STEM_LEN 30 -void ScoreEdit::configQuant() - { - if (!quantConfig) { - quantConfig = new QuantConfig(_quantStrength, _quantLimit, _quantLen); - connect(quantConfig, SIGNAL(setQuantStrength(int)), SLOT(setQuantStrength(int))); - connect(quantConfig, SIGNAL(setQuantLimit(int)), SLOT(setQuantLimit(int))); - connect(quantConfig, SIGNAL(setQuantLen(bool)), SLOT(setQuantLen(bool))); - } - quantConfig->show(); - } +#define DOTTED_RESTS true +#define UNSPLIT_RESTS true -//--------------------------------------------------------- -// setSteprec -//--------------------------------------------------------- +#define AUX_LINE_LEN 1.5 -void ScoreEdit::setSteprec(bool flag) - { - canvas->setSteprec(flag); - if (flag == false) - midiin->setChecked(flag); - } +#define ACCIDENTIAL_DIST 11 +#define KEYCHANGE_ACC_DIST 9 +#define KEYCHANGE_ACC_LEFTDIST 3 +#define KEYCHANGE_ACC_RIGHTDIST 3 -//--------------------------------------------------------- -// eventColorModeChanged -//--------------------------------------------------------- -void ScoreEdit::eventColorModeChanged(int mode) - { - colorMode = mode; - colorModeInit = colorMode; - - ((PianoCanvas*)(canvas))->setColorMode(colorMode); - } +#define stdmap std::map -//--------------------------------------------------------- -// setEventColorMode -//--------------------------------------------------------- +#define no_notepos note_pos_t() -void ScoreEdit::setEventColorMode(int mode) - { - colorMode = mode; - colorModeInit = colorMode; - - ///eventColor->setItemChecked(0, mode == 0); - ///eventColor->setItemChecked(1, mode == 1); - ///eventColor->setItemChecked(2, mode == 2); - evColorBlueAction->setChecked(mode == 0); - evColorPitchAction->setChecked(mode == 1); - evColorVelAction->setChecked(mode == 2); - - ((PianoCanvas*)(canvas))->setColorMode(colorMode); - } +#define TIE_DIST 5 +#define TIE_HEIGHT 6 +#define TIE_THICKNESS 3 -//--------------------------------------------------------- -// clipboardChanged -//--------------------------------------------------------- +void ScoreCanvas::draw_tie (QPainter& p, int x1, int x4, int yo, bool up) +{ + QPainterPath path; + + int y1, y2, y3; + + if (up) + { + y1 = yo - TIE_DIST; + y2 = y1 - TIE_HEIGHT; + y3=y2-TIE_THICKNESS; + } + else + { + y1 = yo + TIE_DIST; + y2 = y1 + TIE_HEIGHT; + y3=y2+TIE_THICKNESS; + } + + int x2 = x1 + (x4-x1)/4; + int x3 = x4 - (x4-x1)/4; + + path.moveTo(x1,y1); + path.cubicTo( x2,y2 , x3,y2 , x4,y1 ); + path.cubicTo( x3,y3 , x2,y3 , x1,y1 ); + + p.setPen(Qt::black); + p.setBrush(Qt::black); + + p.drawPath(path); +} -void ScoreEdit::clipboardChanged() - { - editPasteAction->setEnabled(QApplication::clipboard()->mimeData()->hasFormat(QString("text/x-muse-eventlist"))); - } +ScoreItemList ScoreCanvas::create_itemlist(ScoreEventList& eventlist) +{ + ScoreItemList itemlist; + tonart_t tmp_key=C; + int lastevent=0; + int next_measure=-1; + + for (ScoreEventList::iterator it=eventlist.begin(); it!=eventlist.end(); it++) + { + int t, pitch, len, velo, actual_tick; + FloEvent::typeEnum type; + t=it->first; + pitch=it->second.pitch; + velo=it->second.vel; + len=it->second.len; + type=it->second.type; + actual_tick=it->second.tick; + if (actual_tick==-1) actual_tick=t; + + note_pos_t notepos=note_pos(pitch,tmp_key,VIOLIN); //TODO einstellmöglichkeiten + + printf("FLO: t=%i\ttype=%i\tpitch=%i\tvel=%i\tlen=%i\n",it->first, it->second.type, it->second.pitch, it->second.vel, it->second.len); + cout << "\tline="<<notepos.height<<"\tvorzeichen="<<notepos.vorzeichen << endl; + + + if (type==FloEvent::BAR) + { + // if neccessary, insert rest at between last note and end-of-measure + int rest=t-lastevent; + if (rest) + { + printf("\tend-of-measure: set rest at %i with len %i\n",lastevent,rest); + + list<note_len_t> lens=parse_note_len(rest,DOTTED_RESTS,UNSPLIT_RESTS,lastevent); + unsigned tmppos=lastevent; + for (list<note_len_t>::iterator x=lens.begin(); x!=lens.end(); x++) + { + cout << "\t\tpartial rest with len="<<x->len<<", dots="<<x->dots<<endl; + itemlist[tmppos].insert( FloItem(FloItem::REST,notepos,x->len,x->dots) ); + tmppos+=calc_len(x->len,x->dots); + itemlist[tmppos].insert( FloItem(FloItem::REST_END,notepos,0,0) ); + } + } + + lastevent=t; + next_measure=t+len; + + itemlist[t].insert( FloItem(FloItem::BAR,no_notepos,0,0) ); + } + else if (type==FloEvent::NOTE_ON) + { + int rest=t-lastevent; + if (rest) + { + printf("\tset rest at %i with len %i\n",lastevent,rest); + // no need to check if the rest crosses measure boundaries; + // it can't. + + list<note_len_t> lens=parse_note_len(rest,DOTTED_RESTS,UNSPLIT_RESTS,lastevent); + unsigned tmppos=lastevent; + for (list<note_len_t>::iterator x=lens.begin(); x!=lens.end(); x++) + { + cout << "\t\tpartial rest with len="<<x->len<<", dots="<<x->dots<<endl; + itemlist[tmppos].insert( FloItem(FloItem::REST,notepos,x->len,x->dots) ); + tmppos+=calc_len(x->len,x->dots); + itemlist[tmppos].insert( FloItem(FloItem::REST_END,notepos,0,0) ); + } + } + + + + printf("\tset note at %i with len=%i\n", t, len); + + int tmplen; + bool tied_note; + + // if the note exceeds the current measure, split it. + if (t+len>next_measure) + { + tmplen=next_measure-t; + tied_note=true; + + //append the "rest" of the note to our EventList, so that + //it gets processed again when entering the new measure + int newlen=len-tmplen; + eventlist.insert(pair<unsigned, FloEvent>(next_measure, FloEvent(actual_tick,pitch, velo,0,FloEvent::NOTE_OFF, it->second.source_part, it->second.source_event))); + eventlist.insert(pair<unsigned, FloEvent>(next_measure, FloEvent(actual_tick,pitch, velo,newlen,FloEvent::NOTE_ON, it->second.source_part, it->second.source_event))); + + cout << "\t\tnote was split to length "<<tmplen<<" + " << newlen<<endl; + } + else + { + tmplen=len; + tied_note=false; + } + + list<note_len_t> lens=parse_note_len(tmplen,true,true,t); + unsigned tmppos=t; + int n_lens=lens.size(); + int count=0; + for (list<note_len_t>::iterator x=lens.begin(); x!=lens.end(); x++) + { + cout << "\t\tpartial note with len="<<x->len<<", dots="<<x->dots<<endl; + count++; + + bool tie; + + if (count<n_lens) + tie=true; // all notes except the last are always tied + else + tie=tied_note; // only the last respects tied_note + + itemlist[tmppos].insert( FloItem(FloItem::NOTE,notepos,x->len,x->dots, tie, actual_tick, it->second.source_part, it->second.source_event) ); + tmppos+=calc_len(x->len,x->dots); + itemlist[tmppos].insert( FloItem(FloItem::NOTE_END,notepos,0,0) ); + } + } + else if (type==FloEvent::NOTE_OFF) + { + lastevent=t; + } + else if (type==FloEvent::TIME_SIG) + { + cout << "inserting TIME SIGNATURE "<<it->second.num<<"/"<<it->second.denom<<" at "<<t<<endl; + itemlist[t].insert( FloItem(FloItem::TIME_SIG, it->second.num, it->second.denom) ); + } + else if (type==FloEvent::KEY_CHANGE) + { + cout << "inserting KEY CHANGE ("<<it->second.tonart<<") at "<<t<<endl; + itemlist[t].insert( FloItem(FloItem::KEY_CHANGE, it->second.tonart) ); + tmp_key=it->second.tonart; // TODO FINDMICH MARKER das muss schöner werden + } + } + + return itemlist; +} -//--------------------------------------------------------- -// selectionChanged -//--------------------------------------------------------- +void ScoreCanvas::process_itemlist(ScoreItemList& itemlist) +{ + stdmap<int,int> occupied; + + //iterate through all times with items + for (ScoreItemList::iterator it2=itemlist.begin(); it2!=itemlist.end(); it2++) + { + set<FloItem, floComp>& curr_items=it2->second; + + cout << "at t="<<it2->first<<endl; + + // phase 0: keep track of active notes and rests ------------------- + // (and occupied lines) + for (set<FloItem, floComp>::iterator it=curr_items.begin(); it!=curr_items.end(); it++) + { + if ((it->type==FloItem::NOTE) || (it->type==FloItem::REST)) + occupied[it->pos.height]++; + else if ((it->type==FloItem::NOTE_END) || (it->type==FloItem::REST_END)) + occupied[it->pos.height]--; + } + + cout << "occupied: "; + for (stdmap<int,int>::iterator i=occupied.begin(); i!=occupied.end(); i++) + if (i->second) cout << i->first << "("<<i->second<<") "; + cout << endl; + + + + + + // phase 1: group rests together ----------------------------------- + int n_groups=0; + bool dont_group=false; + + //iterate through all rests R at that time + // iterate through all rests X at that time below R + // if something is between X and R ("barrier"), stop + // else: group them together + for (set<FloItem, floComp>::iterator it=curr_items.begin(); it!=curr_items.end();) + { + //only operate on rests; ignore rests which are created by this code + //(can be seen on already_grouped) + if ((it->type==FloItem::REST) && (it->already_grouped==false)) + { + cout << "trying to group" << endl; + + int lastheight; + int height_cumulative=0; + int counter=0; + + lastheight=it->pos.height; + + set<FloItem, floComp>::iterator tmp; + for (tmp=it; tmp!=curr_items.end();) + { + cout << "checking if we can proceed with an item at height="<<tmp->pos.height<<endl; + + for (int i=lastheight+1; i<=tmp->pos.height-1; i++) + if (occupied[i]!=0) + { + cout << "we can NOT, because occ["<<i<<"] != 0" << endl; + //stop grouping that rest + goto get_out_here; + } + + lastheight=tmp->pos.height; + + // the current item is a rest with equal len? cool! + if (tmp->type==FloItem::REST && tmp->len==it->len && tmp->dots==it->dots) + { + // füge diese pause zur gruppe dazu und entferne sie von diesem set hier + // entfernen aber nur, wenn sie nicht it, also die erste pause ist, die brauchen wir noch! + cout << "\tgrouping rest at height="<<tmp->pos.height<<endl; + height_cumulative+=tmp->pos.height; + counter++; + if (tmp!=it) + curr_items.erase(tmp++); + else + tmp++; + } + else //it's something else? well, we can stop grouping that rest then + { + cout << "we can NOT, because that item is not a rest" << endl; + //stop grouping that rest + goto get_out_here; + } + } + cout << "no items to proceed on left, continuing" << endl; + get_out_here: + + n_groups++; + + // entferne it vom set und + // füge eine pause mit dem "mittelwert" ein. + // occupied und die "_END"-events bleiben unberührt + + FloItem temp=*it; + temp.already_grouped=true; + + // have we grouped all available rests into one single? + if ( (n_groups==1) && (tmp==curr_items.end()) && !dont_group) + { + cout << "wow, we were able to group all rests into one single" << endl; + if (temp.len==0) //the whole rest is shifted one line (one space and one line) + temp.pos.height=DEFAULT_REST_HEIGHT+2; + else + temp.pos.height=DEFAULT_REST_HEIGHT; + } + else + { + cout << "creating group #"<<n_groups<<endl; + temp.pos.height=rint((float)height_cumulative/counter); + } + + // do NOT first insert, then erase, because if temp.height == + // it->height, the set considers temp and it equal (it doesn't + // take already_grouped into account) + // the result of this: insert does nothing, and erase erases + // the item. effect: you don't have the rest at all + curr_items.erase(it++); + + cout << "replacing all grouped rests with a rest at height="<<temp.pos.height<<endl; + + curr_items.insert(temp); + } + else + { + if (it->type==FloItem::NOTE) + dont_group=true; + + it++; + } + } + + + + + + // phase 2: avoid collisions of items ------------------------------ + set<FloItem, floComp>::iterator lastit, groupbegin, invalid; + invalid=curr_items.end(); + lastit=invalid; + groupbegin=invalid; + int count; + + //TODO FINDMICH MARKER: is "grouping" notes and rests together okay? + // or is it better to ignore rests when grouping? + for (set<FloItem, floComp>::iterator it=curr_items.begin(); it!=curr_items.end(); it++) + if ( (it->type==FloItem::NOTE) || (it->type==FloItem::REST) ) + { + if (lastit != invalid) + { + if (it->pos.height == lastit->pos.height+1) // they would collide? + { + if (groupbegin==invalid) // we have no group atm? + { + groupbegin=lastit; // start a new group + count=1; // because lastit has to be taken into account. + // for "it", there's a count++ later + } + + // the following will work even on start-new-group, + // because lastit will be "untouched", and that's why + // still be initalized to "false" + it->ausweich=!lastit->ausweich; + + count++; + } + else + { + if (groupbegin!=invalid) //this is the first item which + { //doesn't belong to the previous group any more + if (count%2 == 0) //count is even? + if (modulo(groupbegin->pos.height, 2) == AUSWEICHEN_BEVORZUGT) + for (set<FloItem, floComp>::iterator tmp=groupbegin; tmp!=it; tmp++) + tmp->ausweich=!tmp->ausweich; + + groupbegin=invalid; + } + // else: everything is ok :) + } + } + + lastit=it; + } + + // this could be the case if the last processed item before end() + // still belonged to a group. finalize this last group as well: + if (groupbegin!=invalid) + { + if (count%2 == 0) //count is even? + if (modulo(groupbegin->pos.height, 2) == AUSWEICHEN_BEVORZUGT) + for (set<FloItem, floComp>::iterator tmp=groupbegin; tmp!=curr_items.end(); tmp++) + tmp->ausweich=!tmp->ausweich; + } + // else: everything is ok :) + + + + + + // phase 3: group notes by their length and ------------------------ + // find out appropriate stem directions +group_them_again: + stdmap<int, cumulative_t> lengths; + bool has_whole=false; + + // find out which note lengths are present at that time + for (set<FloItem, floComp>::iterator it=curr_items.begin(); it!=curr_items.end(); it++) + if (it->type==FloItem::NOTE) + lengths[it->len].add(it->pos.height); + + cout << "note lengths at that time are:"; + for (stdmap<int, cumulative_t>::iterator it=lengths.begin(); it!=lengths.end(); it++) + cout << it->first << "("<< it->second.mean() <<") "; + cout << endl; + + if (lengths.erase(0)) // in case "0" is in the set, erase it + has_whole=true; // but remember there were whole notes + + if (lengths.size()==0) + { + cout << "no notes other than wholes, or no notes at all. we can relax" << endl; + } + else if (lengths.size()==1) + { + pair<const int, cumulative_t>& group=*(lengths.begin()); + stem_t stem; + int shift=0; + cout << "only one non-whole note group (len="<<group.first<<") at height="<<group.second.mean()<< endl; + + if (group.second.mean()>=6) + { + stem=DOWNWARDS; + if (has_whole) + shift=-1; + } + else + { + stem=UPWARDS; + if (has_whole) + shift=1; + } + + // for each note in that group + for (set<FloItem, floComp>::iterator it=curr_items.begin(); it!=curr_items.end(); it++) + if ( (it->type==FloItem::NOTE) && (it->len==group.first) ) + { + it->stem=stem; + it->shift=shift; + } + } + else if (lengths.size()==2) + { + stdmap<int, cumulative_t>::iterator it=lengths.begin(); + pair<const int, cumulative_t>& group1=*it; + it++; + pair<const int, cumulative_t>& group2=*it; + stem_t stem1, stem2; + int shift1=0, shift2=0; + cout << "two non-whole note group: len="<<group1.first<<" at height="<<group1.second.mean()<<" and len="<<group2.first<<" at height="<<group2.second.mean()<< endl; + + if (group1.second.mean()<group2.second.mean()) + { + stem1=DOWNWARDS; + stem2=UPWARDS; + shift1=-1; + if (has_whole) + shift2=1; + } + else + { + stem1=UPWARDS; + stem2=DOWNWARDS; + shift2=-1; + if (has_whole) + shift1=1; + } + + // for each note in group1 + for (set<FloItem, floComp>::iterator it=curr_items.begin(); it!=curr_items.end(); it++) + if ( (it->type==FloItem::NOTE) && (it->len==group1.first) ) + { + it->stem=stem1; + it->shift=shift1; + } + + // for each note in group2 + for (set<FloItem, floComp>::iterator it=curr_items.begin(); it!=curr_items.end(); it++) + if ( (it->type==FloItem::NOTE) && (it->len==group2.first) ) + { + it->stem=stem2; + it->shift=shift2; + } + } + else //more than 2 groups + { + //at this time, there are no iterators pointing to curr_items. + //this means, we can erase and insert safely into curr_items here. + + //group1 contains the longer notes, group2 the shorter + + int group1_n=lengths.size()/2; //round down + int group2_n=lengths.size()-group1_n; + + int group1_len, group2_len; + int group1_len_ticks, group2_len_ticks; + + + stdmap<int, cumulative_t>::iterator lit=lengths.begin(); + for (int i=0;i<group1_n-1;i++) lit++; //go to the group1_n-th entry + group1_len=lit->first; + for (int i=0;i<group2_n;i++) lit++; //go to the (group1_n+group2_n)-th entry (i.e., the last before end() ) + group2_len=lit->first; + + group1_len_ticks=calc_len(group1_len,0); + group2_len_ticks=calc_len(group2_len,0); + + cout << "we have "<<lengths.size()<<" groups. putting the "<<group1_n<<" longest and the "<<group2_n<<"shortest groups together"<<endl; + cout << "\tgroup1 will have len="<<group1_len<<" ("<<group1_len_ticks<<" ticks), group2 will have len="<<group2_len<<" ("<<group2_len_ticks<<" ticks)"<<endl; + + for (set<FloItem, floComp>::iterator it=curr_items.begin(); it!=curr_items.end();) + if (it->type==FloItem::NOTE) + { + //if *it belongs to group1 and has not already its destination length + cout << "\tprocessing note-item with len="<<it->len<<endl; + if (it->len<group1_len) + { + cout << "\t\thas to be changed to fit into group 1" << endl; + FloItem tmp=*it; + curr_items.erase(it++); + + int len_ticks_remaining=calc_len(tmp.len, tmp.dots)-group1_len_ticks; + bool tied_note=tmp.tied; + + + //shorten the current item to it's group's length + tmp.len=group1_len; + tmp.dots=0; + tmp.tied=true; + curr_items.insert(tmp); + + //create items for the remaining lengths (and a note_END for the just created shortened note) + int t=it2->first+group1_len_ticks; + + itemlist[t].insert( FloItem(FloItem::NOTE_END,tmp.pos,0,0) ); + + list<note_len_t> lens=parse_note_len(len_ticks_remaining,true,true,t); + unsigned tmppos=t; + int n_lens=lens.size(); + int count=0; + for (list<note_len_t>::iterator x=lens.begin(); x!=lens.end(); x++) + { + cout << "\t\twhile regrouping: partial note with len="<<x->len<<", dots="<<x->dots<<endl; + count++; + + bool tie; + + if (count<n_lens) + tie=true; // all notes except the last are always tied + else + tie=tied_note; // only the last respects tied_note + + itemlist[tmppos].insert( FloItem(FloItem::NOTE, tmp.pos,x->len,x->dots, tie, tmp.begin_tick, tmp.source_part, tmp.source_event) ); + tmppos+=calc_len(x->len,x->dots); + itemlist[tmppos].insert( FloItem(FloItem::NOTE_END, tmp.pos,0,0) ); + } + + } + //else if *it belongs to group2 and has not already its destination length + else if ((it->len<group2_len) && (it->len>group1_len)) + { + cout << "\t\thas to be changed to fit into group 2" << endl; + + FloItem tmp=*it; + curr_items.erase(it++); + + int len_ticks_remaining=calc_len(tmp.len, tmp.dots)-group2_len_ticks; + bool tied_note=tmp.tied; + + + //shorten the current item to it's group's length + tmp.len=group2_len; + tmp.dots=0; + tmp.tied=true; + curr_items.insert(tmp); + + //create items for the remaining lengths (and a note_END for the just created shortened note) + int t=it2->first+group2_len_ticks; + + itemlist[t].insert( FloItem(FloItem::NOTE_END,tmp.pos,0,0) ); + + list<note_len_t> lens=parse_note_len(len_ticks_remaining,true,true,t); + unsigned tmppos=t; + int n_lens=lens.size(); + int count=0; + for (list<note_len_t>::iterator x=lens.begin(); x!=lens.end(); x++) + { + cout << "\t\twhile regrouping: partial note with len="<<x->len<<", dots="<<x->dots<<endl; + count++; + + bool tie; + + if (count<n_lens) + tie=true; // all notes except the last are always tied + else + tie=tied_note; // only the last respects tied_note + + itemlist[tmppos].insert( FloItem(FloItem::NOTE,tmp.pos,x->len,x->dots, tie, tmp.begin_tick, tmp.source_part, tmp.source_event) ); + tmppos+=calc_len(x->len,x->dots); + itemlist[tmppos].insert( FloItem(FloItem::NOTE_END,tmp.pos,0,0) ); + } + + } + else //nothing to do? + { + cout << "\t\tnothing to do" << endl; + it++; + } + } + else + it++; + + goto group_them_again; //do it again + } + + } +} -void ScoreEdit::selectionChanged() - { - bool flag = canvas->selectionSize() > 0; - editCutAction->setEnabled(flag); - editCopyAction->setEnabled(flag); - editDelEventsAction->setEnabled(flag); - } +//draw a pixmap centered +void ScoreCanvas::draw_pixmap(QPainter& p, int x, int y, const QPixmap& pm) +{ + cout << "drawing pixmap width size="<<pm.width()<<"/"<<pm.height()<<" at "<<x<<"/"<<y<<endl; + p.drawPixmap(x-pm.width()/2,y-pm.height()/2,pm); +} -//--------------------------------------------------------- -// setSpeaker -//--------------------------------------------------------- +QRect bbox_center(int x, int y, const QSize& size) +{ + //why x-foo/2+foo? because due to integer divisions, + // x-foo/2+foo can be smaller than x+foo/2! + return QRect(x-size.width()/2,y-size.height()/2,size.width(),size.height()); +} -void ScoreEdit::setSpeaker(bool val) - { - _playEvents = val; - canvas->playEvents(_playEvents); - } +QRect FloItem::bbox() const +{ + return bbox_center(x,y,pix->size()); +} -//--------------------------------------------------------- -// resizeEvent -//--------------------------------------------------------- +void ScoreCanvas::draw_note_lines(QPainter& p) +{ + int xend=width(); + + for (int i=0;i<5;i++) + p.drawLine(0,YDIST+i*YLEN,xend,YDIST+i*YLEN); +} -void ScoreEdit::resizeEvent(QResizeEvent* ev) - { - QWidget::resizeEvent(ev); - _widthInit = ev->size().width(); - _heightInit = ev->size().height(); - } +void ScoreCanvas::calc_item_pos(ScoreItemList& itemlist) +{ + tonart_t curr_key=C; + int pos_add=0; + + for (ScoreItemList::iterator it2=itemlist.begin(); it2!=itemlist.end(); it2++) + { + for (set<FloItem, floComp>::iterator it=it2->second.begin(); it!=it2->second.end();it++) + { + //if this changes, also change the line(s) with Y_MARKER + it->x=it2->first * PIXELS_PER_WHOLE/TICKS_PER_WHOLE +pos_add; + it->y=YDIST+4*YLEN - (it->pos.height-2)*YLEN/2; + + if (it->type==FloItem::NOTE) + { + it->x+=NOTE_MOVE_X + it->shift*NOTE_SHIFT; + + switch (it->len) + { + case 0: it->pix=&pix_whole; break; + case 1: it->pix=&pix_half; break; + default: it->pix=&pix_quarter; break; + } + + it->stem_x=it->x; + + if (it->ausweich) + { + if ((it->stem==UPWARDS) || (it->len==0)) + it->x += it->pix->width()-1; //AUSWEICH_X + else + it->x -= it->pix->width()-1; //AUSWEICH_X + } + + //if there's a tie, try to find the tie's destination and set is_tie_dest + if (it->tied) + { + set<FloItem, floComp>::iterator dest; + set<FloItem, floComp>& desttime = itemlist[it2->first+calc_len(it->len,it->dots)]; + for (dest=desttime.begin(); dest!=desttime.end();dest++) + if ((dest->type==FloItem::NOTE) && (dest->pos==it->pos)) + { + dest->is_tie_dest=true; + dest->tie_from_x=it->x; + break; + } + + if (dest==desttime.end()) + cout << "THIS SHOULD NEVER HAPPEN: did not find destination note for tie!" << endl; + } + } + else if (it->type==FloItem::REST) + { + switch (it->len) + { + case 0: it->pix=&pix_r1; break; + case 1: it->pix=&pix_r2; break; + case 2: it->pix=&pix_r4; break; + case 3: it->pix=&pix_r8; break; + case 4: it->pix=&pix_r16; break; + } + + it->x+=NOTE_MOVE_X + (it->ausweich ? REST_AUSWEICH_X : 0); //AUSWEICH_X + } + else if (it->type==FloItem::BAR) + { + //nothing to do :) + } + else if (it->type==FloItem::TIME_SIG) + { + pos_add+=TIMESIG_POSADD; + + pos_add_list[it2->first]+=TIMESIG_POSADD; + //+= is used instead of =, because a key- and time- + //change can occur at the same time. + } + else if (it->type==FloItem::KEY_CHANGE) + { + tonart_t new_key=it->tonart; + + int aufloes_begin; //einschließlich + int aufloes_end; //ausschließlich! + int neue_vz_end; //ausschließlich! + + neue_vz_end=n_accidentials(new_key); + + int n_acc_drawn=0; + + //both are sharp or b keys? + if (is_sharp_key(curr_key) == is_sharp_key(new_key)) + { + // wenn new weniger vorzeichen hat als curr: + // löse nur diese auf + if (n_accidentials(curr_key)>n_accidentials(new_key)) + { + aufloes_begin=n_accidentials(new_key); + aufloes_end=n_accidentials(curr_key); + } + else + { + aufloes_begin=aufloes_end=0; //löse garnichts auf + } + } + else //different key-families (# and b mixed up) + { + aufloes_begin=0; + aufloes_end=n_accidentials(curr_key); + } + + n_acc_drawn=aufloes_end-aufloes_begin + neue_vz_end; + + pos_add+=n_acc_drawn*KEYCHANGE_ACC_DIST+ KEYCHANGE_ACC_LEFTDIST+ KEYCHANGE_ACC_RIGHTDIST; + + pos_add_list[it2->first]+=n_acc_drawn*KEYCHANGE_ACC_DIST+ KEYCHANGE_ACC_LEFTDIST+ KEYCHANGE_ACC_RIGHTDIST; + //+= is used instead of =, because a key- and time- + //change can occur at the same time. + + curr_key=new_key; + } + } + } +} -/* -//--------------------------------------------------------- -// trackInfoScroll -//--------------------------------------------------------- +void ScoreCanvas::draw_items(QPainter& p, ScoreItemList& itemlist, int x1, int x2) +{ + int from_tick, to_tick; + ScoreItemList::iterator from_it, to_it; + + //in general: drawing too much isn't bad. drawing too few is. + + from_tick=x_to_tick(x1); + from_it=itemlist.lower_bound(from_tick); + //from_it now contains the first time which is fully drawn + //however, the previous beat could still be relevant, when it's + //partly drawn. so we decrement from_it + if (from_it!=itemlist.begin()) from_it--; + + //decrement until we're at a time with a bar + //otherwise, drawing accidentials will be broken + while (from_it!=itemlist.begin() && from_it->second.find(FloItem(FloItem::BAR))==from_it->second.end()) + from_it--; + + + to_tick=x_to_tick(x2); + to_it=itemlist.upper_bound(to_tick); + //to_it now contains the first time which is not drawn at all any more + //however, a tie from 1:04 to 2:01 is stored in 2:01, not in 1:04, + //so for drawing ties, we need to increment to_it, so that the + //"first time not drawn at all any more" is the last which gets + //actually drawn. + if (to_it!=itemlist.end()) to_it++; //do one tick more than neccessary. this will draw ties + + draw_items(p,itemlist,from_it, to_it); +} -void ScoreEdit::trackInfoScroll(int y) - { - if (trackInfo->visibleWidget()) - trackInfo->visibleWidget()->move(0, -y); - } -*/ +void ScoreCanvas::draw_items(QPainter& p, ScoreItemList& itemlist) +{ + draw_items(p,itemlist,x_pos,x_pos+width()); +} -//--------------------------------------------------------- -// initShortcuts -//--------------------------------------------------------- +void ScoreCanvas::draw_items(QPainter& p, ScoreItemList& itemlist, ScoreItemList::iterator from_it, ScoreItemList::iterator to_it) +{ + vorzeichen_t curr_accidential[7]; + vorzeichen_t default_accidential[7]; + for (int i=0;i<7;i++) default_accidential[i]=NONE; + tonart_t curr_key=C; + + for (ScoreItemList::iterator it2=from_it; it2!=to_it; it2++) + { + cout << "at t="<<it2->first << endl; + + int upstem_y1 = -1, upstem_y2=-1, upstem_x=-1, upflag=-1; + int downstem_y1 = -1, downstem_y2=-1, downstem_x=-1, downflag=-1; + + for (set<FloItem, floComp>::iterator it=it2->second.begin(); it!=it2->second.end();it++) + { + if (it->type==FloItem::NOTE) + { + cout << "\tNOTE at line"<<it->pos.height<<" with acc.="<<it->pos.vorzeichen<<", len="<<pow(2,it->len); + for (int i=0;i<it->dots;i++) cout << "."; + cout << " , stem="; + if (it->stem==UPWARDS) + cout << "UPWARDS"; + else + cout << "DOWNWARDS"; + + cout << " , shift="<<it->shift<<", ausweich="<<it->ausweich<<", "; + if (!it->tied) cout << "un"; + cout << "tied, is_tie_dest="<<it->is_tie_dest<<endl; + + if (it->len!=0) //only for non-whole notes the stems are relevant! + { + if (it->stem==UPWARDS) + { + if (upstem_y1 == -1) + upstem_y1=it->y; + + upstem_y2=it->y; + + + if ((upflag!=-1) && (upflag!=it->len)) + cout << "WARNING: THIS SHOULD NEVER HAPPEN: upflag != this->flag" << endl; + upflag=it->len; + + if ((upstem_x!=-1) && (upstem_x!=it->stem_x )) + cout << "WARNING: THIS SHOULD NEVER HAPPEN: upstem_x != x_result" << endl; + upstem_x=it->stem_x; + } + else + { + if (downstem_y1 == -1) + downstem_y1=it->y; + + downstem_y2=it->y; + + + if ((downflag!=-1) && (downflag!=it->len)) + cout << "WARNING: THIS SHOULD NEVER HAPPEN: downflag != this->flag" << endl; + downflag=it->len; + + if ((downstem_x!=-1) && (downstem_x!=it->stem_x)) + cout << "WARNING: THIS SHOULD NEVER HAPPEN: downstem_x != x_result" << endl; + downstem_x=it->stem_x; //important: before the below calculation! + } + } + + + if (it->pos.height <= 0) //we need auxiliary lines on the bottom? + { //Y_MARKER + for (int i=0; i>=it->pos.height; i-=2) + p.drawLine(it->x-it->pix->width()*AUX_LINE_LEN/2 -x_pos,YDIST+4*YLEN - (i-2)*YLEN/2,it->x+it->pix->width()*AUX_LINE_LEN/2-x_pos,YDIST+4*YLEN - (i-2)*YLEN/2); + } + else if (it->pos.height >= 12) //we need auxiliary lines on the top? + { //Y_MARKER + for (int i=12; i<=it->pos.height; i+=2) + p.drawLine(it->x-it->pix->width()*AUX_LINE_LEN/2 -x_pos,YDIST+4*YLEN - (i-2)*YLEN/2,it->x+it->pix->width()*AUX_LINE_LEN/2-x_pos,YDIST+4*YLEN - (i-2)*YLEN/2); + } + + + draw_pixmap(p,it->x -x_pos,it->y,*it->pix); + + //draw dots + + int x_dot=DOT_XBEGIN; + int y_dot; + if (modulo(it->pos.height, 2) == 0) //note is on a line? + y_dot=YLEN * 0.33; // actually 0.5, but that would be _exactly_ in the space + else //note is between two lines? + y_dot=YLEN * 0.1; + + if (it->stem==DOWNWARDS) + y_dot=-y_dot; + //else y_dot=y_dot; + + for (int i=0;i<it->dots;i++) + { + draw_pixmap(p,it->x+x_dot -x_pos,it->y+y_dot,pix_dot); + x_dot+=DOT_XDIST; + } + + + + //draw accidentials + if (it->pos.vorzeichen != curr_accidential[modulo(it->pos.height,7)]) + { + QPixmap* acc_pix; + switch (it->pos.vorzeichen) + { + case NONE: acc_pix=&pix_noacc; break; + case SHARP: acc_pix=&pix_sharp; break; + case B: acc_pix=&pix_b; break; + } + draw_pixmap(p,it->x-ACCIDENTIAL_DIST -x_pos,it->y, *acc_pix); + + curr_accidential[modulo(it->pos.height,7)]=it->pos.vorzeichen; + } + + + //if needed, draw tie + if (it->is_tie_dest) + { + cout << "drawing tie" << endl; + draw_tie(p,it->tie_from_x-x_pos,it->x -x_pos,it->y, (it->len==0) ? true : (it->stem==DOWNWARDS) ); + // in english: "if it's a whole note, tie is upwards (true). if not, tie is upwards if + // stem is downwards and vice versa" + } + } + else if (it->type==FloItem::REST) + { + cout << "\tREST at line"<<it->pos.height<<" with len="<<pow(2,it->len); + for (int i=0;i<it->dots;i++) cout << "."; + cout << " , ausweich="<<it->ausweich<<endl; + + draw_pixmap(p,it->x -x_pos,it->y,*it->pix); + + + //draw dots + + int x_dot=DOT_XBEGIN_REST; + int y_dot; + if (modulo(it->pos.height, 2) == 0) //rest is on a line? + y_dot=YLEN * 0.33; // actually 0.5, but that would be _exactly_ in the space + else //note is between two lines? + y_dot=YLEN * 0.1; + + if (it->len!=0) // all rests except the whole are treated as + y_dot=-y_dot; // if they had a downwards stem + + for (int i=0;i<it->dots;i++) + { + draw_pixmap(p,it->x+x_dot -x_pos,it->y+y_dot,pix_dot); + x_dot+=DOT_XDIST; + } + } + else if (it->type==FloItem::BAR) + { + cout << "\tBAR" << endl; + + p.drawLine(it->x -x_pos,YDIST,it->x -x_pos,YDIST+4*YLEN); + + for (int i=0;i<7;i++) + curr_accidential[i]=default_accidential[i]; + } + else if (it->type==FloItem::TIME_SIG) + { + cout << "\tTIME SIGNATURE: "<<it->num<<"/"<<it->denom<<endl; + + int y_coord=YDIST+2*YLEN; + p.drawPixmap(it->x + 5 -x_pos, y_coord, pix_num[it->denom]); + p.drawPixmap(it->x + 5 -x_pos, y_coord-NUMBER_HEIGHT, pix_num[it->num]); + } + else if (it->type==FloItem::KEY_CHANGE) + { + tonart_t new_key=it->tonart; + cout << "\tKEY CHANGE: from "<<curr_key<<" to "<<new_key<<endl; + + int sharp_pos[]={10,7,11,8,5,9,6}; + int b_pos[]={6,9,5,8,4,7,3}; + + int* aufloes_ptr; + int* neue_vz_ptr; + + aufloes_ptr = is_sharp_key(curr_key) ? sharp_pos : b_pos; + neue_vz_ptr = is_sharp_key(new_key) ? sharp_pos : b_pos; + + int aufloes_begin; //einschließlich + int aufloes_end; //ausschließlich! + int neue_vz_end; //ausschließlich! + + neue_vz_end=n_accidentials(new_key); + + int n_acc_drawn=0; + + //both are sharp or b keys? + if (is_sharp_key(curr_key) == is_sharp_key(new_key)) + { + cout << "\tboth are # or b-keys" << endl; + // wenn new weniger vorzeichen hat als curr: + // löse nur diese auf + if (n_accidentials(curr_key)>n_accidentials(new_key)) + { + aufloes_begin=n_accidentials(new_key); + aufloes_end=n_accidentials(curr_key); + cout << "\taufloesen im intervall ["<<aufloes_begin<<";"<<aufloes_end<<"["<<endl; + } + else + { + aufloes_begin=aufloes_end=0; //löse garnichts auf + cout << "\tnichts wird aufgeloest"<<endl; + } + } + else + { + cout << "\tdifferent key-families (# and b mixed up)" << endl; + // löse alle auf + aufloes_begin=0; + aufloes_end=n_accidentials(curr_key); + cout << "\talle werden aufgeloest" << endl; + } + + // vorzeichen von [aufloes_begin;aufloes_end[ aus curr_key auflösen + for (int i=aufloes_begin; i<aufloes_end; i++) + { + int y_coord=YDIST+4*YLEN - ( aufloes_ptr[i] -2)*YLEN/2; //Y_MARKER + draw_pixmap(p,it->x + n_acc_drawn*KEYCHANGE_ACC_DIST + KEYCHANGE_ACC_LEFTDIST -x_pos,y_coord,pix_noacc); + n_acc_drawn++; + } + + QPixmap* pix = is_sharp_key(new_key) ? &pix_sharp : &pix_b; + vorzeichen_t new_accidential = is_sharp_key(new_key) ? SHARP : B; + + for (int i=0;i<7;i++) + default_accidential[i]=NONE; + + // alle vorzeichen aus new_key zeichnen + for (int i=0; i<neue_vz_end; i++) + { + int y_coord=YDIST+4*YLEN - ( neue_vz_ptr[i] -2)*YLEN/2; //Y_MARKER + draw_pixmap(p,it->x + n_acc_drawn*KEYCHANGE_ACC_DIST + KEYCHANGE_ACC_LEFTDIST -x_pos,y_coord,*pix); + n_acc_drawn++; + + default_accidential[neue_vz_ptr[i]%7]=new_accidential; + } + + curr_key=new_key; + for (int i=0;i<7;i++) + curr_accidential[i]=default_accidential[i]; + } + } + + //note: y1 is bottom, y2 is top! + if (upstem_x!=-1) + { + upstem_x=upstem_x-pix_quarter.width()/2 +pix_quarter.width() -1; + p.drawLine(upstem_x -x_pos, upstem_y1, upstem_x -x_pos, upstem_y2-STEM_LEN); + + if (upflag>=3) //if the note needs a flag + p.drawPixmap(upstem_x -x_pos,upstem_y2-STEM_LEN,pix_flag_up[upflag-3]); + } + if (downstem_x!=-1) + { + downstem_x=downstem_x-pix_quarter.width()/2; + p.drawLine(downstem_x -x_pos, downstem_y1+STEM_LEN, downstem_x -x_pos, downstem_y2); + + if (downflag>=3) //if the note needs a flag + p.drawPixmap(downstem_x -x_pos,downstem_y1+STEM_LEN-pix_flag_down[downflag-3].height(),pix_flag_down[downflag-3]); + } + } +} -void ScoreEdit::initShortcuts() - { - editCutAction->setShortcut(shortcuts[SHRT_CUT].key); - editCopyAction->setShortcut(shortcuts[SHRT_COPY].key); - editPasteAction->setShortcut(shortcuts[SHRT_PASTE].key); - editDelEventsAction->setShortcut(shortcuts[SHRT_DELETE].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); - //evColorBlueAction->setShortcut(shortcuts[ ].key); - //evColorPitchAction->setShortcut(shortcuts[ ].key); - //evColorVelAction->setShortcut(shortcuts[ ].key); - - funcOverQuantAction->setShortcut(shortcuts[SHRT_OVER_QUANTIZE].key); - funcNoteOnQuantAction->setShortcut(shortcuts[SHRT_ON_QUANTIZE].key); - funcNoteOnOffQuantAction->setShortcut(shortcuts[SHRT_ONOFF_QUANTIZE].key); - funcIterQuantAction->setShortcut(shortcuts[SHRT_ITERATIVE_QUANTIZE].key); - - funcConfigQuantAction->setShortcut(shortcuts[SHRT_CONFIG_QUANT].key); - - funcGateTimeAction->setShortcut(shortcuts[SHRT_MODIFY_GATE_TIME].key); - funcModVelAction->setShortcut(shortcuts[SHRT_MODIFY_VELOCITY].key); - funcCrescendoAction->setShortcut(shortcuts[SHRT_CRESCENDO].key); - funcTransposeAction->setShortcut(shortcuts[SHRT_TRANSPOSE].key); - funcThinOutAction->setShortcut(shortcuts[SHRT_THIN_OUT].key); - funcEraseEventAction->setShortcut(shortcuts[SHRT_ERASE_EVENT].key); - funcNoteShiftAction->setShortcut(shortcuts[SHRT_NOTE_SHIFT].key); - funcMoveClockAction->setShortcut(shortcuts[SHRT_MOVE_CLOCK].key); - funcCopyMeasureAction->setShortcut(shortcuts[SHRT_COPY_MEASURE].key); - funcEraseMeasureAction->setShortcut(shortcuts[SHRT_ERASE_MEASURE].key); - funcDelMeasureAction->setShortcut(shortcuts[SHRT_DELETE_MEASURE].key); - funcCreateMeasureAction->setShortcut(shortcuts[SHRT_CREATE_MEASURE].key); - funcSetFixedLenAction->setShortcut(shortcuts[SHRT_FIXED_LEN].key); - funcDelOverlapsAction->setShortcut(shortcuts[SHRT_DELETE_OVERLAPS].key); - - } -//--------------------------------------------------------- -// execDeliveredScript -//--------------------------------------------------------- -void ScoreEdit::execDeliveredScript(int id) +void ScoreCanvas::draw(QPainter& p, const QRect& rect) { - //QString scriptfile = QString(INSTPREFIX) + SCRIPTSSUFFIX + deliveredScriptNames[id]; - QString scriptfile = song->getScriptPath(id, true); - song->executeScript(scriptfile.toAscii().data(), parts(), quant(), true); + cout <<"now in ScoreCanvas::draw"<<endl; + + + + p.setPen(Qt::black); + + draw_note_lines(p); + draw_items(p, itemlist); } -//--------------------------------------------------------- -// execUserScript -//--------------------------------------------------------- -void ScoreEdit::execUserScript(int id) +int ScoreCanvas::tick_to_x(int t) { - QString scriptfile = song->getScriptPath(id, false); - song->executeScript(scriptfile.toAscii().data(), parts(), quant(), true); + int x=t*PIXELS_PER_WHOLE/TICKS_PER_WHOLE; + + for (std::map<int,int>::iterator it=pos_add_list.begin(); it!=pos_add_list.end() && it->first<=t; it++) + x+=it->second; + + return x; } -//--------------------------------------------------------- -// newCanvasWidth -//--------------------------------------------------------- +int ScoreCanvas::calc_posadd(int t) +{ + int result=0; -void ScoreEdit::newCanvasWidth(int /*w*/) - { -/* - int nw = w + (vscroll->width() - 18); // 18 is the fixed width of the CtlEdit VScale widget. - if(nw < 1) - nw = 1; - - for (std::list<CtrlEdit*>::iterator i = ctrlEditList.begin(); - i != ctrlEditList.end(); ++i) { - // Changed by Tim. p3.3.7 - //(*i)->setCanvasWidth(w); - (*i)->setCanvasWidth(nw); - } - - updateHScrollRange(); -*/ - } + for (std::map<int,int>::iterator it=pos_add_list.begin(); it!=pos_add_list.end() && it->first<t; it++) + result+=it->second; -//--------------------------------------------------------- -// toggleTrackInfo -//--------------------------------------------------------- + return result; +} + +//doesn't round mathematically correct, but i don't think this +//will be a problem, because a tick is pretty small +int ScoreCanvas::x_to_tick(int x) +{ + int t=TICKS_PER_WHOLE * x/PIXELS_PER_WHOLE; + int min_t=0; + + cout << "t="<<t<<endl; + + for (std::map<int,int>::iterator it=pos_add_list.begin(); it!=pos_add_list.end() && it->first<t; it++) + { + cout << "at pos_add event at t="<<it->first<<", add="<<it->second<<endl; + min_t=it->first; + x-=it->second; + t=TICKS_PER_WHOLE * x/PIXELS_PER_WHOLE; + } + + return t > min_t ? t : min_t; +} + + +#define DRAG_INIT_DISTANCE 5 + +void ScoreCanvas::mousePressEvent (QMouseEvent* event) +{ + // den errechneten tick immer ABrunden! + // denn der "bereich" eines schlags geht von schlag_begin bis nächsterschlag_begin-1 + // noten werden aber genau in die mitte dieses bereiches gezeichnet + + int y=event->y(); + int x=event->x()+x_pos; + int tick= int(x_to_tick(x) / FLO_QUANT) * FLO_QUANT; + //TODO quantizing must (maybe?) be done with the proper functions + + + cout << "mousePressEvent at "<<x<<"/"<<y<<"; tick="<<tick<<endl; + + for (set<FloItem, floComp>::iterator it=itemlist[tick].begin(); it!=itemlist[tick].end(); it++) + if (it->type==FloItem::NOTE) + if (it->bbox().contains(x,y)) + { + mouse_down_pos=event->pos(); + mouse_operation=NO_OP; + + int t=tick; + set<FloItem, floComp>::iterator found; + + do + { + found=itemlist[t].find(FloItem(FloItem::NOTE, it->pos)); + if (found == itemlist[t].end()) + { + cout << "FATAL: THIS SHOULD NEVER HAPPEN: could not find the note's tie-destination" << endl; + break; + } + else + { + t+=calc_len(found->len, found->dots); + } + } while (found->tied); + + int total_begin=it->begin_tick; + int total_end=t; + + int this_begin=tick; + int this_end=this_begin+calc_len(it->len, it->dots); + + //that's the only note corresponding to the event? + if (this_begin==total_begin && this_end==total_end) + { + if (x < it->x) + mouse_x_drag_operation=BEGIN; + else + mouse_x_drag_operation=LENGTH; + } + //that's NOT the only note? + else + { + if (this_begin==total_begin) + mouse_x_drag_operation=BEGIN; + else if (this_end==total_end) + mouse_x_drag_operation=LENGTH; + else + mouse_x_drag_operation=NO_OP; + } + + cout << "you clicked at a note with begin at "<<it->begin_tick<<" and end at "<<t<<endl; + cout << "x-drag-operation will be "<<mouse_x_drag_operation<<endl; + cout << "pointer to part is "<<it->source_part; + if (!it->source_part) cout << " (WARNING! THIS SHOULD NEVER HAPPEN!)"; + cout << endl; + + dragged_event=*it->source_event; + dragged_event_part=it->source_part; + dragged_event_original_pitch=dragged_event.pitch(); + + setMouseTracking(true); + + break; + } + +} + +void ScoreCanvas::mouseReleaseEvent (QMouseEvent* event) +{ + setMouseTracking(false); +} + +#define PITCH_DELTA 5 -void ScoreEdit::toggleTrackInfo() +void ScoreCanvas::mouseMoveEvent (QMouseEvent* event) { - bool vis = midiTrackInfo->isVisible(); - infoScroll->setVisible(!vis); - infoScroll->setEnabled(!vis); + int dx=event->x()-mouse_down_pos.x(); + int dy=event->y()-mouse_down_pos.y(); + + int y=event->y(); + int x=event->x()+x_pos; + int tick= int(x_to_tick(x) / FLO_QUANT) * FLO_QUANT; + + if (mouse_operation==NO_OP) + { + if ((abs(dx)>DRAG_INIT_DISTANCE) && (mouse_x_drag_operation!=NO_OP)) + { + cout << "mouse-operation is now "<<mouse_x_drag_operation<<endl; + mouse_operation=mouse_x_drag_operation; + } + else if (abs(dy)>DRAG_INIT_DISTANCE) + { + cout << "mouse-operation is now PITCH" << endl; + mouse_operation=PITCH; + } + } + + int new_pitch; + + switch (mouse_operation) + { + case NONE: + break; + + case PITCH: + cout << "changing pitch, delta="<<dy/PITCH_DELTA<<endl; + new_pitch=dragged_event_original_pitch - dy/PITCH_DELTA; + + if (dragged_event.pitch()!=new_pitch) + { + Event tmp=dragged_event.clone(); + tmp.setPitch(dragged_event_original_pitch- dy/PITCH_DELTA); + + audio->msgChangeEvent(dragged_event, tmp, dragged_event_part, false, false, false); + dragged_event=tmp; + + song_changed(0); + } + + break; + + case BEGIN: + if (dragged_event.tick() != tick) + { + Event tmp=dragged_event.clone(); + tmp.setTick(tick); + + audio->msgChangeEvent(dragged_event, tmp, dragged_event_part, false, false, false); + dragged_event=tmp; + + song_changed(0); + } + + break; + + case LENGTH: + tick+=FLO_QUANT; + if (dragged_event.tick()+dragged_event.lenTick() != tick) + { + Event tmp=dragged_event.clone(); + tmp.setLenTick(tick-dragged_event.tick()); + + audio->msgChangeEvent(dragged_event, tmp, dragged_event_part, false, false, false); + dragged_event=tmp; + + song_changed(0); + } + + break; + } } + +void ScoreCanvas::scroll_event(int x) +{ + cout << "SCROLL EVENT: x="<<x<<endl; + x_pos=x; + redraw(); +} + + +// TODO: testen: kommen die segfaults von muse oder von mir? [ von mir ] +// TODO: testen, ob das noten-splitten korrekt arbeitet [ scheint zu klappen ] +// TODO: testen, ob shift immer korrekt gesetzt wird [ scheint zu klappen ] + +//the following assertions are made: +// pix_quarter.width() == pix_half.width() + + +// pix->width()-1 + 1/2*pix->width() + SHIFT + ADD_SPACE +// 10-1+5+3+3=20 <- um so viel wird der taktstrich verschoben +// um das doppelte (20*2=40) werden die kleinsten schläge gegeneinander versetzt + + + +// bei änderung +// takt- oder tonartänderung: +// alles ab der betreffenden position löschen +// alles ab dort neu berechnen +// notenänderung: +// alle von der note betroffenen takte löschen und neuberechnen +// aus erstem betroffenen takt müssen tie-infos gesichert werden +// im takt nach dem letzten betroffenen müssen die tie-infos +// geupdated werden. ggf. löschen ist unnötig, da ties nur wandern, +// aber nicht verschwinden oder neu dazukommen werden. + + +//hint: recalculating event- and itemlists "from zero" +// could happen in realtime, as it is pretty fast. +// however, this adds unneccessary cpu usage. +// it is NO problem to recalc the stuff "from zero" +// every time something changes. + + + + + +//TODO WICHTIG TÖDLICH: +// bei parse_note_len: BUG (siehe auch bug.mpt) +// die anfangszeit wird in absoluten ticks statt +// ticks seit taktbeginn gegeben -> fehler1 +// außerdem funzt die funktion nicht richtig, wenn +// der takt nicht 4/4 ist! + + +// TODO FINDMICH: tonart aus muses eventliste in meine übernehmen! + +// TODO: schöne funktionen fürs takt- und tonart-vorzeichen zeichnen +// (mit längenangabe!) + + +// TODO: notenschlüssel zeichen! +// TODO: balken bei 8teln und 16teln etc +// TODO: die ItemList als map< pos_t , der_rest_t > umändern + +//TODO: ausweichen bei ganzen noten! + + + +//TODO: while dragging notes: +// send the correct events +// do undo() stuff +// etc. + +//TODO: support inserting and deleting notes +//TODO: when a note gets resized so that len=0, erase it +// but watch out for quantisation stuff then! +// (when the start is slightly before the beat, len is 2 instead of 0) +//TODO: deal with double notes? diff --git a/muse2/muse/midiedit/scoreedit.h b/muse2/muse/midiedit/scoreedit.h index 767dc463..340d8c02 100644 --- a/muse2/muse/midiedit/scoreedit.h +++ b/muse2/muse/midiedit/scoreedit.h @@ -1,8 +1,8 @@ //========================================================= // MusE // Linux Music Editor -// $Id: pianoroll.h,v 1.5.2.4 2009/11/16 11:29:33 lunar_shuttle Exp $ -// (C) Copyright 1999 Werner Schweer (ws@seh.de) +// scoreedit.cpp +// (C) Copyright 2011 Florian Jung (florian.a.jung@web.de) //========================================================= #ifndef __SCOREEDIT_H__ @@ -12,6 +12,8 @@ #include <QResizeEvent> #include <QLabel> #include <QKeyEvent> +#include <QPainter> +#include <QPixmap> #include <values.h> #include "noteinfo.h" @@ -19,6 +21,16 @@ #include "midieditor.h" #include "tools.h" #include "event.h" +#include "view.h" + +#include <set> +#include <map> +#include <list> + +using std::set; +using std::pair; +using std::map; +using std::list; class MidiPart; class TimeLabel; @@ -47,155 +59,413 @@ class MidiTrackInfo; class QScrollArea; //--------------------------------------------------------- -// PianoRoll +// ScoreEdit //--------------------------------------------------------- -class ScoreEdit : public MidiEditor { - Event selEvent; - MidiPart* selPart; - int selTick; - - //enum { CMD_EVENT_COLOR, CMD_CONFIG_QUANT, CMD_LAST }; - //int menu_ids[CMD_LAST]; - //Q3PopupMenu *menuEdit, *menuFunctions, *menuSelect, *menuConfig, *menuPlugins; - - - QMenu *menuEdit, *menuFunctions, *menuSelect, *menuConfig, *eventColor, *menuPlugins; - MidiTrackInfo *midiTrackInfo; - Track* selected; - - QAction* editCutAction; - QAction* editCopyAction; - QAction* editPasteAction; - QAction* editDelEventsAction; - - QAction* selectAllAction; - QAction* selectNoneAction; - QAction* selectInvertAction; - QAction* selectInsideLoopAction; - QAction* selectOutsideLoopAction; - QAction* selectPrevPartAction; - QAction* selectNextPartAction; - - QAction* evColorBlueAction; - QAction* evColorPitchAction; - QAction* evColorVelAction; - - QAction* funcOverQuantAction; - QAction* funcNoteOnQuantAction; - QAction* funcNoteOnOffQuantAction; - QAction* funcIterQuantAction; - QAction* funcConfigQuantAction; - QAction* funcGateTimeAction; - QAction* funcModVelAction; - QAction* funcCrescendoAction; - QAction* funcTransposeAction; - QAction* funcThinOutAction; - QAction* funcEraseEventAction; - QAction* funcNoteShiftAction; - QAction* funcMoveClockAction; - QAction* funcCopyMeasureAction; - QAction* funcEraseMeasureAction; - QAction* funcDelMeasureAction; - QAction* funcCreateMeasureAction; - QAction* funcSetFixedLenAction; - QAction* funcDelOverlapsAction; - - - int tickOffset; - int lenOffset; - int pitchOffset; - int veloOnOffset; - int veloOffOffset; - bool deltaMode; - - NoteInfo* info; - QToolButton* srec; - QToolButton* midiin; - - Toolbar1* toolbar; - Splitter* splitter; - Splitter* hsplitter; - Splitter* ctrlLane; - - QToolButton* speaker; - QToolBar* tools; - EditToolBar* tools2; - - int colorMode; - - static int _quantInit, _rasterInit; - static int _widthInit, _heightInit; - - static int _quantStrengthInit; - static int _quantLimitInit; - static bool _quantLenInit; - static int _toInit; - static int colorModeInit; - - int _quantStrength; - int _quantLimit; - int _to; - bool _quantLen; - QuantConfig* quantConfig; - bool _playEvents; - - //QScrollBar* infoScroll; - QScrollArea* infoScroll; - - Q_OBJECT - void initShortcuts(); - void setEventColorMode(int); - QWidget* genToolbar(QWidget* parent); - virtual void closeEvent(QCloseEvent*); - virtual void keyPressEvent(QKeyEvent*); - virtual void resizeEvent(QResizeEvent*); - - private slots: - void setSelection(int, Event&, Part*); - void noteinfoChanged(NoteInfo::ValType, int); - //CtrlEdit* addCtrl(); - void removeCtrl(CtrlEdit* ctrl); - void soloChanged(bool flag); - //void trackInfoScroll(int); - void setRaster(int); - void setQuant(int); - void configQuant(); - void setQuantStrength(int val) { _quantStrength = val; } - void setQuantLimit(int val) { _quantLimit = val; } - void setQuantLen(bool val) { _quantLen = val; } - void cmd(int); - void setSteprec(bool); - void setTo(int val) { _to = val; } - void eventColorModeChanged(int); - void clipboardChanged(); // enable/disable "Paste" - void selectionChanged(); // enable/disable "Copy" & "Paste" - void setSpeaker(bool); - void setTime(unsigned); - void follow(int pos); - void songChanged1(int); - void configChanged(); - void newCanvasWidth(int); - void toggleTrackInfo(); - void updateTrackInfo(); - - signals: - void deleted(unsigned long); - +class ScoreEdit : public MidiEditor +{ + Q_OBJECT + + private: + + + private slots: + + + signals: + void deleted(unsigned long); + + public slots: + CtrlEdit* addCtrl() {return NULL;}; //TODO does nothing + + public: + ScoreEdit(PartList*, QWidget* parent = 0, const char* name = 0, unsigned initPos = MAXINT); + ~ScoreEdit(); + static void readConfiguration(Xml&){}; //TODO does nothing + static void writeConfiguration(int, Xml&){}; //TODO does nothing + }; + + + + + +enum tonart_t +{ + SHARP_BEGIN, + C, // C or am, uses # for "black keys" + G, + D, + A, + E, + H, + FIS, //produces a #E (sounds like a F) + SHARP_END, + B_BEGIN, + C_B, // the same as C, but uses b for "black keys" + F, + ES, + AS, + DES, + GES, //sounds like FIS, but uses b instead of # + B_END +}; + +enum stem_t +{ + UPWARDS, + DOWNWARDS +}; + +enum vorzeichen_t +{ + B=-1, + NONE=0, + SHARP=1 +}; + +struct note_pos_t +{ + int height; // 0 means "C-line", 1 "D-line" and so on + vorzeichen_t vorzeichen; + + bool operator== (const note_pos_t& that) const + { + return (this->height==that.height) && (this->vorzeichen == that.vorzeichen); + } +}; + +bool operator< (const note_pos_t& a, const note_pos_t& b); + + +class FloEvent +{ + public: + enum typeEnum { NOTE_ON = 30, NOTE_OFF = 10, BAR = 20, TIME_SIG=23, KEY_CHANGE=26 }; //the order matters! + typeEnum type; + unsigned tick; + Part* source_part; + Event* source_event; + + int pitch; + int vel; + int len; + + int num; + int denom; + + tonart_t tonart; + + + FloEvent(unsigned ti, int p,int v,int l,typeEnum t, Part* part=NULL, Event* event=NULL) + { + pitch=p; + vel=v; + len=l; + type= t; + tick=ti; + source_event=event; + source_part=part; + } + FloEvent(unsigned ti, typeEnum t, int num_, int denom_) + { + type=t; + num=num_; + denom=denom_; + tick=ti; + source_event=NULL; + source_part=NULL; + } + FloEvent(unsigned ti, typeEnum t, tonart_t k) + { + type=t; + tonart=k; + tick=ti; + source_event=NULL; + source_part=NULL; + } +}; +class FloItem +{ + public: + enum typeEnum { NOTE=21, REST=22, NOTE_END=01, REST_END=02, BAR =10, TIME_SIG=13, KEY_CHANGE=16}; //the order matters! + typeEnum type; + unsigned begin_tick; + Event* source_event; + Part* source_part; + + note_pos_t pos; + int len; + int dots; + bool tied; + bool already_grouped; + + int num; + int denom; + + tonart_t tonart; + + mutable stem_t stem; + mutable int shift; + mutable bool ausweich; + mutable bool is_tie_dest; + mutable int tie_from_x; + + mutable int x; + mutable int y; + mutable int stem_x; + mutable QPixmap* pix; + + QRect bbox() const; + + + + FloItem(typeEnum t, note_pos_t p, int l=0,int d=0, bool ti=false, unsigned beg=0, Part* part=NULL, Event* event=NULL) + { + pos=p; + dots=d; + len=l; + type=t; + already_grouped=false; + tied=ti; + shift=0; + ausweich=false; + is_tie_dest=false; + begin_tick=beg; + source_event=event; + source_part=part; + } + + FloItem(typeEnum t, int num_, int denom_) + { + type=t; + num=num_; + denom=denom_; + begin_tick=-1; + source_event=NULL; + source_part=NULL; + } + + FloItem(typeEnum t, tonart_t k) + { + type=t; + tonart=k; + begin_tick=-1; + source_event=NULL; + source_part=NULL; + } + + FloItem(typeEnum t) + { + type=t; + + already_grouped=false; + tied=false; + shift=0; + ausweich=false; + is_tie_dest=false; + begin_tick=-1; + source_event=NULL; + source_part=NULL; + } + + FloItem() + { + already_grouped=false; + tied=false; + shift=0; + ausweich=false; + is_tie_dest=false; + begin_tick=-1; + source_event=NULL; + source_part=NULL; + } + + bool operator==(const FloItem& that) + { + if (this->type != that.type) return false; + + switch(type) + { + case NOTE: + case REST: + case NOTE_END: + case REST_END: + return (this->pos == that.pos); + + //the following may only occurr once in a set + //so we don't search for "the time signature with 4/4 + //at t=0", but only for "some time signature at t=0" + //that's why true is returned, and not some conditional + //expression + case BAR: + case KEY_CHANGE: + case TIME_SIG: + return true; + } + } +}; +struct floComp +{ + bool operator() (const pair<unsigned, FloEvent>& a, const pair<unsigned, FloEvent>& b ) + { + if (a.first < b.first) return true; + if (a.first > b.first) return false; + + if (a.second.type<b.second.type) return true; + if (a.second.type>b.second.type) return false; + + return (a.second.pitch<b.second.pitch); + } + bool operator() (const FloItem& a, const FloItem& b ) + { + if (a.type < b.type) return true; + if (a.type > b.type) return false; + + switch(a.type) + { + case FloItem::NOTE: + case FloItem::REST: + case FloItem::NOTE_END: + case FloItem::REST_END: + return (a.pos < b.pos); + + //the following may only occurr once in a set + //so we don't search for "the time signature with 4/4 + //at t=0", but only for "some time signature at t=0" + //that's why true is returned, and not some conditional + //expression + case FloItem::BAR: + case FloItem::KEY_CHANGE: + case FloItem::TIME_SIG: + return false; + } + return (a.pos < b.pos); + } +}; + +typedef set< pair<unsigned, FloEvent>, floComp > ScoreEventList; +typedef map< unsigned, set<FloItem, floComp> > ScoreItemList; + +enum clef_t +{ + VIOLIN, + BASS +}; + + +struct note_len_t +{ + int len; + int dots; + + note_len_t(int l, int d) + { + len=l; dots=d; + } + + note_len_t(int l) + { + len=l; dots=0; + } +}; + +bool operator< (const note_len_t& a,const note_len_t& b); + +struct cumulative_t +{ + int count; + int cumul; + + cumulative_t() + { + count=0; + cumul=0; + } + + void add(int v) + { + count++; + cumul+=v; + } + + float mean() + { + return (float)cumul/count; + } +}; + + +class ScoreCanvas : public View +{ + Q_OBJECT + private: + void load_pixmaps(); + ScoreEventList createAppropriateEventList(PartList* pl, Track* track); + note_pos_t note_pos_(int note, tonart_t key); + note_pos_t note_pos (int note, tonart_t key, clef_t clef); + int calc_len(int l, int d); + list<note_len_t> parse_note_len(int len_ticks, bool allow_dots=true, bool allow_normal=true, int begin_tick=0); + void draw_tie (QPainter& p, int x1, int x4, int yo, bool up=true); + ScoreItemList create_itemlist(ScoreEventList& eventlist); + void process_itemlist(ScoreItemList& itemlist); + void draw_pixmap(QPainter& p, int x, int y, const QPixmap& pm); + void draw_note_lines(QPainter& p); + void draw_items(QPainter& p, ScoreItemList& itemlist, ScoreItemList::iterator from_it, ScoreItemList::iterator to_it); + void draw_items(QPainter& p, ScoreItemList& itemlist, int x1, int x2); + void draw_items(QPainter& p, ScoreItemList& itemlist); + void calc_item_pos(ScoreItemList& itemlist); + + int tick_to_x(int t); + int x_to_tick(int x); + int calc_posadd(int t); + + QPixmap pix_whole, pix_half, pix_quarter; + QPixmap pix_r1, pix_r2, pix_r4, pix_r8, pix_r16; + QPixmap pix_dot, pix_flag_up[4], pix_flag_down[4]; + QPixmap pix_b, pix_sharp, pix_noacc; + QPixmap pix_num[10]; + + + std::map<int,int> pos_add_list; + ScoreEventList eventlist; + ScoreItemList itemlist; + int x_pos; + + + QPoint mouse_down_pos; + bool mouse_down; + enum operation_t + { + NO_OP=0, + BEGIN=1, + LENGTH=2, + PITCH=3 + }; + operation_t mouse_operation; + operation_t mouse_x_drag_operation; + + Part* dragged_event_part; + Event dragged_event; + int dragged_event_original_pitch; + + public slots: - virtual void updateHScrollRange(); - void execDeliveredScript(int id); - void execUserScript(int id); - CtrlEdit* addCtrl(); - - public: - ScoreEdit(PartList*, QWidget* parent = 0, const char* name = 0, unsigned initPos = MAXINT); - ~ScoreEdit(); - virtual void readStatus(Xml&); - virtual void writeStatus(int, Xml&) const; - static void readConfiguration(Xml&); - static void writeConfiguration(int, Xml&); - }; + void scroll_event(int); + void song_changed(int); + + + protected: + virtual void draw(QPainter& p, const QRect& rect); + MidiEditor* editor; + + virtual void mousePressEvent (QMouseEvent* event); + virtual void mouseMoveEvent (QMouseEvent* event); + virtual void mouseReleaseEvent (QMouseEvent* event); + + public: + ScoreCanvas(MidiEditor*, QWidget*, int, int); + ~ScoreCanvas(){}; +}; #endif |