diff options
-rw-r--r-- | muse2/muse/functions.cpp | 69 | ||||
-rw-r--r-- | muse2/muse/functions.h | 4 | ||||
-rw-r--r-- | muse2/muse/midiedit/pianoroll.cpp | 12 | ||||
-rw-r--r-- | muse2/muse/midiedit/prcanvas.h | 2 | ||||
-rw-r--r-- | muse2/muse/midiedit/scoreedit.cpp | 25 | ||||
-rw-r--r-- | muse2/muse/midiedit/scoreedit.h | 3 | ||||
-rw-r--r-- | muse2/muse/widgets/function_dialogs/CMakeLists.txt | 3 | ||||
-rw-r--r-- | muse2/muse/widgets/function_dialogs/legato.cpp | 88 | ||||
-rw-r--r-- | muse2/muse/widgets/function_dialogs/legato.h | 42 | ||||
-rw-r--r-- | muse2/muse/widgets/function_dialogs/legatobase.ui | 233 |
10 files changed, 474 insertions, 7 deletions
diff --git a/muse2/muse/functions.cpp b/muse2/muse/functions.cpp index 1ad81693..89a66fa7 100644 --- a/muse2/muse/functions.cpp +++ b/muse2/muse/functions.cpp @@ -27,6 +27,7 @@ Setlen* set_notelen_dialog=NULL; Move* move_notes_dialog=NULL; Transpose* transpose_dialog=NULL; Crescendo* crescendo_dialog=NULL; +Legato* legato_dialog=NULL; void init_function_dialogs(QWidget* parent) { @@ -39,6 +40,7 @@ void init_function_dialogs(QWidget* parent) move_notes_dialog = new Move(parent); transpose_dialog = new Transpose(parent); crescendo_dialog = new Crescendo(parent); + legato_dialog = new Legato(parent); } set<Part*> partlist_to_set(PartList* pl) @@ -181,6 +183,16 @@ bool crescendo(const set<Part*>& parts) return true; } +bool legato(const set<Part*>& parts) +{ + if (!legato_dialog->exec()) + return false; + + legato(parts,legato_dialog->range, legato_dialog->min_len, !legato_dialog->allow_shortening); + + return true; +} + void modify_velocity(const set<Part*>& parts, int range, int rate, int offset) @@ -535,6 +547,60 @@ void delete_overlaps(const set<Part*>& parts, int range) } } +void legato(const set<Part*>& parts, int range, int min_len, bool dont_shorten) +{ + map<Event*, Part*> events = get_events(parts, range); + bool undo_started=false; + + if (min_len<=0) min_len=1; + + if (!events.empty()) + { + for (map<Event*, Part*>::iterator it1=events.begin(); it1!=events.end(); it1++) + { + Event& event1=*(it1->first); + Part* part1=it1->second; + + unsigned len=MAXINT; + // we may NOT optimize by letting it2 start at (it1 +1); this optimisation + // is only allowed when events was sorted by time. it is, however, sorted + // randomly by pointer. + for (map<Event*, Part*>::iterator it2=events.begin(); it2!=events.end(); it2++) + { + Event& event2=*(it2->first); + Part* part2=it2->second; + + bool relevant = (event2.tick() >= event1.tick() + min_len); + if (dont_shorten) + relevant = relevant && (event2.tick() >= event1.endTick()); + + if ( (part1->events()==part2->events()) && // part1 and part2 are the same or are duplicates + relevant && // they're not too near (respect min_len and dont_shorten) + (event2.tick()-event1.tick() < len ) ) // that's the nearest relevant following note + len=event2.tick()-event1.tick(); + } + + if (len==MAXINT) len=event1.lenTick(); // if no following note was found, keep the length + + if (event1.lenTick() != len) + { + if (undo_started==false) + { + song->startUndo(); + undo_started=true; + } + + Event new_event1 = event1.clone(); + new_event1.setLenTick(len); + + audio->msgChangeEvent(event1, new_event1, part1, false, false, false); + } + } + + if (undo_started) song->endUndo(SC_EVENT_MODIFIED); + } +} + void read_function_dialog_config(Xml& xml) @@ -574,6 +640,8 @@ void read_function_dialog_config(Xml& xml) transpose_dialog->read_configuration(xml); else if (tag == "crescendo") crescendo_dialog->read_configuration(xml); + else if (tag == "legato") + legato_dialog->read_configuration(xml); else xml.unknown("function_dialogs"); break; @@ -601,6 +669,7 @@ void write_function_dialog_config(int level, Xml& xml) move_notes_dialog->write_configuration(level, xml); transpose_dialog->write_configuration(level, xml); crescendo_dialog->write_configuration(level, xml); + legato_dialog->write_configuration(level, xml); xml.tag(level, "/dialogs"); } diff --git a/muse2/muse/functions.h b/muse2/muse/functions.h index 18f6e3ec..40e5f0e0 100644 --- a/muse2/muse/functions.h +++ b/muse2/muse/functions.h @@ -17,6 +17,7 @@ #include "widgets/function_dialogs/setlen.h" #include "widgets/function_dialogs/move.h" #include "widgets/function_dialogs/deloverlaps.h" +#include "widgets/function_dialogs/legato.h" #include <set> #include "part.h" @@ -31,6 +32,7 @@ extern Setlen* set_notelen_dialog; extern Move* move_notes_dialog; extern Transpose* transpose_dialog; extern Crescendo* crescendo_dialog; +extern Legato* legato_dialog; void init_function_dialogs(QWidget* parent); @@ -49,6 +51,7 @@ void set_notelen(const std::set<Part*>& parts, int range, int len); void move_notes(const std::set<Part*>& parts, int range, signed int ticks); void transpose_notes(const std::set<Part*>& parts, int range, signed int halftonesteps); void crescendo(const std::set<Part*>& parts, int range, int start_val, int end_val, bool absolute); +void legato(const std::set<Part*>& parts, int range, int min_len=1, bool dont_shorten=false); //the below functions automatically open the dialog @@ -62,6 +65,7 @@ bool transpose_notes(const std::set<Part*>& parts); bool crescendo(const std::set<Part*>& parts); bool erase_notes(const std::set<Part*>& parts); bool delete_overlaps(const std::set<Part*>& parts); +bool legato(const std::set<Part*>& parts); diff --git a/muse2/muse/midiedit/pianoroll.cpp b/muse2/muse/midiedit/pianoroll.cpp index 9105a446..ab83e85f 100644 --- a/muse2/muse/midiedit/pianoroll.cpp +++ b/muse2/muse/midiedit/pianoroll.cpp @@ -198,11 +198,11 @@ PianoRoll::PianoRoll(PartList* pl, QWidget* parent, const char* name, unsigned i mapper->setMapping(funcTransposeAction, PianoCanvas::CMD_TRANSPOSE); connect(funcTransposeAction, SIGNAL(triggered()), mapper, SLOT(map())); - funcEraseEventAction = menuFunctions->addAction(tr("Erase Event")); + funcEraseEventAction = menuFunctions->addAction(tr("Erase Events")); mapper->setMapping(funcEraseEventAction, PianoCanvas::CMD_ERASE_EVENT); connect(funcEraseEventAction, SIGNAL(triggered()), mapper, SLOT(map())); - funcNoteShiftAction = menuFunctions->addAction(tr("Note Shift")); + funcNoteShiftAction = menuFunctions->addAction(tr("Move Notes")); mapper->setMapping(funcNoteShiftAction, PianoCanvas::CMD_NOTE_SHIFT); connect(funcNoteShiftAction, SIGNAL(triggered()), mapper, SLOT(map())); @@ -213,7 +213,12 @@ PianoRoll::PianoRoll(PartList* pl, QWidget* parent, const char* name, unsigned i funcDelOverlapsAction = menuFunctions->addAction(tr("Delete Overlaps")); mapper->setMapping(funcDelOverlapsAction, PianoCanvas::CMD_DELETE_OVERLAPS); connect(funcDelOverlapsAction, SIGNAL(triggered()), mapper, SLOT(map())); - + + QAction* funcLegatoAction = menuFunctions->addAction(tr("Legato")); + mapper->setMapping(funcLegatoAction, PianoCanvas::CMD_LEGATO); + connect(funcLegatoAction, SIGNAL(triggered()), mapper, SLOT(map())); + + menuPlugins = menuBar()->addMenu(tr("&Plugins")); song->populateScriptMenu(menuPlugins, this); @@ -610,6 +615,7 @@ void PianoRoll::cmd(int cmd) case PianoCanvas::CMD_NOTE_SHIFT: move_notes(partlist_to_set(parts())); break; case PianoCanvas::CMD_FIXED_LEN: set_notelen(partlist_to_set(parts())); break; case PianoCanvas::CMD_DELETE_OVERLAPS: delete_overlaps(partlist_to_set(parts())); break; + case PianoCanvas::CMD_LEGATO: legato(partlist_to_set(parts())); break; default: ((PianoCanvas*)canvas)->cmd(cmd); } diff --git a/muse2/muse/midiedit/prcanvas.h b/muse2/muse/midiedit/prcanvas.h index a04ca514..3ca9ab52 100644 --- a/muse2/muse/midiedit/prcanvas.h +++ b/muse2/muse/midiedit/prcanvas.h @@ -98,7 +98,7 @@ class PianoCanvas : public EventCanvas { CMD_TRANSPOSE, CMD_THIN_OUT, CMD_ERASE_EVENT, CMD_NOTE_SHIFT, CMD_MOVE_CLOCK, CMD_COPY_MEASURE, CMD_ERASE_MEASURE, CMD_DELETE_MEASURE, CMD_CREATE_MEASURE, - CMD_FIXED_LEN, CMD_DELETE_OVERLAPS + CMD_FIXED_LEN, CMD_DELETE_OVERLAPS, CMD_LEGATO }; PianoCanvas(MidiEditor*, QWidget*, int, int); diff --git a/muse2/muse/midiedit/scoreedit.cpp b/muse2/muse/midiedit/scoreedit.cpp index 2636d4f2..0019de91 100644 --- a/muse2/muse/midiedit/scoreedit.cpp +++ b/muse2/muse/midiedit/scoreedit.cpp @@ -365,10 +365,23 @@ ScoreEdit::ScoreEdit(QWidget* parent, const char* name, unsigned initPos) QAction* func_notelen_action = functions_menu->addAction(tr("Change note &length"), menu_mapper, SLOT(map())); QAction* func_velocity_action = functions_menu->addAction(tr("Change note &velocity"), menu_mapper, SLOT(map())); QAction* func_cresc_action = functions_menu->addAction(tr("Crescendo/Decrescendo"), menu_mapper, SLOT(map())); + QAction* func_transpose_action = functions_menu->addAction(tr("Transpose"), menu_mapper, SLOT(map())); + QAction* func_erase_action = functions_menu->addAction(tr("Erase Events"), menu_mapper, SLOT(map())); + QAction* func_move_action = functions_menu->addAction(tr("Move Notes"), menu_mapper, SLOT(map())); + QAction* func_fixed_len_action = functions_menu->addAction(tr("Set Fixed Length"), menu_mapper, SLOT(map())); + QAction* func_del_overlaps_action = functions_menu->addAction(tr("Delete Overlaps"), menu_mapper, SLOT(map())); + QAction* func_legato_action = functions_menu->addAction(tr("Legato"), menu_mapper, SLOT(map())); menu_mapper->setMapping(func_quantize_action, CMD_QUANTIZE); menu_mapper->setMapping(func_notelen_action, CMD_NOTELEN); menu_mapper->setMapping(func_velocity_action, CMD_VELOCITY); menu_mapper->setMapping(func_cresc_action, CMD_CRESCENDO); + menu_mapper->setMapping(func_transpose_action, CMD_TRANSPOSE); + menu_mapper->setMapping(func_erase_action, CMD_ERASE); + menu_mapper->setMapping(func_move_action, CMD_MOVE); + menu_mapper->setMapping(func_fixed_len_action, CMD_FIXED_LEN); + menu_mapper->setMapping(func_del_overlaps_action, CMD_DELETE_OVERLAPS); + menu_mapper->setMapping(func_legato_action, CMD_LEGATO); + if (!default_toolbar_state.isEmpty()) restoreState(default_toolbar_state); @@ -572,7 +585,13 @@ void ScoreEdit::menu_command(int cmd) case CMD_VELOCITY: modify_velocity(score_canvas->get_all_parts()); break; case CMD_CRESCENDO: crescendo(score_canvas->get_all_parts()); break; case CMD_NOTELEN: modify_notelen(score_canvas->get_all_parts()); break; - + case CMD_TRANSPOSE: transpose_notes(score_canvas->get_all_parts()); break; + case CMD_ERASE: erase_notes(score_canvas->get_all_parts()); break; + case CMD_MOVE: move_notes(score_canvas->get_all_parts()); break; + case CMD_FIXED_LEN: set_notelen(score_canvas->get_all_parts()); break; + case CMD_DELETE_OVERLAPS: delete_overlaps(score_canvas->get_all_parts()); break; + case CMD_LEGATO: legato(score_canvas->get_all_parts()); break; + default: score_canvas->menu_command(cmd); } @@ -4261,7 +4280,9 @@ void staff_t::apply_lasso(QRect rect, set<Event*>& already_processed) * between, for example, when a cis is tied to a des * * CURRENT TODO - * o legato: extend length to next note + * > o legato: extend length to next note + * o add music-keyboard-bindings for "insert rest" and "increase note length" + * o maybe support step-recording in score editor as well? * * IMPORTANT TODO * o do partial recalculating; recalculating can take pretty long diff --git a/muse2/muse/midiedit/scoreedit.h b/muse2/muse/midiedit/scoreedit.h index f587483a..2ce8d645 100644 --- a/muse2/muse/midiedit/scoreedit.h +++ b/muse2/muse/midiedit/scoreedit.h @@ -58,7 +58,8 @@ enum {CMD_COLOR_BLACK, CMD_COLOR_VELO, CMD_COLOR_PART, CMD_NOTELEN_1, CMD_NOTELEN_2, CMD_NOTELEN_4, CMD_NOTELEN_8, CMD_NOTELEN_16, CMD_NOTELEN_32, CMD_NOTELEN_LAST, - CMD_QUANTIZE, CMD_VELOCITY, CMD_CRESCENDO, CMD_NOTELEN }; + CMD_QUANTIZE, CMD_VELOCITY, CMD_CRESCENDO, CMD_NOTELEN, CMD_TRANSPOSE, + CMD_ERASE, CMD_MOVE, CMD_FIXED_LEN, CMD_DELETE_OVERLAPS, CMD_LEGATO }; class ScoreCanvas; diff --git a/muse2/muse/widgets/function_dialogs/CMakeLists.txt b/muse2/muse/widgets/function_dialogs/CMakeLists.txt index 7ddc6bee..db1f3229 100644 --- a/muse2/muse/widgets/function_dialogs/CMakeLists.txt +++ b/muse2/muse/widgets/function_dialogs/CMakeLists.txt @@ -30,6 +30,7 @@ QT4_WRAP_CPP (widgets_functiondialogs_mocs remove.h setlen.h transpose.h + legato.h velocity.h ) @@ -45,6 +46,7 @@ file (GLOB widgets_functiondialogs_ui_files removebase.ui setlenbase.ui transposebase.ui + legatobase.ui velocitybase.ui ) @@ -62,6 +64,7 @@ file (GLOB widgets_functiondialogs_source_files remove.cpp setlen.cpp transpose.cpp + legato.cpp velocity.cpp ) diff --git a/muse2/muse/widgets/function_dialogs/legato.cpp b/muse2/muse/widgets/function_dialogs/legato.cpp new file mode 100644 index 00000000..0a181106 --- /dev/null +++ b/muse2/muse/widgets/function_dialogs/legato.cpp @@ -0,0 +1,88 @@ +//========================================================= +// MusE +// Linux Music Editor +// $Id: legato.cpp,v 1.1.1.1 2011/05/05 18:51:04 flo93 Exp $ +// (C) Copyright 2011 Florian Jung (flo93@sourceforge.net) +//========================================================= + +#include <QButtonGroup> +#include "legato.h" +#include "xml.h" + +Legato::Legato(QWidget* parent) + : QDialog(parent) +{ + setupUi(this); + range_group = new QButtonGroup; + range_group->addButton(all_events_button,0); + range_group->addButton(selected_events_button,1); + range_group->addButton(looped_events_button,2); + range_group->addButton(selected_looped_button,3); + + pull_values(); +} + +void Legato::pull_values() +{ + range = range_group->checkedId(); + min_len = len_spinbox->value(); + allow_shortening = allow_shorten_checkbox->isChecked(); +} + +void Legato::accept() +{ + pull_values(); + QDialog::accept(); +} + +int Legato::exec() +{ + if ((range < 0) || (range > 3)) range=0; + + range_group->button(range)->setChecked(true); + len_spinbox->setValue(min_len); + allow_shorten_checkbox->setChecked(allow_shortening); + + return QDialog::exec(); +} + +void Legato::read_configuration(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 == "range") + range=xml.parseInt(); + else if (tag == "min_len") + min_len=xml.parseInt(); + else if (tag == "allow_shortening") + allow_shortening=xml.parseInt(); + else + xml.unknown("Legato"); + break; + + case Xml::TagEnd: + if (tag == "legato") + return; + + default: + break; + } + } +} + +void Legato::write_configuration(int level, Xml& xml) +{ + xml.tag(level++, "legato"); + xml.intTag(level, "range", range); + xml.intTag(level, "min_len", min_len); + xml.intTag(level, "allow_shortening", allow_shortening); + xml.tag(level, "/legato"); +} diff --git a/muse2/muse/widgets/function_dialogs/legato.h b/muse2/muse/widgets/function_dialogs/legato.h new file mode 100644 index 00000000..80b371ca --- /dev/null +++ b/muse2/muse/widgets/function_dialogs/legato.h @@ -0,0 +1,42 @@ +//========================================================= +// MusE +// Linux Music Editor +// $Id: legato.h,v 1.1.1.1 2011/05/05 18:51:04 flo93 Exp $ +// (C) Copyright 2011 Florian Jung (flo93@sourceforge.net) +//========================================================= + +#ifndef __LEGATO_H__ +#define __LEGATO_H__ + +#include "ui_legatobase.h" + +class QButtonGroup; +class Xml; + +class Legato : public QDialog, public Ui::LegatoBase +{ + private: + Q_OBJECT + QButtonGroup* range_group; + + protected slots: + void accept(); + void pull_values(); + + public: + Legato(QWidget* parent = 0); + + int range; + int min_len; + bool allow_shortening; + + void read_configuration(Xml& xml); + void write_configuration(int level, Xml& xml); + + + public slots: + int exec(); +}; + +#endif + diff --git a/muse2/muse/widgets/function_dialogs/legatobase.ui b/muse2/muse/widgets/function_dialogs/legatobase.ui new file mode 100644 index 00000000..7bc406df --- /dev/null +++ b/muse2/muse/widgets/function_dialogs/legatobase.ui @@ -0,0 +1,233 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>LegatoBase</class> + <widget class="QDialog" name="LegatoBase"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>275</width> + <height>289</height> + </rect> + </property> + <property name="windowTitle"> + <string>MusE: Legato</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <property name="spacing"> + <number>6</number> + </property> + <property name="margin"> + <number>11</number> + </property> + <item> + <widget class="QGroupBox" name="rangeBox"> + <property name="title"> + <string>Range</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <property name="spacing"> + <number>6</number> + </property> + <property name="margin"> + <number>11</number> + </property> + <item> + <widget class="QRadioButton" name="all_events_button"> + <property name="text"> + <string>All Events</string> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="selected_events_button"> + <property name="text"> + <string>Selected Events</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="looped_events_button"> + <property name="text"> + <string>Looped Events</string> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="selected_looped_button"> + <property name="text"> + <string>Selected Looped</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_2"> + <property name="title"> + <string>Settings</string> + </property> + <property name="flat"> + <bool>false</bool> + </property> + <property name="checkable"> + <bool>false</bool> + </property> + <layout class="QGridLayout" name="gridLayout"> + <property name="margin"> + <number>11</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <item row="0" column="1"> + <widget class="QSpinBox" name="len_spinbox"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="accelerated"> + <bool>true</bool> + </property> + <property name="suffix"> + <string> ticks</string> + </property> + <property name="minimum"> + <number>0</number> + </property> + <property name="maximum"> + <number>10000</number> + </property> + <property name="singleStep"> + <number>1</number> + </property> + <property name="value"> + <number>0</number> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Minimum Length</string> + </property> + </widget> + </item> + <item row="1" column="0" colspan="2"> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <widget class="QLabel" name="label"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Allow shortening notes</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="allow_shorten_checkbox"> + <property name="layoutDirection"> + <enum>Qt::RightToLeft</enum> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <property name="spacing"> + <number>6</number> + </property> + <item> + <spacer name="Spacer1"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Expanding</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="okButton"> + <property name="text"> + <string>OK</string> + </property> + <property name="autoDefault"> + <bool>false</bool> + </property> + <property name="default"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="cancelButton"> + <property name="text"> + <string>Cancel</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <layoutdefault spacing="6" margin="11"/> + <resources/> + <connections> + <connection> + <sender>okButton</sender> + <signal>clicked()</signal> + <receiver>LegatoBase</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel"> + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + <connection> + <sender>cancelButton</sender> + <signal>clicked()</signal> + <receiver>LegatoBase</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>20</x> + <y>20</y> + </hint> + <hint type="destinationlabel"> + <x>20</x> + <y>20</y> + </hint> + </hints> + </connection> + </connections> +</ui> |