summaryrefslogtreecommitdiff
path: root/muse2/muse/functions.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'muse2/muse/functions.cpp')
-rw-r--r--muse2/muse/functions.cpp605
1 files changed, 449 insertions, 156 deletions
diff --git a/muse2/muse/functions.cpp b/muse2/muse/functions.cpp
index b178bcb6..8e9a7cd5 100644
--- a/muse2/muse/functions.cpp
+++ b/muse2/muse/functions.cpp
@@ -23,11 +23,24 @@
#include "functions.h"
#include "song.h"
#include "undo.h"
+#include "helper.h"
#include "event.h"
#include "audio.h"
#include "gconfig.h"
+#include "widgets/function_dialogs/velocity.h"
+#include "widgets/function_dialogs/quantize.h"
+#include "widgets/function_dialogs/crescendo.h"
+#include "widgets/function_dialogs/gatetime.h"
+#include "widgets/function_dialogs/remove.h"
+#include "widgets/function_dialogs/transpose.h"
+#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 "widgets/pasteeventsdialog.h"
+
#include <values.h>
#include <iostream>
#include <errno.h>
@@ -43,32 +56,20 @@
#include <QMessageBox>
#include <QClipboard>
+
+
using namespace std;
-MusEDialog::GateTime* gatetime_dialog=NULL;
-MusEDialog::Velocity* velocity_dialog=NULL;
-MusEDialog::Quantize* quantize_dialog=NULL;
-MusEDialog::Remove* erase_dialog=NULL;
-MusEDialog::DelOverlaps* del_overlaps_dialog=NULL;
-MusEDialog::Setlen* set_notelen_dialog=NULL;
-MusEDialog::Move* move_notes_dialog=NULL;
-MusEDialog::Transpose* transpose_dialog=NULL;
-MusEDialog::Crescendo* crescendo_dialog=NULL;
-MusEDialog::Legato* legato_dialog=NULL;
-
-void init_function_dialogs(QWidget* parent)
-{
- gatetime_dialog = new MusEDialog::GateTime(parent);
- velocity_dialog = new MusEDialog::Velocity(parent);
- quantize_dialog = new MusEDialog::Quantize(parent);
- erase_dialog = new MusEDialog::Remove(parent);
- del_overlaps_dialog = new MusEDialog::DelOverlaps(parent);
- set_notelen_dialog = new MusEDialog::Setlen(parent);
- move_notes_dialog = new MusEDialog::Move(parent);
- transpose_dialog = new MusEDialog::Transpose(parent);
- crescendo_dialog = new MusEDialog::Crescendo(parent);
- legato_dialog = new MusEDialog::Legato(parent);
-}
+using MusEConfig::config;
+
+
+// unit private functions:
+
+bool read_eventlist_and_part(Xml& xml, EventList* el, int* part_id);
+
+// -----------------------
+
+
set<Part*> partlist_to_set(PartList* pl)
{
@@ -87,6 +88,37 @@ set<Part*> part_to_set(Part* p)
return result;
}
+set<Part*> get_all_parts()
+{
+ set<Part*> result;
+
+ TrackList* tracks=song->tracks();
+ for (TrackList::const_iterator t_it=tracks->begin(); t_it!=tracks->end(); t_it++)
+ {
+ const PartList* parts=(*t_it)->cparts();
+ for (ciPart p_it=parts->begin(); p_it!=parts->end(); p_it++)
+ result.insert(p_it->second);
+ }
+
+ return result;
+}
+
+set<Part*> get_all_selected_parts()
+{
+ set<Part*> result;
+
+ TrackList* tracks=song->tracks();
+ for (TrackList::const_iterator t_it=tracks->begin(); t_it!=tracks->end(); t_it++)
+ {
+ const PartList* parts=(*t_it)->cparts();
+ for (ciPart p_it=parts->begin(); p_it!=parts->end(); p_it++)
+ if (p_it->second->selected())
+ result.insert(p_it->second);
+ }
+
+ return result;
+}
+
bool is_relevant(const Event& event, const Part* part, int range)
{
unsigned tick;
@@ -118,6 +150,8 @@ map<Event*, Part*> get_events(const set<Part*>& parts, int range)
}
+
+
bool modify_notelen(const set<Part*>& parts)
{
if (!gatetime_dialog->exec())
@@ -229,6 +263,180 @@ bool legato(const set<Part*>& parts)
+bool modify_notelen()
+{
+ if (!gatetime_dialog->exec())
+ return false;
+
+ set<Part*> parts;
+ if (gatetime_dialog->range & FUNCTION_RANGE_ONLY_SELECTED)
+ parts=get_all_selected_parts();
+ else
+ parts=get_all_parts();
+
+ modify_notelen(parts,gatetime_dialog->range & FUNCTION_RANGE_ONLY_BETWEEN_MARKERS, gatetime_dialog->rateVal,gatetime_dialog->offsetVal);
+
+ return true;
+}
+
+bool modify_velocity()
+{
+ if (!velocity_dialog->exec())
+ return false;
+
+ set<Part*> parts;
+ if (velocity_dialog->range & FUNCTION_RANGE_ONLY_SELECTED)
+ parts=get_all_selected_parts();
+ else
+ parts=get_all_parts();
+
+ modify_velocity(parts,velocity_dialog->range & FUNCTION_RANGE_ONLY_BETWEEN_MARKERS,velocity_dialog->rateVal,velocity_dialog->offsetVal);
+
+ return true;
+}
+
+bool quantize_notes()
+{
+ if (!quantize_dialog->exec())
+ return false;
+
+ set<Part*> parts;
+ if (quantize_dialog->range & FUNCTION_RANGE_ONLY_SELECTED)
+ parts=get_all_selected_parts();
+ else
+ parts=get_all_parts();
+
+ quantize_notes(parts, quantize_dialog->range & FUNCTION_RANGE_ONLY_BETWEEN_MARKERS, (config.division*4)/(1<<quantize_dialog->raster_power2),
+ quantize_dialog->quant_len, quantize_dialog->strength, quantize_dialog->swing,
+ quantize_dialog->threshold);
+
+ return true;
+}
+
+bool erase_notes()
+{
+ if (!erase_dialog->exec())
+ return false;
+
+ set<Part*> parts;
+ if (erase_dialog->range & FUNCTION_RANGE_ONLY_SELECTED)
+ parts=get_all_selected_parts();
+ else
+ parts=get_all_parts();
+
+ erase_notes(parts,erase_dialog->range & FUNCTION_RANGE_ONLY_BETWEEN_MARKERS, erase_dialog->velo_threshold, erase_dialog->velo_thres_used,
+ erase_dialog->len_threshold, erase_dialog->len_thres_used );
+
+ return true;
+}
+
+bool delete_overlaps()
+{
+ if (!del_overlaps_dialog->exec())
+ return false;
+
+ set<Part*> parts;
+ if (del_overlaps_dialog->range & FUNCTION_RANGE_ONLY_SELECTED)
+ parts=get_all_selected_parts();
+ else
+ parts=get_all_parts();
+
+ delete_overlaps(parts,erase_dialog->range & FUNCTION_RANGE_ONLY_BETWEEN_MARKERS);
+
+ return true;
+}
+
+bool set_notelen()
+{
+ if (!set_notelen_dialog->exec())
+ return false;
+
+ set<Part*> parts;
+ if (set_notelen_dialog->range & FUNCTION_RANGE_ONLY_SELECTED)
+ parts=get_all_selected_parts();
+ else
+ parts=get_all_parts();
+
+ set_notelen(parts,set_notelen_dialog->range & FUNCTION_RANGE_ONLY_BETWEEN_MARKERS, set_notelen_dialog->len);
+
+ return true;
+}
+
+bool move_notes()
+{
+ if (!move_notes_dialog->exec())
+ return false;
+
+ set<Part*> parts;
+ if (move_notes_dialog->range & FUNCTION_RANGE_ONLY_SELECTED)
+ parts=get_all_selected_parts();
+ else
+ parts=get_all_parts();
+
+ move_notes(parts,move_notes_dialog->range & FUNCTION_RANGE_ONLY_BETWEEN_MARKERS, move_notes_dialog->amount);
+
+ return true;
+}
+
+bool transpose_notes()
+{
+ if (!transpose_dialog->exec())
+ return false;
+
+ set<Part*> parts;
+ if (transpose_dialog->range & FUNCTION_RANGE_ONLY_SELECTED)
+ parts=get_all_selected_parts();
+ else
+ parts=get_all_parts();
+
+ transpose_notes(parts,transpose_dialog->range & FUNCTION_RANGE_ONLY_BETWEEN_MARKERS, transpose_dialog->amount);
+
+ return true;
+}
+
+bool crescendo()
+{
+ if (song->rpos() <= song->lpos())
+ {
+ QMessageBox::warning(NULL, QObject::tr("Error"), QObject::tr("Please first select the range for crescendo with the loop markers."));
+ return false;
+ }
+
+ if (!crescendo_dialog->exec())
+ return false;
+
+ set<Part*> parts;
+ if (crescendo_dialog->range & FUNCTION_RANGE_ONLY_SELECTED)
+ parts=get_all_selected_parts();
+ else
+ parts=get_all_parts();
+
+ crescendo(parts,crescendo_dialog->range & FUNCTION_RANGE_ONLY_BETWEEN_MARKERS, crescendo_dialog->start_val,crescendo_dialog->end_val,crescendo_dialog->absolute);
+
+ return true;
+}
+
+bool legato()
+{
+ if (!legato_dialog->exec())
+ return false;
+
+ set<Part*> parts;
+ if (legato_dialog->range & FUNCTION_RANGE_ONLY_SELECTED)
+ parts=get_all_selected_parts();
+ else
+ parts=get_all_parts();
+
+ legato(parts,legato_dialog->range & FUNCTION_RANGE_ONLY_BETWEEN_MARKERS, legato_dialog->min_len, !legato_dialog->allow_shortening);
+
+ return true;
+}
+
+
+
+
+
+
bool modify_velocity(const set<Part*>& parts, int range, int rate, int offset)
{
map<Event*, Part*> events = get_events(parts, range);
@@ -667,34 +875,93 @@ void copy_notes(const set<Part*>& parts, int range)
QApplication::clipboard()->setMimeData(drag, QClipboard::Clipboard);
}
-void paste_notes(Part* dest_part)
+unsigned get_groupedevents_len(const QString& pt)
{
- QString tmp="x-muse-eventlist"; // QClipboard::text() expects a QString&, not a QString :(
- QString s = QApplication::clipboard()->text(tmp, QClipboard::Clipboard); // TODO CHECK Tim.
- paste_at(dest_part, s, song->cpos());
+ unsigned maxlen=0;
+
+ Xml xml(pt.toLatin1().constData());
+ for (;;)
+ {
+ Xml::Token token = xml.parse();
+ const QString& tag = xml.s1();
+ switch (token)
+ {
+ case Xml::Error:
+ case Xml::End:
+ return maxlen;
+
+ case Xml::TagStart:
+ if (tag == "eventlist")
+ {
+ EventList el;
+ int part_id;
+ if (read_eventlist_and_part(xml, &el, &part_id))
+ {
+ unsigned len = el.rbegin()->first;
+ if (len > maxlen) maxlen=len;
+ }
+ }
+ else
+ xml.unknown("get_clipboard_len");
+ break;
+
+ case Xml::Attribut:
+ case Xml::TagEnd:
+ default:
+ break;
+ }
+ }
+
+ return maxlen; // see also the return statement above!
}
-QMimeData* selected_events_to_mime(const set<Part*>& parts, int range)
+unsigned get_clipboard_len()
{
- map<Event*, Part*> events=get_events(parts,range);
+ QString tmp="x-muse-groupedeventlists"; // QClipboard::text() expects a QString&, not a QString :(
+ QString s = QApplication::clipboard()->text(tmp, QClipboard::Clipboard); // TODO CHECK Tim.
+
+ return get_groupedevents_len(s);
+}
- //---------------------------------------------------
- // generate event list from selected events
- //---------------------------------------------------
+bool paste_notes(Part* paste_into_part)
+{
+ unsigned temp_begin = AL::sigmap.raster1(song->cpos(),0);
+ unsigned temp_end = AL::sigmap.raster2(temp_begin + get_clipboard_len(), 0);
+ paste_events_dialog->raster = temp_end - temp_begin;
+ paste_events_dialog->into_single_part_allowed = (paste_into_part!=NULL);
+
+ if (!paste_events_dialog->exec())
+ return false;
+
+ paste_notes(paste_events_dialog->max_distance, paste_events_dialog->always_new_part,
+ paste_events_dialog->never_new_part, paste_events_dialog->into_single_part ? paste_into_part : NULL,
+ paste_events_dialog->number, paste_events_dialog->raster);
+
+ return true;
+}
- EventList el;
- unsigned startTick = MAXINT; //will be the tick of the first event or MAXINT if no events are there
+void paste_notes(int max_distance, bool always_new_part, bool never_new_part, Part* paste_into_part, int amount, int raster)
+{
+ QString tmp="x-muse-groupedeventlists"; // QClipboard::text() expects a QString&, not a QString :(
+ QString s = QApplication::clipboard()->text(tmp, QClipboard::Clipboard); // TODO CHECK Tim.
+ paste_at(s, song->cpos(), max_distance, always_new_part, never_new_part, paste_into_part, amount, raster);
+}
- for (map<Event*, Part*>::iterator it=events.begin(); it!=events.end(); it++)
- {
- Event& e = *it->first;
-
- if (e.tick() < startTick)
- startTick = e.tick();
-
- el.add(e);
- }
+// if nothing is selected/relevant, this function returns NULL
+QMimeData* selected_events_to_mime(const set<Part*>& parts, int range)
+{
+ unsigned start_tick = MAXINT; //will be the tick of the first event or MAXINT if no events are there
+
+ for (set<Part*>::iterator part=parts.begin(); part!=parts.end(); part++)
+ for (iEvent ev=(*part)->events()->begin(); ev!=(*part)->events()->end(); ev++)
+ if (is_relevant(ev->second, *part, range))
+ if (ev->second.tick() < start_tick)
+ start_tick=ev->second.tick();
+
+ if (start_tick == MAXINT)
+ return NULL;
+
//---------------------------------------------------
// write events as XML into tmp file
//---------------------------------------------------
@@ -709,10 +976,14 @@ QMimeData* selected_events_to_mime(const set<Part*>& parts, int range)
Xml xml(tmp);
int level = 0;
- xml.tag(level++, "eventlist");
- for (ciEvent e = el.begin(); e != el.end(); ++e)
- e->second.write(level, xml, -startTick);
- xml.etag(--level, "eventlist");
+ for (set<Part*>::iterator part=parts.begin(); part!=parts.end(); part++)
+ {
+ xml.tag(level++, "eventlist part_id=\"%d\"", (*part)->sn());
+ for (iEvent ev=(*part)->events()->begin(); ev!=(*part)->events()->end(); ev++)
+ if (is_relevant(ev->second, *part, range))
+ ev->second.write(level, xml, -start_tick);
+ xml.etag(--level, "eventlist");
+ }
//---------------------------------------------------
// read tmp file into drag Object
@@ -722,7 +993,7 @@ QMimeData* selected_events_to_mime(const set<Part*>& parts, int range)
struct stat f_stat;
if (fstat(fileno(tmp), &f_stat) == -1)
{
- fprintf(stderr, "PianoCanvas::copy() fstat failed:<%s>\n",
+ fprintf(stderr, "copy_notes() fstat failed:<%s>\n",
strerror(errno));
fclose(tmp);
return 0;
@@ -735,7 +1006,7 @@ QMimeData* selected_events_to_mime(const set<Part*>& parts, int range)
QByteArray data(fbuf);
QMimeData* md = new QMimeData();
- md->setData("text/x-muse-eventlist", data);
+ md->setData("text/x-muse-groupedeventlists", data);
munmap(fbuf, n);
fclose(tmp);
@@ -743,10 +1014,53 @@ QMimeData* selected_events_to_mime(const set<Part*>& parts, int range)
return md;
}
-void paste_at(Part* dest_part, const QString& pt, int pos)
+bool read_eventlist_and_part(Xml& xml, EventList* el, int* part_id) // true on success, false on failure
+{
+ *part_id = -1;
+
+ for (;;)
+ {
+ Xml::Token token = xml.parse();
+ const QString& tag = xml.s1();
+ switch (token)
+ {
+ case Xml::Error:
+ case Xml::End:
+ return false;
+
+ case Xml::Attribut:
+ if (tag == "part_id")
+ *part_id = xml.s2().toInt();
+ else
+ printf("unknown attribute '%s' in read_eventlist_and_part(), ignoring it...\n", tag.toAscii().data());
+ break;
+
+ case Xml::TagStart:
+ if (tag == "event")
+ {
+ Event e(Note);
+ e.read(xml);
+ el->add(e);
+ }
+ else
+ xml.unknown("read_eventlist_and_part");
+ break;
+
+ case Xml::TagEnd:
+ if (tag == "eventlist")
+ return true;
+
+ default:
+ break;
+ }
+ }
+}
+
+void paste_at(const QString& pt, int pos, int max_distance, bool always_new_part, bool never_new_part, Part* paste_into_part, int amount, int raster)
{
Undo operations;
- unsigned newpartlen=dest_part->lenTick();
+ map<Part*, unsigned> expand_map;
+ map<Part*, set<Part*> > new_part_map;
Xml xml(pt.toLatin1().constData());
for (;;)
@@ -757,53 +1071,95 @@ void paste_at(Part* dest_part, const QString& pt, int pos)
{
case Xml::Error:
case Xml::End:
- goto end_of_paste_at;
+ goto out_of_paste_at_for;
case Xml::TagStart:
if (tag == "eventlist")
{
EventList el;
- el.read(xml, "eventlist", true);
- for (iEvent i = el.begin(); i != el.end(); ++i)
+ int part_id;
+
+ if (read_eventlist_and_part(xml, &el, &part_id))
{
- Event e = i->second;
- int tick = e.tick() + pos - dest_part->tick();
- if (tick<0)
+ Part* dest_part;
+ Track* dest_track;
+ Part* old_dest_part;
+
+ if (paste_into_part == NULL)
+ dest_part = MusEUtil::partFromSerialNumber(part_id);
+ else
+ dest_part=paste_into_part;
+
+ if (dest_part == NULL)
{
- printf("ERROR: trying to add event before current part!\n");
- goto end_of_paste_at;
+ printf("ERROR: destination part wasn't found. ignoring these events\n");
}
-
- e.setTick(tick);
- e.setSelected(true);
-
- if (e.endTick() > dest_part->lenTick()) // event exceeds part?
+ else
{
- if (dest_part->hasHiddenEvents()) // auto-expanding is forbidden?
- {
- if (e.tick() < dest_part->lenTick())
- e.setLenTick(dest_part->lenTick() - e.tick()); // clip
- else
- e.setLenTick(0); // don't insert that note at all
- }
- else
+ dest_track=dest_part->track();
+ old_dest_part=dest_part;
+ unsigned first_paste_tick = el.begin()->first + pos;
+ bool create_new_part = ( (dest_part->tick() > first_paste_tick) || // dest_part begins too late
+ ( ( (dest_part->endTick() + max_distance < first_paste_tick) || // dest_part is too far away
+ always_new_part ) && !never_new_part ) ); // respect function arguments
+
+ for (int i=0;i<amount;i++)
{
- if (e.endTick() > newpartlen)
- newpartlen=e.endTick();
+ unsigned curr_pos = pos + i*raster;
+ first_paste_tick = el.begin()->first + curr_pos;
+
+ if (create_new_part)
+ {
+ dest_part = dest_track->newPart();
+ dest_part->events()->incARef(-1); // the later song->applyOperationGroup() will increment it
+ // so we must decrement it first :/
+ dest_part->setTick(AL::sigmap.raster1(first_paste_tick, config.division));
+
+ new_part_map[old_dest_part].insert(dest_part);
+ operations.push_back(UndoOp(UndoOp::AddPart, dest_part));
+ }
+
+ for (iEvent i = el.begin(); i != el.end(); ++i)
+ {
+ Event e = i->second.clone();
+ int tick = e.tick() + curr_pos - dest_part->tick();
+ if (tick<0)
+ {
+ printf("ERROR: trying to add event before current part! ignoring this event\n");
+ continue;
+ }
+
+ e.setTick(tick);
+ e.setSelected(true);
+
+ if (e.endTick() > dest_part->lenTick()) // event exceeds part?
+ {
+ if (dest_part->hasHiddenEvents()) // auto-expanding is forbidden?
+ {
+ if (e.tick() < dest_part->lenTick())
+ e.setLenTick(dest_part->lenTick() - e.tick()); // clip
+ else
+ e.setLenTick(0); // don't insert that note at all
+ }
+ else
+ {
+ if (e.endTick() > expand_map[dest_part])
+ expand_map[dest_part]=e.endTick();
+ }
+ }
+
+ if (e.lenTick() != 0) operations.push_back(UndoOp(UndoOp::AddEvent,e, dest_part, false, false));
+ }
}
}
-
- if (e.lenTick() != 0) operations.push_back(UndoOp(UndoOp::AddEvent,e, dest_part, false, false));
}
-
- if (newpartlen != dest_part->lenTick())
- schedule_resize_all_same_len_clone_parts(dest_part, newpartlen, operations);
-
- song->applyOperationGroup(operations);
- goto end_of_paste_at;
+ else
+ {
+ printf("ERROR: reading eventlist from clipboard failed. ignoring this one...\n");
+ }
}
else
- xml.unknown("paste_at");
+ xml.unknown("paste_at");
break;
case Xml::Attribut:
@@ -813,7 +1169,15 @@ void paste_at(Part* dest_part, const QString& pt, int pos)
}
}
- end_of_paste_at:
+ out_of_paste_at_for:
+
+ for (map<Part*, unsigned>::iterator it = expand_map.begin(); it!=expand_map.end(); it++)
+ if (it->second != it->first->lenTick())
+ schedule_resize_all_same_len_clone_parts(it->first, it->second, operations);
+
+ song->informAboutNewParts(new_part_map); // must be called before apply. otherwise
+ // pointer changes (by resize) screw it up
+ song->applyOperationGroup(operations);
song->update(SC_SELECTION);
}
@@ -1026,74 +1390,3 @@ void clean_parts()
song->applyOperationGroup(operations);
}
-
-void read_function_dialog_config(Xml& xml)
-{
- if (erase_dialog==NULL)
- {
- cout << "ERROR: THIS SHOULD NEVER HAPPEN: read_function_dialog_config() called, but\n"
- " dialogs are still uninitalized (NULL)!"<<endl;
- return;
- }
-
- 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 == "mod_len")
- gatetime_dialog->read_configuration(xml);
- else if (tag == "mod_velo")
- velocity_dialog->read_configuration(xml);
- else if (tag == "quantize")
- quantize_dialog->read_configuration(xml);
- else if (tag == "erase")
- erase_dialog->read_configuration(xml);
- else if (tag == "del_overlaps")
- del_overlaps_dialog->read_configuration(xml);
- else if (tag == "setlen")
- set_notelen_dialog->read_configuration(xml);
- else if (tag == "move")
- move_notes_dialog->read_configuration(xml);
- else if (tag == "transpose")
- 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;
-
- case Xml::TagEnd:
- if (tag == "dialogs")
- return;
-
- default:
- break;
- }
- }
-}
-
-void write_function_dialog_config(int level, Xml& xml)
-{
- xml.tag(level++, "dialogs");
-
- gatetime_dialog->write_configuration(level, xml);
- velocity_dialog->write_configuration(level, xml);
- quantize_dialog->write_configuration(level, xml);
- erase_dialog->write_configuration(level, xml);
- del_overlaps_dialog->write_configuration(level, xml);
- set_notelen_dialog->write_configuration(level, 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");
-}